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 @@
|
||||
import * as nl80211 from "nl80211";
|
||||
import * as rtnl from "rtnl";
|
||||
import { readfile, glob, basename, readlink } from "fs";
|
||||
import { readfile, glob, basename, readlink, open } from "fs";
|
||||
|
||||
const iftypes = {
|
||||
ap: nl80211.const.NL80211_IFTYPE_AP,
|
||||
@@ -74,6 +74,14 @@ function find_reusable_wdev(phyidx)
|
||||
return null;
|
||||
}
|
||||
|
||||
function wdev_set_radio_mask(name, mask)
|
||||
{
|
||||
nl80211.request(nl80211.const.NL80211_CMD_SET_INTERFACE, 0, {
|
||||
dev: name,
|
||||
vif_radio_mask: mask
|
||||
});
|
||||
}
|
||||
|
||||
function wdev_create(phy, name, data)
|
||||
{
|
||||
let phyidx = int(readfile(`/sys/class/ieee80211/${phy}/index`));
|
||||
@@ -93,24 +101,24 @@ function wdev_create(phy, name, data)
|
||||
req["4addr"] = data["4addr"];
|
||||
if (data.macaddr)
|
||||
req.mac = data.macaddr;
|
||||
if (data.radio != null && data.radio >= 0)
|
||||
req.vif_radio_mask = 1 << data.radio;
|
||||
|
||||
nl80211.error();
|
||||
|
||||
let reuse_ifname = find_reusable_wdev(phyidx);
|
||||
if (reuse_ifname &&
|
||||
(reuse_ifname == name ||
|
||||
rtnl.request(rtnl.const.RTM_SETLINK, 0, { dev: reuse_ifname, ifname: name}) != false))
|
||||
nl80211.request(
|
||||
nl80211.const.NL80211_CMD_SET_INTERFACE, 0, {
|
||||
wiphy: phyidx,
|
||||
dev: name,
|
||||
iftype: iftypes[data.mode],
|
||||
});
|
||||
else
|
||||
rtnl.request(rtnl.const.RTM_SETLINK, 0, { dev: reuse_ifname, ifname: name}) != false)) {
|
||||
req.dev = req.ifname;
|
||||
delete req.ifname;
|
||||
nl80211.request(nl80211.const.NL80211_CMD_SET_INTERFACE, 0, req);
|
||||
} else {
|
||||
nl80211.request(
|
||||
nl80211.const.NL80211_CMD_NEW_INTERFACE,
|
||||
nl80211.const.NLM_F_CREATE,
|
||||
req);
|
||||
}
|
||||
|
||||
let error = nl80211.error();
|
||||
if (error)
|
||||
@@ -190,7 +198,8 @@ const phy_proto = {
|
||||
},
|
||||
|
||||
macaddr_generate: function(data) {
|
||||
let phy = this.name;
|
||||
let phy = this.phy;
|
||||
let radio_idx = this.radio;
|
||||
let idx = int(data.id ?? 0);
|
||||
let mbssid = int(data.mbssid ?? 0) > 0;
|
||||
let num_global = int(data.num_global ?? 1);
|
||||
@@ -200,22 +209,30 @@ const phy_proto = {
|
||||
if (!base_addr)
|
||||
return null;
|
||||
|
||||
if (!idx && !mbssid)
|
||||
return base_addr;
|
||||
|
||||
let base_mask = phy_sysfs_file(phy, "address_mask");
|
||||
if (!base_mask)
|
||||
return null;
|
||||
|
||||
if (base_mask == "00:00:00:00:00:00" && idx >= num_global) {
|
||||
if (base_mask == "00:00:00:00:00:00" &&
|
||||
(radio_idx > 0 || idx >= num_global)) {
|
||||
let addrs = split(phy_sysfs_file(phy, "addresses"), "\n");
|
||||
|
||||
if (idx < length(addrs))
|
||||
return addrs[idx];
|
||||
if (radio_idx != null) {
|
||||
if (radio_idx && radio_idx < length(addrs))
|
||||
base_addr = addrs[radio_idx];
|
||||
else
|
||||
idx += radio_idx * 16;
|
||||
} else {
|
||||
if (idx < length(addrs))
|
||||
return addrs[idx];
|
||||
|
||||
base_mask = "ff:ff:ff:ff:ff:ff";
|
||||
base_mask = "ff:ff:ff:ff:ff:ff";
|
||||
}
|
||||
}
|
||||
|
||||
if (!idx && !mbssid)
|
||||
return base_addr;
|
||||
|
||||
let addr = macaddr_split(base_addr);
|
||||
let mask = macaddr_split(base_mask);
|
||||
let type;
|
||||
@@ -275,27 +292,55 @@ const phy_proto = {
|
||||
}
|
||||
},
|
||||
|
||||
wdev_add: function(name, data) {
|
||||
let phydev = this;
|
||||
wdev_create(this.phy, name, {
|
||||
...data,
|
||||
radio: this.radio,
|
||||
});
|
||||
},
|
||||
|
||||
for_each_wdev: function(cb) {
|
||||
let wdevs = glob(`/sys/class/ieee80211/${this.name}/device/net/*`);
|
||||
wdevs = map(wdevs, (arg) => basename(arg));
|
||||
let wdevs = nl80211.request(
|
||||
nl80211.const.NL80211_CMD_GET_INTERFACE,
|
||||
nl80211.const.NLM_F_DUMP,
|
||||
{ wiphy: this.idx }
|
||||
);
|
||||
|
||||
let mac_wdev = {};
|
||||
for (let wdev in wdevs) {
|
||||
if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != this.name)
|
||||
if (wdev.iftype == nl80211.const.NL80211_IFTYPE_AP_VLAN)
|
||||
continue;
|
||||
if (this.radio != null && wdev.vif_radio_mask != null &&
|
||||
!(wdev.vif_radio_mask & (1 << this.radio)))
|
||||
continue;
|
||||
mac_wdev[wdev.mac] = wdev;
|
||||
}
|
||||
|
||||
for (let wdev in wdevs) {
|
||||
if (!mac_wdev[wdev.mac])
|
||||
continue;
|
||||
|
||||
cb(wdev);
|
||||
cb(wdev.ifname);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function phy_open(phy)
|
||||
function phy_open(phy, radio)
|
||||
{
|
||||
let phyidx = readfile(`/sys/class/ieee80211/${phy}/index`);
|
||||
if (!phyidx)
|
||||
return null;
|
||||
|
||||
let name = phy;
|
||||
if (radio === "" || radio < 0)
|
||||
radio = null;
|
||||
if (radio != null)
|
||||
name += "." + radio;
|
||||
|
||||
return proto({
|
||||
name: phy,
|
||||
idx: int(phyidx)
|
||||
phy, name, radio,
|
||||
idx: int(phyidx),
|
||||
}, phy_proto);
|
||||
}
|
||||
|
||||
@@ -365,9 +410,9 @@ function is_equal(val1, val2) {
|
||||
|
||||
function vlist_new(cb) {
|
||||
return proto({
|
||||
cb: cb,
|
||||
data: {}
|
||||
}, vlist_proto);
|
||||
cb: cb,
|
||||
data: {}
|
||||
}, vlist_proto);
|
||||
}
|
||||
|
||||
export { wdev_remove, wdev_create, wdev_set_mesh_params, wdev_set_up, is_equal, vlist_new, phy_is_fullmac, phy_open };
|
||||
export { wdev_remove, wdev_create, wdev_set_mesh_params, wdev_set_radio_mask, wdev_set_up, is_equal, vlist_new, phy_is_fullmac, phy_open };
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env ucode
|
||||
'use strict';
|
||||
import { vlist_new, is_equal, wdev_create, wdev_set_mesh_params, wdev_remove, wdev_set_up, phy_open } from "/usr/share/hostap/common.uc";
|
||||
import { vlist_new, is_equal, wdev_set_mesh_params, wdev_remove, wdev_set_up, phy_open } from "/usr/share/hostap/common.uc";
|
||||
import { readfile, writefile, basename, readlink, glob } from "fs";
|
||||
let libubus = require("ubus");
|
||||
|
||||
let keep_devices = {};
|
||||
let phy = shift(ARGV);
|
||||
let phy_name = shift(ARGV);
|
||||
let command = shift(ARGV);
|
||||
let phydev;
|
||||
let phy, phydev;
|
||||
|
||||
function iface_stop(wdev)
|
||||
{
|
||||
@@ -30,7 +30,7 @@ function iface_start(wdev)
|
||||
wdev_config[key] = wdev[key];
|
||||
if (!wdev_config.macaddr && wdev.mode != "monitor")
|
||||
wdev_config.macaddr = phydev.macaddr_next();
|
||||
wdev_create(phy, ifname, wdev_config);
|
||||
phydev.wdev_add(ifname, wdev_config);
|
||||
wdev_set_up(ifname, true);
|
||||
let htmode = wdev.htmode || "NOHT";
|
||||
if (wdev.freq)
|
||||
@@ -85,20 +85,15 @@ function delete_ifname(config)
|
||||
delete config[key].ifname;
|
||||
}
|
||||
|
||||
function add_existing(phy, config)
|
||||
function add_existing(phydev, config)
|
||||
{
|
||||
let wdevs = glob(`/sys/class/ieee80211/${phy}/device/net/*`);
|
||||
wdevs = map(wdevs, (arg) => basename(arg));
|
||||
for (let wdev in wdevs) {
|
||||
phydev.for_each_wdev((wdev) => {
|
||||
if (config[wdev])
|
||||
continue;
|
||||
|
||||
if (basename(readlink(`/sys/class/net/${wdev}/phy80211`)) != phy)
|
||||
continue;
|
||||
return;
|
||||
|
||||
if (trim(readfile(`/sys/class/net/${wdev}/operstate`)) == "down")
|
||||
config[wdev] = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function usage()
|
||||
@@ -114,7 +109,7 @@ Commands:
|
||||
|
||||
const commands = {
|
||||
set_config: function(args) {
|
||||
let statefile = `/var/run/wdev-${phy}.json`;
|
||||
let statefile = `/var/run/wdev-${phy_name}.json`;
|
||||
|
||||
let new_config = shift(args);
|
||||
for (let dev in ARGV)
|
||||
@@ -137,12 +132,12 @@ const commands = {
|
||||
if (type(old_config) == "object")
|
||||
config.data = old_config;
|
||||
|
||||
add_existing(phy, config.data);
|
||||
add_existing(phydev, config.data);
|
||||
add_ifname(config.data);
|
||||
drop_inactive(config.data);
|
||||
|
||||
let ubus = libubus.connect();
|
||||
let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phy });
|
||||
let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phydev.name, radio: phydev.radio ?? -1 });
|
||||
let macaddr_list = [];
|
||||
if (type(data) == "object" && data.macaddr)
|
||||
macaddr_list = data.macaddr;
|
||||
@@ -166,7 +161,7 @@ const commands = {
|
||||
|
||||
let macaddr = phydev.macaddr_generate(data);
|
||||
if (!macaddr) {
|
||||
warn(`Could not get MAC address for phy ${phy}\n`);
|
||||
warn(`Could not get MAC address for phy ${phy_name}\n`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -174,12 +169,14 @@ const commands = {
|
||||
},
|
||||
};
|
||||
|
||||
if (!phy || !command | !commands[command])
|
||||
if (!phy_name || !command | !commands[command])
|
||||
usage();
|
||||
|
||||
phydev = phy_open(phy);
|
||||
let phy_split = split(phy_name, ":");
|
||||
phydev = phy_open(phy_split[0], phy_split[1]);
|
||||
phy = phydev.phy;
|
||||
if (!phydev) {
|
||||
warn(`PHY ${phy} does not exist\n`);
|
||||
warn(`PHY ${phy_name} does not exist\n`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,15 @@ function freq_to_channel(freq) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
function freq_range_match(ranges, freq) {
|
||||
freq *= 1000;
|
||||
for (let range in ranges) {
|
||||
if (freq >= range[0] && freq <= range[1])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function wiphy_detect() {
|
||||
let phys = nl.request(nl.const.NL80211_CMD_GET_WIPHY, nl.const.NLM_F_DUMP, { split_wiphy_dump: true });
|
||||
if (!phys)
|
||||
@@ -88,8 +97,27 @@ function wiphy_detect() {
|
||||
antenna_rx: phy.wiphy_antenna_avail_rx,
|
||||
antenna_tx: phy.wiphy_antenna_avail_tx,
|
||||
bands: {},
|
||||
radios: []
|
||||
};
|
||||
|
||||
for (let radio in phy.radios) {
|
||||
// S1G is not supported yet
|
||||
radio.freq_ranges = filter(radio.freq_ranges,
|
||||
(range) => range.end > 2000000
|
||||
);
|
||||
|
||||
if (!length(radio.freq_ranges))
|
||||
continue;
|
||||
|
||||
push(info.radios, {
|
||||
index: radio.index,
|
||||
freq_ranges: map(radio.freq_ranges,
|
||||
(range) => [ range.start, range.end ]
|
||||
),
|
||||
bands: {}
|
||||
});
|
||||
}
|
||||
|
||||
let bands = info.bands;
|
||||
for (let band in phy.wiphy_bands) {
|
||||
if (!band || !band.freqs)
|
||||
@@ -160,6 +188,25 @@ function wiphy_detect() {
|
||||
if (eht_phy_cap && he_phy_cap & 2)
|
||||
push(modes, "EHT40");
|
||||
|
||||
for (let radio in info.radios) {
|
||||
let freq_match = filter(band.freqs,
|
||||
(freq) => freq_range_match(radio.freq_ranges, freq.freq)
|
||||
);
|
||||
if (!length(freq_match))
|
||||
continue;
|
||||
|
||||
let radio_band = {};
|
||||
radio.bands[band_name] = radio_band;
|
||||
|
||||
freq_match = filter(freq_match,
|
||||
(freq) => !freq.disabled
|
||||
);
|
||||
|
||||
let freq = freq_match[0];
|
||||
if (freq)
|
||||
radio_band.default_channel = freq_to_channel(freq.freq);
|
||||
}
|
||||
|
||||
for (let freq in band.freqs) {
|
||||
if (freq.disabled)
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user