]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: cfg80211/mac80211: validate radio frequency range for monitor mode
authorRyder Lee <ryder.lee@mediatek.com>
Tue, 23 Sep 2025 17:23:22 +0000 (17:23 +0000)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 27 Oct 2025 08:16:23 +0000 (09:16 +0100)
In multi-radio devices, it is possible to have an MLD AP and a monitor
interface active at the same time. In such cases, monitor mode may not
be able to specify a fixed channel and could end up capturing frames
from all radios, including those outside the intended frequency bands.

This patch adds frequency validation for monitor mode. Received frames
are now only processed if their frequency fall within the allowed ranges
of the radios specified by the interface's radio_mask.

This prevents monitor mode from capturing frames outside the supported radio.

Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Link: https://patch.msgid.link/700b8284e845d96654eb98431f8eeb5a81503862.1758647858.git.ryder.lee@mediatek.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
net/mac80211/rx.c
net/wireless/util.c

index 781624f5913af9023de6626b09414bbccf58bab9..3b6f48a783bb29f02dcbcd021d81d6ebef8eb360 100644 (file)
@@ -1015,6 +1015,7 @@ const struct cfg80211_chan_def *
 cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1,
                            const struct cfg80211_chan_def *chandef2);
 
+
 /**
  * nl80211_chan_width_to_mhz - get the channel width in MHz
  * @chan_width: the channel width from &enum nl80211_chan_width
@@ -6882,6 +6883,19 @@ static inline bool cfg80211_channel_is_psc(struct ieee80211_channel *chan)
        return ieee80211_frequency_to_channel(chan->center_freq) % 16 == 5;
 }
 
+/**
+ * ieee80211_radio_freq_range_valid - Check if the radio supports the
+ * specified frequency range
+ *
+ * @radio: wiphy radio
+ * @freq: the frequency (in KHz) to be queried
+ * @width: the bandwidth (in KHz) to be queried
+ *
+ * Return: whether or not the given frequency range is valid for the given radio
+ */
+bool ieee80211_radio_freq_range_valid(const struct wiphy_radio *radio,
+                                     u32 freq, u32 width);
+
 /**
  * cfg80211_radio_chandef_valid - Check if the radio supports the chandef
  *
index 6af43dfefdd6aad2cb3f1b47f6d2e70bb5c1a163..29175a0c9f688f78d39b3790bfa1ff4a8d2bc231 100644 (file)
@@ -763,6 +763,51 @@ ieee80211_make_monitor_skb(struct ieee80211_local *local,
        return skb;
 }
 
+static bool
+ieee80211_validate_monitor_radio(struct ieee80211_sub_if_data *sdata,
+                                struct ieee80211_local *local,
+                                struct ieee80211_rx_status *status)
+{
+       struct wiphy *wiphy = local->hw.wiphy;
+       int i, freq, bw;
+
+       if (!wiphy->n_radio)
+               return true;
+
+       switch (status->bw) {
+       case RATE_INFO_BW_20:
+               bw = 20000;
+               break;
+       case RATE_INFO_BW_40:
+               bw = 40000;
+               break;
+       case RATE_INFO_BW_80:
+               bw = 80000;
+               break;
+       case RATE_INFO_BW_160:
+               bw = 160000;
+               break;
+       case RATE_INFO_BW_320:
+               bw = 320000;
+               break;
+       default:
+               return false;
+       }
+
+       freq = MHZ_TO_KHZ(status->freq);
+
+       for (i = 0; i < wiphy->n_radio; i++) {
+               if (!(sdata->wdev.radio_mask & BIT(i)))
+                       continue;
+
+               if (!ieee80211_radio_freq_range_valid(&wiphy->radio[i], freq, bw))
+                       continue;
+
+               return true;
+       }
+       return false;
+}
+
 /*
  * This function copies a received frame to all monitor interfaces and
  * returns a cleaned-up SKB that no longer includes the FCS nor the
@@ -855,6 +900,10 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                    chandef->chan->center_freq != status->freq)
                        continue;
 
+               if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
+                   !ieee80211_validate_monitor_radio(sdata, local, status))
+                       continue;
+
                if (!prev_sdata) {
                        prev_sdata = sdata;
                        continue;
index 56724b33af045519b537ae12eb6a36c011189f3e..97f40c6d1e9d18eb16aa61eebe6f9b9764b2dd63 100644 (file)
@@ -2942,9 +2942,8 @@ cfg80211_get_iftype_ext_capa(struct wiphy *wiphy, enum nl80211_iftype type)
 }
 EXPORT_SYMBOL(cfg80211_get_iftype_ext_capa);
 
-static bool
-ieee80211_radio_freq_range_valid(const struct wiphy_radio *radio,
-                                u32 freq, u32 width)
+bool ieee80211_radio_freq_range_valid(const struct wiphy_radio *radio,
+                                     u32 freq, u32 width)
 {
        const struct wiphy_radio_freq_range *r;
        int i;
@@ -2958,6 +2957,7 @@ ieee80211_radio_freq_range_valid(const struct wiphy_radio *radio,
 
        return false;
 }
+EXPORT_SYMBOL(ieee80211_radio_freq_range_valid);
 
 bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio,
                                  const struct cfg80211_chan_def *chandef)