Skip to contentSkip to conversation

Adding NO_STA flag and reworking RX link resolution

RFC PATCH In review Benjamin Berg 5 participants 22 messages Feb 23 – Jul 31

Subsystems (9)
lore
Share & tools
lore
#0 lore raw

v2 cover Adding NO_STA flag and reworking RX link resolution

From: Benjamin Berg <[email protected]>

Hi,

This patchset refactors the RX link resolution a bit to fix some issues where mac80211 might accept frames on the wrong link and incorrectly translate the address. It also adds a new NL80211_ATTR_FRAME_CMD_NO_STA flag so that userspace can know whether address translation was done by the kernel on RX and can also prevent address translation for management frames during TX.

This together should be enough to fix the existing issues in hostapd where stations that are still associated try to authenticate again but hostapd for example ends up sending the frame to an old link address.

I would appreciate if you test the patches and work on the hostapd side. Note that I have not properly verified the new nl80211 API, so it could well be that I missed something.

Benjamin

Changes in RFCv2:
* Port other drivers to new API (untested)
* Fix a checkpatch warning

Benjamin Berg (8):
wifi: iwlwifi: use link_sta internally to the driver
wifi: mac80211: change public RX API to use link stations
wifi: mac80211: refactor RX link_id and station handling
wifi: mac80211: rework RX packet handling
wifi: cfg80211: add attribute for TX/RX denoting there is no station
wifi: mac80211: report to cfg80211 when no STA is known for a frame
wifi: mac80211: pass station to ieee80211_tx_skb_tid
wifi: mac80211: pass error station if non-STA transmit was requested

 drivers/net/wireless/ath/ath11k/dp_rx.c       |   2 +-
 drivers/net/wireless/ath/ath12k/dp_mon.c      |  18 +-
 drivers/net/wireless/ath/ath12k/dp_rx.c       |  15 +-
 drivers/net/wireless/intel/iwlwifi/mld/agg.c  |  21 +-
 drivers/net/wireless/intel/iwlwifi/mld/agg.h  |   4 +-
 drivers/net/wireless/intel/iwlwifi/mld/rx.c   |  50 +-
 drivers/net/wireless/intel/iwlwifi/mld/rx.h   |   2 +-
 .../wireless/intel/iwlwifi/mld/tests/agg.c    |   7 +-
 drivers/net/wireless/intel/iwlwifi/mvm/rx.c   |   2 +-
 drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c |   6 +-
 drivers/net/wireless/mediatek/mt76/mac80211.c |  22 +-
 drivers/net/wireless/realtek/rtw89/core.c     |   6 -
 drivers/net/wireless/virtual/mac80211_hwsim.c |   3 -
 include/net/cfg80211.h                        |   4 +
 include/net/mac80211.h                        |  25 +-
 include/uapi/linux/nl80211.h                  |   7 +
 net/mac80211/agg-tx.c                         |   6 +-
 net/mac80211/eht.c                            |   3 -
 net/mac80211/ht.c                             |   4 +-
 net/mac80211/ieee80211_i.h                    |  14 +-
 net/mac80211/iface.c                          |   7 +-
 net/mac80211/mlme.c                           |   9 +-
 net/mac80211/offchannel.c                     |  13 +-
 net/mac80211/rx.c                             | 436 ++++++++++--------
 net/mac80211/scan.c                           |  10 +-
 net/mac80211/tdls.c                           |   4 +-
 net/mac80211/tx.c                             |   8 +-
 net/wireless/nl80211.c                        |   8 +-
 28 files changed, 390 insertions(+), 326 deletions(-)
Signature · 1 line
2.53.0
#1 lore raw

RFC PATCH v2 1/8 wifi: iwlwifi: use link_sta internally to the driver

From: Benjamin Berg <[email protected]>

Using the link_sta is a natural way to pass both the STA and the link ID information at the same time. Use that internally to the driver in preparation to mac80211 changing its API and adopting the same method.

---
 drivers/net/wireless/intel/iwlwifi/mld/agg.c  | 21 +++++----
 drivers/net/wireless/intel/iwlwifi/mld/agg.h  |  4 +-
 drivers/net/wireless/intel/iwlwifi/mld/rx.c   | 45 ++++++++++---------
 drivers/net/wireless/intel/iwlwifi/mld/rx.h   |  2 +-
 .../wireless/intel/iwlwifi/mld/tests/agg.c    |  7 +--
 5 files changed, 42 insertions(+), 37 deletions(-)
drivers/net/wireless/intel/iwlwifi/mld/agg.c +12 -9
index 3bf36f8f6874..c4f3552bd2ab 100644
@@ -1,13 +1,14 @@
 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
  */
 #include "agg.h"
 #include "sta.h"
 #include "hcmd.h"
 
 static void
-iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,
+iwl_mld_reorder_release_frames(struct iwl_mld *mld,
+			       struct ieee80211_link_sta *link_sta,
 			       struct napi_struct *napi,
 			       struct iwl_mld_baid_data *baid_data,
 			       struct iwl_mld_reorder_buffer *reorder_buf,
@@ -32,7 +33,7 @@ iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,
 		while ((skb = __skb_dequeue(skb_list))) {
 			iwl_mld_pass_packet_to_mac80211(mld, napi, skb,
 							reorder_buf->queue,
-							sta);
+							link_sta);
 			reorder_buf->num_stored--;
 		}
 	}
@@ -71,7 +72,7 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld,
 
 	reorder_buf = &ba_data->reorder_buf[queue];
 
-	iwl_mld_reorder_release_frames(mld, link_sta->sta, napi, ba_data,
+	iwl_mld_reorder_release_frames(mld, link_sta, napi, ba_data,
 				       reorder_buf, nssn);
 out_unlock:
 	rcu_read_unlock();
@@ -174,7 +175,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
 	reorder_buf = &ba_data->reorder_buf[queue];
 
 	/* release all frames that are in the reorder buffer to the stack */
-	iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL,
+	iwl_mld_reorder_release_frames(mld, link_sta, NULL,
 				       ba_data, reorder_buf,
 				       ieee80211_sn_add(reorder_buf->head_sn,
 							ba_data->buf_size));
@@ -187,14 +188,14 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
  */
 enum iwl_mld_reorder_result
 iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
-		int queue, struct ieee80211_sta *sta,
+		int queue, struct ieee80211_link_sta *link_sta,
 		struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc)
 {
 	struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
 	struct iwl_mld_baid_data *baid_data;
 	struct iwl_mld_reorder_buffer *buffer;
 	struct iwl_mld_reorder_buf_entry *entries;
-	struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+	struct iwl_mld_sta *mld_sta;
 	struct iwl_mld_link_sta *mld_link_sta;
 	u32 reorder = le32_to_cpu(desc->reorder_data);
 	bool amsdu, last_subframe, is_old_sn, is_dup;
@@ -217,10 +218,12 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
 		return IWL_MLD_PASS_SKB;
 
 	/* no sta yet */
-	if (WARN_ONCE(!sta,
+	if (WARN_ONCE(!link_sta,
 		      "Got valid BAID without a valid station assigned\n"))
 		return IWL_MLD_PASS_SKB;
 
+	mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+
 	/* not a data packet */
 	if (!ieee80211_is_data_qos(hdr->frame_control) ||
 	    is_multicast_ether_addr(hdr->addr1))
@@ -310,7 +313,7 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
 	 * will be released when the frame release notification arrives.
 	 */
 	if (!amsdu || last_subframe)
-		iwl_mld_reorder_release_frames(mld, sta, napi, baid_data,
+		iwl_mld_reorder_release_frames(mld, link_sta, napi, baid_data,
 					       buffer, nssn);
 	else if (buffer->num_stored == 1)
 		buffer->head_sn = nssn;
drivers/net/wireless/intel/iwlwifi/mld/agg.h +2 -2
index 651c80d1c7cd..c6cd5fa219be 100644
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2024 Intel Corporation
+ * Copyright (C) 2024, 2026 Intel Corporation
  */
 #ifndef __iwl_agg_h__
 #define __iwl_agg_h__
@@ -106,7 +106,7 @@ int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta,
 
 enum iwl_mld_reorder_result
 iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
-		int queue, struct ieee80211_sta *sta,
+		int queue, struct ieee80211_link_sta *link_sta,
 		struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc);
 
 void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld,
drivers/net/wireless/intel/iwlwifi/mld/rx.c +23 -22

151 lines not on this page · raw message

drivers/net/wireless/intel/iwlwifi/mld/rx.h +1 -1
index 09dddbd40f55..a54f1a6146ee 100644
@@ -64,7 +64,7 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
 void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
 				     struct napi_struct *napi,
 				     struct sk_buff *skb, int queue,
-				     struct ieee80211_sta *sta);
+				     struct ieee80211_link_sta *link_sta);
 
 void iwl_mld_handle_phy_air_sniffer_notif(struct iwl_mld *mld,
 					  struct napi_struct *napi,
drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c +4 -3
index 29b0248cec3d..e9efe2996f07 100644
@@ -2,7 +2,7 @@
 /*
  * KUnit tests for channel helper functions
  *
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
  */
 #include <kunit/test.h>
 #include <kunit/static_stub.h>
@@ -441,7 +441,7 @@ static void
 fake_iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
 				     struct napi_struct *napi,
 				     struct sk_buff *skb, int queue,
-				     struct ieee80211_sta *sta)
+				     struct ieee80211_link_sta *link_sta)
 {
 	__skb_queue_tail(&g_released_skbs, skb);
 	g_num_released_skbs++;
@@ -630,7 +630,8 @@ static void test_reorder_buffer(struct kunit *test)
 	mpdu_desc = setup_mpdu_desc();
 
 	rcu_read_lock();
-	reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, sta, skb, mpdu_desc);
+	reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, &sta->deflink, skb,
+				      mpdu_desc);
 	rcu_read_unlock();
 
 	KUNIT_ASSERT_EQ(test, reorder_res, param->expected.reorder_res);
Signature · 1 line
2.53.0
#2 lore raw

RFC PATCH v2 2/8 wifi: mac80211: change public RX API to use link stations

In reply to Benjamin Berg

From: Benjamin Berg <[email protected]>

If a station is passed then the link ID also needs to be known. As such, it is a more natural API to simply pass the link station directly rather than pushing the link information into the RX status.

Furthermore, having the link ID in the RX status is not actually correct because the link IDs are VIF specific and there may be multiple VIFs. In the case of a station this relationship is clear, but then one may as well use the link station.

This patch only changes the API and emulates the old (incorrect) behaviour for now. The mac80211 RX code will be updated in later patches.

---
 drivers/net/wireless/ath/ath11k/dp_rx.c       |  2 +-
 drivers/net/wireless/ath/ath12k/dp_mon.c      | 18 +++++++--------
 drivers/net/wireless/ath/ath12k/dp_rx.c       | 15 +++++++++----
 drivers/net/wireless/intel/iwlwifi/mld/rx.c   |  7 +-----
 drivers/net/wireless/intel/iwlwifi/mvm/rx.c   |  2 +-
 drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c |  6 ++---
 drivers/net/wireless/mediatek/mt76/mac80211.c | 22 +++++++++++--------
 drivers/net/wireless/realtek/rtw89/core.c     |  6 -----
 drivers/net/wireless/virtual/mac80211_hwsim.c |  3 ---
 include/net/mac80211.h                        | 16 ++++++++++----
 net/mac80211/rx.c                             | 20 ++++++++++++-----
 11 files changed, 65 insertions(+), 52 deletions(-)
