]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
cfg80211: Use the HE operation IE to determine a 6GHz BSS channel
authorAyala Beker <ayala.beker@intel.com>
Thu, 2 Dec 2021 12:36:10 +0000 (14:36 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 20 Dec 2021 09:38:32 +0000 (10:38 +0100)
A non-collocated AP whose primary channel is not a PSC channel
may transmit a duplicated beacon on the corresponding PSC channel
in which it would indicate its true primary channel.
Use this inforamtion contained in the HE operation IE to determine
the primary channel of the AP.
In case of invalid infomration ignore it and use the channel
the frame was received on.

Signed-off-by: Ayala Beker <ayala.beker@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20211202143322.71eb2176e54e.I130f678e4aa390973ab39d838bbfe7b2d54bff8e@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
net/wireless/scan.c

index e29d904eccff119f6f7cd8b7a384e1b5066c6082..b59baf9dfd6776e0905ea3c38cee6bd5f6bbbbad 100644 (file)
@@ -6385,17 +6385,6 @@ static inline void cfg80211_gen_new_bssid(const u8 *bssid, u8 max_bssid,
        u64_to_ether_addr(new_bssid_u64, new_bssid);
 }
 
-/**
- * cfg80211_get_ies_channel_number - returns the channel number from ies
- * @ie: IEs
- * @ielen: length of IEs
- * @band: enum nl80211_band of the channel
- *
- * Returns the channel number, or -1 if none could be determined.
- */
-int cfg80211_get_ies_channel_number(const u8 *ie, size_t ielen,
-                                   enum nl80211_band band);
-
 /**
  * cfg80211_is_element_inherited - returns if element ID should be inherited
  * @element: element to check
@@ -6431,6 +6420,19 @@ enum cfg80211_bss_frame_type {
        CFG80211_BSS_FTYPE_PRESP,
 };
 
+/**
+ * cfg80211_get_ies_channel_number - returns the channel number from ies
+ * @ie: IEs
+ * @ielen: length of IEs
+ * @band: enum nl80211_band of the channel
+ * @ftype: frame type
+ *
+ * Returns the channel number, or -1 if none could be determined.
+ */
+int cfg80211_get_ies_channel_number(const u8 *ie, size_t ielen,
+                                   enum nl80211_band band,
+                                   enum cfg80211_bss_frame_type ftype);
+
 /**
  * cfg80211_inform_bss_data - inform cfg80211 of a new BSS
  *
index f7bd6810db9744a4c97740564d157c44e42e5ff3..e502c522965dbd6be746c9d7c439688c3c6daa12 100644 (file)
@@ -1793,12 +1793,33 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
 }
 
 int cfg80211_get_ies_channel_number(const u8 *ie, size_t ielen,
-                                   enum nl80211_band band)
+                                   enum nl80211_band band,
+                                   enum cfg80211_bss_frame_type ftype)
 {
        const u8 *tmp;
        int channel_number = -1;
 
-       if (band == NL80211_BAND_S1GHZ) {
+       if (band == NL80211_BAND_6GHZ) {
+               const struct element *elem;
+               struct ieee80211_he_operation *he_oper;
+
+               elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ie,
+                                             ielen);
+               if (elem && elem->datalen >= sizeof(*he_oper) &&
+                   elem->datalen >= ieee80211_he_oper_size(&elem->data[1])) {
+                       const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
+
+                       he_oper = (void *)&elem->data[1];
+
+                       he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper);
+                       if (!he_6ghz_oper)
+                               return channel_number;
+
+                       if (ftype != CFG80211_BSS_FTYPE_BEACON ||
+                           he_6ghz_oper->control & IEEE80211_HE_6GHZ_OPER_CTRL_DUP_BEACON)
+                               channel_number = he_6ghz_oper->primary;
+               }
+       } else if (band == NL80211_BAND_S1GHZ) {
                tmp = cfg80211_find_ie(WLAN_EID_S1G_OPERATION, ie, ielen);
                if (tmp && tmp[1] >= sizeof(struct ieee80211_s1g_oper_ie)) {
                        struct ieee80211_s1g_oper_ie *s1gop = (void *)(tmp + 2);
@@ -1829,18 +1850,20 @@ EXPORT_SYMBOL(cfg80211_get_ies_channel_number);
  * from neighboring channels and the Beacon frames use the DSSS Parameter Set
  * element to indicate the current (transmitting) channel, but this might also
  * be needed on other bands if RX frequency does not match with the actual
- * operating channel of a BSS.
+ * operating channel of a BSS, or if the AP reports a different primary channel.
  */
 static struct ieee80211_channel *
 cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
                         struct ieee80211_channel *channel,
-                        enum nl80211_bss_scan_width scan_width)
+                        enum nl80211_bss_scan_width scan_width,
+                        enum cfg80211_bss_frame_type ftype)
 {
        u32 freq;
        int channel_number;
        struct ieee80211_channel *alt_channel;
 
-       channel_number = cfg80211_get_ies_channel_number(ie, ielen, channel->band);
+       channel_number = cfg80211_get_ies_channel_number(ie, ielen,
+                                                        channel->band, ftype);
 
        if (channel_number < 0) {
                /* No channel information in frame payload */
@@ -1848,6 +1871,16 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
        }
 
        freq = ieee80211_channel_to_freq_khz(channel_number, channel->band);
+
+       /*
+        * In 6GHz, duplicated beacon indication is relevant for
+        * beacons only.
+        */
+       if (channel->band == NL80211_BAND_6GHZ &&
+           (freq == channel->center_freq ||
+            abs(freq - channel->center_freq) > 80))
+               return channel;
+
        alt_channel = ieee80211_get_channel_khz(wiphy, freq);
        if (!alt_channel) {
                if (channel->band == NL80211_BAND_2GHZ) {
@@ -1909,7 +1942,7 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
                return NULL;
 
        channel = cfg80211_get_bss_channel(wiphy, ie, ielen, data->chan,
-                                          data->scan_width);
+                                          data->scan_width, ftype);
        if (!channel)
                return NULL;
 
@@ -2332,6 +2365,7 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
        size_t ielen, min_hdr_len = offsetof(struct ieee80211_mgmt,
                                             u.probe_resp.variable);
        int bss_type;
+       enum cfg80211_bss_frame_type ftype;
 
        BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
                        offsetof(struct ieee80211_mgmt, u.beacon.variable));
@@ -2368,8 +2402,16 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
                        variable = ext->u.s1g_beacon.variable;
        }
 
+       if (ieee80211_is_beacon(mgmt->frame_control))
+               ftype = CFG80211_BSS_FTYPE_BEACON;
+       else if (ieee80211_is_probe_resp(mgmt->frame_control))
+               ftype = CFG80211_BSS_FTYPE_PRESP;
+       else
+               ftype = CFG80211_BSS_FTYPE_UNKNOWN;
+
        channel = cfg80211_get_bss_channel(wiphy, variable,
-                                          ielen, data->chan, data->scan_width);
+                                          ielen, data->chan, data->scan_width,
+                                          ftype);
        if (!channel)
                return NULL;