[openwrt/openwrt] hostapd: add support for MLO interfaces in ucode

LEDE Commits lede-commits at lists.infradead.org
Sat Aug 2 08:44:56 PDT 2025


nbd pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/816c2d86e749de23abd1803a292178e4ad38626a

commit 816c2d86e749de23abd1803a292178e4ad38626a
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Sun Jul 6 18:50:00 2025 +0200

    hostapd: add support for MLO interfaces in ucode
    
    MLO interface config is provided in a separate ubus call before
    adding regular per-phy interfaces.
    Preparation for full MLO support.
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 .../wifi-scripts/files/usr/share/hostap/common.uc  |   2 +-
 package/network/services/hostapd/files/hostapd.uc  | 286 +++++++++++++++++++--
 ...-Fix-hostapd-crash-if-setup-a-iface-with-.patch |  46 ++++
 ...-support-for-specifying-the-link-id-in-th.patch |  58 +++++
 .../services/hostapd/patches/300-noscan.patch      |   2 +-
 .../hostapd/patches/600-ubus_support.patch         |   2 +-
 .../hostapd/patches/601-ucode_support.patch        |  87 ++++++-
 .../hostapd/patches/701-reload_config_inline.patch |   4 +-
 .../hostapd/patches/720-iface_max_num_sta.patch    |   4 +-
 .../hostapd/patches/770-radius_server.patch        |   2 +-
 ...Implement-APuP-Access-Point-Micro-Peering.patch |   4 +-
 .../network/services/hostapd/src/src/ap/ucode.c    |  74 +++++-
 12 files changed, 520 insertions(+), 51 deletions(-)

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 31b526b6ae..caab14bcab 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
@@ -315,7 +315,7 @@ const phy_proto = {
 			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)))
+			    wdev.vif_radio_mask != (1 << this.radio))
 				continue;
 			mac_wdev[wdev.mac] = wdev;
 		}
diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc
index bdcba4880a..a28c282493 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_remove, is_equal, vlist_new, phy_is_fullmac, phy_open, wdev_set_radio_mask } from "common";
+import { wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open, wdev_set_radio_mask, wdev_set_up } from "common";
 
 let ubus = libubus.connect(null, 60);
 
@@ -50,13 +50,16 @@ hostapd.data.bss_info_fields = {
 	owe_transition_ifname: true,
 };
 
+hostapd.data.mld = {};
+
 function iface_remove(cfg)
 {
 	if (!cfg || !cfg.bss || !cfg.bss[0] || !cfg.bss[0].ifname)
 		return;
 
 	for (let bss in cfg.bss)
-		wdev_remove(bss.ifname);
+		if (!bss.mld_ap)
+			wdev_remove(bss.ifname);
 }
 
 function iface_gen_config(config, start_disabled)
@@ -70,10 +73,12 @@ channel=${config.radio.channel}
 		let bss = config.bss[i];
 		let type = i > 0 ? "bss" : "interface";
 		let nasid = bss.nasid ?? replace(bss.bssid, ":", "");
-
+		let bssid = bss.bssid;
+		if (bss.mld_ap)
+			bssid += "\nmld_addr=" + bss.mld_bssid;
 		str += `
 ${type}=${bss.ifname}
-bssid=${bss.bssid}
+bssid=${bssid}
 ${join("\n", bss.data)}
 nas_identifier=${nasid}
 `;