drivers/net/wireless/ath/ath11k/dp_rx.c +1 -1
index b9e976ddcbbf..911e40178234 100644
@@ -2500,7 +2500,7 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap
 	    !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
 		rx_status->flag |= RX_FLAG_8023;
 
-	ieee80211_rx_napi(ar->hw, pubsta, msdu, napi);
+	ieee80211_rx_napi(ar->hw, &pubsta->deflink, msdu, napi);
 }
 
 static int ath11k_dp_rx_process_msdu(struct ath11k *ar,
drivers/net/wireless/ath/ath12k/dp_mon.c +8 -10
index 737287a9aa46..71f14f2e15bd 100644
@@ -508,7 +508,7 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 	};
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_radiotap_he *he = NULL;
-	struct ieee80211_sta *pubsta = NULL;
+	struct ieee80211_link_sta *link_pubsta = NULL;
 	struct ath12k_dp_link_peer *peer;
 	struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
 	struct hal_rx_desc_data rx_info;
@@ -517,8 +517,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 	struct hal_rx_desc *rx_desc = (struct hal_rx_desc *)msdu->data;
 	u8 addr[ETH_ALEN] = {};
 
-	status->link_valid = 0;
-
 	if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) &&
 	    !(status->flag & RX_FLAG_SKIP_MONITOR)) {
 		he = skb_push(msdu, sizeof(known));
@@ -532,12 +530,11 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 	spin_lock_bh(&dp->dp_lock);
 	peer = ath12k_dp_rx_h_find_link_peer(dp_pdev, msdu, &rx_info);
 	if (peer && peer->sta) {
-		pubsta = peer->sta;
-		memcpy(addr, peer->addr, ETH_ALEN);
-		if (pubsta->valid_links) {
-			status->link_valid = 1;
-			status->link_id = peer->link_id;
-		}
+		if (peer->sta->valid_links)
+			link_pubsta =
+				rcu_dereference(peer->sta->link[peer->link_id]);
+		else
+			link_pubsta = &peer->sta->deflink;
 	}
 
 	spin_unlock_bh(&dp->dp_lock);
@@ -583,7 +580,8 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 	    !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
 		rx_status->flag |= RX_FLAG_8023;
 
-	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
+	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu,
+			  napi);
 }
 EXPORT_SYMBOL(ath12k_dp_mon_rx_deliver_msdu);
 
drivers/net/wireless/ath/ath12k/dp_rx.c +11 -4
index a32ee9f8061a..49b161c5a878 100644
@@ -1329,6 +1329,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 	struct ath12k_dp *dp = dp_pdev->dp;
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_sta *pubsta;
+	struct ieee80211_link_sta *link_pubsta = NULL;
 	struct ath12k_dp_peer *peer;
 	struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
 	struct ieee80211_rx_status *status = rx_info->rx_status;
@@ -1340,9 +1341,14 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 
 	pubsta = peer ? peer->sta : NULL;
 
-	if (pubsta && pubsta->valid_links) {
-		status->link_valid = 1;
-		status->link_id = peer->hw_links[rxcb->hw_link_id];
+	if (pubsta) {
+		if (pubsta->valid_links) {
+			u8 link_id = peer->hw_links[rxcb->hw_link_id];
+			link_pubsta =
+				rcu_dereference(pubsta->link[link_id]);
+		} else {
+			link_pubsta = &pubsta->deflink;
+		}
 	}
 
 	ath12k_dbg(dp->ab, ATH12K_DBG_DATA,
@@ -1388,7 +1394,8 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 	    !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
 		rx_status->flag |= RX_FLAG_8023;
 
-	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
+	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu,
+			  napi);
 }
 EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);
 
drivers/net/wireless/intel/iwlwifi/mld/rx.c +1 -6
index de2feeb74009..2a12ae412bfd 100644
@@ -131,7 +131,7 @@ void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
 		return;
 	}
 
-	ieee80211_rx_napi(mld->hw, link_sta->sta, skb, napi);
+	ieee80211_rx_napi(mld->hw, link_sta, skb, napi);
 }
 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211);
 
@@ -1765,11 +1765,6 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
 
 	rx_status = IEEE80211_SKB_RXCB(skb);
 
-	if (link_sta && sta->valid_links) {
-		rx_status->link_valid = true;
-		rx_status->link_id = link_sta->link_id;
-	}
-
 	/* fill checksum */
 	if (ieee80211_is_data(hdr->frame_control) &&
 	    pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) {
drivers/net/wireless/intel/iwlwifi/mvm/rx.c +1 -1
index d0c0faae0122..a83bede06487 100644
@@ -90,7 +90,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
 				fraglen, rxb->truesize);
 	}
 
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, &sta->deflink, skb, napi);
 }
 
 /*
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +3 -3
index 7f0b4f5daa21..fe5a2d0a798b 100644
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2015-2017 Intel Deutschland GmbH
  */
@@ -243,7 +243,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
 		return;
 	}
 
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, &sta->deflink, skb, napi);
 }
 
 static bool iwl_mvm_used_average_energy(struct iwl_mvm *mvm,
@@ -2528,7 +2528,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
 	}
 
 	rcu_read_lock();
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, &sta->deflink, skb, napi);
 	rcu_read_unlock();
 }
 
drivers/net/wireless/mediatek/mt76/mac80211.c +13 -9
index 75772979f438..a0b887c83591 100644
@@ -1232,7 +1232,7 @@ EXPORT_SYMBOL(mt76_rx_signal);
 static void
 mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
 		struct ieee80211_hw **hw,
-		struct ieee80211_sta **sta)
+		struct ieee80211_link_sta **link_sta)
 {
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
@@ -1279,11 +1279,15 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
 	       sizeof(mstat.chain_signal));
 
 	if (mstat.wcid) {
-		status->link_valid = mstat.wcid->link_valid;
-		status->link_id = mstat.wcid->link_id;
+		struct ieee80211_sta *sta = wcid_to_sta(mstat.wcid);
+
+		if (mstat.wcid->link_valid)
+			*link_sta =
+				rcu_dereference(sta->link[mstat.wcid->link_id]);
+		else
+			*link_sta = &sta->deflink;
 	}
 
-	*sta = wcid_to_sta(mstat.wcid);
 	*hw = mt76_phy_hw(dev, mstat.phy_idx);
 }
 
@@ -1507,7 +1511,7 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
 void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 		      struct napi_struct *napi)
 {
-	struct ieee80211_sta *sta;
+	struct ieee80211_link_sta *link_sta;
 	struct ieee80211_hw *hw;
 	struct sk_buff *skb, *tmp;
 	LIST_HEAD(list);
@@ -1518,8 +1522,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 
 		mt76_check_ccmp_pn(skb);
 		skb_shinfo(skb)->frag_list = NULL;
-		mt76_rx_convert(dev, skb, &hw, &sta);
-		ieee80211_rx_list(hw, sta, skb, &list);
+		mt76_rx_convert(dev, skb, &hw, &link_sta);
+		ieee80211_rx_list(hw, link_sta, skb, &list);
 
 		/* subsequent amsdu frames */
 		while (nskb) {
@@ -1527,8 +1531,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 			nskb = nskb->next;
 			skb->next = NULL;
 
-			mt76_rx_convert(dev, skb, &hw, &sta);
-			ieee80211_rx_list(hw, sta, skb, &list);
+			mt76_rx_convert(dev, skb, &hw, &link_sta);
+			ieee80211_rx_list(hw, link_sta, skb, &list);
 		}
 	}
 	spin_unlock(&dev->rx_lock);
drivers/net/wireless/realtek/rtw89/core.c +0 -6
index 6e77522bcd8f..f6bcf309936d 100644
@@ -2953,7 +2953,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
 	struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat;
 	struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
 	struct sk_buff *skb = iter_data->skb;
-	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct rtw89_rx_phy_ppdu *phy_ppdu = iter_data->phy_ppdu;
 	bool is_mld = ieee80211_vif_is_mld(vif);
@@ -2988,11 +2987,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
 	if (!ether_addr_equal(target_bssid, bssid))
 		goto out;
 
-	if (is_mld) {
-		rx_status->link_valid = true;
-		rx_status->link_id = rtwvif_link->link_id;
-	}
-
 	if (ieee80211_is_beacon(hdr->frame_control)) {
 		if (vif->type == NL80211_IFTYPE_STATION &&
 		    !test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) {
drivers/net/wireless/virtual/mac80211_hwsim.c +0 -3
index 4d9f5f87e814..7d529aa129f8 100644
@@ -1755,9 +1755,6 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data,
 				sp->active_links_rx &= ~BIT(link_id);
 			else
 				sp->active_links_rx |= BIT(link_id);
-
-			rx_status->link_valid = true;
-			rx_status->link_id = link_id;
 		}
 		rcu_read_unlock();
 	}
include/net/mac80211.h +12 -4
index 7f9d96939a4e..4d9dbd35369b 100644
@@ -5207,14 +5207,18 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw);
  * mixed for a single hardware. Must not run concurrently with
  * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
  *
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
  * This function must be called with BHs disabled and RCU read lock
  *
  * @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
  * @skb: the buffer to receive, owned by mac80211 after this call
  * @list: the destination list
  */
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_sta,
 		       struct sk_buff *skb, struct list_head *list);
 
 /**
@@ -5232,14 +5236,18 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
  * mixed for a single hardware. Must not run concurrently with
  * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
  *
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
  * This function must be called with BHs disabled.
  *
  * @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
  * @skb: the buffer to receive, owned by mac80211 after this call
  * @napi: the NAPI context
  */
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_sta,
 		       struct sk_buff *skb, struct napi_struct *napi);
 
 /**
net/mac80211/rx.c +15 -5
index 11d6c56c9d7e..4098f63ec824 100644
@@ -5432,7 +5432,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
  * This is the receive path handler. It is called by a low level driver when an
  * 802.11 MPDU is received from the hardware.
  */
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_pubsta,
 		       struct sk_buff *skb, struct list_head *list)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
@@ -5440,6 +5441,7 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_sta *pubsta;
 
 	WARN_ON_ONCE(softirq_count() == 0);
 
@@ -5562,8 +5564,15 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 		}
 	}
 
-	if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED))
-		goto drop;
+	/* FIXME: Emulate the old driver behaviour for now */
+	if (link_pubsta) {
+		status->link_valid = 1;
+		status->link_id = link_pubsta->link_id;
+		pubsta = link_pubsta->sta;
+	} else {
+		status->link_valid = 0;
+		pubsta = NULL;
+	}
 
 	status->rx_flags = 0;
 
