Linux wireless drivers development
 help / color / mirror / Atom feed
From: Felix Fietkau <nbd@nbd.name>
To: linux-wireless@vger.kernel.org
Subject: [PATCH mt76-next 23/25] wifi: mt76: check the RX PN of an MLD peer once per peer
Date: Wed, 28 Oct 2026 13:24:01 +0100	[thread overview]
Message-ID: <20261028122403.796394-23-nbd@nbd.name> (raw)
In-Reply-To: <20261028122403.796394-1-nbd@nbd.name>

For a frame that the hardware decrypted, mt76_check_ccmp_pn() compares
the PN with the last PN of status->wcid. For an MLD peer, mt7996 reports
each frame on the wcid of the link it arrived on, so every link keeps
its own replay counter, although all links of the MLD share the PN space
of the key. With the IV stripped, the function also sets
RX_FLAG_PN_VALIDATED, so mac80211 skips its own check per key. A frame
received on one link and replayed on another one thus passes the replay
check whenever the second link has seen lower PNs.

Keep the replay counters of a peer on its default wcid (def_wcid), like
the reorder state in mt76_rx_aggr_reorder(). The key check still uses
the wcid of the link. This also applies to mt7925, which sets def_wcid
for the links of an MLD station.

A key install resets these counters to the RX sequence of the key in
mac80211, which frames marked RX_FLAG_PN_VALIDATED never advance. In
mt7996 and mt7925, the key of a station is set up link by link, and in
mt7996 a link that is added later gets the installed keys again. Each
of these setups would reset the counters of the whole peer, so that a
frame of the new key received on one link could be replayed after the
setup of the next one. Seed the counters on the default wcid once per
key install, before the key reaches any link of the station, and only
enable the check on each link.

Fixes: dd82a9e02c05 ("wifi: mt76: mt7996: Rely on mt7996_sta_link in sta_add/sta_remove callbacks")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mac80211.c  | 18 ++++++++----------
 drivers/net/wireless/mediatek/mt76/mt76.h      |  7 +++++++
 .../net/wireless/mediatek/mt76/mt7925/main.c   | 13 +++++++++++--
 .../net/wireless/mediatek/mt76/mt7996/main.c   | 11 ++++++++++-
 4 files changed, 36 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 76908bd835f2..08ed3fc6d386 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1209,16 +1209,10 @@ void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
 	struct ieee80211_key_seq seq;
 	int i;
 
-	wcid->rx_check_pn = false;
-
-	if (!key)
+	mt76_wcid_key_link_setup(wcid, key);
+	if (!wcid->rx_check_pn)
 		return;
 
-	if (key->cipher != WLAN_CIPHER_SUITE_CCMP)
-		return;
-
-	wcid->rx_check_pn = true;
-
 	/* data frame */
 	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
 		ieee80211_get_key_rx_seq(key, i, &seq);
@@ -1327,6 +1321,7 @@ mt76_check_ccmp_pn(struct sk_buff *skb)
 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
 	struct mt76_wcid *wcid = status->wcid;
 	struct ieee80211_hdr *hdr;
+	struct mt76_wcid *pn_wcid;
 	int security_idx;
 	int ret;
 
@@ -1339,6 +1334,9 @@ mt76_check_ccmp_pn(struct sk_buff *skb)
 	if (!wcid || !wcid->rx_check_pn)
 		return;
 
+	/* the links of an MLD peer share one PN space per key */
+	pn_wcid = mt76_wcid_primary(wcid);
+
 	security_idx = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;
 	if (status->flag & RX_FLAG_8023)
 		goto skip_hdr_check;
@@ -1366,14 +1364,14 @@ mt76_check_ccmp_pn(struct sk_buff *skb)
 
 skip_hdr_check:
 	BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0]));
-	ret = memcmp(status->iv, wcid->rx_key_pn[security_idx],
+	ret = memcmp(status->iv, pn_wcid->rx_key_pn[security_idx],
 		     sizeof(status->iv));
 	if (ret <= 0) {
 		status->flag |= RX_FLAG_ONLY_MONITOR;
 		return;
 	}
 
-	memcpy(wcid->rx_key_pn[security_idx], status->iv, sizeof(status->iv));
+	memcpy(pn_wcid->rx_key_pn[security_idx], status->iv, sizeof(status->iv));
 
 	if (status->flag & RX_FLAG_IV_STRIPPED)
 		status->flag |= RX_FLAG_PN_VALIDATED;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index fce7bf2f0333..477d33a0022d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -1574,6 +1574,13 @@ int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid,
 		       u16 ssn, u16 size);
 void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid);
 
