Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH RFC wireless-next] wifi: mac80211: correct rx freq handling for S1G
@ 2026-10-02  7:29 Lachlan Hodges
  2026-10-02  7:58 ` Johannes Berg
  2026-10-06  7:44 ` Johannes Berg
  0 siblings, 2 replies; 7+ messages in thread
From: Lachlan Hodges @ 2026-10-02  7:29 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, benjamin.berg, arien.judge, Lachlan Hodges

6d531b9af16e ("wifi: mac80211: rework RX packet handling") reworked
Rx frame handling with a specific focus on MLD, but it also introduced
some changes for non-MLD interfaces. Previously when a station was
looked up (either because the driver did not pass one or handling
a management frame) it was done via hdr->addr2 and that was it. Now
the received frames frequency is validated against the links
control channel alongside the station lookup.

S1G has a unique feature where the control or primary channel can
either be 1MHz or 2MHz. However there is _always_ a 1MHz primary.
S1G drivers advertise these 1MHz primaries but it means if a 2MHz
primary is used the control channel inside the chandef may not be
the channel used to pass management frames. As a result, the rework
causes the following two issues on an S1G link:

1. When data frames are passed to mac80211 without a station (for
   example using ieee80211_rx_ni()) the frames rx status frequency
   is compared against the 1MHz control frequency used by the link
   via ieee80211_rx_valid_freq. Since the rx status reported is of
   the center frequency of transmission (i.e either the operating
   channel or the 2MHz primary in the case of multicast/EAPOL etc.)
   these frames are _all_ dropped.

2. In the case of protected management frames such as ADDBA, deauths
   etc. the station is always looked up so it does not matter if a
   station is passed from the driver, the frequency conversion against
   the 1MHz control channel will always fail and these frames will be
   dropped.

For regular, unprotected management frames such as assoc requests,
auth etc. these frames don't depend on a station and are matched via
BSSID so are unaffected by this change.