@@ -5595,7 +5604,8 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 }
 EXPORT_SYMBOL(ieee80211_rx_list);
 
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_pubsta,
 		       struct sk_buff *skb, struct napi_struct *napi)
 {
 	struct sk_buff *tmp;
@@ -5608,7 +5618,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 	 * receive processing
 	 */
 	rcu_read_lock();
-	ieee80211_rx_list(hw, pubsta, skb, &list);
+	ieee80211_rx_list(hw, link_pubsta, skb, &list);
 	rcu_read_unlock();
 
 	if (!napi) {
Signature · 1 line
2.53.0
#3 lore raw

Re: [RFC PATCH v2 2/8] wifi: mac80211: change public RX API to use link stations

Quoted 66 lines · Benjamin Berg

From: Benjamin Berg <[email protected]>

If a station is passed then the link ID also needs to be known. As such, it is a more natural API to simply pass the link station directly rather than pushing the link information into the RX status.

Furthermore, having the link ID in the RX status is not actually correct because the link IDs are VIF specific and there may be multiple VIFs. In the case of a station this relationship is clear, but then one may as well use the link station.

This patch only changes the API and emulates the old (incorrect) behaviour for now. The mac80211 RX code will be updated in later patches.

---
  drivers/net/wireless/ath/ath11k/dp_rx.c       |  2 +-
  drivers/net/wireless/ath/ath12k/dp_mon.c      | 18 +++++++--------
  drivers/net/wireless/ath/ath12k/dp_rx.c       | 15 +++++++++----
  drivers/net/wireless/intel/iwlwifi/mld/rx.c   |  7 +-----
  drivers/net/wireless/intel/iwlwifi/mvm/rx.c   |  2 +-
  drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c |  6 ++---
  drivers/net/wireless/mediatek/mt76/mac80211.c | 22 +++++++++++--------
  drivers/net/wireless/realtek/rtw89/core.c     |  6 -----
  drivers/net/wireless/virtual/mac80211_hwsim.c |  3 ---
  include/net/mac80211.h                        | 16 ++++++++++----
  net/mac80211/rx.c                             | 20 ++++++++++++-----
  11 files changed, 65 insertions(+), 52 deletions(-)
drivers/net/wireless/ath/ath11k/dp_rx.c +1 -1
index b9e976ddcbbf..911e40178234 100644
@@ -2500,7 +2500,7 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap
  	    !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
  		rx_status->flag |= RX_FLAG_8023;
 
-	ieee80211_rx_napi(ar->hw, pubsta, msdu, napi);
+	ieee80211_rx_napi(ar->hw, &pubsta->deflink, msdu, napi);
  }
 
  static int ath11k_dp_rx_process_msdu(struct ath11k *ar,
drivers/net/wireless/ath/ath12k/dp_mon.c +1 -3
index 737287a9aa46..71f14f2e15bd 100644
@@ -508,7 +508,7 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
  	};
  	struct ieee80211_rx_status *rx_status;
  	struct ieee80211_radiotap_he *he = NULL;
-	struct ieee80211_sta *pubsta = NULL;
+	struct ieee80211_link_sta *link_pubsta = NULL;
  	struct ath12k_dp_link_peer *peer;
  	struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
  	struct hal_rx_desc_data rx_info;
@@ -517,8 +517,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
  	struct hal_rx_desc *rx_desc = (struct hal_rx_desc *)msdu->data;
  	u8 addr[ETH_ALEN] = {};
 
-	status->link_valid = 0;
-
  	if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) &&
  	    !(status->flag & RX_FLAG_SKIP_MONITOR)) {
  		he = skb_push(msdu, sizeof(known));

drivers/net/wireless/ath/ath12k/dp_mon.c void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, · line 536

Quoted 5 lines
drivers/net/wireless/ath/ath12k/dp_mon.c +0 -2
@@ -532,12 +530,11 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
  	spin_lock_bh(&dp->dp_lock);
  	peer = ath12k_dp_rx_h_find_link_peer(dp_pdev, msdu, &rx_info);
  	if (peer && peer->sta) {
-		pubsta = peer->sta;
-		memcpy(addr, peer->addr, ETH_ALEN);

It appears memcpy of peer address is required, this shouldn't be removed, right?

Quoted 336 lines
drivers/net/wireless/ath/ath12k/dp_mon.c +7 -5
-		if (pubsta->valid_links) {
-			status->link_valid = 1;
-			status->link_id = peer->link_id;
-		}
+		if (peer->sta->valid_links)
+			link_pubsta =
+				rcu_dereference(peer->sta->link[peer->link_id]);
+		else
+			link_pubsta = &peer->sta->deflink;
  	}
 
  	spin_unlock_bh(&dp->dp_lock);
@@ -583,7 +580,8 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
  	    !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
  		rx_status->flag |= RX_FLAG_8023;
 
-	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
+	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu,
+			  napi);
  }
  EXPORT_SYMBOL(ath12k_dp_mon_rx_deliver_msdu);
 
drivers/net/wireless/ath/ath12k/dp_rx.c +11 -4
index a32ee9f8061a..49b161c5a878 100644
@@ -1329,6 +1329,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
  	struct ath12k_dp *dp = dp_pdev->dp;
  	struct ieee80211_rx_status *rx_status;
  	struct ieee80211_sta *pubsta;
+	struct ieee80211_link_sta *link_pubsta = NULL;
  	struct ath12k_dp_peer *peer;
  	struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
  	struct ieee80211_rx_status *status = rx_info->rx_status;
@@ -1340,9 +1341,14 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 
  	pubsta = peer ? peer->sta : NULL;
 
-	if (pubsta && pubsta->valid_links) {
-		status->link_valid = 1;
-		status->link_id = peer->hw_links[rxcb->hw_link_id];
+	if (pubsta) {
+		if (pubsta->valid_links) {
+			u8 link_id = peer->hw_links[rxcb->hw_link_id];
+			link_pubsta =
+				rcu_dereference(pubsta->link[link_id]);
+		} else {
+			link_pubsta = &pubsta->deflink;
+		}
  	}
 
  	ath12k_dbg(dp->ab, ATH12K_DBG_DATA,
@@ -1388,7 +1394,8 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
  	    !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
  		rx_status->flag |= RX_FLAG_8023;
 
-	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
+	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu,
+			  napi);
  }
  EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);
 
drivers/net/wireless/intel/iwlwifi/mld/rx.c +1 -6
index de2feeb74009..2a12ae412bfd 100644
@@ -131,7 +131,7 @@ void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
  		return;
  	}
 
-	ieee80211_rx_napi(mld->hw, link_sta->sta, skb, napi);
+	ieee80211_rx_napi(mld->hw, link_sta, skb, napi);
  }
  EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211);
 
@@ -1765,11 +1765,6 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
 
  	rx_status = IEEE80211_SKB_RXCB(skb);
 
-	if (link_sta && sta->valid_links) {
-		rx_status->link_valid = true;
-		rx_status->link_id = link_sta->link_id;
-	}
-
  	/* fill checksum */
  	if (ieee80211_is_data(hdr->frame_control) &&
  	    pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) {
drivers/net/wireless/intel/iwlwifi/mvm/rx.c +1 -1
index d0c0faae0122..a83bede06487 100644
@@ -90,7 +90,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
  				fraglen, rxb->truesize);
  	}
 
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, &sta->deflink, skb, napi);
  }
 
  /*
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +3 -3
index 7f0b4f5daa21..fe5a2d0a798b 100644
@@ -1,6 +1,6 @@
  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  /*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
   * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
   * Copyright (C) 2015-2017 Intel Deutschland GmbH
   */
@@ -243,7 +243,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
  		return;
  	}
 
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, &sta->deflink, skb, napi);
  }
 
  static bool iwl_mvm_used_average_energy(struct iwl_mvm *mvm,
@@ -2528,7 +2528,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
  	}
 
  	rcu_read_lock();
-	ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+	ieee80211_rx_napi(mvm->hw, &sta->deflink, skb, napi);
  	rcu_read_unlock();
  }
 
drivers/net/wireless/mediatek/mt76/mac80211.c +13 -9
index 75772979f438..a0b887c83591 100644
@@ -1232,7 +1232,7 @@ EXPORT_SYMBOL(mt76_rx_signal);
  static void
  mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
  		struct ieee80211_hw **hw,
-		struct ieee80211_sta **sta)
+		struct ieee80211_link_sta **link_sta)
  {
  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
  	struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
@@ -1279,11 +1279,15 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
  	       sizeof(mstat.chain_signal));
 
  	if (mstat.wcid) {
-		status->link_valid = mstat.wcid->link_valid;
-		status->link_id = mstat.wcid->link_id;
+		struct ieee80211_sta *sta = wcid_to_sta(mstat.wcid);
+
+		if (mstat.wcid->link_valid)
+			*link_sta =
+				rcu_dereference(sta->link[mstat.wcid->link_id]);
+		else
+			*link_sta = &sta->deflink;
  	}
 
-	*sta = wcid_to_sta(mstat.wcid);
  	*hw = mt76_phy_hw(dev, mstat.phy_idx);
  }
 
@@ -1507,7 +1511,7 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
  void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
  		      struct napi_struct *napi)
  {
-	struct ieee80211_sta *sta;
+	struct ieee80211_link_sta *link_sta;
  	struct ieee80211_hw *hw;
  	struct sk_buff *skb, *tmp;
  	LIST_HEAD(list);
@@ -1518,8 +1522,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
 
  		mt76_check_ccmp_pn(skb);
  		skb_shinfo(skb)->frag_list = NULL;
-		mt76_rx_convert(dev, skb, &hw, &sta);
-		ieee80211_rx_list(hw, sta, skb, &list);
+		mt76_rx_convert(dev, skb, &hw, &link_sta);
+		ieee80211_rx_list(hw, link_sta, skb, &list);
 
  		/* subsequent amsdu frames */
  		while (nskb) {
@@ -1527,8 +1531,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
  			nskb = nskb->next;
  			skb->next = NULL;
 
-			mt76_rx_convert(dev, skb, &hw, &sta);
-			ieee80211_rx_list(hw, sta, skb, &list);
+			mt76_rx_convert(dev, skb, &hw, &link_sta);
+			ieee80211_rx_list(hw, link_sta, skb, &list);
  		}
  	}
  	spin_unlock(&dev->rx_lock);
drivers/net/wireless/realtek/rtw89/core.c +0 -6
index 6e77522bcd8f..f6bcf309936d 100644
@@ -2953,7 +2953,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
  	struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat;
  	struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
  	struct sk_buff *skb = iter_data->skb;
-	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
  	struct rtw89_rx_phy_ppdu *phy_ppdu = iter_data->phy_ppdu;
  	bool is_mld = ieee80211_vif_is_mld(vif);
@@ -2988,11 +2987,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
  	if (!ether_addr_equal(target_bssid, bssid))
  		goto out;
 
-	if (is_mld) {
-		rx_status->link_valid = true;
-		rx_status->link_id = rtwvif_link->link_id;
-	}
-
  	if (ieee80211_is_beacon(hdr->frame_control)) {
  		if (vif->type == NL80211_IFTYPE_STATION &&
  		    !test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) {
drivers/net/wireless/virtual/mac80211_hwsim.c +0 -3
index 4d9f5f87e814..7d529aa129f8 100644
@@ -1755,9 +1755,6 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data,
  				sp->active_links_rx &= ~BIT(link_id);
  			else
  				sp->active_links_rx |= BIT(link_id);
-
-			rx_status->link_valid = true;
-			rx_status->link_id = link_id;
  		}
  		rcu_read_unlock();
  	}