+static inline void
+mt76_wcid_key_link_setup(struct mt76_wcid *wcid,
+			 struct ieee80211_key_conf *key)
+{
+	wcid->rx_check_pn = key && key->cipher == WLAN_CIPHER_SUITE_CCMP;
+}
+
 void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
 			 struct ieee80211_key_conf *key);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 22cc5b49da0a..ffbc6b954632 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -707,8 +707,11 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 	else
 		goto out;
 
-	mt76_wcid_key_setup(&dev->mt76, wcid,
-			    cmd == SET_KEY ? key : NULL);
+	if (sta)
+		mt76_wcid_key_link_setup(wcid, cmd == SET_KEY ? key : NULL);
+	else
+		mt76_wcid_key_setup(&dev->mt76, wcid,
+				    cmd == SET_KEY ? key : NULL);
 
 	err = mt7925_mcu_add_key(&dev->mt76, vif, &mlink->bip,
 				 key, MCU_UNI_CMD(STA_REC_UPDATE),
@@ -749,6 +752,12 @@ static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
 	mt792x_mutex_acquire(dev);
 
+	/* the links of a station share one RX PN state, seeded before the
+	 * key reaches any of them
+	 */
+	if (sta && cmd == SET_KEY)
+		mt76_wcid_key_setup(&dev->mt76, &msta->deflink.wcid, key);
+
 	if (ieee80211_vif_is_mld(vif)) {
 		unsigned int link_id;
 		unsigned long add;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
index ec7c5e8f3fda..82143ade1cc0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -268,7 +268,7 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 	if (cmd != SET_KEY && !is_bigtk)
 		return 0;
 
-	mt76_wcid_key_setup(&dev->mt76, &msta_link->wcid, key);
+	mt76_wcid_key_link_setup(&msta_link->wcid, key);
 
 	err = mt7996_mcu_add_key(&dev->mt76, link, key,
 				 MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
@@ -837,6 +837,15 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 	else
 		links = BIT(0);
 
+	/* the links of a station share one RX PN state, seeded before the
+	 * key reaches any of them
+	 */
+	if (sta && cmd == SET_KEY) {
+		struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+
+		mt76_wcid_key_setup(&dev->mt76, &msta->deflink.wcid, key);
+	}
+
 	for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
 		err = mt7996_set_hw_key(hw, cmd, vif, sta, link_id, key);
 		if (err)
-- 
2.47.3


  parent reply	other threads:[~2026-10-28 12:24 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-28 12:23 [PATCH mt76-next 01/25] wifi: mt76: mt7996: take the link of an 802.3 frame from its station Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 02/25] wifi: mt76: mt7996: do not update beacons while CSA is active Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 03/25] wifi: mt76: mt7996: only remove a BMC STA_REC that was added Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 04/25] wifi: mt76: mt7996: pass the puncturing bitmap to the firmware Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 05/25] wifi: mt76: abort a scan when the chanctx changes Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 06/25] wifi: mt76: set the channel again when the chanctx puncturing changes Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 07/25] wifi: mt76: mt7996: advertise preamble puncturing support Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 08/25] wifi: mt76: mt7996: keep scan and ROC links out of the MLD group Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 09/25] wifi: mt76: mt7996: factor the vif link teardown out of remove_interface Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 10/25] wifi: mt76: mt7996: destroy station links before the MLD group is freed Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 11/25] wifi: mt76: mt7996: send MLD link operations for an AP MLD only Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 12/25] wifi: mt76: mt7996: set up a vif link again on another radio Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 13/25] wifi: mt76: mt7996: describe an MLD peer with the PEER_MLD command Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 14/25] wifi: mt76: mt7915: honour the valid flag in efuse block reads Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 15/25] wifi: mt76: mt7996: skip MLD address translation for encap offload frames Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 16/25] wifi: mt76: mt7921: honour the valid flag in efuse block reads Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 17/25] wifi: mt76: mt7915: upload the CSA beacon only once Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 18/25] wifi: mt76: read the chanctx of a link once Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 19/25] wifi: mt76: restart the beacon monitor of a link that gets a chanctx again Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 20/25] wifi: mt76: publish the beacon monitor interval after its timestamp Felix Fietkau
2026-10-28 12:23 ` [PATCH mt76-next 21/25] wifi: mt76: mt7996: keep rx header translation off while a monitor runs Felix Fietkau
2026-10-28 12:24 ` [PATCH mt76-next 22/25] wifi: mt76: mt7996: apply monitor mode again after a full reset Felix Fietkau
2026-10-28 12:24 ` Felix Fietkau [this message]
2026-10-28 12:24 ` [PATCH mt76-next 24/25] wifi: mt76: mt7996: send no MLD_SETUP when station links only go away Felix Fietkau
2026-10-28 12:24 ` [PATCH mt76-next 25/25] wifi: mt76: mt7996: keep a unicast management frame on its MLO link Felix Fietkau

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261028122403.796394-23-nbd@nbd.name \
    --to=nbd@nbd.name \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox