| Message ID | CAG0XsJHUbC3YUs17JRfyEz-=T9AktgS-uWNvRHbk_a9pebDwWw@mail.gmail.com |
|---|---|
| State | Changes Requested |
| Headers | show |
| Series | ap: skip untagged STA VLAN binding for MLO stations | expand |
On Sat, Sep 26, 2026 at 03:39:44PM +0000, hc86862997@gmail.com wrote: > hostapd's dynamic-VLAN-for-MLO support sends SET_STATION with > NL80211_ATTR_STA_VLAN (plus the MLD link id) for MLO stations even when > no VLAN is configured (vlan_id == 0), and the bind is deliberately not > skipped for vlan_id == old_vlanid == 0 (the "needs rebind after > sta_remove" path in ap_sta_bind_vlan_helper()). > > On kernels without ML VLAN support (no CFG80211_STA_AP_ML_STA; > mac80211/cfg80211 reject VLAN operations for ML stations) the kernel > answers -EINVAL, and every rebind - triggered by association and by > each rekey/EAPOL reauth - fails association processing and tears the > MLO client down: > > hostapd: nl80211: NL80211_ATTR_STA_VLAN (addr=<sta> ifname=ap-mld1 > vlan_id=0) failed: -22 (Invalid argument) > > Observed as a Wi-Fi 7 MLO client being kicked off an ap-mld interface > every ~60-90 seconds, deterministically, on MT7996 AP hardware (hostapd > 831364bf, kernel 6.18.53 / backports 7.2). > > These two guards skip the driver call for untagged MLO stations only; > dynamic VLAN keeps its previous behaviour until the kernel side gains > ML VLAN support: > > 1. src/ap/sta_info.c - skip the helper-level bind for MLO stations with > vlan_id == 0 (covers the normal association path). > 2. src/drivers/driver_nl80211.c - funnel guard in i802_set_sta_vlan(): > skip any untagged (vlan_id == 0) bind that carries a link id, which > covers every caller by construction. > > With this applied, zero STA_VLAN errors across many MLO associations > and rekeys, and the client stays connected; legacy clients on non-MLD > interfaces and dynamic-VLAN users behave exactly as before. (Also > reported to OpenWrt, which carries the patch: > https://github.com/openwrt/openwrt/issues/25415 ) > > --- Please read the top level CONTRIBUTIONS file. I would need a Signed-off-by: line from the author or copyright holder of the patch to be able to consider applying it to hostap.git. > --- a/src/ap/sta_info.c > +++ b/src/ap/sta_info.c > @@ -1493,6 +1493,19 @@ > + /* > + * The kernel side used in this build rejects VLAN operations for > + * MLO stations with -EINVAL (see the driver_nl80211.c part of this > + * patch), and the error makes ap_sta_bind_vlan() fail association This comment should not reference "this patch" since that context is quite unclear once the code is applied. Furthermore, sta_info.c should not have any nl80211 specific behavior or comments since this is supposed to be independent of the driver interface. > + /* > + * Every STA VLAN binding funnels through here. On an MLD AP each > + * command carries NL80211_ATTR_MLO_LINK_ID, and the kernel side > + * used in this build (no ML VLAN support) rejects such untagged What is "this build" referring to? That sounds quite unclear and most likely incorrect for many cases. > + * binds with -EINVAL, which hostapd turns into a station teardown. > + * vlan_id 0 carries no VLAN semantics, so skip the command whenever > + * a link id is present; the station stays bound to its own BSS > + * interface as intended. Dynamic VLAN (vlan_id > 0) keeps its > + * previous behaviour. > + */ > + if (vlan_id == 0 && link_id != NL80211_DRV_LINK_ID_NA) > + return 0; Where is the dynamic VLAN case handled for ML associations if this place skips it?
--- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1493,6 +1493,19 @@ } #endif /* CONFIG_IEEE80211BE */ + /* + * The kernel side used in this build rejects VLAN operations for + * MLO stations with -EINVAL (see the driver_nl80211.c part of this + * patch), and the error makes ap_sta_bind_vlan() fail association + * processing, which bounces MLO clients. An untagged station + * (vlan_id 0) has no VLAN binding to perform anyway, so skip the + * driver call in that case. Dynamic VLAN together with MLO keeps + * its previous (rejected) behaviour until the kernel side gains + * ML VLAN support. + */ + if (ap_sta_is_mld(hapd, sta) && sta->vlan_id == 0) + return 0; + if ((sta->flags & WLAN_STA_WDS) && sta->vlan_id == 0) { wpa_printf(MSG_DEBUG, "Do not override WDS VLAN assignment for STA " --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -9134,6 +9134,18 @@ ", ifname=%s[%d], vlan_id=%d)", bss->ifname, if_nametoindex(bss->ifname), MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id); + /* + * Every STA VLAN binding funnels through here. On an MLD AP each + * command carries NL80211_ATTR_MLO_LINK_ID, and the kernel side + * used in this build (no ML VLAN support) rejects such untagged + * binds with -EINVAL, which hostapd turns into a station teardown. + * vlan_id 0 carries no VLAN semantics, so skip the command whenever + * a link id is present; the station stays bound to its own BSS + * interface as intended. Dynamic VLAN (vlan_id > 0) keeps its + * previous behaviour. + */ + if (vlan_id == 0 && link_id != NL80211_DRV_LINK_ID_NA) + return 0; if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&