]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
wifi-scripts: simplify MLO handling
authorFelix Fietkau <nbd@nbd.name>
Wed, 17 Sep 2025 19:31:51 +0000 (21:31 +0200)
committerFelix Fietkau <nbd@nbd.name>
Wed, 24 Sep 2025 11:45:40 +0000 (13:45 +0200)
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 <nbd@nbd.name>
package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh
package/network/config/wifi-scripts/files-ucode/usr/share/schema/wireless.wifi-iface.json
package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc
package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc
package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc
package/network/config/wifi-scripts/files/lib/netifd/wireless.uc

index 584a1423d3e13cec6716bbac3a29d288f805d076..ab8b2d15f9387c6810133a5b027291941ba8a7df 100755 (executable)
@@ -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;
                }
 
index d7efe4751622f6b756c6bff811b859d1c978697d..bb651052da965eb9107bd60357b0c63d2a9905d6 100644 (file)
                "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"
index 6ae53df3777b3a6aa66836ddfc4bc135a611925a..91a90447bd0f606fe2b5c2845e1d5c62a2c04f86 100644 (file)
@@ -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);
index eea93ca3efd11b64c771e9d5cdedf3d2fa919f78..216b41a6c271f37ec2387a21517bb89f26931e14 100644 (file)
@@ -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;
index c16ef5f0ff26c4c73aa9c093e917baf8c145bcb5..d793ef8bfe9a0c635791a6ca5769d8ece15990d9 100644 (file)
@@ -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;
index 22f6e25ce46013f6c497f65f4aa4e9d7defd0b3d..360fe2503eb0ff90aa81a2b65a9b3bd5f01b6ec9 100644 (file)
@@ -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,