include/net/mac80211.h +12 -4
index 7f9d96939a4e..4d9dbd35369b 100644
@@ -5207,14 +5207,18 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw);
   * mixed for a single hardware. Must not run concurrently with
   * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
   *
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
   * This function must be called with BHs disabled and RCU read lock
   *
   * @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
   * @skb: the buffer to receive, owned by mac80211 after this call
   * @list: the destination list
   */
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_sta,
  		       struct sk_buff *skb, struct list_head *list);
 
  /**
@@ -5232,14 +5236,18 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
   * mixed for a single hardware. Must not run concurrently with
   * ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
   *
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
   * This function must be called with BHs disabled.
   *
   * @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
   * @skb: the buffer to receive, owned by mac80211 after this call
   * @napi: the NAPI context
   */
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_sta,
  		       struct sk_buff *skb, struct napi_struct *napi);
 
  /**
net/mac80211/rx.c +15 -5
index 11d6c56c9d7e..4098f63ec824 100644
@@ -5432,7 +5432,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
   * This is the receive path handler. It is called by a low level driver when an
   * 802.11 MPDU is received from the hardware.
   */
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_pubsta,
  		       struct sk_buff *skb, struct list_head *list)
  {
  	struct ieee80211_local *local = hw_to_local(hw);
@@ -5440,6 +5441,7 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
  	struct ieee80211_supported_band *sband;
  	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_sta *pubsta;
 
  	WARN_ON_ONCE(softirq_count() == 0);
 
@@ -5562,8 +5564,15 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
  		}
  	}
 
-	if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED))
-		goto drop;
+	/* FIXME: Emulate the old driver behaviour for now */
+	if (link_pubsta) {
+		status->link_valid = 1;
+		status->link_id = link_pubsta->link_id;
+		pubsta = link_pubsta->sta;
+	} else {
+		status->link_valid = 0;
+		pubsta = NULL;
+	}
 
  	status->rx_flags = 0;
 
@@ -5595,7 +5604,8 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
  }
  EXPORT_SYMBOL(ieee80211_rx_list);
 
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+		       struct ieee80211_link_sta *link_pubsta,
  		       struct sk_buff *skb, struct napi_struct *napi)
  {
  	struct sk_buff *tmp;
@@ -5608,7 +5618,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
  	 * receive processing
  	 */
  	rcu_read_lock();
-	ieee80211_rx_list(hw, pubsta, skb, &list);
+	ieee80211_rx_list(hw, link_pubsta, skb, &list);
  	rcu_read_unlock();
 
  	if (!napi) {
#4 lore raw

Re: [RFC PATCH v2 2/8] wifi: mac80211: change public RX API to use link stations

Hi,

Quoted 10 lines · Ramasamy Kaliappan

drivers/net/wireless/ath/ath12k/dp_mon.c void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, · line 536

Quoted 7 lines · Benjamin Berg

[SNIP]

drivers/net/wireless/ath/ath12k/dp_mon.c +0 -2
@@ -532,12 +530,11 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
   	spin_lock_bh(&dp->dp_lock);
   	peer = ath12k_dp_rx_h_find_link_peer(dp_pdev, msdu, &rx_info);
   	if (peer && peer->sta) {
-		pubsta = peer->sta;
-		memcpy(addr, peer->addr, ETH_ALEN);

It appears memcpy of peer address is required, this shouldn't be removed, right?

Yup, that was accidental.

Benjamin

Quoted 1 line
Quoted 1 line

[SNIP]

Intel Deutschland GmbH
Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht München HRB 186928

#5 lore raw

Re: [RFC PATCH v2 2/8] wifi: mac80211: change public RX API to use link stations

In reply to Benjamin Berg

drivers/net/wireless/ath/ath12k/dp_rx.c void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc · line 1346

Quoted 10 lines · Benjamin Berg
drivers/net/wireless/ath/ath12k/dp_rx.c +3 -3
@@ -1340,9 +1341,14 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
 
 	pubsta = peer ? peer->sta : NULL;
 
-	if (pubsta && pubsta->valid_links) {
-		status->link_valid = 1;
-		status->link_id = peer->hw_links[rxcb->hw_link_id];
+	if (pubsta) {
+		if (pubsta->valid_links) {
+			u8 link_id = peer->hw_links[rxcb->hw_link_id];

nit: Missing a blank line after declarations

Quoted 8 lines
drivers/net/wireless/ath/ath12k/dp_rx.c +5 -0
+			link_pubsta =
+				rcu_dereference(pubsta->link[link_id]);
+		} else {
+			link_pubsta = &pubsta->deflink;
+		}
 	}
 
 	ath12k_dbg(dp->ab, ATH12K_DBG_DATA,
#6 lore raw

RFC PATCH v2 3/8 wifi: mac80211: refactor RX link_id and station handling

In reply to Benjamin Berg

From: Benjamin Berg <[email protected]>

The link ID for one frequency may be different between VIFs. As such, the sensible thing is to set the link ID later in the flow once the SKB is duplicated for each VIF. As the link ID is not passed in from outside mac80211 it is always valid and the corresponding bit can be dropped.

Also switch a few more places to pass the link STA as that is a natural way to pass the link information around.

This fixes the per-link statistics when frames are reordered.

Note that this patch deliberately leaves some places unchanged and even incorrect as this will be addressed by further refactorings.

---
 include/net/mac80211.h |   9 +-
 net/mac80211/eht.c     |   3 -
 net/mac80211/iface.c   |   7 +-
 net/mac80211/mlme.c    |   9 +-
 net/mac80211/rx.c      | 209 ++++++++++++++++++-----------------------
 net/mac80211/scan.c    |  10 +-
 6 files changed, 105 insertions(+), 142 deletions(-)
include/net/mac80211.h +4 -5
index 4d9dbd35369b..6db15227d4e1 100644
@@ -1655,10 +1655,9 @@ enum mac80211_rx_encoding {
  * @ampdu_reference: A-MPDU reference number, must be a different value for
  *	each A-MPDU but the same for each subframe within one A-MPDU
  * @zero_length_psdu_type: radiotap type of the 0-length PSDU
- * @link_valid: if the link which is identified by @link_id is valid. This flag
- *	is set only when connection is MLO.
- * @link_id: id of the link used to receive the packet. This is used along with
- *	@link_valid.
+ * @link_id: id of the link used to receive the packet. Set and used by
+ *	mac80211 internally, it uses @freq set by the driver to identify the
+ *	correct link per vif.
  */
 struct ieee80211_rx_status {
 	u64 mactime;
@@ -1698,7 +1697,7 @@ struct ieee80211_rx_status {
 	u8 chains;
 	s8 chain_signal[IEEE80211_MAX_CHAINS];
 	u8 zero_length_psdu_type;
-	u8 link_valid:1, link_id:4;
+	u8 link_id:4;
 };
 
 static inline u32
net/mac80211/eht.c +0 -3
index 75096b2195d2..f49c4729e8e8 100644
@@ -174,9 +174,6 @@ void ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
 	if (!ift_ext_capa)
 		return;
 
-	if (!status->link_valid)
-		return;
-
 	sta = sta_info_get_bss(sdata, mgmt->sa);
 	if (!sta)
 		return;
net/mac80211/iface.c +2 -5
index 676b2a43c9f2..ddb384d7cfa2 100644
@@ -1611,11 +1611,8 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
 				break;
 
 			status = IEEE80211_SKB_RXCB(skb);
-			if (!status->link_valid)
-				link_sta = &sta->deflink;
-			else
-				link_sta = rcu_dereference_protected(sta->link[status->link_id],
-							lockdep_is_held(&local->hw.wiphy->mtx));
+			link_sta = wiphy_dereference(local->hw.wiphy,
+						     sta->link[status->link_id]);
 			if (link_sta)
 				ieee80211_ht_handle_chanwidth_notif(local, sdata, sta,
 								    link_sta, chanwidth,
net/mac80211/mlme.c +3 -6
index e83582b2c377..4190c2830ac5 100644
@@ -8273,12 +8273,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 	mgmt = (struct ieee80211_mgmt *) skb->data;
 	fc = le16_to_cpu(mgmt->frame_control);
 
-	if (rx_status->link_valid) {
-		link = sdata_dereference(sdata->link[rx_status->link_id],
-					 sdata);
-		if (!link)
-			return;
-	}
+	link = sdata_dereference(sdata->link[rx_status->link_id], sdata);
+	if (!link)
+		return;
 
 	switch (fc & IEEE80211_FCTL_STYPE) {
 	case IEEE80211_STYPE_BEACON:
net/mac80211/rx.c +92 -117

440 lines not on this page · raw message

net/mac80211/scan.c +4 -6
index 4823c8d45639..bcbae67e2f49 100644
@@ -9,7 +9,7 @@
  * Copyright 2007, Michael Wu <[email protected]>
  * Copyright 2013-2015  Intel Mobile Communications GmbH
  * Copyright 2016-2017  Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  */
 
 #include <linux/if_arp.h>
@@ -204,12 +204,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
 		 * an indication on which of the links the frame was received
 		 */
 		if (ieee80211_vif_is_mld(&scan_sdata->vif)) {
-			if (rx_status->link_valid) {
-				s8 link_id = rx_status->link_id;
+			s8 link_id = rx_status->link_id;
 
-				link_conf =
-					rcu_dereference(scan_sdata->vif.link_conf[link_id]);
-			}
+			link_conf =
+				rcu_dereference(scan_sdata->vif.link_conf[link_id]);
 		} else {
 			link_conf = &scan_sdata->vif.bss_conf;
 		}
Signature · 1 line
2.53.0
#7 lore raw

RFC PATCH v2 4/8 wifi: mac80211: rework RX packet handling

In reply to Benjamin Berg

From: Benjamin Berg <[email protected]>

The code has grown over time and it was not correctly handling all cases. Examples of issues are that for management frames we would match the received address against the MLD address even though we should not resolve the station in that case. Another issue was that for data frames we would not correctly fall back to use link addresses when the driver does not provide the pubsta already.

The new code still uses the same approach as before. For data frames, assume that we a valid station exists and interfaces do not need to be iterated. On the other hand, for non-data frames (or if a data frame without a station is received) all interfaces must be iterated.

Note that this rework makes mac80211 slightly more strict. For example, previously mac80211 would have incorrectly accepted a data frame that was transmitted on the wrong link.

---
 net/mac80211/rx.c | 282 +++++++++++++++++++++++++++-------------------
 1 file changed, 169 insertions(+), 113 deletions(-)
net/mac80211/rx.c +169 -113
index 1b8ec391991f..d76e8ee1a607 100644
@@ -4284,9 +4284,33 @@ static bool
 ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
 {
 	if (sta->mlo)
-		return !!(sta->valid_links & BIT(link_id));
-	else
-		return sta->deflink.link_id == link_id;
+		return sta->valid_links & BIT(link_id);
+
+	return sta->deflink.link_id == link_id;
+}
+
+static bool ieee80211_rx_data_set_link_sta(struct ieee80211_rx_data *rx,
+					   struct link_sta_info *link_sta)
+{
+	struct sta_info *sta;
+
+	if (WARN_ON_ONCE(!link_sta) ||
+	    !ieee80211_rx_is_valid_sta_link_id(&link_sta->sta->sta,
+					       link_sta->link_id))
+		return false;
+
+	sta = link_sta->sta;
+
+	rx->sta = sta;
+	rx->local = sta->sdata->local;
+	rx->link = rcu_dereference(sta->sdata->link[link_sta->link_id]);
+	if (!rx->link)
+		return false;
+
+	rx->link_id = rx->link->link_id;
+	rx->link_sta = link_sta;
+
+	return true;
 }
 
 static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,
@@ -5192,53 +5216,18 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
 	dev_kfree_skb(skb);
 }
 
-static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
-				       struct sk_buff *skb, bool consume)
+static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link)
 {
-	struct link_sta_info *link_sta;
-	struct ieee80211_hdr *hdr = (void *)skb->data;
-	struct sta_info *sta;
-	int link_id = -1;
+	struct ieee80211_chanctx_conf *conf;
 
-	/*
-	 * FIXME: Here we can assume that link addresses have not
-	 * been translated.
-	 *
-	 * Look up link station first, in case there's a
-	 * chance that they might have a link address that
-	 * is identical to the MLD address, that way we'll
-	 * have the link information if needed.
-	 */
-	link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2);
-	if (link_sta) {
-		sta = link_sta->sta;
-		link_id = link_sta->link_id;
-	} else {
-		struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
-		sta = sta_info_get_bss(rx->sdata, hdr->addr2);
-		if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
-		    status->freq) {
-			struct ieee80211_link_data *link;
-			struct ieee80211_chanctx_conf *conf;
-
-			for_each_link_data_rcu(rx->sdata, link) {
-				conf = rcu_dereference(link->conf->chanctx_conf);
-				if (!conf || !conf->def.chan)
-					continue;
-
-				if (status->freq == conf->def.chan->center_freq) {
-					link_id = link->link_id;
-					break;
-				}
-			}
-		}
-	}
+	if (!freq || link->sdata->vif.type == NL80211_IFTYPE_NAN)
+		return true;
 
-	if (!ieee80211_rx_data_set_sta(rx, sta, link_id))
+	conf = rcu_dereference(link->conf->chanctx_conf);
+	if (!conf || !conf->def.chan)
 		return false;
 
-	return ieee80211_prepare_and_rx_handle(rx, skb, consume);
+	return freq == conf->def.chan->center_freq;
 }
 
 /*
@@ -5252,11 +5241,14 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr;
+	struct link_sta_info *link_sta;
+	struct sta_info *sta;
 	__le16 fc;
 	struct ieee80211_rx_data rx;
-	struct ieee80211_sub_if_data *prev;
 	struct rhlist_head *tmp;
+	bool rx_data_pending;
 	int err = 0;
 
 	fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
@@ -5292,92 +5284,102 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		     ieee80211_is_s1g_beacon(hdr->frame_control)))
 		ieee80211_scan_rx(local, skb);
 
+	/*
+	 * RX of a data frame should only happen to existing stations.
+	 * It is therefore more efficient to use the one provided by the driver
+	 * or search based on the station's address.
+	 *
+	 * Note we will fall through and handle a spurious data frame in the
+	 * same way as a management frame.
+	 */
 	if (ieee80211_is_data(fc)) {
-		struct sta_info *sta, *prev_sta;
-
 		if (link_pubsta) {
-			sta = container_of(link_pubsta->sta,
-					   struct sta_info, sta);
-			if (!ieee80211_rx_data_set_sta(&rx, sta,
-						       link_pubsta->link_id))
-				goto out;
+			sta = container_of(link_pubsta->sta, struct sta_info,
+					   sta);
+			link_sta = rcu_dereference(sta->link[link_pubsta->link_id]);
 
-			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
+			rx.sdata = sta->sdata;
+			if (ieee80211_rx_data_set_link_sta(&rx, link_sta) &&
+			    ieee80211_prepare_and_rx_handle(&rx, skb, true))
 				return;
+
 			goto out;
 		}
 
-		prev_sta = NULL;
+		rx_data_pending = false;
 
+		/* Search for stations on non-MLD interfaces */
 		for_each_sta_info(local, hdr->addr2, sta, tmp) {
-			int link_id;
+			struct ieee80211_link_data *link;
 
-			if (!prev_sta) {
-				prev_sta = sta;
+			if (ieee80211_vif_is_mld(&sta->sdata->vif))
 				continue;
-			}
-
-			rx.sdata = prev_sta->sdata;
-
-			/*
-			 * FIXME: This is not correct as the addr2 cannot be a
-			 * link address if the loop itself is iterated.
-			 */
-			if (prev_sta->sta.mlo) {
-				struct link_sta_info *link_sta;
 
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					continue;
+			link = &sta->sdata->deflink;
+			if (!ieee80211_rx_valid_freq(status->freq, link))
+				continue;
 
-				link_id = link_sta->link_id;
-			} else {
-				link_id = sta->deflink.link_id;
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb,
+								false);
+				rx_data_pending = false;
 			}
 
-			if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
-				goto out;
-
-			ieee80211_prepare_and_rx_handle(&rx, skb, false);
+			rx.sdata = sta->sdata;
+			if (!ieee80211_rx_data_set_link_sta(&rx, &sta->deflink))
+				continue;
 
-			prev_sta = sta;
+			rx_data_pending = true;
 		}
 
-		if (prev_sta) {
-			int link_id;
+		/* And search stations on MLD interfaces */
+		for_each_link_sta_info(local, hdr->addr2, link_sta, tmp) {
+			struct ieee80211_link_data *link;
 
-			rx.sdata = prev_sta->sdata;
+			sta = link_sta->sta;
+			sdata =	sta->sdata;
+			link = rcu_dereference(sdata->link[link_sta->link_id]);
 
-			/*
-			 * FIXME: This is not correct as the addr2 cannot be a
-			 * link address if the loop itself is iterated.
-			 */
-			if (prev_sta->sta.mlo) {
-				struct link_sta_info *link_sta;
-
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					goto out;
+			if (!ieee80211_rx_valid_freq(status->freq, link))
+				continue;
 
-				link_id = link_sta->link_id;
-			} else {
-				link_id = sta->deflink.link_id;
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb,
+								false);
+				rx_data_pending = false;
 			}
 
-			if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
-				goto out;
+			rx.sdata = sta->sdata;
+			if (!ieee80211_rx_data_set_link_sta(&rx, link_sta))
+				continue;
+
+			rx_data_pending = true;
+		}
 
+		if (rx_data_pending) {
 			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
 				return;
+
 			goto out;
 		}
+
+		/* fall through, e.g. for spurious frame notification */
 	}
 
-	prev = NULL;
+	/*
+	 * This is a management frame (or a data frame without a station) and
+	 * it will be delivered independent of whether a station exists,
+	 * so iterate the interfaces.
+	 */
+	rx_data_pending = false;
+
+	/* We expect the driver to provide frequency information */
+	if (WARN_ON_ONCE(!local->emulate_chanctx && !status->freq))
+		goto out;
 
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		struct ieee80211_link_data *link = NULL;
+
 		if (!ieee80211_sdata_running(sdata))
 			continue;
 
@@ -5385,30 +5387,84 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		    sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 			continue;
 
+		/* Try to resolve the station */
+		if (!ieee80211_vif_is_mld(&sdata->vif)) {
+			sta = sta_info_get_bss(sdata, hdr->addr2);
+
+			if (sta) {
+				link_sta = &sta->deflink;
+				link = &sdata->deflink;
+			}
+		} else {
+			link_sta = link_sta_info_get_bss(sdata, hdr->addr2);
+
+			if (link_sta) {
+				sta = link_sta->sta;
+				link = rcu_dereference(sdata->link[link_sta->link_id]);
+			}
+		}
+
 		/*
-		 * frame is destined for this interface, but if it's
-		 * not also for the previous one we handle that after
-		 * the loop to avoid copying the SKB once too much
+		 * If we found a STA and it has a valid link information, then
+		 * RX using the station.
 		 */
+		if (link_sta && link &&
+		    ieee80211_rx_valid_freq(status->freq, link)) {
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb, false);
+				rx_data_pending = false;
+			}
+
+			/* No valid_links check as we need to RX beacons */
+
+			rx.sdata = sdata;
+			if (ieee80211_rx_data_set_link_sta(&rx, link_sta))
+				rx_data_pending = true;
 
-		if (!prev) {
-			prev = sdata;
 			continue;
 		}
 
-		rx.sdata = prev;
-		ieee80211_rx_for_interface(&rx, skb, false);
+		/* No station, try to resolve the link and RX */
+		if (ieee80211_vif_is_mld(&sdata->vif)) {
+			struct ieee80211_chanctx_conf *conf;
+			bool found = false;
 
-		prev = sdata;
-	}
+			for_each_link_data_rcu(sdata, link) {
+				conf = rcu_dereference(link->conf->chanctx_conf);
+				if (!conf || !conf->def.chan)
+					continue;
+
+				if (status->freq == conf->def.chan->center_freq) {
+					found = true;
+					break;
+				}
+			}
 
-	if (prev) {
-		rx.sdata = prev;
+			if (!found)
+				link = &sdata->deflink;
+		} else {
+			link = &sdata->deflink;
+		}
 
-		if (ieee80211_rx_for_interface(&rx, skb, true))
-			return;
+		if (rx_data_pending) {
+			ieee80211_prepare_and_rx_handle(&rx, skb, false);
+			rx_data_pending = false;
+		}
+
+		rx.sdata = sdata;
+		rx.local = sdata->local;
+		rx.link = link;
+		rx.link_id = link->link_id;
+		rx.sta = NULL;
+		rx.link_sta = NULL;
+
+		rx_data_pending = true;
 	}
 
+	if (rx_data_pending &&
+	    ieee80211_prepare_and_rx_handle(&rx, skb, true))
+		return;
+
  out:
 	dev_kfree_skb(skb);
 }
Signature · 1 line
2.53.0
#8 lore raw

RFC PATCH v2 5/8 wifi: cfg80211: add attribute for TX/RX denoting there is no station

In reply to Benjamin Berg

From: Benjamin Berg <[email protected]>

For MLD stations, userspace may need to explicitly transmit a frame to a specific link address. In that case, it needs to ensure that no address translation happens.

In the reverse case of an RX, userspace may need to know the link address for a frame. By passing the information whether a STA is known for the frame, userspace knows whether link translation happened and can do the reverse lookup when needed.

This is important for flows where a STA is still registered but the connection has been lost and it is returning again.

---
 include/net/cfg80211.h       | 4 ++++
 include/uapi/linux/nl80211.h | 7 +++++++
 net/wireless/nl80211.c       | 8 +++++++-
 3 files changed, 18 insertions(+), 1 deletion(-)
include/net/cfg80211.h +4 -0
index fc01de19c798..5063911cba56 100644
@@ -3919,6 +3919,7 @@ struct cfg80211_update_ft_ies_params {
  * @link_id: for MLO, the link ID to transmit on, -1 if not given; note
  *	that the link ID isn't validated (much), it's in range but the
  *	link might not exist (or be used by the receiver STA)
+ * @no_sta: set if the frame should not be transmitted using an existing STA
  */
 struct cfg80211_mgmt_tx_params {
 	struct ieee80211_channel *chan;
@@ -3931,6 +3932,7 @@ struct cfg80211_mgmt_tx_params {
 	int n_csa_offsets;
 	const u16 *csa_offsets;
 	int link_id;
+	bool no_sta;
 };
 
 /**
@@ -9025,6 +9027,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
  * @flags: flags, as defined in &enum nl80211_rxmgmt_flags
  * @rx_tstamp: Hardware timestamp of frame RX in nanoseconds
  * @ack_tstamp: Hardware timestamp of ack TX in nanoseconds
+ * @no_sta: set if no station is known for the frame (relevant for MLD)
  */
 struct cfg80211_rx_info {
 	int freq;
@@ -9036,6 +9039,7 @@ struct cfg80211_rx_info {
 	u32 flags;
 	u64 rx_tstamp;
 	u64 ack_tstamp;
+	bool no_sta;
 };
 
 /**
include/uapi/linux/nl80211.h +7 -0
index b63f71850906..1466c043974c 100644
@@ -2984,6 +2984,11 @@ enum nl80211_commands {
  *	this feature during association. This is a flag attribute.
  *	Currently only supported in mac80211 drivers.
  *
+ * @NL80211_ATTR_FRAME_CMD_NO_STA: Valid for NL80211_CMD_FRAME to denote that
+ *	the kernel had no station for a received frame or should not use a
+ *	known station to transmit a frame. This is relevant to know whether
+ *	MLD address translation happened or to disable it when sending a frame.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3557,6 +3562,8 @@ enum nl80211_attrs {
 	NL80211_ATTR_UHR_CAPABILITY,
 	NL80211_ATTR_DISABLE_UHR,
 
+	NL80211_ATTR_FRAME_CMD_NO_STA,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
net/wireless/nl80211.c +7 -1
index 6e58b238a1f8..56a9c63ddd76 100644
@@ -946,6 +946,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[NL80211_ATTR_UHR_CAPABILITY] =
 		NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_capa, 255),
 	[NL80211_ATTR_DISABLE_UHR] = { .type = NLA_FLAG },
+	[NL80211_ATTR_FRAME_CMD_NO_STA] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -14020,6 +14021,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 	    !(wdev->valid_links & BIT(params.link_id)))
 		return -EINVAL;
 
+	params.no_sta =
+		nla_get_flag(info->attrs[NL80211_ATTR_FRAME_CMD_NO_STA]);
+
 	params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
 	params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
 
@@ -20581,7 +20585,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 	    (info->ack_tstamp && nla_put_u64_64bit(msg,
 						   NL80211_ATTR_TX_HW_TIMESTAMP,
 						   info->ack_tstamp,
-						   NL80211_ATTR_PAD)))
+						   NL80211_ATTR_PAD)) ||
+	    (info->no_sta &&
+	     nla_put_flag(msg, NL80211_ATTR_FRAME_CMD_NO_STA)))
 		goto nla_put_failure;
 
 	genlmsg_end(msg, hdr);
Signature · 1 line
2.53.0
#9 lore raw

RFC PATCH v2 6/8 wifi: mac80211: report to cfg80211 when no STA is known for a frame

In reply to Benjamin Berg

From: Benjamin Berg <[email protected]>

This is relevant for hostapd to know whether address translation was done on a received management frame.

---
 net/mac80211/rx.c | 1 +
 1 file changed, 1 insertion(+)
net/mac80211/rx.c +1 -0
index d76e8ee1a607..ec718b7f3e16 100644
@@ -3948,6 +3948,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
 		.len = rx->skb->len,
 		.link_id = rx->link_id,
 		.have_link_id = rx->link_id >= 0,
+		.no_sta = !rx->sta,
 	};
 
 	/* skip known-bad action frames and return them in the next handler */
Signature · 1 line
2.53.0
#10 lore raw

RFC PATCH v2 7/8 wifi: mac80211: pass station to ieee80211_tx_skb_tid

In reply to Benjamin Berg

From: Benjamin Berg <[email protected]>

The station may be relevant for queuing and will also generally be resolved in some cases. However, we want to be able to prevent looking up the station based on the address.

Add a station parameter, which can be set to the correct station, to an error value to prevent station lookup or to NULL to get the old behaviour where the address is used to find the appropriate station.

Also disable the station lookup for ieee80211_tx_skb_tid_band already as it does not make any sense to find a station when doing an off-channel transmit.

---
 net/mac80211/agg-tx.c      |  6 +++---
 net/mac80211/ht.c          |  4 ++--
 net/mac80211/ieee80211_i.h | 14 ++++++++------
 net/mac80211/offchannel.c  |  2 +-
 net/mac80211/rx.c          |  2 +-
 net/mac80211/tdls.c        |  4 ++--
 net/mac80211/tx.c          |  8 +++++---
 7 files changed, 22 insertions(+), 18 deletions(-)
net/mac80211/agg-tx.c +3 -3
index d981b0fc57bf..6a5754351f08 100644
@@ -9,7 +9,7 @@
  * Copyright 2007, Michael Wu <[email protected]>
  * Copyright 2007-2010, Intel Corporation
  * Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2024 Intel Corporation
+ * Copyright (C) 2018 - 2024, 2026 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -97,7 +97,7 @@ static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid,
 	if (sta->sta.deflink.he_cap.has_he)
 		ieee80211_add_addbaext(skb, 0, agg_size);
 
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
 }
 
 void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
@@ -126,7 +126,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
 
 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
 					IEEE80211_TX_CTL_REQ_TX_STATUS;
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
 }
 EXPORT_SYMBOL(ieee80211_send_bar);
 
net/mac80211/ht.c +2 -2
index 1c82a28b03de..f98f5a9a2ebe 100644
@@ -9,7 +9,7 @@
  * Copyright 2007, Michael Wu <[email protected]>
  * Copyright 2007-2010, Intel Corporation
  * Copyright 2017	Intel Deutschland GmbH
- * Copyright(c) 2020-2025 Intel Corporation
+ * Copyright(c) 2020-2026 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -571,7 +571,7 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
 	info->status_data = IEEE80211_STATUS_TYPE_SMPS |
 			    u16_encode_bits(status_link_id << 2 | smps,
 					    IEEE80211_STATUS_SUBDATA_MASK);
-	ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
 
 	return 0;
 }
net/mac80211/ieee80211_i.h +8 -6
index e60b814dd89e..793331c1d748 100644
@@ -2393,7 +2393,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
 		    struct sta_info *sta, struct sk_buff *skb);
 
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,
+				 int tid, int link_id,
 				 enum nl80211_band band);
 
 static inline bool ieee80211_require_encrypted_assoc(__le16 fc,
@@ -2411,22 +2412,23 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
 
 static inline void
 ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid,
-			  enum nl80211_band band)
+			  struct sk_buff *skb, int tid, enum nl80211_band band)
 {
 	rcu_read_lock();
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, -1, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, ERR_PTR(-ENOENT),
+				    tid, -1, band);
 	rcu_read_unlock();
 }
 
 void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id);
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id);
 
 static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
 				    struct sk_buff *skb)
 {
 	/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
-	ieee80211_tx_skb_tid(sdata, skb, 7, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, -1);
 }
 
 /**
net/mac80211/offchannel.c +1 -1
index ae82533e3c02..0a8b4c5e8c12 100644
@@ -1026,7 +1026,7 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	}
 
 	if (!need_offchan) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
 		ret = 0;
 		goto out_unlock;
 	}
net/mac80211/rx.c +1 -1
index ec718b7f3e16..5109791546a7 100644
@@ -4072,7 +4072,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
 					local->hw.offchannel_tx_hw_queue;
 		}
 
-		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, -1,
+		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, rx->sta, 7, -1,
 					    status->band);
 	}
 
net/mac80211/tdls.c +2 -2
index dbbfe2d6842f..39a880ab7edb 100644
@@ -6,7 +6,7 @@
  * Copyright 2014, Intel Corporation
  * Copyright 2014  Intel Mobile Communications GmbH
  * Copyright 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2019, 2021-2025 Intel Corporation
+ * Copyright (C) 2019, 2021-2026 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -1067,7 +1067,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
 	}
 
 	if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb, sta, 7, link_id);
 		return 0;
 	}
 
net/mac80211/tx.c +5 -3
index 007f5a368d41..c788d48ef365 100644
@@ -6235,7 +6235,8 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
 EXPORT_SYMBOL(ieee80211_unreserve_tid);
 
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,
+				 int tid, int link_id,
 				 enum nl80211_band band)
 {
 	const struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -6292,7 +6293,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 }
 
 void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id)
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id)
 {
 	struct ieee80211_chanctx_conf *chanctx_conf;
 	enum nl80211_band band;
@@ -6317,7 +6319,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
 		band = 0;
 	}
 
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, link_id, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, sta, tid, link_id, band);
 	rcu_read_unlock();
 }
 
Signature · 1 line
2.53.0
#11 lore raw

Re: [RFC PATCH v2 7/8] wifi: mac80211: pass station to ieee80211_tx_skb_tid

Quoted 93 lines · Benjamin Berg

From: Benjamin Berg <[email protected]>

The station may be relevant for queuing and will also generally be resolved in some cases. However, we want to be able to prevent looking up the station based on the address.

Add a station parameter, which can be set to the correct station, to an error value to prevent station lookup or to NULL to get the old behaviour where the address is used to find the appropriate station.

Also disable the station lookup for ieee80211_tx_skb_tid_band already as it does not make any sense to find a station when doing an off-channel transmit.

---
  net/mac80211/agg-tx.c      |  6 +++---
  net/mac80211/ht.c          |  4 ++--
  net/mac80211/ieee80211_i.h | 14 ++++++++------
  net/mac80211/offchannel.c  |  2 +-
  net/mac80211/rx.c          |  2 +-
  net/mac80211/tdls.c        |  4 ++--
  net/mac80211/tx.c          |  8 +++++---
  7 files changed, 22 insertions(+), 18 deletions(-)
net/mac80211/agg-tx.c +3 -3
index d981b0fc57bf..6a5754351f08 100644
@@ -9,7 +9,7 @@
   * Copyright 2007, Michael Wu <[email protected]>
   * Copyright 2007-2010, Intel Corporation
   * Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2024 Intel Corporation
+ * Copyright (C) 2018 - 2024, 2026 Intel Corporation
   */
 
  #include <linux/ieee80211.h>
@@ -97,7 +97,7 @@ static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid,
  	if (sta->sta.deflink.he_cap.has_he)
  		ieee80211_add_addbaext(skb, 0, agg_size);
 
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
  }
 
  void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
@@ -126,7 +126,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
 
  	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
  					IEEE80211_TX_CTL_REQ_TX_STATUS;
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
  }
  EXPORT_SYMBOL(ieee80211_send_bar);
 
net/mac80211/ht.c +2 -2
index 1c82a28b03de..f98f5a9a2ebe 100644
@@ -9,7 +9,7 @@
   * Copyright 2007, Michael Wu <[email protected]>
   * Copyright 2007-2010, Intel Corporation
   * Copyright 2017	Intel Deutschland GmbH
- * Copyright(c) 2020-2025 Intel Corporation
+ * Copyright(c) 2020-2026 Intel Corporation
   */
 
  #include <linux/ieee80211.h>