@@ -142,6 +147,9 @@ function iface_add(phy, config, phy_status)
 function iface_config_macaddr_list(config)
 {
 	let macaddr_list = {};
+	for (let name, mld in hostapd.data.mld)
+		if (mld.macaddr)
+			macaddr_list[mld.macaddr] = -1;
 	for (let i = 0; i < length(config.bss); i++) {
 		let bss = config.bss[i];
 		if (!bss.default_macaddr)
@@ -154,8 +162,11 @@ function iface_config_macaddr_list(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);
+	for (let name, mld in hostapd.data.mld)
+		if (mld.macaddr)
+			push(macaddr_list, mld.macaddr);
+	for (let bss in config.bss)
+		push(macaddr_list, bss.bssid);
 	ubus.defer("wpa_supplicant", "phy_set_macaddr_list", {
 		phy: phydev.name,
 		radio: phydev.radio ?? -1,
@@ -178,13 +189,15 @@ function __iface_pending_next(pending, state, ret, data)
 		iface_update_supplicant_macaddr(phydev, config);
 		return "create_bss";
 	case "create_bss":
-		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;
+		if (!bss.mld_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", {
@@ -550,6 +563,10 @@ function iface_reload_config(name, phydev, config, old_config)
 
 		let cur_config = config.bss[i];
 		let prev_config = old_config.bss[prev];
+		if (prev_config.force_reload) {
+			delete prev_config.force_reload;
+			continue;
+		}
 
 		let prev_bss = get_config_bss(name, old_config, prev);
 		if (!prev_bss)
@@ -606,7 +623,8 @@ function iface_reload_config(name, phydev, config, old_config)
 		let ifname = old_config.bss[i].ifname;
 		hostapd.printf(`Remove bss '${ifname}' on phy '${name}'`);
 		prev_bss.delete();
-		wdev_remove(ifname);
+		if (!old_config.bss[i].mld_ap)
+			wdev_remove(ifname);
 	}
 
 	// Step 4: rename preserved interfaces, use temporary name on duplicates
@@ -737,17 +755,78 @@ function iface_reload_config(name, phydev, config, old_config)
 	return true;
 }
 
+function bss_check_mld(phydev, iface_name, bss)
+{
+	if (!bss.ifname)
+		return;
+
+	let mld_data = hostapd.data.mld[bss.ifname];
+	if (!mld_data || !mld_data.ifname || !mld_data.macaddr)
+		return;
+
+	bss.mld_bssid = mld_data.macaddr;
+	mld_data.iface[iface_name] = true;
+	if (mld_data.has_wdev)
+		return true;
+
+	hostapd.printf(`Create MLD interface ${bss.ifname} on phy ${phydev.name}, radio mask: ${mld_data.radio_mask}`);
+	let err = phydev.wdev_add(bss.ifname, {
+		mode: "ap",
+		macaddr: mld_data.macaddr,
+		radio_mask: mld_data.radio_mask,
+	});
+	wdev_set_up(bss.ifname, true);
+	if (err) {
+		hostapd.printf(`Failed to create MLD ${bss.ifname} on phy ${phydev.name}: ${err}`);
+		delete mld_data.iface[iface_name];
+		return;
+	}
+
+	mld_data.has_wdev = true;
+
+	return true;
+}
+
+function iface_check_mld(phydev, name, config)
+{
+	phydev = phy_open(phydev.phy);
+
+	for (let mld_name, mld_data in hostapd.data.mld)
+		delete mld_data.iface[name];
+
+	for (let i = 0; i < length(config.bss); i++) {
+		let bss = config.bss[i];
+		if (!bss.mld_ap)
+			continue;
+
+		if (!bss_check_mld(phydev, name, bss)) {
+			hostapd.printf(`Skip MLD interface ${name} on phy ${phydev.name}`);
+			splice(config.bss, i--, 1);
+		}
+	}
+
+	for (let mld_name, mld_data in hostapd.data.mld) {
+		if (length(mld_data.iface) > 0)
+			continue;
+
+		hostapd.printf(`Remove MLD interface ${mld_name}`);
+		wdev_remove(mld_name);
+		delete mld_data.has_wdev;
+	}
+}
+
+function iface_config_remove(name, old_config)
+{
+	hostapd.remove_iface(name);
+	return iface_remove(old_config);
+}
+
 function iface_set_config(name, config)
 {
 	let old_config = hostapd.data.config[name];
 
 	hostapd.data.config[name] = config;
 
-	if (!config) {
-		hostapd.remove_iface(name);
-		return iface_remove(old_config);
-	}
-
 	let phy = config.phy;
 	let phydev = phy_open(phy, config.radio_idx);
 	if (!phydev) {
@@ -755,6 +834,11 @@ function iface_set_config(name, config)
 		return false;
 	}
 
+	config.orig_bss = [ ...config.bss ];
+	iface_check_mld(phydev, name, config);
+	if (!length(config.bss))
+		return iface_config_remove(name, old_config);
+
 	try {
 		let ret = iface_reload_config(name, phydev, config, old_config);
 		if (ret) {
@@ -787,10 +871,6 @@ function config_add_bss(config, name)
 
 function iface_load_config(phy, radio, filename)
 {
-	let f = open(filename, "r");
-	if (!f)
-		return null;
-
 	if (radio < 0)
 		radio = null;
 
@@ -804,6 +884,10 @@ function iface_load_config(phy, radio, filename)
 		orig_file: filename,
 	};
 
+	let f = open(filename, "r");
+	if (!f)
+		return config;
+
 	let bss;
 	let line;
 	while ((line = rtrim(f.read("line"), "\n")) != null) {
@@ -847,6 +931,9 @@ function iface_load_config(phy, radio, filename)
 		if (val[0] == "nas_identifier")
 			bss.nasid = val[1];
 
+		if (val[0] == "mld_ap")
+			bss[val[0]] = int(val[1]);
+
 		if (val[0] == "bss") {
 			bss = config_add_bss(config, val[1]);
 			continue;
@@ -901,6 +988,134 @@ function bss_config(bss_name) {
 	}
 }
 
+function mld_rename_bss(data, name)
+{
+	if (data.ifname == name)
+		return true;
+
+	// TODO: handle rename gracefully
+	return false;
+}
+
+function mld_add_bss(name, data, phy_list, i)
+{
+	let config = data.config;
+	if (!config.phy)
+		return;
+
+	wdev_remove(name);
+	let phydev = phy_list[config.phy];
+	if (!phydev) {
+		phydev = phy_open(config.phy, 0);
+		if (!phydev)
+			return;
+
+		let macaddr_list = {};
+		let phy_config = hostapd.data.config[phy_name(config.phy, 0)];
+		if (phy_config)
+			macaddr_list = iface_config_macaddr_list(phy_config);
+		iface_macaddr_init(phydev, data.config, macaddr_list);
+
+		phy_list[config.phy] = phydev;
+	}
+
+	data.macaddr = config.macaddr;
+	if (!data.macaddr) {
+		data.macaddr = phydev.macaddr_next();
+		data.default_macaddr = true;
+	}
+
+	let radio_mask = 0;
+	for (let r in config.radios)
+		if (r != null)
+			radio_mask |= 1 << r;
+
+	data.radio_mask = radio_mask;
+	data.ifname = name;
+}
+
+function mld_find_matching_config(list, config)
+{
+	for (let name, data in list)
+		if (is_equal(data.config, config))
+			return name;
+}
+
+function mld_reload_interface(name)
+{
+	let config = hostapd.data.config[name];
+	if (!config)
+		return;
+
+	config = { ...config };
+	config.bss = config.orig_bss;
+
+	iface_set_config(name, config);
+}
+
+function mld_set_config(config)
+{
+	let prev_mld = { ...hostapd.data.mld };
+	let new_mld = {};
+	let phy_list = {};
+	let new_config = !length(prev_mld);
+
+	hostapd.printf(`Set MLD config: ${keys(config)}`);
+
+	// find renamed/new interfaces
+	for (let name, data in config) {
+		let prev = mld_find_matching_config(prev_mld, data);
+		if (prev) {
+			let data = prev_mld[prev];
+			if (mld_rename_bss(data, name)) {
+				new_mld[name] = data;
+				delete prev_mld[prev];
+				continue;
+			}
+		}
+
+		new_mld[name] = {
+			config: data,
+			iface: {},
+		};
+	}
+
+	let reload_iface = {};
+	for (let name, data in prev_mld) {
+		delete hostapd.data.mld[name];
+
+		if (!data.ifname)
+			continue;
+
+		for (let iface, bss_list in hostapd.bss) {
+			if (!bss_list[name])
+				continue;
+			reload_iface[iface] = true;
+		}
+	}
+
+	for (let name in reload_iface)
+		mld_reload_interface(name);
+
+	for (let name, data in prev_mld) {
+		if (data.ifname)
+			hostapd.printf(`Remove MLD interface ${name}`);
+		wdev_remove(name);
+	}
+
+	// add new interfaces
+	hostapd.data.mld = new_mld;
+	for (let name, data in new_mld)
+		mld_add_bss(name, data, phy_list);
+
+	if (!new_config)
+		return;
+
+	hostapd.printf(`Reload all interfaces`);
+	for (let name in hostapd.data.config)
+		mld_reload_interface(name);
+}
+
 let main_obj = {
 	reload: {
 		args: {
@@ -988,6 +1203,31 @@ let main_obj = {
 			return ret;
 		})
 	},
+	mld_set: {
+		args: {
+			config: {}
+		},
+		call: ex_wrap(function(req) {
+			if (!req.args.config)
+				return libubus.STATUS_INVALID_ARGUMENT;
+
+			mld_set_config(req.args.config);
+
+			return {
+				pid: hostapd.getpid()
+			};
+		})
+	},
+	config_reset: {
+		args: {
+		},
+		call: ex_wrap(function(req) {
+			for (let name in hostapd.data.config)
+				iface_set_config(name);
+			mld_set_config({});
+			return 0;
+		})
+	},
 	config_set: {
 		args: {
 			phy: "",
diff --git a/package/network/services/hostapd/patches/190-hostapd-Fix-hostapd-crash-if-setup-a-iface-with-.patch b/package/network/services/hostapd/patches/190-hostapd-Fix-hostapd-crash-if-setup-a-iface-with-.patch
new file mode 100644
index 0000000000..d05f272afe
--- /dev/null
+++ b/package/network/services/hostapd/patches/190-hostapd-Fix-hostapd-crash-if-setup-a-iface-with-.patch
@@ -0,0 +1,46 @@
+From c14e53ea013415a29e9c493e9dacafb6dc5b31ee Mon Sep 17 00:00:00 2001
+From: Michael-CY Lee <michael-cy.lee at mediatek.com>
+Date: Fri, 8 Nov 2024 10:20:03 +0800
+Subject: [PATCH] hostapd: Fix hostapd crash if setup a iface with a link bss failed
+
+The crash occurs while some link bsses is traversing all the links by using
+for_each_mld_link(), and hostapd access to the link bss which is already
+been freed.
+
+If hostapd setup a link bss failed, the link should be removed from
+its hostapd_mld. However, the function hostapd_bss_link_deinit
+doesn't remove the link bss correctly if it is the first bss and
+hapd->drv_priv is null. Therefore we should refator the remove iface flow
+as hostapd_remove_iface (used in wifi down cmd).
+
+There are some cases that setup a bss may fail (e.g. afc query failed) or
+trigger channel switch while hostapd is setting up other links.
+The failed link would be add into hostapd_mld while driver_init().
+
+Signed-off-by: Allen Ye <allen.ye at mediatek.com>
+Signed-off-by: Michael-CY Lee <michael-cy.lee at mediatek.com>
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+ src/ap/hostapd.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -3878,6 +3878,7 @@ int hostapd_add_iface(struct hapd_interf
+ 			}
+ 
+ 			if (hostapd_setup_interface(hapd_iface)) {
++				hostapd_bss_link_deinit(hapd_iface->bss[0]);
+ 				hostapd_deinit_driver(
+ 					hapd_iface->bss[0]->driver,
+ 					hapd_iface->bss[0]->drv_priv,
+@@ -5135,6 +5136,9 @@ int hostapd_mld_remove_link(struct hosta
+ 	if (!mld)
+ 		return -1;
+ 
++	if (!hapd->link.next)
++		return 0;
++
+ 	dl_list_del(&hapd->link);
+ 	mld->num_links--;
+ 
diff --git a/package/network/services/hostapd/patches/191-hostapd-add-support-for-specifying-the-link-id-in-th.patch b/package/network/services/hostapd/patches/191-hostapd-add-support-for-specifying-the-link-id-in-th.patch
new file mode 100644
index 0000000000..fa44875aa6
--- /dev/null
+++ b/package/network/services/hostapd/patches/191-hostapd-add-support-for-specifying-the-link-id-in-th.patch
@@ -0,0 +1,58 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Thu, 3 Jul 2025 11:22:26 +0200
+Subject: [PATCH] hostapd: add support for specifying the link id in the config
+
+Makes it easier to dynamically manage links for a MLD at run time.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4934,6 +4934,8 @@ static int hostapd_config_fill(struct ho
+ 		conf->punct_acs_threshold = val;
+ 	} else if (os_strcmp(buf, "mld_ap") == 0) {
+ 		bss->mld_ap = !!atoi(pos);
++	} else if (os_strcmp(buf, "mld_link_id") == 0) {
++		bss->mld_link_id = atoi(pos);
+ 	} else if (os_strcmp(buf, "mld_addr") == 0) {
+ 		if (hwaddr_aton(pos, bss->mld_addr)) {
+ 			wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr",
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -177,6 +177,10 @@ void hostapd_config_defaults_bss(struct
+ 	bss->pasn_comeback_after = 10;
+ 	bss->pasn_noauth = 1;
+ #endif /* CONFIG_PASN */
++
++#ifdef CONFIG_IEEE80211BE
++	bss->mld_link_id = -1;
++#endif
+ }
+ 
+ 
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -967,6 +967,8 @@ struct hostapd_bss_config {
+ 	/* The AP's MLD MAC address within the AP MLD */
+ 	u8 mld_addr[ETH_ALEN];
+ 
++	s8 mld_link_id;
++
+ #ifdef CONFIG_TESTING_OPTIONS
+ 	/*
+ 	 * If set indicate the AP as disabled in the RNR element included in the
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -3105,7 +3105,10 @@ struct hostapd_iface * hostapd_alloc_ifa
+ #ifdef CONFIG_IEEE80211BE
+ static void hostapd_bss_alloc_link_id(struct hostapd_data *hapd)
+ {
+-	hapd->mld_link_id = hapd->mld->next_link_id++;
++	if (hapd->conf->mld_link_id >= 0)
++		hapd->mld_link_id = hapd->conf->mld_link_id;
++	else
++		hapd->mld_link_id = hapd->mld->next_link_id++;
+ 	wpa_printf(MSG_DEBUG, "AP MLD: %s: Link ID %d assigned.",
+ 		   hapd->mld->name, hapd->mld_link_id);
+ }
diff --git a/package/network/services/hostapd/patches/300-noscan.patch b/package/network/services/hostapd/patches/300-noscan.patch
index 6d97691855..314f0eff1b 100644
--- a/package/network/services/hostapd/patches/300-noscan.patch
+++ b/package/network/services/hostapd/patches/300-noscan.patch
@@ -18,7 +18,7 @@ Subject: [PATCH] Add noscan, no_ht_coex config options
  	} else if (os_strcmp(buf, "ht_capab") == 0) {
 --- a/src/ap/ap_config.h
 +++ b/src/ap/ap_config.h
-@@ -1105,6 +1105,8 @@ struct hostapd_config {
+@@ -1107,6 +1107,8 @@ struct hostapd_config {
  
  	int ht_op_mode_fixed;
  	u16 ht_capab;
diff --git a/package/network/services/hostapd/patches/600-ubus_support.patch b/package/network/services/hostapd/patches/600-ubus_support.patch
index 0368726c81..b66d61bad5 100644
--- a/package/network/services/hostapd/patches/600-ubus_support.patch
+++ b/package/network/services/hostapd/patches/600-ubus_support.patch
@@ -159,7 +159,7 @@ probe/assoc/auth requests via object subscribe.
  
  	if (iface->is_no_ir) {
  		hostapd_set_state(iface, HAPD_IFACE_NO_IR);
-@@ -3527,6 +3532,7 @@ void hostapd_interface_deinit_free(struc
+@@ -3530,6 +3535,7 @@ void hostapd_interface_deinit_free(struc
  		   (unsigned int) iface->conf->num_bss);
  	driver = iface->bss[0]->driver;
  	drv_priv = iface->bss[0]->drv_priv;
diff --git a/package/network/services/hostapd/patches/601-ucode_support.patch b/package/network/services/hostapd/patches/601-ucode_support.patch
index ec25cd4084..a5aa2e6a79 100644
--- a/package/network/services/hostapd/patches/601-ucode_support.patch
+++ b/package/network/services/hostapd/patches/601-ucode_support.patch
@@ -113,6 +113,15 @@ as adding/removing interfaces.
  	hostapd_ubus_free_bss(hapd);
  	accounting_deinit(hapd);
  	hostapd_deinit_wpa(hapd);
+@@ -625,7 +628,7 @@ void hostapd_free_hapd_data(struct hosta
+  * If the BSS being removed is the first link, the next link becomes the first
+  * link.
+  */
+-static void hostapd_bss_link_deinit(struct hostapd_data *hapd)
++void hostapd_bss_link_deinit(struct hostapd_data *hapd)
+ {
+ #ifdef CONFIG_IEEE80211BE
+ 	int i;
 @@ -737,6 +740,7 @@ void hostapd_cleanup_iface_partial(struc
  static void hostapd_cleanup_iface(struct hostapd_iface *iface)
  {
@@ -139,7 +148,47 @@ as adding/removing interfaces.
  {
  	struct hostapd_bss_config *conf = hapd->conf;
  	u8 ssid[SSID_MAX_LEN + 1];
-@@ -1518,6 +1522,8 @@ setup_mld:
+@@ -1434,7 +1438,13 @@ static int hostapd_setup_bss(struct host
+ 
+ 	if (!first || first == -1) {
+ 		u8 *addr = hapd->own_addr;
++		bool use_existing = first == -1;
+ 
++#ifdef CONFIG_IEEE80211BE
++		if (hapd->conf->mld_ap) {
++			addr = NULL;
++		} else
++#endif /* CONFIG_IEEE80211BE */
+ 		if (!is_zero_ether_addr(conf->bssid)) {
+ 			/* Allocate the configured BSSID. */
+ 			os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
+@@ -1469,6 +1479,7 @@ static int hostapd_setup_bss(struct host
+ 					   hapd->mld_link_id, hapd->conf->iface);
+ 				goto setup_mld;
+ 			}
++			use_existing = true;
+ 		}
+ #endif /* CONFIG_IEEE80211BE */
+ 
+@@ -1477,7 +1488,7 @@ static int hostapd_setup_bss(struct host
+ 				   conf->iface, addr, hapd,
+ 				   &hapd->drv_priv, force_ifname, if_addr,
+ 				   conf->bridge[0] ? conf->bridge : NULL,
+-				   first == -1)) {
++				   use_existing)) {
+ 			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+ 				   MACSTR ")", MAC2STR(hapd->own_addr));
+ 			hapd->interface_added = 0;
+@@ -1500,7 +1511,7 @@ static int hostapd_setup_bss(struct host
+ 
+ #ifdef CONFIG_IEEE80211BE
+ setup_mld:
+-	if (hapd->conf->mld_ap && !first) {
++	if (hapd->conf->mld_ap && first != 1) {
+ 		wpa_printf(MSG_DEBUG,
+ 			   "MLD: Set link_id=%u, mld_addr=" MACSTR
+ 			   ", own_addr=" MACSTR,
+@@ -1518,6 +1529,8 @@ setup_mld:
  	}
  #endif /* CONFIG_IEEE80211BE */
  
@@ -148,7 +197,16 @@ as adding/removing interfaces.
  	if (conf->wmm_enabled < 0)
  		conf->wmm_enabled = hapd->iconf->ieee80211n |
  			hapd->iconf->ieee80211ax;
-@@ -2516,7 +2522,7 @@ static int hostapd_owe_iface_iter2(struc
+@@ -1843,7 +1856,7 @@ int hostapd_set_acl(struct hostapd_data
+ }
+ 
+ 
+-static int hostapd_set_ctrl_sock_iface(struct hostapd_data *hapd)
++int hostapd_set_ctrl_sock_iface(struct hostapd_data *hapd)
+ {
+ #ifdef CONFIG_IEEE80211BE
+ 	int ret;
+@@ -2516,7 +2529,7 @@ static int hostapd_owe_iface_iter2(struc
  #endif /* CONFIG_OWE */
  
  
@@ -157,7 +215,7 @@ as adding/removing interfaces.
  {
  #ifdef CONFIG_OWE
  	/* Check whether the enabled BSS can complete OWE transition mode
-@@ -2986,7 +2992,7 @@ hostapd_alloc_bss_data(struct hostapd_if
+@@ -2986,7 +2999,7 @@ hostapd_alloc_bss_data(struct hostapd_if
  }
  
  
@@ -166,7 +224,16 @@ as adding/removing interfaces.
  {
  	if (!hapd)
  		return;
-@@ -4070,7 +4076,8 @@ int hostapd_remove_iface(struct hapd_int
+@@ -3194,7 +3207,7 @@ fail:
+ }
+ 
+ 
+-static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces)
++void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces)
+ {
+ #ifdef CONFIG_IEEE80211BE
+ 	struct hostapd_mld *mld, **all_mld;
+@@ -4074,7 +4087,8 @@ int hostapd_remove_iface(struct hapd_int
  		hapd_iface = interfaces->iface[i];
  		if (hapd_iface == NULL)
  			return -1;
@@ -213,16 +280,24 @@ as adding/removing interfaces.
  	void *owner;
  	char *config_fname;
  	struct hostapd_config *conf;
-@@ -787,6 +794,8 @@ struct hostapd_iface * hostapd_init(stru
+@@ -787,11 +794,16 @@ struct hostapd_iface * hostapd_init(stru
  struct hostapd_iface *
  hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
  			   const char *config_fname, int debug);
++int hostapd_set_ctrl_sock_iface(struct hostapd_data *hapd);
 +int hostapd_setup_bss(struct hostapd_data *hapd, int first, bool start_beacon);
++void hostapd_bss_link_deinit(struct hostapd_data *hapd);
 +void hostapd_bss_deinit(struct hostapd_data *hapd);
  void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
  				  struct hapd_interfaces *interfaces);
  void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
-@@ -817,6 +826,7 @@ hostapd_switch_channel_fallback(struct h
+ 			   int reassoc);
+ void hostapd_interface_deinit_free(struct hostapd_iface *iface);
++void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces);
+ int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
+ int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+ int hostapd_reload_bss_only(struct hostapd_data *bss);
+@@ -817,6 +829,7 @@ hostapd_switch_channel_fallback(struct h
  void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
  void hostapd_periodic_iface(struct hostapd_iface *iface);
  int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
diff --git a/package/network/services/hostapd/patches/701-reload_config_inline.patch b/package/network/services/hostapd/patches/701-reload_config_inline.patch
index 236337979a..b91ff62e05 100644
--- a/package/network/services/hostapd/patches/701-reload_config_inline.patch
+++ b/package/network/services/hostapd/patches/701-reload_config_inline.patch
@@ -8,7 +8,7 @@ as adding/removing interfaces.
 
 --- a/hostapd/config_file.c
 +++ b/hostapd/config_file.c
-@@ -4981,7 +4981,14 @@ struct hostapd_config * hostapd_config_r
+@@ -4983,7 +4983,14 @@ struct hostapd_config * hostapd_config_r
  	int errors = 0;
  	size_t i;
  
@@ -58,7 +58,7 @@ as adding/removing interfaces.
  		return NULL;
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -3380,8 +3380,13 @@ hostapd_interface_init_bss(struct hapd_i
+@@ -3400,8 +3400,13 @@ hostapd_interface_init_bss(struct hapd_i
  		}
  	}
  
diff --git a/package/network/services/hostapd/patches/720-iface_max_num_sta.patch b/package/network/services/hostapd/patches/720-iface_max_num_sta.patch
index 5dfe839e5a..f0e3da9f59 100644
--- a/package/network/services/hostapd/patches/720-iface_max_num_sta.patch
+++ b/package/network/services/hostapd/patches/720-iface_max_num_sta.patch
@@ -25,7 +25,7 @@ full device, e.g. in order to deal with hardware/driver limitations
  	} else if (os_strcmp(buf, "extended_key_id") == 0) {
 --- a/src/ap/ap_config.h
 +++ b/src/ap/ap_config.h
-@@ -1069,6 +1069,8 @@ struct hostapd_config {
+@@ -1071,6 +1071,8 @@ struct hostapd_config {
  	unsigned int track_sta_max_num;
  	unsigned int track_sta_max_age;
  
@@ -79,7 +79,7 @@ full device, e.g. in order to deal with hardware/driver limitations
  {
 --- a/src/ap/hostapd.h
 +++ b/src/ap/hostapd.h
-@@ -828,6 +828,7 @@ void hostapd_periodic_iface(struct hosta
+@@ -831,6 +831,7 @@ void hostapd_periodic_iface(struct hosta
  int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
  void hostapd_owe_update_trans(struct hostapd_iface *iface);;
  void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
diff --git a/package/network/services/hostapd/patches/770-radius_server.patch b/package/network/services/hostapd/patches/770-radius_server.patch
index d6fdb167f6..fdf5fed397 100644
--- a/package/network/services/hostapd/patches/770-radius_server.patch
+++ b/package/network/services/hostapd/patches/770-radius_server.patch
@@ -29,7 +29,7 @@ handle reload.
  
  #ifndef CONFIG_NO_HOSTAPD_LOGGER
  static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
-@@ -834,6 +835,11 @@ int main(int argc, char *argv[])
+@@ -838,6 +839,11 @@ int main(int argc, char *argv[])
  	if (os_program_init())
  		return -1;
  
diff --git a/package/network/services/hostapd/patches/780-Implement-APuP-Access-Point-Micro-Peering.patch b/package/network/services/hostapd/patches/780-Implement-APuP-Access-Point-Micro-Peering.patch
index 9528900e46..271b88c7c0 100644
--- a/package/network/services/hostapd/patches/780-Implement-APuP-Access-Point-Micro-Peering.patch
+++ b/package/network/services/hostapd/patches/780-Implement-APuP-Access-Point-Micro-Peering.patch
@@ -53,7 +53,7 @@ Hotfix-by: Sebastian Gottschall https://github.com/mirror/dd-wrt/commit/0c3001a6
  
 --- a/hostapd/config_file.c
 +++ b/hostapd/config_file.c
-@@ -4974,6 +4974,15 @@ static int hostapd_config_fill(struct ho
+@@ -4976,6 +4976,15 @@ static int hostapd_config_fill(struct ho
  		bss->mld_indicate_disabled = atoi(pos);
  #endif /* CONFIG_TESTING_OPTIONS */
  #endif /* CONFIG_IEEE80211BE */
@@ -71,7 +71,7 @@ Hotfix-by: Sebastian Gottschall https://github.com/mirror/dd-wrt/commit/0c3001a6
  			   "Line %d: unknown configuration item '%s'",
 --- a/src/ap/ap_config.h
 +++ b/src/ap/ap_config.h
-@@ -982,6 +982,35 @@ struct hostapd_bss_config {
+@@ -984,6 +984,35 @@ struct hostapd_bss_config {
  	int mbssid_index;
  
  	bool spp_amsdu;
diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c
index d3eadba15a..cbe2ee9506 100644
--- a/package/network/services/hostapd/src/src/ap/ucode.c
+++ b/package/network/services/hostapd/src/src/ap/ucode.c
@@ -201,6 +201,49 @@ bss_reload_vlans(struct hostapd_data *hapd, struct hostapd_bss_config *bss)
 	return 0;
 }
 
+static void
+__uc_hostapd_bss_stop(struct hostapd_data *hapd)
+{
+	struct hostapd_iface *iface = hapd->iface;
+
+	if (!hapd->started)
+		return;
+
+	hostapd_bss_deinit_no_free(hapd);
+	hostapd_drv_stop_ap(hapd);
+	hostapd_bss_link_deinit(hapd);
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd == iface->bss[0])
+	        hostapd_if_link_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface,
+                       hapd->mld_link_id);
+#endif
+
+	hostapd_free_hapd_data(hapd);
+}
+
+static int
+__uc_hostapd_bss_start(struct hostapd_data *hapd)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	bool first = hapd == iface->bss[0];
+	int ret;
+
+	if (hapd->started)
+		return 0;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		first = false;
+#endif
+
+	ret = hostapd_setup_bss(hapd, first, true);
+	hostapd_neighbor_set_own_report(hapd);
+	hostapd_owe_update_trans(iface);
+
+	return ret;
+}
+
 static uc_value_t *
 uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
 {
@@ -241,12 +284,10 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
 
 		swap_field(ssid.wpa_psk_file);
 		ret = bss_reload_vlans(hapd, bss);
-		goto done;
+		goto free;
 	}
 
-	hostapd_bss_deinit_no_free(hapd);
-	hostapd_drv_stop_ap(hapd);
-	hostapd_free_hapd_data(hapd);
+	__uc_hostapd_bss_stop(hapd);
 
 	old_bss = hapd->conf;
 	for (i = 0; i < iface->conf->num_bss; i++)
@@ -258,13 +299,9 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
 	if (hapd == iface->bss[0])
 		memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN);
 
-	hostapd_setup_bss(hapd, hapd == iface->bss[0], true);
-	hostapd_neighbor_set_own_report(hapd);
+	ret = __uc_hostapd_bss_start(hapd);
 	hostapd_ucode_update_interfaces();
-	hostapd_owe_update_trans(iface);
 
-done:
-	ret = 0;
 free:
 	hostapd_config_free(conf);
 out:
@@ -326,8 +363,13 @@ uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
 	hostapd_bss_deinit(hapd);
 	hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
 	hostapd_config_free_bss(hapd->conf);
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->mld)
+		hapd->mld->refcount--;
+#endif
 	os_free(hapd);
 
+	hostapd_cleanup_unused_mlds(iface->interfaces);
 	hostapd_ucode_update_interfaces();
 
 	return NULL;
@@ -365,7 +407,12 @@ uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
 #ifdef CONFIG_IEEE80211BE
 	os_strlcpy(hapd->ctrl_sock_iface, hapd->conf->iface,
 		   sizeof(hapd->ctrl_sock_iface));
+	if (hapd->conf->mld_ap) {
+		hostapd_bss_setup_multi_link(hapd, iface->interfaces);
+		hostapd_set_ctrl_sock_iface(hapd);
+	}
 #endif
+
 	if (interfaces->ctrl_iface_init &&
 	    interfaces->ctrl_iface_init(hapd) < 0)
 		goto free_hapd;
@@ -673,6 +720,7 @@ uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
 {
 	struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
 	uc_value_t *ifname_arg = uc_fn_arg(0);
+	uc_value_t *skip_rename = uc_fn_arg(1);
 	char prev_ifname[IFNAMSIZ + 1];
 	struct sta_info *sta;
 	const char *ifname;
@@ -688,9 +736,11 @@ uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
 	if (interfaces->ctrl_iface_deinit)
 		interfaces->ctrl_iface_deinit(hapd);
 
-	ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
-	if (ret)
-		goto out;
+	if (!ucv_is_truish(skip_rename)) {
+		ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
+		if (ret)
+			goto out;
+	}
 
 	for (sta = hapd->sta_list; sta; sta = sta->next) {
 		char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];




More information about the lede-commits mailing list