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:
Felix Fietkau
2024-06-12 15:06:05 +02:00
parent cb60bee04d
commit 04fb05914e
7 changed files with 370 additions and 189 deletions

View File

@@ -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);
},

View File

@@ -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;