@@ -571,7 +571,7 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
  	info->status_data = IEEE80211_STATUS_TYPE_SMPS |
  			    u16_encode_bits(status_link_id << 2 | smps,
  					    IEEE80211_STATUS_SUBDATA_MASK);
-	ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
 
  	return 0;
  }
net/mac80211/ieee80211_i.h +2 -1
index e60b814dd89e..793331c1d748 100644
@@ -2393,7 +2393,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
  		    struct sta_info *sta, struct sk_buff *skb);
 
  void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,
+				 int tid, int link_id,
  				 enum nl80211_band band);
 
  static inline bool ieee80211_require_encrypted_assoc(__le16 fc,

net/mac80211/ieee80211_i.h int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, · line 2415

Quoted 6 lines
net/mac80211/ieee80211_i.h +1 -2
@@ -2411,22 +2412,23 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
 
  static inline void
  ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid,
-			  enum nl80211_band band)
+			  struct sk_buff *skb, int tid, enum nl80211_band band)

do we need this hunk? There’s no functional change.

Quoted 75 lines
net/mac80211/ieee80211_i.h +5 -3
  {
  	rcu_read_lock();
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, -1, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, ERR_PTR(-ENOENT),
+				    tid, -1, band);
  	rcu_read_unlock();
  }
 
  void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id);
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id);
 
  static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
  				    struct sk_buff *skb)
  {
  	/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
-	ieee80211_tx_skb_tid(sdata, skb, 7, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, -1);
  }
 
  /**
net/mac80211/offchannel.c +1 -1
index ae82533e3c02..0a8b4c5e8c12 100644
@@ -1026,7 +1026,7 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
  	}
 
  	if (!need_offchan) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
  		ret = 0;
  		goto out_unlock;
  	}
net/mac80211/rx.c +1 -1
index ec718b7f3e16..5109791546a7 100644
@@ -4072,7 +4072,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
  					local->hw.offchannel_tx_hw_queue;
  		}
 
-		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, -1,
+		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, rx->sta, 7, -1,
  					    status->band);
  	}
 
net/mac80211/tdls.c +2 -2
index dbbfe2d6842f..39a880ab7edb 100644
@@ -6,7 +6,7 @@
   * Copyright 2014, Intel Corporation
   * Copyright 2014  Intel Mobile Communications GmbH
   * Copyright 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2019, 2021-2025 Intel Corporation
+ * Copyright (C) 2019, 2021-2026 Intel Corporation
   */
 
  #include <linux/ieee80211.h>
@@ -1067,7 +1067,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
  	}
 
  	if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb, sta, 7, link_id);
  		return 0;
  	}
 
net/mac80211/tx.c +0 -0
index 007f5a368d41..c788d48ef365 100644

net/mac80211/tx.c void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid) · line 6238

Quoted 5 lines
net/mac80211/tx.c +1 -1
@@ -6235,7 +6235,8 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
  EXPORT_SYMBOL(ieee80211_unreserve_tid);
 
  void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,

*sta is not used anywhere in this function, do we actually need to add it?

Quoted 15 lines
net/mac80211/tx.c +3 -1
+				 int tid, int link_id,
  				 enum nl80211_band band)
  {
  	const struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -6292,7 +6293,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
  }
 
  void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id)
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id)
  {
  	struct ieee80211_chanctx_conf *chanctx_conf;
  	enum nl80211_band band;

net/mac80211/tx.c void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, · line 6325

Quoted 8 lines
net/mac80211/tx.c +1 -1
@@ -6317,7 +6319,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
  		band = 0;
  	}
 
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, link_id, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, sta, tid, link_id, band);
  	rcu_read_unlock();
  }
 

For the no_sta tx path, The sta lookup happens in ieee80211_tx_prepare() (invoked by ieee80211_tx). My understanding is that the skb still ends up being queued with a sta. Is that correct?

Thanks,
Ramasamy

#12 lore raw

Re: [RFC PATCH v2 7/8] wifi: mac80211: pass station to ieee80211_tx_skb_tid

Hi,

Quoted 4 lines · Ramasamy Kaliappan

[SNIP]
For the no_sta tx path, The sta lookup happens in ieee80211_tx_prepare() (invoked by ieee80211_tx). My understanding is that the skb still ends up being queued with a sta.  Is that correct?

Right, should have replied here maybe. Obviously, what we want is that the error number to be passed down to ieee80211_tx_prepare so that it will not do a station lookup.

The update of the ieee80211_xmit call to pass sta is missing in this patch version. With that added, ieee80211_tx_prepare should get the error number and take the branch that sta is not NULL. However, it will leave rx->sta NULL as it is an error pointer.

At least that is the intention.

Benjamin

#13 lore raw

RFC PATCH v2 8/8 wifi: mac80211: pass error station if non-STA transmit was requested

In reply to Benjamin Berg

From: Benjamin Berg <[email protected]>

When cfg80211 requested a transmit without a station, pass an error station to ieee80211_tx_skb_tid instead of the correct one.

---
 net/mac80211/offchannel.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
net/mac80211/offchannel.c +9 -4
index 0a8b4c5e8c12..24a55186b87f 100644
@@ -857,8 +857,10 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 			need_offchan = true;
 
 		rcu_read_lock();
-		sta = sta_info_get_bss(sdata, mgmt->da);
-		mlo_sta = sta && sta->sta.mlo;
+		if (!params->no_sta) {
+			sta = sta_info_get_bss(sdata, mgmt->da);
+			mlo_sta = sta && sta->sta.mlo;
+		}
 
 		if (!ieee80211_is_action(mgmt->frame_control) ||
 		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
@@ -887,7 +889,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 		     local->ops->remain_on_channel &&
 		     memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) {
 			need_offchan = true;
-		} else if (sdata->u.mgd.associated &&
+		} else if (!params->no_sta &&
+			   sdata->u.mgd.associated &&
 			   ether_addr_equal(sdata->vif.cfg.ap_addr, mgmt->da)) {
 			sta = sta_info_get_bss(sdata, mgmt->da);
 			mlo_sta = sta && sta->sta.mlo;
@@ -1026,7 +1029,9 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	}
 
 	if (!need_offchan) {
-		ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb,
+				     sta ? sta : ERR_PTR(-ENOENT),
+				     7, link_id);
 		ret = 0;
 		goto out_unlock;
 	}
Signature · 1 line
2.53.0
#14 lore raw

Re: [RFC PATCH v2 8/8] wifi: mac80211: pass error station if non-STA transmit was requested

Quoted 38 lines · Benjamin Berg

From: Benjamin Berg <[email protected]>

When cfg80211 requested a transmit without a station, pass an error station to ieee80211_tx_skb_tid instead of the correct one.

---
  net/mac80211/offchannel.c | 13 +++++++++----
  1 file changed, 9 insertions(+), 4 deletions(-)
net/mac80211/offchannel.c +6 -3
index 0a8b4c5e8c12..24a55186b87f 100644
@@ -857,8 +857,10 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
  			need_offchan = true;
 
  		rcu_read_lock();
-		sta = sta_info_get_bss(sdata, mgmt->da);
-		mlo_sta = sta && sta->sta.mlo;
+		if (!params->no_sta) {
+			sta = sta_info_get_bss(sdata, mgmt->da);
+			mlo_sta = sta && sta->sta.mlo;
+		}
 
  		if (!ieee80211_is_action(mgmt->frame_control) ||
  		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
@@ -887,7 +889,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
  		     local->ops->remain_on_channel &&
  		     memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) {
  			need_offchan = true;
-		} else if (sdata->u.mgd.associated &&
+		} else if (!params->no_sta &&
+			   sdata->u.mgd.associated &&
  			   ether_addr_equal(sdata->vif.cfg.ap_addr, mgmt->da)) {
  			sta = sta_info_get_bss(sdata, mgmt->da);
  			mlo_sta = sta && sta->sta.mlo;

net/mac80211/offchannel.c int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, · line 1033

Quoted 6 lines
net/mac80211/offchannel.c +2 -1
@@ -1026,7 +1029,9 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
  	}
 
  	if (!need_offchan) {
-		ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb,
+				     sta ? sta : ERR_PTR(-ENOENT),

Do we need to pass sta here? I didn't see sta actually being used in __ieee80211_tx_skb_tid_band().

net/mac80211/offchannel.c int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, · line 1037

Quoted 4 lines
net/mac80211/offchannel.c +1 -0
+				     7, link_id);
  		ret = 0;
  		goto out_unlock;
  	}

Thanks,
Ramasamy

#15 lore raw

Re: [RFC PATCH v2 8/8] wifi: mac80211: pass error station if non-STA transmit was requested

Quoted 11 lines · Ramasamy Kaliappan

net/mac80211/offchannel.c int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, · line 1033

Quoted 6 lines · Benjamin Berg

[SNIP]
   if (!need_offchan) {

(quoted diff) +0 -1
-		ieee80211_tx_skb_tid(sdata, skb, NULL, 7,

link_id);

net/mac80211/offchannel.c +2 -0
+		ieee80211_tx_skb_tid(sdata, skb,
+				     sta ? sta : ERR_PTR(-ENOENT),

Do we need to pass sta here? I didn't see sta actually being used in __ieee80211_tx_skb_tid_band().

Uh, it should be.

You are right, in the end I forget to update the call to ieee80211_xmit to pass sta instead of NULL there.

Benjamin

Quoted 1 line
Quoted 1 line

[SNIP]

#16 lore raw
In reply to Benjamin Berg
Quoted 11 lines · Benjamin Berg

From: Benjamin Berg <[email protected]>

Hi,

This patchset refactors the RX link resolution a bit to fix some issues where mac80211 might accept frames on the wrong link and incorrectly translate the address. It also adds a new NL80211_ATTR_FRAME_CMD_NO_STA flag so that userspace can know whether address translation was done by the kernel on RX and can also prevent address translation for management frames during TX.

Quoted 8 lines

This together should be enough to fix the existing issues in hostapd where stations that are still associated try to authenticate again but hostapd for example ends up sending the frame to an old link address.

I would appreciate if you test the patches and work on the hostapd side. Note that I have not properly verified the new nl80211 API, so it could well be that I missed something.

I'll work on the hostapd side accordingly and share the patches for review.

Quoted 37 lines

Benjamin

Changes in RFCv2:
* Port other drivers to new API (untested)
* Fix a checkpatch warning

Benjamin Berg (8):
wifi: iwlwifi: use link_sta internally to the driver
wifi: mac80211: change public RX API to use link stations
wifi: mac80211: refactor RX link_id and station handling
wifi: mac80211: rework RX packet handling
wifi: cfg80211: add attribute for TX/RX denoting there is no station
wifi: mac80211: report to cfg80211 when no STA is known for a frame
wifi: mac80211: pass station to ieee80211_tx_skb_tid
wifi: mac80211: pass error station if non-STA transmit was requested

  drivers/net/wireless/ath/ath11k/dp_rx.c       |   2 +-
  drivers/net/wireless/ath/ath12k/dp_mon.c      |  18 +-
  drivers/net/wireless/ath/ath12k/dp_rx.c       |  15 +-
  drivers/net/wireless/intel/iwlwifi/mld/agg.c  |  21 +-
  drivers/net/wireless/intel/iwlwifi/mld/agg.h  |   4 +-
  drivers/net/wireless/intel/iwlwifi/mld/rx.c   |  50 +-
  drivers/net/wireless/intel/iwlwifi/mld/rx.h   |   2 +-
  .../wireless/intel/iwlwifi/mld/tests/agg.c    |   7 +-
  drivers/net/wireless/intel/iwlwifi/mvm/rx.c   |   2 +-
  drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c |   6 +-
  drivers/net/wireless/mediatek/mt76/mac80211.c |  22 +-
  drivers/net/wireless/realtek/rtw89/core.c     |   6 -
  drivers/net/wireless/virtual/mac80211_hwsim.c |   3 -
  include/net/cfg80211.h                        |   4 +
  include/net/mac80211.h                        |  25 +-
  include/uapi/linux/nl80211.h                  |   7 +
  net/mac80211/agg-tx.c                         |   6 +-
  net/mac80211/eht.c                            |   3 -
  net/mac80211/ht.c                             |   4 +-
  net/mac80211/ieee80211_i.h                    |  14 +-
  net/mac80211/iface.c                          |   7 +-
Quoted 9 lines
  net/mac80211/mlme.c                           |   9 +-
  net/mac80211/offchannel.c                     |  13 +-
  net/mac80211/rx.c                             | 436 ++++++++++--------
  net/mac80211/scan.c                           |  10 +-
  net/mac80211/tdls.c                           |   4 +-
  net/mac80211/tx.c                             |   8 +-
  net/wireless/nl80211.c                        |   8 +-
  28 files changed, 390 insertions(+), 326 deletions(-)

Thanks,
Ramasamy

#17 lore raw
In reply to Benjamin Berg
Quoted 56 lines · Benjamin Berg

From: Benjamin Berg <[email protected]>

Hi,

This patchset refactors the RX link resolution a bit to fix some issues where mac80211 might accept frames on the wrong link and incorrectly translate the address. It also adds a new NL80211_ATTR_FRAME_CMD_NO_STA flag so that userspace can know whether address translation was done by the kernel on RX and can also prevent address translation for management frames during TX.

This together should be enough to fix the existing issues in hostapd where stations that are still associated try to authenticate again but hostapd for example ends up sending the frame to an old link address.

I would appreciate if you test the patches and work on the hostapd side. Note that I have not properly verified the new nl80211 API, so it could well be that I missed something.

Benjamin

Changes in RFCv2:
* Port other drivers to new API (untested)
* Fix a checkpatch warning

Benjamin Berg (8):
wifi: iwlwifi: use link_sta internally to the driver
wifi: mac80211: change public RX API to use link stations
wifi: mac80211: refactor RX link_id and station handling
wifi: mac80211: rework RX packet handling
wifi: cfg80211: add attribute for TX/RX denoting there is no station
wifi: mac80211: report to cfg80211 when no STA is known for a frame
wifi: mac80211: pass station to ieee80211_tx_skb_tid
wifi: mac80211: pass error station if non-STA transmit was requested

  drivers/net/wireless/ath/ath11k/dp_rx.c       |   2 +-
  drivers/net/wireless/ath/ath12k/dp_mon.c      |  18 +-
  drivers/net/wireless/ath/ath12k/dp_rx.c       |  15 +-
  drivers/net/wireless/intel/iwlwifi/mld/agg.c  |  21 +-
  drivers/net/wireless/intel/iwlwifi/mld/agg.h  |   4 +-
  drivers/net/wireless/intel/iwlwifi/mld/rx.c   |  50 +-
  drivers/net/wireless/intel/iwlwifi/mld/rx.h   |   2 +-
  .../wireless/intel/iwlwifi/mld/tests/agg.c    |   7 +-
  drivers/net/wireless/intel/iwlwifi/mvm/rx.c   |   2 +-
  drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c |   6 +-
  drivers/net/wireless/mediatek/mt76/mac80211.c |  22 +-
  drivers/net/wireless/realtek/rtw89/core.c     |   6 -
  drivers/net/wireless/virtual/mac80211_hwsim.c |   3 -
  include/net/cfg80211.h                        |   4 +
  include/net/mac80211.h                        |  25 +-
  include/uapi/linux/nl80211.h                  |   7 +
  net/mac80211/agg-tx.c                         |   6 +-
  net/mac80211/eht.c                            |   3 -
  net/mac80211/ht.c                             |   4 +-
  net/mac80211/ieee80211_i.h                    |  14 +-
  net/mac80211/iface.c                          |   7 +-
Quoted 9 lines
  net/mac80211/mlme.c                           |   9 +-
  net/mac80211/offchannel.c                     |  13 +-
  net/mac80211/rx.c                             | 436 ++++++++++--------
  net/mac80211/scan.c                           |  10 +-
  net/mac80211/tdls.c                           |   4 +-
  net/mac80211/tx.c                             |   8 +-
  net/wireless/nl80211.c                        |   8 +-
  28 files changed, 390 insertions(+), 326 deletions(-)

The hostapd changes were made and validated against the relevant test cases. During testing, we observed some complexities in specific scenarios particularly when a Non‑AP STA first associates as L1 (M1) and then reappears as L1 (M2) during the authentication/association phase.
In this case, on the RX path, when an authentication frame for L1 (M2 in the ML IE) is received, the driver finds the existing STA L1 (M1) and therefore does not add the NO_STA attribute.

As a result, the complexity of handling authentication and association frames increases within the user‑space hostapd application. While processing the authentication frame, hostapd parses the ML IE but fails to create a new STA entry for L1 (M2) in the driver, especially in scenarios involving different MLD addresses.

To simplify this handling, we would like to explore an alternative approach that avoids address translation for management frames (authentication, association, and reassociation), regardless of whether the STA already exists.

However, this approach may introduce backward compatibility concerns when either the driver or hostapd is not updated. To address this, we propose introducing a capability indication mechanism between the driver and user space (driver ↔ hostapd). Based on the advertised capabilities, mac80211 can then skip management‑frame address translation accordingly.

Benjamin and Jouni, Could you please help share your thoughts on this?

Thanks,
Ramasamy

#18 lore raw

Hi,

Quoted 8 lines · Ramasamy Kaliappan

[SNIP]
The hostapd changes were made and validated against the relevant test cases. During testing, we observed some complexities in specific scenarios particularly when a Non‑AP STA first associates as L1 (M1) and then reappears as L1 (M2) during the authentication/association phase. In this case, on the RX path, when an authentication frame for L1 (M2 in the ML IE) is received, the driver finds the existing STA L1 (M1) and therefore does not add the NO_STA attribute.

Yes, the address will be translated in that case.

Quoted 10 lines

As a result, the complexity of handling authentication and association frames increases within the user‑space hostapd application. While processing the authentication frame, hostapd parses the ML IE but fails to create a new STA entry for L1 (M2) in the driver, especially in scenarios involving different MLD addresses.

To simplify this handling, we would like to explore an alternative approach that avoids address translation for management frames (authentication, association, and reassociation), regardless of whether the STA already exists.

I am not sure why that would be easier. Also, my hunch is that it makes sense to translate the address in particular with encrypted association frames.

hostapd receives the link ID that the frame was received on. So when the address was translated (no NO_STA flag), hostapd can use the MLD Address + Link ID to do a lookup in its own station database and use that to look up the SA address that was used to TX the frame. So you should be able to emulate the suggested mac80211 behaviour inside hostapd.

Note that when a STA that first used L1 (M1) reappears using L1 (M2), then hostapd cannot create a STA entry in mac80211. So the existing M1 address should be used to TX the SA Query to the old station, while an explicit TX to L1 with the NO_STA flag needs to be done for the comeback frame.

I believe that hostapd will need a bit of refactoring, including improved tracking to know whether a STA has been inserted into the driver and whether to TX the AUTH frames using the link address and NO_STA flag or using the MLD Address.

Quoted 8 lines

However, this approach may introduce backward compatibility concerns when either the driver or hostapd is not updated.
To address this, we propose introducing a capability indication mechanism between the driver and user space (driver ↔ hostapd). Based on the advertised capabilities, mac80211 can then skip management‑frame address translation accordingly.

Benjamin and Jouni, Could you please help share your thoughts on this?

Right, I would like to avoid introducing a new capability mechanism unless it is a real improvement for the hostapd implementation. It could well be that I am missing something, but right now I do not see that this is the case.

If you are still unsure, then I guess we should probably go back to the list of scenarios we had and check for each one how it can be detected and how it should be handled.

Benjamin

#19 lore raw
Quoted 4 lines · Benjamin Berg

I believe that hostapd will need a bit of refactoring, including improved tracking to know whether a STA has been inserted into the driver and whether to TX the AUTH frames using the link address and NO_STA flag or using the MLD Address.

I agree with this design. Refactoring hostapd to handle all STA consistently both in the driver and in hostapd in scenarios where a STA roams with MLD and link address swaps occurs, in both legacy and MLO cases.

Additionally, the design should help accommodate upcoming requirements such as Wi‑Fi 8 (802.11bi). In particular, it can address scenarios where a non‑AP ML STA link address needs to be translated to a DS address in the STA mode, while on the AP mode the link address translated to the MLD address. I believe these aspects can be handled as part of this design.

Thanks,
Ramasamy.

#20 lore raw

Hi,

Quoted 9 lines · Ramasamy Kaliappan
Quoted 4 lines · Benjamin Berg

I believe that hostapd will need a bit of refactoring, including improved tracking to know whether a STA has been inserted into the driver and whether to TX the AUTH frames using the link address and NO_STA flag or using the MLD Address.

I agree with this design. Refactoring hostapd to handle all STA consistently both in the driver and in hostapd in scenarios where a STA roams with MLD and link address swaps occurs, in both legacy and MLO cases.

Quoted 6 lines

Additionally, the design should help accommodate upcoming requirements such as Wi‑Fi 8 (802.11bi). In particular, it can address scenarios where a non‑AP ML STA link address needs to be translated to a DS address in the STA mode, while on the AP mode the link address translated to the MLD address. I believe these aspects can be handled as part of this design.

While posting the patch again I noticed that I never replied to this.

I am not exactly sure what you wanted to say here. My thought right now is that the issue of the DS address is a separate one. The DS address will affect some related code paths, but only affects data frames at a later point. The translation will happen eventually but only after all the MLD/link address translations have already been dealt with (or in HW with RX_FLAG_8023).

So, my current view is that the DS MAC configuration affects related code, but it is not relevant to mac80211 in this case.

Benjamin

maintainer
#21 lore raw
In reply to Ramasamy Kaliappan
Quoted 5 lines · Ramasamy Kaliappan

To simplify this handling, we would like to explore an alternative approach that avoids address translation for management frames (authentication, association, and reassociation), regardless of whether the STA already exists.

I'm not convinced that making one specific case a little bit easier is worth the trade-off for making *all* (other) cases handle link addresses throughout the entire wifi stack.

Quoted 4 lines

However, this approach may introduce backward compatibility concerns when either the driver or hostapd is not updated.
To address this, we propose introducing a capability indication mechanism between the driver and user space (driver ↔ hostapd).

Even assuming we make the (IMHO misleading) effort of not translating and having it handled throughout the stack, this also can't really work, unless we suddenly start translating (or not) for each individual application that may be using action frame registration _separately_.

I don't believe this approach makes more sense than translating and sorting out the corner cases.

johannes