mt76: mt7996 sends 802.11s mesh peering frames with zero addr3, breaking interop with ath11k (patch + validation)
openwrt at optimcloud.com
openwrt at optimcloud.com
Fri Sep 11 00:03:27 PDT 2026
Reporting an 802.11s interoperability bug in mt76/mt7996, with a patch that
is validated on hardware. Pull request against openwrt/mt76 is open here:
https://github.com/openwrt/mt76/pull/1132
Details below.
---
mt7996 transmits 802.11s Mesh Peering Management frames with
Address 3 = 00:00:00:00:00:00. This breaks mesh interoperability with
peers that filter received management frames on BSSID; in particular a
BPI-R4 (mt7996) cannot form a peering with ath11k (QCN9074) nodes.
Root cause
----------
mac80211 deliberately leaves bss_conf.bssid as zero_addr for mesh
interfaces, because an MBSS has no BSSID in the infrastructure sense:
net/mac80211/mesh.c: sdata->vif.bss_conf.bssid = zero_addr;
mt7996_mcu_bss_basic_tlv() (drivers/net/wireless/mediatek/mt76/mt7996/mcu.c)
copies that value into the hardware BSS entry:
memcpy(bss->bssid, link_conf->bssid, ETH_ALEN);
and the mt7996 hardware stamps the programmed BSS BSSID into Address 3 of
management frames it transmits. Every Mesh Peering Management frame
(Self-Protected Action, category 15) therefore leaves the radio with
addr3 zeroed.
That value is wrong for the wire. mac80211's own kernel MPM puts the
transmitter's MAC in addr3:
net/mac80211/mesh_plink.c: memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
and hostapd/wpa_supplicant does the same for user-space MPM
(wpa_driver_nl80211_send_action() is called with bssid = wpa_s->own_addr).
bss_conf.bssid == zero is a "no associated BSS" marker, not a value to
transmit.
Observed behaviour
------------------
Measured with a laptop in monitor mode as an independent observer, plus
instrumentation on both endpoints.
Frames as handed to the driver (pr_info added at mt7996_tx() entry),
and the same frames on air in the same 25 s window:
mt7996_tx() entry : 1035 frames, addr3 = 02:0c:43:26:60:11 (own MAC)
on air : 915 frames, addr3 = 00:00:00:00:00:00
so the header is correct when mac80211 hands it over and zeroed by the
time it is transmitted. mld=0 on every sample, so the MLD address
rewrite in mt7996_tx() is not involved (it is gated on
ieee80211_vif_is_mld() and would also have rewritten addr2, which is
intact).
Effect on the peer (ath11k QCN9074, same 35 s window):
BPI Mesh Peering Open -> peer : 217 on air, 0 delivered to host
BPI Peering Confirm -> peer : 194 on air, 0 delivered to host
BPI Authentication (SAE)-> peer : 400 on air, 108 delivered to host
The Authentication frames carry a correct addr3 and are delivered, so SAE
completes; the peering frames are ACKed at the MAC layer and then
silently discarded, so MPM never completes. The peer eventually closes
with reason 56 (MESH_MAX_RETRIES) reporting our link ID as 0, i.e. it
never recorded the peering at all.
Ruled out by measurement before finding this: wpad/hostapd version
differences (sources byte-identical), RF/signal, sae_pwe, rsn_overriding,
the EHT elements mt7996 adds (suppressed and verified absent on air,
still failed), PHY rate (all frames legacy OFDM 6 Mbit/s), the Protected
bit (clear on both sides), and the QCA firmware build (swapped to a QSDK
build, no change).
Fix
---
Use the interface's own address as the BSS BSSID for mesh interfaces.
--- a/mt7996/mcu.c
+++ b/mt7996/mcu.c
@@ -1260,7 +1260,15 @@
return 0;
}
- memcpy(bss->bssid, link_conf->bssid, ETH_ALEN);
+ /* mac80211 sets bss_conf.bssid = zero_addr for mesh interfaces, and
+ * the mt7996 HW stamps the programmed BSS BSSID into addr3 of TXed
+ * mgmt frames. That would send mesh peering frames with a zero addr3,
+ * which peers filtering on BSSID (e.g. ath11k) silently drop.
+ */
+ if (vif->type == NL80211_IFTYPE_MESH_POINT)
+ memcpy(bss->bssid, link_conf->addr, ETH_ALEN);
+ else
+ memcpy(bss->bssid, link_conf->bssid, ETH_ALEN);
bss->bcn_interval = cpu_to_le16(link_conf->beacon_int);
bss->dtim_period = link_conf->dtim_period;
bss->phymode = mt76_connac_get_phy_mode(phy, vif,
Validation
----------
With the patch applied, on the same hardware and configuration:
- on air: 211 mt7996 management frames now carry
addr3 = 02:0c:43:26:60:11 (previously all zero)
- the BPI-R4 reaches plink ESTAB with all three ath11k nodes
- all four nodes report estab=3 and exchange batman-adv originators
Environment
-----------
mt76 : openwrt/mt76 be5ce7910521492d4a2e4ce7ee3843680a46c047
(PKG_SOURCE_DATE 2026-09-01)
BPI-R4 : OpenWrt r36157-f3614686ab, kernel 6.18.44, mt7996e
peer : IPQ5018 + QCN9074, ath11k, kernel 6.12.77,
mac80211 backports-6.18.7
mesh : 802.11s, SAE, 5 GHz ch36 HE80, batman-adv on top
Note: the same pattern appears in the shared helper used by the other
drivers, so they are likely affected too. I only have mt7996 hardware, so
I have not verified these:
mt76_connac_mcu.c:2945 memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN);
(mt76_connac_mcu_bss_basic_tlv(), used by
mt7615/mt7915/mt7921)
mt7925/mcu.c:2736 memcpy(basic_req->bssid, link_conf->bssid, ETH_ALEN);
In both the mesh case falls through the iftype switch without special
handling, so bss_conf.bssid (zero for mesh) is what gets programmed.
More information about the openwrt-devel
mailing list