* [BUG] mac80211: ap_isolate is not applied to AP_VLAN interfaces
@ 2026-11-06 2:07 Christopher Hill
2026-11-07 3:52 ` [PATCH wireless] wifi: mac80211: apply ap_isolate to AP_VLAN stations Christopher Hill
0 siblings, 1 reply; 2+ messages in thread
From: Christopher Hill @ 2026-11-06 2:07 UTC (permalink / raw)
To: johannes; +Cc: linux-wireless
Hi,
When an AP uses AP_VLAN interfaces (hostapd dynamic_vlan, e.g. VLAN
assignment via wpa_psk_file or RADIUS), the ap_isolate setting of the
BSS is not applied to stations bound to an AP_VLAN. mac80211 keeps
forwarding frames between stations of the same AP_VLAN even with
ap_isolate=1, so client isolation is silently ineffective for those
stations.
Code (wireless tree as of 2026-11-04, also v6.18 and mainline
7.4-rc1):
1. ieee80211_change_bss() (cfg.c) stores ap_isolate only on the
sdata the request was issued for, i.e. the AP interface:
if (params->ap_isolate >= 0) {
if (params->ap_isolate)
sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
else
sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
ieee80211_check_fast_rx_iface(sdata);
}
2. When an AP_VLAN is opened (ieee80211_do_open(), iface.c), several
settings are copied from the master AP sdata (control_port_*,
cab_queue, hw_queue, chandef, tailroom count), but not the
IEEE80211_SDATA_DONT_BRIDGE_PACKETS flag. There is also no
propagation to existing VLANs when ap_isolate changes later.
3. The intra-BSS forwarding decisions test the flag on the station’s
own sdata, which is the AP_VLAN sdata for VLAN stations:
- ieee80211_deliver_skb() (rx.c), sdata = rx->sdata:
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && …
- ieee80211_check_fast_rx() (rx.c), sdata = sta->sdata:
fastrx.internal_forward =
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && ...
Interestingly, __ieee80211_check_fast_rx_iface() already iterates
stations whose sdata->bss matches the AP, i.e. it re-evaluates fast-rx
for AP_VLAN stations when ap_isolate changes - but the evaluated flag
lives on the AP_VLAN sdata, which is never updated.
By contrast, multicast_to_unicast is stored in struct ieee80211_if_ap
(sdata->u.ap) and read via sdata->bss in
ieee80211_multicast_to_unicast() (tx.c), so it does apply to AP_VLAN
interfaces.
Observed effect:
Linksys E8450 (MT7622 + MT7915E, mt76), OpenWrt 25.12.6 (mac80211
from backports 6.18.55), hostapd 2025-08-26 (ca266cc2), dynamic_vlan=2
with VLANs from wpa_psk_file, ap_isolate=1, multicast_to_unicast=1.
The AP_VLAN netdevs (wlan1-vlanN) are bridge ports with hairpin on (as
OpenWrt sets up for multicast_to_unicast, expecting ap_isolate to stop
mac80211's own intra-BSS forwarding). Every multicast frame from a
station is delivered twice to the other stations on the same AP_VLAN:
once by mac80211 (not isolated) and once by the bridge (hairpin).
E.g. an IPv6 all-nodes ping from one station gets two replies per
sequence from every other station on the same radio/VLAN, one from
wired hosts. Turning hairpin off on the AP_VLAN bridge port stops the
duplicates immediately, while the stations still answer - i.e. the
remaining copy is forwarded by mac80211 despite ap_isolate=1.
The sdata flags in debugfs confirm that the isolation bit
(IEEE80211_SDATA_DONT_BRIDGE_PACKETS, 0x8) is only set on the AP
interfaces, never on the AP_VLANs (ap_isolate=1 in hostapd):
/sys/kernel/debug/ieee80211/<phy>/netdev:<if>/flags
wlan0 flags=0x28 (IN_DRIVER|DONT_BRIDGE_PACKETS)
wlan0-vlan10 flags=0x1 (ALLMULTI)
wlan0-vlan20 flags=0x1
wlan0-vlan30 flags=0x1
wlan0-vlan40 flags=0x1
wlan1 flags=0x28 (IN_DRIVER|DONT_BRIDGE_PACKETS)
wlan1-vlan10 flags=0x1 (ALLMULTI)
wlan1-vlan20 flags=0x1
wlan1-vlan30 flags=0x1
wlan1-vlan40 flags=0x1
Reproduction:
1. hostapd AP with dynamic_vlan=1 or 2 and per-station VLAN
assignment (wpa_psk_file "vlanid=10 ..." or RADIUS), ap_isolate=1.
2. Check the sdata flags: cat
/sys/kernel/debug/ieee80211/<phy>/netdev:<ap-if>/flags shows 0x8
set; the same file for the AP_VLAN netdev does not.
3. Bridge the AP_VLAN netdev (e.g. wlan0-vlan10) with hairpin OFF.
4. Associate two stations A and B that both land on VLAN 10 on the
same radio.
5. With correct isolation, A and B must not reach each other (no
bridge hairpin, no mac80211 intra-BSS forwarding).
6. Observed: A pings B (IPv4 unicast): replies received.
ping -6 ff02::1 from A: B replies (once per sequence).
Both stations on the same radio and VLAN, hairpin off on the
AP_VLAN bridge port, ap_isolate=1: the traffic can only have been
forwarded by mac80211.
Expected: ap_isolate applies to all stations of the BSS, including
those on AP_VLAN interfaces.
Possible fix (untested sketch, for discussion only): evaluate the
master AP's flag for AP_VLAN sdata in both rx.c places, e.g.
static bool ieee80211_sdata_dont_bridge(struct ieee80211_sub_if_data *sdata)
{
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sdata->bss)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data, u.ap);
return sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
}
or move the setting into struct ieee80211_if_ap next to
multicast_to_unicast.
Thanks,
Christopher Hill
(Disclosure, bug investigation was AI assisted)
^ permalink raw reply [flat|nested] 2+ messages in thread* [PATCH wireless] wifi: mac80211: apply ap_isolate to AP_VLAN stations
2026-11-06 2:07 [BUG] mac80211: ap_isolate is not applied to AP_VLAN interfaces Christopher Hill
@ 2026-11-07 3:52 ` Christopher Hill
0 siblings, 0 replies; 2+ messages in thread
From: Christopher Hill @ 2026-11-07 3:52 UTC (permalink / raw)
To: Johannes Berg; +Cc: Christopher Hill, linux-wireless
ap_isolate is set via nl80211 SET_BSS, which is only accepted on AP and
P2P GO interfaces, so ieee80211_change_bss() stores it as
IEEE80211_SDATA_DONT_BRIDGE_PACKETS on the AP sdata. AP_VLAN interfaces
never inherit it, but the intra-BSS forwarding decisions in
ieee80211_deliver_skb() and ieee80211_check_fast_rx() test the flag on
the station's own sdata, which is the AP_VLAN for stations assigned to a
VLAN (e.g. hostapd dynamic_vlan). As a result, mac80211 keeps forwarding
frames between stations of the same AP_VLAN although ap_isolate is
enabled, and client isolation is silently ineffective for them.
Store the setting in struct ieee80211_if_ap, next to
multicast_to_unicast, and read it via sdata->bss so that it applies to
the AP and all of its AP_VLAN interfaces. The now unused
IEEE80211_SDATA_DONT_BRIDGE_PACKETS flag is removed, as was done for
IEEE80211_SDATA_OPERATING_GMODE in commit 39eac2de0098 ("wifi: mac80211:
move IEEE80211_SDATA_OPERATING_GMODE to link").
__ieee80211_check_fast_rx_iface() already re-evaluates the fast-rx state
of AP_VLAN stations when ap_isolate changes.
The issue was found and this change drafted with the help of an AI
coding assistant (Kiro); it was reviewed, built and tested on hardware
by the submitter.
Fixes: 7b7b5e56d7bd ("mac80211: implement ap isolation support")
Closes: https://lore.kernel.org/linux-wireless/20E87267-920A-4FD9-9443-050AE7676E45@gmail.com/
Signed-off-by: Christopher Hill <ch6574@gmail.com>
---
Tested on a Linksys E8450 (MT7622 + MT7915E, mt76), OpenWrt 25.12.6 with
mac80211 from backports 6.18.55 (patch applied with line offsets only),
hostapd dynamic_vlan=2, VLANs from wpa_psk_file, ap_isolate=1,
multicast_to_unicast=1, two stations on the same radio and VLAN:
- AP_VLAN bridge port with hairpin on: stations reach each other via the
bridge, one copy per frame (before: every multicast frame duplicated).
- hairpin off: no unicast or multicast replies between the stations
(before: replies received, i.e. isolation bypassed).
Build-tested on wireless/main (c4328134f) with W=1 (net/wireless and
net/mac80211, x86_64 defconfig + CFG80211/MAC80211=m): no new warnings;
also applies to wireless-next/main. Not tested with mac80211_hwsim.
The debugfs "flags" file no longer shows the isolation bit, as the flag
is removed. An alternative that keeps the flag would be to evaluate the
AP's flag for AP_VLAN interfaces in both rx.c places instead.
net/mac80211/cfg.c | 5 +----
net/mac80211/ieee80211_i.h | 5 +----
net/mac80211/rx.c | 4 ++--
3 files changed, 4 insertions(+), 10 deletions(-)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 9608c07bce..cc9d2384d5 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3706,10 +3706,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
}
if (params->ap_isolate >= 0) {
- if (params->ap_isolate)
- sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
- else
- sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
+ sdata->u.ap.isolate = params->ap_isolate;
ieee80211_check_fast_rx_iface(sdata);
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 1a6cbbb5a1..e74e30c070 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -344,6 +344,7 @@ struct ieee80211_if_ap {
atomic_t num_mcast_sta; /* number of stations receiving multicast */
bool multicast_to_unicast;
+ bool isolate;
bool active;
};
@@ -871,9 +872,6 @@ struct ieee80211_if_mesh {
* enum ieee80211_sub_if_data_flags - virtual interface flags
*
* @IEEE80211_SDATA_ALLMULTI: interface wants all multicast packets
- * @IEEE80211_SDATA_DONT_BRIDGE_PACKETS: bridge packets between
- * associated stations and deliver multicast frames both
- * back to wireless media and to the local net stack.
* @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
* @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver
* @IEEE80211_SDATA_DISCONNECT_HW_RESTART: Disconnect after hardware restart
@@ -881,7 +879,6 @@ struct ieee80211_if_mesh {
*/
enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_ALLMULTI = BIT(0),
- IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3),
IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4),
IEEE80211_SDATA_IN_DRIVER = BIT(5),
IEEE80211_SDATA_DISCONNECT_HW_RESTART = BIT(6),
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index ecee9348c7..a36d0f64c6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2799,7 +2799,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
- !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
+ !(sdata->bss && sdata->bss->isolate) &&
ehdr->h_proto != rx->sdata->control_port_protocol &&
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) {
if (is_multicast_ether_addr(ehdr->h_dest) &&
@@ -4826,7 +4826,7 @@ void ieee80211_check_fast_rx(struct sta_info *sta)
fastrx.expected_ds_bits = cpu_to_le16(IEEE80211_FCTL_TODS);
fastrx.internal_forward =
- !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
+ !(sdata->bss && sdata->bss->isolate) &&
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN ||
!sdata->u.vlan.sta);
--
2.50.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-07 3:54 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-06 2:07 [BUG] mac80211: ap_isolate is not applied to AP_VLAN interfaces Christopher Hill
2026-11-07 3:52 ` [PATCH wireless] wifi: mac80211: apply ap_isolate to AP_VLAN stations Christopher Hill
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.