From 009a9e04787c3de2699bbaacee3d96919fbaa82f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 17 Sep 2025 21:31:51 +0200 Subject: [PATCH] wifi-scripts: simplify MLO handling Move mlo specific hostapd ubus call from wireless handler to netifd core ucode script. This avoids unnecessary queueing and the fake MLO wireless device. Signed-off-by: Felix Fietkau --- .../lib/netifd/wireless/mac80211.sh | 34 +------ .../usr/share/schema/wireless.wifi-iface.json | 4 + .../files-ucode/usr/share/ucode/wifi/ap.uc | 4 +- .../usr/share/ucode/wifi/hostapd.uc | 2 +- .../files/lib/netifd/wireless-device.uc | 28 ------ .../wifi-scripts/files/lib/netifd/wireless.uc | 99 +++++++++++-------- 6 files changed, 66 insertions(+), 105 deletions(-) diff --git a/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh b/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh index 584a1423d3..ab8b2d15f9 100755 --- a/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh +++ b/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh @@ -157,39 +157,9 @@ function config_add_mesh_params(config, data) { config_add(config, param, data[param]); } -function setup_mlo(data) { - let config = {}; - let idx = 0; - - for (let k, v in data.interfaces) { - let ifname = v.config.ifname; - if (!ifname) - ifname = 'ap-mld' + idx++; - - delete v.config.ifname; - config[ifname] = v.config; - netifd.set_vif(k, ifname); - - v.config.phy = find_phy(v.config.radio_config[0], true); - delete v.config.radio_config; - } - - let ret = ubus.call('hostapd', 'mld_set', { config }); - if (type(ret) != "object") - return netifd.setup_failed('HOSTAPD_START_FAILED'); - - netifd.add_process('/usr/sbin/hostapd', ret.pid, true, true); - netifd.set_up(); - - return 0; -} - function setup() { let data = json(ARGV[3]); - if (ARGV[2] == "#mlo") - return setup_mlo(data); - data.phy = find_phy(data.config, true); if (!data.phy) { log('Bug: PHY is undefined for device'); @@ -230,7 +200,6 @@ function setup() { } switch (mode) { - case 'link': case 'ap': has_ap = true; for (let _, sta in v.stas) @@ -243,8 +212,7 @@ function setup() { data.config.noscan = true; validate('iface', v.config); iface.prepare(v.config, data.phy + data.phy_suffix, data.config.num_global_macaddr, data.config.macaddr_base); - if (mode != "link") - netifd.set_vif(k, v.config.ifname); + netifd.set_vif(k, v.config.ifname); break; } diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/schema/wireless.wifi-iface.json b/package/network/config/wifi-scripts/files-ucode/usr/share/schema/wireless.wifi-iface.json index d7efe47516..bb651052da 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/schema/wireless.wifi-iface.json +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/schema/wireless.wifi-iface.json @@ -689,6 +689,10 @@ "mesh_ttl": { "type": "number" }, + "mlo": { + "description": "Multi-Link Operation", + "type": "boolean" + }, "mobility_domain": { "description": "DID is used to indicate a group of APs between which a STA can use Fast BSS Transition.", "type": "string" diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc index 6ae53df377..91a90447bd 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc @@ -482,7 +482,7 @@ export function generate(interface, data, config, vlans, stas, phy_features) { 'rsn_override_mfp' ]); - if (config.mode == 'link') { + if (config.mlo) { config.rsn_override_mfp_2 ??= config.rsn_override_mfp; config.rsn_override_key_mgmt_2 ??= config.rsn_override_key_mgmt; config.rsn_override_pairwise_2 ??= config.rsn_override_pairwise; @@ -499,7 +499,7 @@ export function generate(interface, data, config, vlans, stas, phy_features) { for (let raw in config.hostapd_options) append_raw(raw); - if (config.mode == 'link') { + if (config.mlo) { append_raw('mld_ap=1'); if (data.config.radio != null) append_raw('mld_link_id=' + data.config.radio); diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc index eea93ca3ef..216b41a6c2 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc @@ -554,7 +554,7 @@ export function setup(data) { append('\n#macaddr_base', data.config.macaddr_base); for (let k, interface in data.interfaces) { - if (interface.config.mode != 'ap' && interface.config.mode != 'link') + if (interface.config.mode != 'ap') continue; interface.config.network_bridge = interface.bridge; diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc index c16ef5f0ff..d793ef8bfe 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc @@ -12,9 +12,6 @@ const NOTIFY_CMD_SET_RETRY = 4; const DEFAULT_RETRY = 3; const DEFAULT_SCRIPT_TIMEOUT = 30 * 1000; -export const mlo_name = "#mlo"; - -let mlo_wdev; let wdev_cur; let wdev_handler = {}; let wdev_script_task, wdev_script_timeout; @@ -64,23 +61,6 @@ function handle_link(dev, data, up) }); } -function wdev_mlo_fixup(config) -{ - if (!mlo_wdev) - return; - - for (let name, iface in config.interfaces) { - let config = iface.config; - - if (config.mode != "link") - continue; - - let mlo_config = mlo_wdev.handler_data[iface.name]; - if (mlo_config && mlo_config.ifname) - config.ifname = mlo_config.ifname; - } -} - function wdev_config_init(wdev) { let data = wdev.data; @@ -199,9 +179,6 @@ function handler_sort_fn(a, b) function __run_next_handler_name() { - if (wdev_handler[mlo_name]) - return mlo_name; - return sort(keys(wdev_handler), handler_sort_fn)[0]; } @@ -219,8 +196,6 @@ function __run_next_handler() let cb = wdev_cur.cb; wdev.dbg("run " + op); - if (name != mlo_name) - wdev_mlo_fixup(wdev.handler_config); wdev.handler_config.data = wdev.handler_data[wdev.name] ?? {}; wdev_script_task = netifd.process({ cb: () => run_handler_cb(wdev, cb), @@ -442,9 +417,6 @@ function wdev_mark_up(wdev) if (wdev.state != "setup") return; - if (wdev.name == mlo_name) - mlo_wdev = wdev; - if (wdev.config_change) { wdev.setup(); return; diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc index 22f6e25ce4..360fe2503e 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc @@ -7,16 +7,44 @@ import { parse_attribute_list, parse_bool, parse_array, TYPE_ARRAY, TYPE_STRING, TYPE_INT, TYPE_BOOL } from "./utils.uc"; +import { find_phy } from "wifi.utils"; import * as wdev from "./wireless-device.uc"; let wireless = netifd.wireless = { handlers: {}, devices: {}, + mlo: {}, path: realpath(netifd.main_path + "/wireless"), }; -function update_config(new_devices) +function hostapd_update_mlo() { + let config = {}; + + for (let ifname, data in wireless.mlo) { + if (data.mode != "ap") + continue; + + data.phy = find_phy(data.radio_config[0], true); + if (!data.phy) + continue; + + config[ifname] = data; + } + + ubus.call({ + object: "hostapd", + method: "mld_set", + return: "ignore", + data: { config }, + }); +} + +function update_config(new_devices, mlo_vifs) +{ + wireless.mlo = mlo_vifs; + hostapd_update_mlo(); + for (let name, dev in wireless.devices) if (!new_devices[name]) dev.destroy(); @@ -44,7 +72,7 @@ function config_init(uci) let handlers = {}; let devices = {}; let vifs = {}; - let mlo_device; + let mlo_vifs = {}; let sections = { device: {}, @@ -53,6 +81,7 @@ function config_init(uci) station: {}, }; let radio_idx = {}; + let vif_idx = {}; for (let name, data in config) { let type = data[".type"]; @@ -65,20 +94,6 @@ function config_init(uci) let list = sections[substr(type, 5)]; if (list) list[name] = data; - - if (type == "wifi-iface" && parse_bool(data.mlo)) - mlo_device = true; - } - - if (mlo_device) { - devices[wdev.mlo_name] = { - name: wdev.mlo_name, - config: { - type: "mac80211", - }, - vif: [], - }; - handlers[wdev.mlo_name] = wireless.handlers.mac80211; } for (let name, data in sections.device) { @@ -108,8 +123,8 @@ function config_init(uci) let radios = map(dev_names, (v) => radio_idx[v]); radios = filter(radios, (v) => v != null); let radio_config = map(dev_names, (v) => devices[v].config); - if (mlo_vif) - dev_names = [ wdev.mlo_name, ...dev_names ]; + let ifname; + for (let dev_name in dev_names) { let dev = devices[dev_name]; if (!dev) @@ -120,19 +135,28 @@ function config_init(uci) continue; let config = parse_attribute_list(data, handler.iface); - if (mlo_vif) - if (dev_name == wdev.mlo_name) - config.radio_config = radio_config; - else - config.mode = "link"; config.radios = radios; + if (mlo_vif && dev_name == dev_names[0]) { + let mlo_config = { ...config }; + + mlo_config.radio_config = radio_config; + ifname = config.ifname; + if (!ifname) { + let idx = vif_idx[config.mode] ?? 0; + vif_idx[config.mode] = idx + 1; + ifname = config.mode + "-mld" + idx; + } + + mlo_vifs[ifname] = mlo_config; + } + + if (ifname) + config.ifname = ifname; if (dev_name != dev_names[0]) delete config.macaddr; - if (dev_name != wdev.mlo_name && config.radio_macaddr) { + if (config.radio_macaddr) { let idx = index(dev_names, dev_name); - if (mlo_vif) - idx--; let macaddr = idx >= 0 ? config.radio_macaddr[idx] : null; if (macaddr) config.macaddr = macaddr; @@ -277,7 +301,7 @@ function config_init(uci) } } - update_config(devices); + update_config(devices, mlo_vifs); } function config_start() @@ -352,11 +376,8 @@ function wdev_call(req, cb) return cb(dev); } - for (let name, dev in wireless.devices) { - if (name == wdev.mlo_name) - continue; + for (let name, dev in wireless.devices) cb(dev); - } return 0; } @@ -397,9 +418,7 @@ const ubus_obj = { up: { args: wdev_args, call: function(req) { - let mlo_dev = wireless.devices[wdev.mlo_name]; - if (mlo_dev) - mlo_dev.start(); + hostapd_update_mlo(); return wdev_call(req, (dev) => { dev.start(); @@ -410,10 +429,6 @@ const ubus_obj = { down: { args: wdev_args, call: function(req) { - let mlo_dev = wireless.devices[wdev.mlo_name]; - if (mlo_dev) - mlo_dev.config_change = true; - return wdev_call(req, (dev) => { dev.stop(); return 0; @@ -423,9 +438,7 @@ const ubus_obj = { reconf: { args: wdev_args, call: function(req) { - let mlo_dev = wireless.devices[wdev.mlo_name]; - if (mlo_dev) - mlo_dev.update(); + hostapd_update_mlo(); return wdev_call(req, (dev) => { dev.update(); @@ -500,6 +513,10 @@ handler_load(wireless.path, (script, data) => { }); wireless.obj = ubus.publish("network.wireless", ubus_obj); +wireless.listener = ubus.listener("ubus.object.add", (event, msg) => { + if (msg.path == "hostapd") + hostapd_update_mlo(); +}); return { hotplug, -- 2.30.2