To fix, we can reuse the logic implemented in 31e7681da78d ("wifi:
mac80211: correctly initialise S1G chandef for STA") where we find
the sibling 1MHz and calculate the 2MHz center frequency. For the
ocassional protected management frames this is fine, obviously it
is still ideal for drivers to pass the station directly to mac80211
such that the lookup avoids needing the extra work of finding the
2MHz center frequency.

Fixes: 6d531b9af16e ("wifi: mac80211: rework RX packet handling")
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---

A followup question:

1. is rx_status->freq always meant to be the control channel? For
   management frames obviously makes sense but for data frames
   sent on a wider channel wouldn't this be set to the operating
   channel? Especially for i.e sniffer? The mm81x driver reports
   it like this but maybe it should just report the control channel?
   I guess my confusion stems from that if we have a data frame
   wouldn't we compare against the chandefs frequency? Or am I
   misunderstanding something?

Thanks,
lachlan

---
 include/net/mac80211.h     |  2 ++
 net/mac80211/ieee80211_i.h |  3 +++
 net/mac80211/mlme.c        | 36 ++-----------------------------
 net/mac80211/rx.c          | 43 ++++++++++++++++++++++++++++++++------
 4 files changed, 44 insertions(+), 40 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index cb9f8e14b3b6..b2a7857f9503 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1741,6 +1741,8 @@ enum mac80211_rx_encoding {
  * @freq: frequency the radio was tuned to when receiving this frame, in MHz
  *	This field must be set for management frames, but isn't strictly needed
  *	for data (other) frames - for those it only affects radiotap reporting.
+ *	For S1G, when operating on a 2MHz primary channel, this may be the
+ *	center frequency of the 2MHz primary rather than the 1MHz primary.
  * @freq_offset: @freq has a positive offset of 500Khz.
  * @signal: signal strength when receiving this frame, either in dBm, in dB or
  *	unspecified depending on the hardware capabilities flags
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 1430527d216c..2ad93cb462ad 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2012,6 +2012,9 @@ void ieee80211_clear_fast_rx(struct sta_info *sta);
 
 bool ieee80211_is_our_addr(struct ieee80211_sub_if_data *sdata,
 			   const u8 *addr, int *out_link_id);
+bool __ieee80211_rx_valid_freq(struct wiphy *wiphy,
+			       struct ieee80211_rx_status *status,
+			       const struct cfg80211_chan_def *chandef);
 
 /* AP code */
 void ieee80211_ap_rx_queued_frame(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 3a17d1ccd32a..8a07d14b4c43 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -8091,38 +8091,6 @@ static bool ieee80211_mgd_ssid_mismatch(struct ieee80211_sub_if_data *sdata,
 	return memcmp(elems->ssid, cfg->ssid, cfg->ssid_len);
 }
 
-static bool
-ieee80211_rx_beacon_freq_valid(struct ieee80211_local *local,
-			       struct ieee80211_mgmt *mgmt,
-			       struct ieee80211_rx_status *rx_status,
-			       struct ieee80211_chanctx_conf *chanctx)
-{
-	u32 pri_2mhz_khz;
-	struct ieee80211_channel *s1g_sibling_1mhz;
-	u32 pri_khz = ieee80211_channel_to_khz(chanctx->def.chan);
-	u32 rx_khz = ieee80211_rx_status_to_khz(rx_status);
-
-	if (rx_khz == pri_khz)
-		return true;
-
-	if (!chanctx->def.s1g_primary_2mhz)
-		return false;
-
-	/*
-	 * If we have an S1G interface with a 2MHz primary, beacons are
-	 * sent on the center frequency of the 2MHz primary. Find the sibling
-	 * 1MHz channel and calculate the 2MHz primary center frequency.
-	 */
-	s1g_sibling_1mhz = cfg80211_s1g_get_primary_sibling(local->hw.wiphy,
-							    &chanctx->def);
-	if (!s1g_sibling_1mhz)
-		return false;
-
-	pri_2mhz_khz =
-		(pri_khz + ieee80211_channel_to_khz(s1g_sibling_1mhz)) / 2;
-	return rx_khz == pri_2mhz_khz;
-}
-
 static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
 				     struct ieee80211_hdr *hdr, size_t len,
 				     struct ieee80211_rx_status *rx_status)
@@ -8176,8 +8144,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
 		return;
 	}
 
-	if (!ieee80211_rx_beacon_freq_valid(local, mgmt, rx_status,
-					    chanctx_conf)) {
+	if (!__ieee80211_rx_valid_freq(local->hw.wiphy, rx_status,
+				       &chanctx_conf->def)) {
 		rcu_read_unlock();
 		return;
 	}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index b3990b7a7299..9a29928a3a52 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -5318,11 +5318,41 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
 	dev_kfree_skb(skb);
 }
 
-static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link)
+bool __ieee80211_rx_valid_freq(struct wiphy *wiphy,
+			       struct ieee80211_rx_status *status,
+			       const struct cfg80211_chan_def *chandef)
+{
+	u32 pri_khz = ieee80211_channel_to_khz(chandef->chan);
+	u32 rx_khz = ieee80211_rx_status_to_khz(status);
+	struct ieee80211_channel *sibling;
+
+	if (rx_khz == pri_khz)
+		return true;
+
+	/* Any non-S1G case from here is not a valid freq */
+	if (!cfg80211_chandef_is_s1g(chandef))
+		return false;
+
+	if (!chandef->s1g_primary_2mhz)
+		return false;
+
+	/*
+	 * Find the sibling 1MHz channel of the 2MHz primary to calculate
+	 * the 2MHz primary center frequency.
+	 */
+	sibling = cfg80211_s1g_get_primary_sibling(wiphy, chandef);
+	if (!sibling)
+		return false;
+
+	return rx_khz == (pri_khz + ieee80211_channel_to_khz(sibling)) / 2;
+}
+
+static bool ieee80211_rx_valid_freq(struct ieee80211_rx_status *status,
+				    struct ieee80211_link_data *link)
 {
 	struct ieee80211_chanctx_conf *conf;
 
-	if (!freq || link->sdata->vif.type == NL80211_IFTYPE_NAN ||
+	if (!status->freq || link->sdata->vif.type == NL80211_IFTYPE_NAN ||
 	    link->sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
 		return true;
 
@@ -5330,7 +5360,8 @@ static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link)
 	if (!conf || !conf->def.chan)
 		return false;
 
-	return freq == conf->def.chan->center_freq;
+	return __ieee80211_rx_valid_freq(link->sdata->local->hw.wiphy, status,
+					 &conf->def);
 }
 
 /*
@@ -5430,7 +5461,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 				continue;
 
 			link = &sta->sdata->deflink;
-			if (!ieee80211_rx_valid_freq(status->freq, link))
+			if (!ieee80211_rx_valid_freq(status, link))
 				continue;
 
 			if (rx_data_pending) {
@@ -5454,7 +5485,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 			sdata =	sta->sdata;
 			link = rcu_dereference(sdata->link[link_sta->link_id]);
 
-			if (!ieee80211_rx_valid_freq(status->freq, link))
+			if (!ieee80211_rx_valid_freq(status, link))
 				continue;
 
 			if (rx_data_pending) {
@@ -5524,7 +5555,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		 * RX using the station.
 		 */
 		if (link_sta && link &&
-		    ieee80211_rx_valid_freq(status->freq, link)) {
+		    ieee80211_rx_valid_freq(status, link)) {
 			if (rx_data_pending) {
 				ieee80211_prepare_and_rx_handle(&rx, skb, false);
 				rx_data_pending = false;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-10-06  7:44 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-02  7:29 [PATCH RFC wireless-next] wifi: mac80211: correct rx freq handling for S1G Lachlan Hodges
2026-10-02  7:58 ` Johannes Berg
2026-10-02  8:11   ` Johannes Berg
2026-10-02  9:54   ` Lachlan Hodges
2026-10-05 13:53     ` Johannes Berg
2026-10-06  6:06       ` Lachlan Hodges
2026-10-06  7:44 ` Johannes Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox