From 8be9eb94da7936dc366f9cd4c4704bc7ddf166d6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 29 Sep 2026 15:30:01 +0200 Subject: [PATCH] hostapd: keep the configured BSSID of the first link of an AP MLD 601-ucode_support.patch adds the first link of an AP MLD on the existing netdev with no address, and hostapd_setup_bss() then takes the netdev address, the MLD address, as the link address. A link on a radio other than radio 0, whose configured BSSID hostapd.uc keeps apart from the MLD address (bss_mld_addr()), beaconed with the MLD address, while ubus get_status reported the configured BSSID. A subscriber that built its BSS Transition candidates from get_status named a BSSID that nothing transmitted. Add a patch that keeps the configured BSSID as the link address and takes the netdev address as the MLD address, as hostapd_driver_init() does for the first interface. Without a configured BSSID, the link takes a random address with the OUI of the netdev, as hostapd_driver_init() does (pre_setup_mld), so that the link address cannot be the MLD address: a later link that is given the MLD address as its BSSID would fail ADD_LINK with -EALREADY. hostapd.uc always sets a BSSID, so OpenWrt configurations take the configured address. Have get_status and the channel-switch event report hapd->own_addr, the address the BSS transmits with, instead of conf->bssid, which is all zeros where the configuration names none. Fixes: 816c2d86e749 ("hostapd: add support for MLO interfaces in ucode") Signed-off-by: Felix Fietkau --- ...e-configured-bssid-of-the-first-link.patch | 56 +++++++++++++++++++ .../hostapd/patches/600-ubus_support.patch | 6 +- .../hostapd/patches/601-ucode_support.patch | 12 ++-- .../patches/701-reload_config_inline.patch | 2 +- .../services/hostapd/src/src/ap/ubus.c | 4 +- 5 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 package/network/services/hostapd/patches/196-ap-mld-keep-the-configured-bssid-of-the-first-link.patch diff --git a/package/network/services/hostapd/patches/196-ap-mld-keep-the-configured-bssid-of-the-first-link.patch b/package/network/services/hostapd/patches/196-ap-mld-keep-the-configured-bssid-of-the-first-link.patch new file mode 100644 index 00000000000..b8a888b5282 --- /dev/null +++ b/package/network/services/hostapd/patches/196-ap-mld-keep-the-configured-bssid-of-the-first-link.patch @@ -0,0 +1,56 @@ +From 6970ef8912a1141c812f5f1d27ad78c0e968bdd1 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Tue, 29 Sep 2026 09:20:27 +0200 +Subject: [PATCH] AP MLD: keep the configured BSSID of the first link + +hostapd_setup_bss() adds the first link of an AP MLD on an existing +netdev with no address (601-ucode_support.patch sets addr = NULL for +mld_ap), takes the netdev address as own_addr, and copies it into +mld->mld_addr. The configured BSSID of that link is lost: the link +beacons with the MLD address, while hostapd's ubus get_status still +reports conf->bssid. A BSS Transition candidate list built from +get_status then names a BSSID that nothing transmits. + +hostapd.uc gives the MLD address as its BSSID to a link on radio 0 +only (bss_mld_addr()), so a single-link AP MLD on another radio, and +the first link of any AP MLD that comes up on another radio, ran with +the wrong link address. Where the radio-0 link came up second, its +ADD_LINK carried the MLD address a second time, which mac80211 +ieee80211_check_dup_link_addrs() refuses with -EALREADY. + +Take the netdev address as the MLD address and keep the configured +BSSID as the link address, as hostapd_driver_init() does for the first +interface (pre_setup_mld). Without a configured BSSID, take a random +address with the OUI of the netdev, as pre_setup_mld does, so that the +link address cannot be the MLD address that a later link is given. A +link on radio 0 has the MLD address as its configured BSSID, and runs +with it as before. + +Fixes: 816c2d86e749 ("hostapd: add support for MLO interfaces in ucode") +Signed-off-by: Felix Fietkau +--- + src/ap/hostapd.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/src/ap/hostapd.c ++++ b/src/ap/hostapd.c +@@ -1604,12 +1604,17 @@ static int hostapd_setup_bss(struct host + os_memcpy(hapd->own_addr, if_addr, ETH_ALEN); + + #ifdef CONFIG_IEEE80211BE ++ /* As hostapd_driver_init() does for the first interface */ + if (hapd->conf->mld_ap) { + wpa_printf(MSG_DEBUG, + "Setup of first link (%d) BSS of MLD %s", + hapd->mld_link_id, hapd->conf->iface); +- os_memcpy(hapd->mld->mld_addr, hapd->own_addr, +- ETH_ALEN); ++ os_memcpy(hapd->mld->mld_addr, if_addr, ETH_ALEN); ++ if (!is_zero_ether_addr(conf->bssid)) ++ os_memcpy(hapd->own_addr, conf->bssid, ++ ETH_ALEN); ++ else ++ random_mac_addr_keep_oui(hapd->own_addr); + } + #endif /* CONFIG_IEEE80211BE */ + } diff --git a/package/network/services/hostapd/patches/600-ubus_support.patch b/package/network/services/hostapd/patches/600-ubus_support.patch index bcf9a0e3b66..03e5b41d29f 100644 --- a/package/network/services/hostapd/patches/600-ubus_support.patch +++ b/package/network/services/hostapd/patches/600-ubus_support.patch @@ -143,7 +143,7 @@ probe/assoc/auth requests via object subscribe. return 0; } -@@ -2676,6 +2679,7 @@ static int hostapd_setup_interface_compl +@@ -2681,6 +2684,7 @@ static int hostapd_setup_interface_compl if (err) goto fail; @@ -151,7 +151,7 @@ probe/assoc/auth requests via object subscribe. wpa_printf(MSG_DEBUG, "Completing interface initialization"); if (iface->freq) { #ifdef NEED_AP_MLME -@@ -2905,6 +2909,7 @@ dfs_offload: +@@ -2910,6 +2914,7 @@ dfs_offload: fail: wpa_printf(MSG_ERROR, "Interface initialization failed"); @@ -159,7 +159,7 @@ probe/assoc/auth requests via object subscribe. if (iface->is_no_ir) { hostapd_set_state(iface, HAPD_IFACE_NO_IR); -@@ -3655,6 +3660,7 @@ void hostapd_interface_deinit_free(struc +@@ -3660,6 +3665,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 a7f153295cc..fb0d9965ad4 100644 --- a/package/network/services/hostapd/patches/601-ucode_support.patch +++ b/package/network/services/hostapd/patches/601-ucode_support.patch @@ -324,7 +324,7 @@ as adding/removing interfaces. wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" MACSTR ")", MAC2STR(hapd->own_addr)); hapd->interface_added = 0; -@@ -1637,6 +1648,8 @@ setup_mld: +@@ -1642,6 +1653,8 @@ setup_mld: } #endif /* CONFIG_IEEE80211BE */ @@ -333,7 +333,7 @@ as adding/removing interfaces. if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n | hapd->iconf->ieee80211ax; -@@ -1965,7 +1978,7 @@ int hostapd_set_acl(struct hostapd_data +@@ -1970,7 +1983,7 @@ int hostapd_set_acl(struct hostapd_data } @@ -342,7 +342,7 @@ as adding/removing interfaces. { #ifdef CONFIG_IEEE80211BE int ret; -@@ -2638,7 +2651,7 @@ static int hostapd_owe_iface_iter2(struc +@@ -2643,7 +2656,7 @@ static int hostapd_owe_iface_iter2(struc #endif /* CONFIG_OWE */ @@ -351,7 +351,7 @@ as adding/removing interfaces. { #ifdef CONFIG_OWE /* Check whether the enabled BSS can complete OWE transition mode -@@ -3110,7 +3123,7 @@ hostapd_alloc_bss_data(struct hostapd_if +@@ -3115,7 +3128,7 @@ hostapd_alloc_bss_data(struct hostapd_if } @@ -360,7 +360,7 @@ as adding/removing interfaces. { if (!hapd) return; -@@ -3319,7 +3332,7 @@ fail: +@@ -3324,7 +3337,7 @@ fail: } @@ -369,7 +369,7 @@ as adding/removing interfaces. { #ifdef CONFIG_IEEE80211BE struct hostapd_mld *mld, **all_mld; -@@ -4200,7 +4213,8 @@ int hostapd_remove_iface(struct hapd_int +@@ -4205,7 +4218,8 @@ int hostapd_remove_iface(struct hapd_int hapd_iface = interfaces->iface[i]; if (hapd_iface == NULL) return -1; 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 f1ce85fff23..4f464993698 100644 --- a/package/network/services/hostapd/patches/701-reload_config_inline.patch +++ b/package/network/services/hostapd/patches/701-reload_config_inline.patch @@ -58,7 +58,7 @@ as adding/removing interfaces. return NULL; --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c -@@ -3780,8 +3780,13 @@ hostapd_interface_init_bss(struct hapd_i +@@ -3785,8 +3785,13 @@ hostapd_interface_init_bss(struct hapd_i } } diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c index 741f18642eb..25c11d2e301 100644 --- a/package/network/services/hostapd/src/src/ap/ubus.c +++ b/package/network/services/hostapd/src/src/ap/ubus.c @@ -453,7 +453,7 @@ hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj, blob_buf_init(&b, 0); blobmsg_add_string(&b, "driver", hapd->driver->name); blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state)); - blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->conf->bssid)); + blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->own_addr)); memset(ssid, 0, SSID_MAX_LEN + 1); memcpy(ssid, hapd->conf->ssid.ssid, ssid_len); @@ -2757,7 +2757,7 @@ void hostapd_ubus_notify_csa(struct hostapd_data *hapd, int freq) blob_buf_init(&b, 0); blobmsg_add_string(&b, "ifname", hapd->conf->iface); blobmsg_add_u32(&b, "freq", freq); - blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->conf->bssid)); + blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->own_addr)); ubus_notify(ctx, obj, "channel-switch", b.head, -1); }