wifi-scripts: add multi-radio config support
Emit one wifi-device section per wiphy radio Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
let libubus = require("ubus");
|
||||
import { open, readfile } from "fs";
|
||||
import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common";
|
||||
import { wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open, wdev_set_radio_mask } from "common";
|
||||
|
||||
let ubus = libubus.connect(null, 60);
|
||||
|
||||
@@ -56,7 +56,7 @@ function iface_remove(cfg)
|
||||
wdev_remove(bss.ifname);
|
||||
}
|
||||
|
||||
function iface_gen_config(phy, config, start_disabled)
|
||||
function iface_gen_config(config, start_disabled)
|
||||
{
|
||||
let str = `data:
|
||||
${join("\n", config.radio.data)}
|
||||
@@ -117,7 +117,7 @@ function iface_freq_info(iface, config, params)
|
||||
|
||||
function iface_add(phy, config, phy_status)
|
||||
{
|
||||
let config_inline = iface_gen_config(phy, config, !!phy_status);
|
||||
let config_inline = iface_gen_config(config, !!phy_status);
|
||||
|
||||
let bss = config.bss[0];
|
||||
let ret = hostapd.add_iface(`bss_config=${phy}:${config_inline}`);
|
||||
@@ -148,12 +148,16 @@ function iface_config_macaddr_list(config)
|
||||
return macaddr_list;
|
||||
}
|
||||
|
||||
function iface_update_supplicant_macaddr(phy, config)
|
||||
function iface_update_supplicant_macaddr(phydev, config)
|
||||
{
|
||||
let macaddr_list = [];
|
||||
for (let i = 0; i < length(config.bss); i++)
|
||||
push(macaddr_list, config.bss[i].bssid);
|
||||
ubus.defer("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
|
||||
ubus.defer("wpa_supplicant", "phy_set_macaddr_list", {
|
||||
phy: phydev.name,
|
||||
radio: phydev.radio ?? -1,
|
||||
macaddr: macaddr_list
|
||||
});
|
||||
}
|
||||
|
||||
function __iface_pending_next(pending, state, ret, data)
|
||||
@@ -168,19 +172,22 @@ function __iface_pending_next(pending, state, ret, data)
|
||||
delete pending.defer;
|
||||
switch (state) {
|
||||
case "init":
|
||||
let macaddr_list = [];
|
||||
for (let i = 0; i < length(config.bss); i++)
|
||||
push(macaddr_list, config.bss[i].bssid);
|
||||
pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
|
||||
iface_update_supplicant_macaddr(phydev, config);
|
||||
return "create_bss";
|
||||
case "create_bss":
|
||||
let err = wdev_create(phy, bss.ifname, { mode: "ap" });
|
||||
let err = phydev.wdev_add(bss.ifname, {
|
||||
mode: "ap",
|
||||
radio: phydev.radio,
|
||||
});
|
||||
if (err) {
|
||||
hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
pending.call("wpa_supplicant", "phy_status", { phy: phy });
|
||||
pending.call("wpa_supplicant", "phy_status", {
|
||||
phy: phydev.phy,
|
||||
radio: phydev.radio,
|
||||
});
|
||||
return "check_phy";
|
||||
case "check_phy":
|
||||
let phy_status = data;
|
||||
@@ -190,12 +197,20 @@ function __iface_pending_next(pending, state, ret, data)
|
||||
|
||||
hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
|
||||
}
|
||||
pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
|
||||
pending.call("wpa_supplicant", "phy_set_state", {
|
||||
phy: phydev.phy,
|
||||
radio: phydev.radio,
|
||||
stop: true
|
||||
});
|
||||
return "wpas_stopped";
|
||||
case "wpas_stopped":
|
||||
if (!iface_add(phy, config))
|
||||
hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
|
||||
pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
|
||||
pending.call("wpa_supplicant", "phy_set_state", {
|
||||
phy: phydev.phy,
|
||||
radio: phydev.radio,
|
||||
stop: false
|
||||
});
|
||||
return null;
|
||||
case "done":
|
||||
default:
|
||||
@@ -210,9 +225,14 @@ function iface_pending_next(ret, data)
|
||||
let cfg = this;
|
||||
|
||||
while (pending) {
|
||||
this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
|
||||
if (!this.next_state) {
|
||||
__iface_pending_next(cfg, "done");
|
||||
try {
|
||||
this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
|
||||
if (!this.next_state) {
|
||||
__iface_pending_next(cfg, "done");
|
||||
return;
|
||||
}
|
||||
} catch(e) {
|
||||
hostapd.printf(`Exception: ${e}\n${e.stacktrace[0].context}`);
|
||||
return;
|
||||
}
|
||||
pending = !this.defer;
|
||||
@@ -398,7 +418,7 @@ function get_config_bss(config, idx)
|
||||
return hostapd.bss[ifname];
|
||||
}
|
||||
|
||||
function iface_reload_config(phydev, config, old_config)
|
||||
function iface_reload_config(name, phydev, config, old_config)
|
||||
{
|
||||
let phy = phydev.name;
|
||||
|
||||
@@ -408,13 +428,13 @@ function iface_reload_config(phydev, config, old_config)
|
||||
if (is_equal(old_config.bss, config.bss))
|
||||
return true;
|
||||
|
||||
if (hostapd.data.pending_config[phy])
|
||||
if (hostapd.data.pending_config[name])
|
||||
return false;
|
||||
|
||||
if (!old_config.bss || !old_config.bss[0])
|
||||
return false;
|
||||
|
||||
let iface = hostapd.interfaces[phy];
|
||||
let iface = hostapd.interfaces[name];
|
||||
let iface_name = old_config.bss[0].ifname;
|
||||
if (!iface) {
|
||||
hostapd.printf(`Could not find previous interface ${iface_name}`);
|
||||
@@ -509,7 +529,7 @@ function iface_reload_config(phydev, config, old_config)
|
||||
return false;
|
||||
|
||||
let ifname = old_config.bss[i].ifname;
|
||||
hostapd.printf(`Remove bss '${ifname}' on phy '${phy}'`);
|
||||
hostapd.printf(`Remove bss '${ifname}' on phy '${name}'`);
|
||||
prev_bss.delete();
|
||||
wdev_remove(ifname);
|
||||
}
|
||||
@@ -574,13 +594,13 @@ function iface_reload_config(phydev, config, old_config)
|
||||
|
||||
let addr = phydev.macaddr_next(i);
|
||||
if (!addr) {
|
||||
hostapd.printf(`Failed to generate mac address for phy ${phy}`);
|
||||
hostapd.printf(`Failed to generate mac address for phy ${name}`);
|
||||
return false;
|
||||
}
|
||||
bsscfg.bssid = addr;
|
||||
}
|
||||
|
||||
let config_inline = iface_gen_config(phy, config);
|
||||
let config_inline = iface_gen_config(config);
|
||||
|
||||
// Step 7: fill in the gaps with new interfaces
|
||||
for (let i = 0; i < length(config.bss); i++) {
|
||||
@@ -590,17 +610,17 @@ function iface_reload_config(phydev, config, old_config)
|
||||
if (bss)
|
||||
continue;
|
||||
|
||||
hostapd.printf(`Add bss ${ifname} on phy ${phy}`);
|
||||
hostapd.printf(`Add bss ${ifname} on phy ${name}`);
|
||||
bss_list[i] = iface.add_bss(config_inline, i);
|
||||
if (!bss_list[i]) {
|
||||
hostapd.printf(`Failed to add new bss ${ifname} on phy ${phy}`);
|
||||
hostapd.printf(`Failed to add new bss ${ifname} on phy ${name}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 8: update interface bss order
|
||||
if (!iface.set_bss_order(bss_list)) {
|
||||
hostapd.printf(`Failed to update BSS order on phy '${phy}'`);
|
||||
hostapd.printf(`Failed to update BSS order on phy '${name}'`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -631,7 +651,7 @@ function iface_reload_config(phydev, config, old_config)
|
||||
if (is_equal(config.bss[i], bss_list_cfg[i]))
|
||||
continue;
|
||||
|
||||
hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${phy}'`);
|
||||
hostapd.printf(`Reload config for bss '${config.bss[0].ifname}' on phy '${name}'`);
|
||||
if (bss.set_config(config_inline, i) < 0) {
|
||||
hostapd.printf(`Failed to set config for bss ${ifname}`);
|
||||
return false;
|
||||
@@ -641,35 +661,36 @@ function iface_reload_config(phydev, config, old_config)
|
||||
return true;
|
||||
}
|
||||
|
||||
function iface_set_config(phy, config)
|
||||
function iface_set_config(name, config)
|
||||
{
|
||||
let old_config = hostapd.data.config[phy];
|
||||
let old_config = hostapd.data.config[name];
|
||||
|
||||
hostapd.data.config[phy] = config;
|
||||
hostapd.data.config[name] = config;
|
||||
|
||||
if (!config) {
|
||||
hostapd.remove_iface(phy);
|
||||
hostapd.remove_iface(name);
|
||||
return iface_remove(old_config);
|
||||
}
|
||||
|
||||
let phydev = phy_open(phy);
|
||||
let phy = config.phy;
|
||||
let phydev = phy_open(phy, config.radio_idx);
|
||||
if (!phydev) {
|
||||
hostapd.printf(`Failed to open phy ${phy}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
let ret = iface_reload_config(phydev, config, old_config);
|
||||
let ret = iface_reload_config(name, phydev, config, old_config);
|
||||
if (ret) {
|
||||
iface_update_supplicant_macaddr(phy, config);
|
||||
hostapd.printf(`Reloaded settings for phy ${phy}`);
|
||||
iface_update_supplicant_macaddr(phydev, config);
|
||||
hostapd.printf(`Reloaded settings for phy ${name}`);
|
||||
return 0;
|
||||
}
|
||||
} catch (e) {
|
||||
hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
|
||||
hostapd.printf(`Error reloading config: ${e}\n${e.stacktrace[0].context}`);
|
||||
}
|
||||
|
||||
hostapd.printf(`Restart interface for phy ${phy}`);
|
||||
hostapd.printf(`Restart interface for phy ${name}`);
|
||||
let ret = iface_restart(phydev, config, old_config);
|
||||
|
||||
return ret;
|
||||
@@ -688,13 +709,18 @@ function config_add_bss(config, name)
|
||||
return bss;
|
||||
}
|
||||
|
||||
function iface_load_config(filename)
|
||||
function iface_load_config(phy, radio, filename)
|
||||
{
|
||||
let f = open(filename, "r");
|
||||
if (!f)
|
||||
return null;
|
||||
|
||||
if (radio < 0)
|
||||
radio = null;
|
||||
|
||||
let config = {
|
||||
phy,
|
||||
radio_idx: radio,
|
||||
radio: {
|
||||
data: []
|
||||
},
|
||||
@@ -769,6 +795,17 @@ function ex_wrap(func) {
|
||||
};
|
||||
}
|
||||
|
||||
function phy_name(phy, radio)
|
||||
{
|
||||
if (!phy)
|
||||
return null;
|
||||
|
||||
if (radio != null && radio >= 0)
|
||||
phy += "." + radio;
|
||||
|
||||
return phy;
|
||||
}
|
||||
|
||||
function bss_config(bss_name) {
|
||||
for (let phy, config in hostapd.data.config) {
|
||||
if (!config)
|
||||
@@ -784,12 +821,13 @@ let main_obj = {
|
||||
reload: {
|
||||
args: {
|
||||
phy: "",
|
||||
radio: 0,
|
||||
},
|
||||
call: ex_wrap(function(req) {
|
||||
let phy_list = req.args.phy ? [ req.args.phy ] : keys(hostapd.data.config);
|
||||
let phy_list = req.args.phy ? [ phy_name(req.args.phy, req.args.radio) ] : keys(hostapd.data.config);
|
||||
for (let phy_name in phy_list) {
|
||||
let phy = hostapd.data.config[phy_name];
|
||||
let config = iface_load_config(phy.orig_file);
|
||||
let config = iface_load_config(phy.phy, radio, phy.orig_file);
|
||||
iface_set_config(phy_name, config);
|
||||
}
|
||||
|
||||
@@ -799,6 +837,7 @@ let main_obj = {
|
||||
apsta_state: {
|
||||
args: {
|
||||
phy: "",
|
||||
radio: 0,
|
||||
up: true,
|
||||
frequency: 0,
|
||||
sec_chan_offset: 0,
|
||||
@@ -806,10 +845,10 @@ let main_obj = {
|
||||
csa_count: 0,
|
||||
},
|
||||
call: ex_wrap(function(req) {
|
||||
if (req.args.up == null || !req.args.phy)
|
||||
let phy = phy_name(req.args.phy, req.args.radio);
|
||||
if (req.args.up == null || !phy)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
let phy = req.args.phy;
|
||||
let config = hostapd.data.config[phy];
|
||||
if (!config || !config.bss || !config.bss[0] || !config.bss[0].ifname)
|
||||
return 0;
|
||||
@@ -845,10 +884,11 @@ let main_obj = {
|
||||
},
|
||||
config_get_macaddr_list: {
|
||||
args: {
|
||||
phy: ""
|
||||
phy: "",
|
||||
radio: 0,
|
||||
},
|
||||
call: ex_wrap(function(req) {
|
||||
let phy = req.args.phy;
|
||||
let phy = phy_name(req.args.phy, req.args.radio);
|
||||
if (!phy)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
@@ -867,31 +907,34 @@ let main_obj = {
|
||||
config_set: {
|
||||
args: {
|
||||
phy: "",
|
||||
radio: 0,
|
||||
config: "",
|
||||
prev_config: "",
|
||||
},
|
||||
call: ex_wrap(function(req) {
|
||||
let phy = req.args.phy;
|
||||
let radio = req.args.radio;
|
||||
let name = phy_name(phy, radio);
|
||||
let file = req.args.config;
|
||||
let prev_file = req.args.prev_config;
|
||||
|
||||
if (!phy)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if (prev_file && !hostapd.data.config[phy]) {
|
||||
let config = iface_load_config(prev_file);
|
||||
if (prev_file && !hostapd.data.config[name]) {
|
||||
let config = iface_load_config(phy, radio, prev_file);
|
||||
if (config)
|
||||
config.radio.data = [];
|
||||
hostapd.data.config[phy] = config;
|
||||
hostapd.data.config[name] = config;
|
||||
}
|
||||
|
||||
let config = iface_load_config(file);
|
||||
let config = iface_load_config(phy, radio, file);
|
||||
|
||||
hostapd.printf(`Set new config for phy ${phy}: ${file}`);
|
||||
iface_set_config(phy, config);
|
||||
hostapd.printf(`Set new config for phy ${name}: ${file}`);
|
||||
iface_set_config(name, config);
|
||||
|
||||
if (hostapd.data.auth_obj)
|
||||
hostapd.data.auth_obj.notify("reload", { phy });
|
||||
hostapd.data.auth_obj.notify("reload", { phy, radio });
|
||||
|
||||
return {
|
||||
pid: hostapd.getpid()
|
||||
@@ -975,10 +1018,18 @@ function bss_event(type, name, data) {
|
||||
return {
|
||||
shutdown: function() {
|
||||
for (let phy in hostapd.data.config)
|
||||
iface_set_config(phy, null);
|
||||
iface_set_config(phy);
|
||||
hostapd.udebug_set(null);
|
||||
hostapd.ubus.disconnect();
|
||||
},
|
||||
bss_create: function(phy, name, obj) {
|
||||
phy = hostapd.data.config[phy];
|
||||
if (!phy)
|
||||
return;
|
||||
|
||||
if (phy.radio_idx != null && phy.radio_idx >= 0)
|
||||
wdev_set_radio_mask(name, 1 << phy.radio_idx);
|
||||
},
|
||||
bss_add: function(phy, name, obj) {
|
||||
bss_event("add", name);
|
||||
},
|
||||
|
||||
@@ -37,7 +37,7 @@ function iface_start(phydev, iface, macaddr_list)
|
||||
|
||||
wpas.data.iface_phy[ifname] = phy;
|
||||
wdev_remove(ifname);
|
||||
let ret = wdev_create(phy, ifname, wdev_config);
|
||||
let ret = phydev.wdev_add(ifname, wdev_config);
|
||||
if (ret)
|
||||
wpas.printf(`Failed to create device ${ifname}: ${ret}`);
|
||||
wdev_set_up(ifname, true);
|
||||
@@ -61,22 +61,27 @@ function iface_cb(new_if, old_if)
|
||||
iface_stop(old_if);
|
||||
}
|
||||
|
||||
function prepare_config(config)
|
||||
function prepare_config(config, radio)
|
||||
{
|
||||
config.config_data = readfile(config.config);
|
||||
|
||||
return { config: config };
|
||||
return { config };
|
||||
}
|
||||
|
||||
function set_config(phy_name, num_global_macaddr, config_list)
|
||||
function set_config(config_name, phy_name, radio, num_global_macaddr, config_list)
|
||||
{
|
||||
let phy = wpas.data.config[phy_name];
|
||||
let phy = wpas.data.config[config_name];
|
||||
|
||||
if (radio < 0)
|
||||
radio = null;
|
||||
|
||||
if (!phy) {
|
||||
phy = vlist_new(iface_cb, false);
|
||||
wpas.data.config[phy_name] = phy;
|
||||
phy.name = phy_name;
|
||||
wpas.data.config[config_name] = phy;
|
||||
}
|
||||
|
||||
phy.radio = radio;
|
||||
phy.num_global_macaddr = num_global_macaddr;
|
||||
|
||||
let values = [];
|
||||
@@ -94,7 +99,7 @@ function start_pending(phy_name)
|
||||
if (!phy || !phy.data)
|
||||
return;
|
||||
|
||||
let phydev = phy_open(phy_name);
|
||||
let phydev = phy_open(phy.name, phy.radio);
|
||||
if (!phydev) {
|
||||
wpas.printf(`Could not open phy ${phy_name}`);
|
||||
return;
|
||||
@@ -107,17 +112,30 @@ function start_pending(phy_name)
|
||||
iface_start(phydev, phy.data[ifname]);
|
||||
}
|
||||
|
||||
function phy_name(phy, radio)
|
||||
{
|
||||
if (!phy)
|
||||
return null;
|
||||
|
||||
if (radio != null && radio >= 0)
|
||||
phy += "." + radio;
|
||||
|
||||
return phy;
|
||||
}
|
||||
|
||||
let main_obj = {
|
||||
phy_set_state: {
|
||||
args: {
|
||||
phy: "",
|
||||
radio: 0,
|
||||
stop: true,
|
||||
},
|
||||
call: function(req) {
|
||||
if (!req.args.phy || req.args.stop == null)
|
||||
let name = phy_name(req.args.phy, req.args.radio);
|
||||
if (!name || req.args.stop == null)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
let phy = wpas.data.config[req.args.phy];
|
||||
let phy = wpas.data.config[name];
|
||||
if (!phy)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
|
||||
@@ -126,7 +144,7 @@ let main_obj = {
|
||||
for (let ifname in phy.data)
|
||||
iface_stop(phy.data[ifname]);
|
||||
} else {
|
||||
start_pending(req.args.phy);
|
||||
start_pending(name);
|
||||
}
|
||||
} catch (e) {
|
||||
wpas.printf(`Error chaging state: ${e}\n${e.stacktrace[0].context}`);
|
||||
@@ -138,10 +156,11 @@ let main_obj = {
|
||||
phy_set_macaddr_list: {
|
||||
args: {
|
||||
phy: "",
|
||||
radio: 0,
|
||||
macaddr: [],
|
||||
},
|
||||
call: function(req) {
|
||||
let phy = req.args.phy;
|
||||
let phy = phy_name(req.args.phy, req.args.radio);
|
||||
if (!phy)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
@@ -151,13 +170,15 @@ let main_obj = {
|
||||
},
|
||||
phy_status: {
|
||||
args: {
|
||||
phy: ""
|
||||
phy: "",
|
||||
radio: 0,
|
||||
},
|
||||
call: function(req) {
|
||||
if (!req.args.phy)
|
||||
let phy = phy_name(req.args.phy, req.args.radio);
|
||||
if (!phy)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
let phy = wpas.data.config[req.args.phy];
|
||||
phy = wpas.data.config[phy];
|
||||
if (!phy)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
|
||||
@@ -187,21 +208,23 @@ let main_obj = {
|
||||
config_set: {
|
||||
args: {
|
||||
phy: "",
|
||||
radio: 0,
|
||||
num_global_macaddr: 0,
|
||||
config: [],
|
||||
defer: true,
|
||||
},
|
||||
call: function(req) {
|
||||
if (!req.args.phy)
|
||||
let phy = phy_name(req.args.phy, req.args.radio);
|
||||
if (!phy)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
wpas.printf(`Set new config for phy ${req.args.phy}`);
|
||||
wpas.printf(`Set new config for phy ${phy}`);
|
||||
try {
|
||||
if (req.args.config)
|
||||
set_config(req.args.phy, req.args.num_global_macaddr, req.args.config);
|
||||
set_config(phy, req.args.phy, req.args.radio, req.args.num_global_macaddr, req.args.config);
|
||||
|
||||
if (!req.args.defer)
|
||||
start_pending(req.args.phy);
|
||||
start_pending(phy);
|
||||
} catch (e) {
|
||||
wpas.printf(`Error loading config: ${e}\n${e.stacktrace[0].context}`);
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
Reference in New Issue
Block a user