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: 816c2d86e7 ("hostapd: add support for MLO interfaces in ucode")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Felix Fietkau committed 2026-10-08 09:50:38 +02:00
1 parent c7caee422b
commit 8be9eb94da
5 files changed
+68 -12

No files matched your search

@@ -0,0 +1,56 @@
From 6970ef8912a1141c812f5f1d27ad78c0e968bdd1 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@nbd.name>
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 <nbd@nbd.name>
---
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 */
}
@@ -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;
@@ -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;
@@ -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
}
}
@@ -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);
}