From 04fb05914ea77db9a0be2ef82249bdfd7c80fc65 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 12 Jun 2024 15:06:05 +0200 Subject: [PATCH] wifi-scripts: add multi-radio config support Emit one wifi-device section per wiphy radio Signed-off-by: Felix Fietkau --- .../files/lib/netifd/wireless/mac80211.sh | 72 +++++---- .../wifi-scripts/files/lib/wifi/mac80211.uc | 90 ++++++----- .../files/usr/share/hostap/common.uc | 101 ++++++++---- .../files/usr/share/hostap/wdev.uc | 37 ++--- .../files/usr/share/hostap/wifi-detect.uc | 47 ++++++ .../network/services/hostapd/files/hostapd.uc | 153 ++++++++++++------ .../services/hostapd/files/wpa_supplicant.uc | 59 ++++--- 7 files changed, 370 insertions(+), 189 deletions(-) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh b/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh index 23e557105b..9998e26ec1 100755 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh @@ -29,7 +29,7 @@ drv_mac80211_init_device_config() { config_add_string path phy 'macaddr:macaddr' config_add_string tx_burst config_add_string distance - config_add_int beacon_int chanbw frag rts + config_add_int radio beacon_int chanbw frag rts config_add_int rxantenna txantenna txpower min_tx_power config_add_int num_global_macaddr multiple_bssid config_add_boolean noscan ht_coex acs_exclude_dfs background_radar @@ -556,7 +556,7 @@ mac80211_hostapd_setup_bss() { } [ "$staidx" -gt 0 -o "$start_disabled" -eq 1 ] && append hostapd_cfg "start_disabled=1" "$N" - cat >> /var/run/hostapd-$phy.conf <> /var/run/hostapd-$phy$vif_phy_suffix.conf < /dev/null + ubus_call hostapd config_set '{ "phy": "'"$phy"'", "radio": '"$radio"', "config": "", "prev_config": "'"${hostapd_conf_file}.prev"'" }' > /dev/null return 0; } ubus wait_for hostapd - local hostapd_res="$(ubus_call hostapd config_set "{ \"phy\": \"$phy\", \"config\":\"${hostapd_conf_file}\", \"prev_config\": \"${hostapd_conf_file}.prev\"}")" + local hostapd_res="$(ubus_call hostapd config_set "{ \"phy\": \"$phy\", \"radio\": $radio, \"config\":\"${hostapd_conf_file}\", \"prev_config\": \"${hostapd_conf_file}.prev\"}")" ret="$?" [ "$ret" != 0 -o -z "$hostapd_res" ] && { wireless_setup_failed HOSTAPD_START_FAILED @@ -965,10 +975,11 @@ hostapd_set_config() { wpa_supplicant_start() { local phy="$1" + local radio="$2" [ -n "$wpa_supp_init" ] || return 0 - ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'", "num_global_macaddr": '"$num_global_macaddr"' }' > /dev/null + ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'", "radio": '"$radio"', "num_global_macaddr": '"$num_global_macaddr"' }' > /dev/null } mac80211_setup_supplicant() { @@ -1073,18 +1084,23 @@ drv_mac80211_cleanup() { } mac80211_reset_config() { - local phy="$1" + hostapd_conf_file="/var/run/hostapd-$phy$vif_phy_suffix.conf" + ubus_call hostapd config_set '{ "phy": "'"$phy"'", "radio": '"$radio"', "config": "", "prev_config": "'"$hostapd_conf_file"'" }' > /dev/null + ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'", "radio": '"$radio"', "config": [] }' > /dev/null + wdev_tool "$phy$phy_suffix" set_config '{}' +} - hostapd_conf_file="/var/run/hostapd-$phy.conf" - ubus_call hostapd config_set '{ "phy": "'"$phy"'", "config": "", "prev_config": "'"$hostapd_conf_file"'" }' > /dev/null - ubus_call wpa_supplicant config_set '{ "phy": "'"$phy"'", "config": [] }' > /dev/null - wdev_tool "$phy" set_config '{}' +mac80211_set_suffix() { + [ "$radio" = "-1" ] && radio= + phy_suffix="${radio:+:$radio}" + vif_phy_suffix="${radio:+.$radio}" + set_default radio -1 } drv_mac80211_setup() { json_select config json_get_vars \ - phy macaddr path \ + radio phy macaddr path \ country chanbw distance \ txpower \ rxantenna txantenna \ @@ -1094,6 +1110,8 @@ drv_mac80211_setup() { json_get_values scan_list scan_list json_select .. + mac80211_set_suffix + json_select data && { json_get_var prev_rxantenna rxantenna json_get_var prev_txantenna txantenna @@ -1120,7 +1138,7 @@ drv_mac80211_setup() { } } - hostapd_conf_file="/var/run/hostapd-$phy.conf" + hostapd_conf_file="/var/run/hostapd-$phy$vif_phy_suffix.conf" macidx=0 staidx=0 @@ -1139,17 +1157,11 @@ drv_mac80211_setup() { [ "$rxantenna" = "all" ] && rxantenna=0xffffffff [ "$rxantenna" = "$prev_rxantenna" -a "$txantenna" = "$prev_txantenna" ] || mac80211_reset_config "$phy" - wireless_set_data phy="$phy" txantenna="$txantenna" rxantenna="$rxantenna" + wireless_set_data phy="$phy" radio="$radio" txantenna="$txantenna" rxantenna="$rxantenna" iw phy "$phy" set antenna $txantenna $rxantenna >/dev/null 2>&1 iw phy "$phy" set distance "$distance" >/dev/null 2>&1 - if [ -n "$txpower" ]; then - iw phy "$phy" set txpower fixed "${txpower%%.*}00" - else - iw phy "$phy" set txpower auto - fi - [ -n "$frag" ] && iw phy "$phy" set frag "${frag%%.*}" [ -n "$rts" ] && iw phy "$phy" set rts "${rts%%.*}" @@ -1177,13 +1189,13 @@ drv_mac80211_setup() { for_each_interface "ap sta adhoc mesh monitor" mac80211_prepare_vif for_each_interface "ap sta adhoc mesh monitor" mac80211_setup_vif - [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_set_config "$phy" - [ -x /usr/sbin/hostapd ] && hostapd_set_config "$phy" + [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_set_config "$phy" "$radio" + [ -x /usr/sbin/hostapd ] && hostapd_set_config "$phy" "$radio" - [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_start "$phy" + [ -x /usr/sbin/wpa_supplicant ] && wpa_supplicant_start "$phy" "$radio" json_set_namespace wdev_uc prev - wdev_tool "$phy" set_config "$(json_dump)" $active_ifnames + wdev_tool "$phy$phy_suffix" set_config "$(json_dump)" $active_ifnames json_set_namespace "$prev" for_each_interface "ap sta adhoc mesh monitor" mac80211_set_vif_txpower @@ -1210,19 +1222,15 @@ list_phy_interfaces() { drv_mac80211_teardown() { json_select data - json_get_vars phy + json_get_vars phy radio json_select .. [ -n "$phy" ] || { echo "Bug: PHY is undefined for device '$1'" return 1 } + mac80211_set_suffix mac80211_reset_config "$phy" - - for wdev in $(list_phy_interfaces "$phy"); do - ip link set dev "$wdev" down - iw dev "$wdev" del - done } add_driver mac80211 diff --git a/package/network/config/wifi-scripts/files/lib/wifi/mac80211.uc b/package/network/config/wifi-scripts/files/lib/wifi/mac80211.uc index 9fd6c35e8b..e82525e247 100644 --- a/package/network/config/wifi-scripts/files/lib/wifi/mac80211.uc +++ b/package/network/config/wifi-scripts/files/lib/wifi/mac80211.uc @@ -14,10 +14,12 @@ let commit; let config = uci.cursor().get_all("wireless") ?? {}; -function radio_exists(path, macaddr, phy) { +function radio_exists(path, macaddr, phy, radio) { for (let name, s in config) { if (s[".type"] != "wifi-device") continue; + if (radio != null && int(s.radio) != radio) + continue; if (s.macaddr & lc(s.macaddr) == lc(macaddr)) return true; if (s.phy == phy) @@ -34,55 +36,61 @@ for (let phy_name, phy in board.wlan) { if (!info || !length(info.bands)) continue; - while (config[`radio${idx}`]) - idx++; - let name = "radio" + idx++; + let radios = length(info.radios) > 0 ? info.radios : [{ bands: info.bands }]; + for (let radio in radios) { + while (config[`radio${idx}`]) + idx++; + let name = "radio" + idx; - let s = "wireless." + name; - let si = "wireless.default_" + name; + let s = "wireless." + name; + let si = "wireless.default_" + name; - let band_name = filter(bands_order, (b) => info.bands[b])[0]; - if (!band_name) - continue; + let band_name = filter(bands_order, (b) => radio.bands[b])[0]; + if (!band_name) + continue; - let band = info.bands[band_name]; - let channel = band.default_channel ?? "auto"; + let band = info.bands[band_name]; + let rband = radio.bands[band_name]; + let channel = rband.default_channel ?? "auto"; - let width = band.max_width; - if (band_name == "2G") - width = 20; - else if (width > 80) - width = 80; + let width = band.max_width; + if (band_name == "2G") + width = 20; + else if (width > 80) + width = 80; - let htmode = filter(htmode_order, (m) => band[lc(m)])[0]; - if (htmode) - htmode += width; - else - htmode = "NOHT"; + let htmode = filter(htmode_order, (m) => band[lc(m)])[0]; + if (htmode) + htmode += width; + else + htmode = "NOHT"; - if (!phy.path) - continue; + if (!phy.path) + continue; - let macaddr = trim(readfile(`/sys/class/ieee80211/${phy_name}/macaddress`)); - if (radio_exists(phy.path, macaddr, phy_name)) - continue; + let macaddr = trim(readfile(`/sys/class/ieee80211/${phy_name}/macaddress`)); + if (radio_exists(phy.path, macaddr, phy_name, radio.index)) + continue; - let id = `phy='${phy_name}'`; - if (match(phy_name, /^phy[0-9]/)) - id = `path='${phy.path}'`; + let id = `phy='${phy_name}'`; + if (match(phy_name, /^phy[0-9]/)) + id = `path='${phy.path}'`; - band_name = lc(band_name); + band_name = lc(band_name); - let country, defaults, num_global_macaddr; - if (board.wlan.defaults) { - defaults = board.wlan.defaults.ssids?.[band_name]?.ssid ? board.wlan.defaults.ssids?.[band_name] : board.wlan.defaults.ssids?.all; - country = board.wlan.defaults.country; - if (!country && band_name != '2g') - defaults = null; - num_global_macaddr = board.wlan.defaults.ssids?.[band_name]?.mac_count; - } + let country, defaults, num_global_macaddr; + if (board.wlan.defaults) { + defaults = board.wlan.defaults.ssids?.[band_name]?.ssid ? board.wlan.defaults.ssids?.[band_name] : board.wlan.defaults.ssids?.all; + country = board.wlan.defaults.country; + if (!country && band_name != '2g') + defaults = null; + num_global_macaddr = board.wlan.defaults.ssids?.[band_name]?.mac_count; + } - print(`set ${s}=wifi-device + if (length(info.radios) > 0) + id += `\nset ${s}.radio='${radio.index}'`; + + print(`set ${s}=wifi-device set ${s}.type='mac80211' set ${s}.${id} set ${s}.band='${band_name}' @@ -101,7 +109,9 @@ set ${si}.encryption='${defaults?.encryption || "none"}' set ${si}.key='${defaults?.key || ""}' `); - commit = true; + config[name] = {}; + commit = true; + } } if (commit) diff --git a/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc index 750e3ae71c..c6bfb62ef3 100644 --- a/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc @@ -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; + } - cb(wdev); + for (let wdev in wdevs) { + if (!mac_wdev[wdev.mac]) + continue; + + 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 }; diff --git a/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc index d505e3035f..d836a930fc 100644 --- a/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc @@ -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); } diff --git a/package/network/config/wifi-scripts/files/usr/share/hostap/wifi-detect.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/wifi-detect.uc index ee9155b3d0..db862d40c2 100644 --- a/package/network/config/wifi-scripts/files/usr/share/hostap/wifi-detect.uc +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/wifi-detect.uc @@ -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; diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index e666ad4faa..2d9ce28749 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -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); }, diff --git a/package/network/services/hostapd/files/wpa_supplicant.uc b/package/network/services/hostapd/files/wpa_supplicant.uc index eed34f8513..fbea27628e 100644 --- a/package/network/services/hostapd/files/wpa_supplicant.uc +++ b/package/network/services/hostapd/files/wpa_supplicant.uc @@ -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; -- 2.30.2