From: james-ly huang Start a dedicated p2p.c/p2p.h module for the P2P-only logic the generic mt7925 data paths hook into, and put the RX-side filter there first: - a Probe Response received while P2P discovery walks the social channels may carry IEs advertising a band/channel other than the one it was received on (WFA P2P IE only) - drop it; - an active P2P GO only cares about broadcast Probe Requests that arrive on its own operating channel - drop foreign-channel ones. mac.c hooks the filter in mt7925_queue_rx_skb() via the new mt7925_p2p_rx_drop_chan_mismatch(). Signed-off-by: james-ly huang --- .../wireless/mediatek/mt76/mt7925/Makefile | 2 +- .../net/wireless/mediatek/mt76/mt7925/mac.c | 10 ++ .../net/wireless/mediatek/mt76/mt7925/p2p.c | 166 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/p2p.h | 18 ++ 4 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/p2p.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/p2p.h diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile index 33539121a15c..697a2bbce46c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o obj-$(CONFIG_MT7925E) += mt7925e.o obj-$(CONFIG_MT7925U) += mt7925u.o -mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o +mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o p2p.o mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o mt7925e-y := pci.o pci_mac.o pci_mcu.o cached_cal.o mt7925u-y := usb.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 7af996abe294..6e67ef3c52fa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -5,6 +5,7 @@ #include #include #include "mt7925.h" +#include "p2p.h" #include "../dma.h" #include "regd.h" #include "mac.h" @@ -1459,6 +1460,15 @@ void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, case PKT_TYPE_NORMAL_MCU: case PKT_TYPE_NORMAL: if (!mt7925_mac_fill_rx(dev, skb)) { + struct mt76_rx_status *status = + (struct mt76_rx_status *)skb->cb; + + if (!(status->flag & RX_FLAG_8023) && + mt7925_p2p_rx_drop_chan_mismatch(dev, skb)) { + dev_kfree_skb(skb); + return; + } + mt76_rx(&dev->mt76, q, skb); return; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c new file mode 100644 index 000000000000..12d6eee8ce10 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2026 MediaTek Inc. */ + +/* Everything in this file is P2P-only logic that the generic mt7925 data + * paths hook into: RX-side filtering of foreign-channel discovery frames, + * TX-side TXD (lifetime / retry / timestamp) overrides for off-channel and + * GO Probe Response frames, the per-P2P-interface BMC wcid bookkeeping, + * the pre-grant .change_chanctx gate and the P2P-only MCU event handlers + * (SAP aging timeout, BSS absence). + */ + +#include +#include +#include + +#include "../mt76.h" +#include "../mt76_connac.h" +#include "../mt792x.h" +#include "mt7925.h" +#include "mac.h" +#include "mcu.h" +#include "p2p.h" + +/* ------------------------------------------------------------------ */ +/* RX: foreign-channel discovery frames */ +/* ------------------------------------------------------------------ */ + +/* Channel number a management frame advertises in its own IEs: DSSS Parameter + * Set, or the HT Operation primary channel when there is no DS Params element. + */ +static u8 mt7925_rx_ie_chan(const u8 *ies, int ies_len) +{ + const struct ieee80211_ht_operation *ht_oper; + const struct element *elem; + + elem = cfg80211_find_elem(WLAN_EID_DS_PARAMS, ies, ies_len); + if (elem && elem->datalen == 1) + return elem->data[0]; + + elem = cfg80211_find_elem(WLAN_EID_HT_OPERATION, ies, ies_len); + if (elem && elem->datalen >= sizeof(*ht_oper)) { + ht_oper = (const void *)elem->data; + return ht_oper->primary_chan; + } + + return 0; +} + +/* A Probe Response received while P2P discovery walks the social channels may + * carry IEs advertising a band/channel other than the one it was received on. + * Scope: only Probe Responses that carry the WFA P2P IE. + */ +static bool +mt7925_rx_drop_p2p_probe_resp(struct sk_buff *skb, + struct mt76_rx_status *status) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + int hdr_len = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + const u8 *ies; + int ies_len; + u8 ie_chan; + + if (skb->len <= hdr_len) + return false; + + ies = mgmt->u.probe_resp.variable; + ies_len = skb->len - hdr_len; + + if (!cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, + ies, ies_len)) + return false; + + ie_chan = mt7925_rx_ie_chan(ies, ies_len); + if (!ie_chan) + return false; + + if (ieee80211_channel_to_frequency(ie_chan, status->band) == + status->freq) + return false; + + return true; +} + +struct mt7925_rx_chan_iter_data { + u16 freq; + u8 band; + bool p2p_go_active; + bool chan_match; +}; + +static void mt7925_rx_chan_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt7925_rx_chan_iter_data *data = priv; + unsigned int link_id; + + for (link_id = 0; link_id < ARRAY_SIZE(vif->link_conf); link_id++) { + struct ieee80211_chanctx_conf *ctx; + struct ieee80211_bss_conf *conf; + + conf = rcu_dereference(vif->link_conf[link_id]); + if (!conf) + continue; + + ctx = rcu_dereference(conf->chanctx_conf); + if (!ctx || !ctx->def.chan) + continue; + + if (vif->type == NL80211_IFTYPE_AP && vif->p2p) + data->p2p_go_active = true; + + if (ctx->def.chan->band == data->band && + ctx->def.chan->center_freq == data->freq) + data->chan_match = true; + } +} + +/* An active P2P GO only cares about broadcast Probe Requests that arrive on its + * own operating channel. Drop foreign-channel broadcast Probe Requests. + */ +static bool +mt7925_rx_drop_go_probe_req(struct mt792x_dev *dev, struct sk_buff *skb, + struct mt76_rx_status *status) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct mt7925_rx_chan_iter_data data = { + .freq = status->freq, + .band = status->band, + }; + + if (!is_broadcast_ether_addr(mgmt->da)) + return false; + + if (test_bit(MT76_STATE_ROC, &dev->mphy.state)) + return false; + + ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_rx_chan_iter, &data); + + if (!data.p2p_go_active || data.chan_match) + return false; + + return true; +} + +/* Drop management frames whose own notion of the channel contradicts the + * channel the hardware reported them on. + */ +bool +mt7925_p2p_rx_drop_chan_mismatch(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (skb->len < sizeof(struct ieee80211_hdr_3addr) || !status->freq) + return false; + + if (ieee80211_is_probe_resp(hdr->frame_control)) + return mt7925_rx_drop_p2p_probe_resp(skb, status); + + if (ieee80211_is_probe_req(hdr->frame_control)) + return mt7925_rx_drop_go_probe_req(dev, skb, status); + + return false; +} + diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h new file mode 100644 index 000000000000..ed51670d9633 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2026 MediaTek Inc. */ + +#ifndef __MT7925_P2P_H +#define __MT7925_P2P_H + +#include + +#include "../mt76.h" +#include "../mt792x.h" + +/* TX/RX path overrides, BMC wcid bookkeeping and the P2P-only MCU event + * handlers. + */ +bool mt7925_p2p_rx_drop_chan_mismatch(struct mt792x_dev *dev, + struct sk_buff *skb); + +#endif /* __MT7925_P2P_H */ -- 2.45.2