mac80211: avoid using ext NSS high BW if not supported
authorJohannes Berg <johannes.berg@intel.com>
Thu, 28 May 2020 19:34:35 +0000 (21:34 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Sun, 31 May 2020 09:26:50 +0000 (11:26 +0200)
If the AP advertises inconsistent data, namely it has CCFS1 or CCFS2,
but doesn't advertise support for 160/80+80 bandwidth or "Extended NSS
BW Support", then we cannot use any MCSes in the the higher bandwidth.
Thus, avoid connecting with higher bandwidth since it's less efficient
that way.

Link: https://lore.kernel.org/r/20200528213443.0e55d40c3ccc.I6fd0b4708ebd087e5e46466c3e91f6efbcbef668@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/mesh.c
net/mac80211/mlme.c
net/mac80211/scan.c
net/mac80211/spectmgmt.c
net/mac80211/util.c

index 2479cd48fed096e6ac283989c5d00f101a20050d..81d26fef41e98cab2962dfa3117049e528b30508 100644 (file)
@@ -9,7 +9,7 @@
  * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright(c) 2016 Intel Deutschland GmbH
- * Copyright(c) 2018-2019 Intel Corporation
+ * Copyright(c) 2018-2020 Intel Corporation
  */
 
 #include <linux/delay.h>
@@ -781,6 +781,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        enum nl80211_channel_type ch_type;
        int err;
        u32 sta_flags;
+       u32 vht_cap_info = 0;
 
        sdata_assert_lock(sdata);
 
@@ -798,9 +799,13 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                break;
        }
 
+       if (elems->vht_cap_elem)
+               vht_cap_info = le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
+
        memset(&params, 0, sizeof(params));
        err = ieee80211_parse_ch_switch_ie(sdata, elems,
                                           ifibss->chandef.chan->band,
+                                          vht_cap_info,
                                           sta_flags, ifibss->bssid, &csa_ie);
        /* can't switch to destination channel, fail */
        if (err < 0)
@@ -1060,8 +1065,10 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata,
                        /* we both use VHT */
                        struct ieee80211_vht_cap cap_ie;
                        struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap;
+                       u32 vht_cap_info =
+                               le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
 
-                       ieee80211_chandef_vht_oper(&local->hw,
+                       ieee80211_chandef_vht_oper(&local->hw, vht_cap_info,
                                                   elems->vht_operation,
                                                   elems->ht_operation,
                                                   &chandef);
index 9f874ce500f6d5a749b23eb54d8849ba9f047763..0cc584574976458ad3db73a918e54cb4201b39be 100644 (file)
@@ -111,6 +111,8 @@ struct ieee80211_bss {
        size_t supp_rates_len;
        struct ieee80211_rate *beacon_rate;
 
+       u32 vht_cap_info;
+
        /*
         * During association, we save an ERP value from a probe response so
         * that we can feed ERP info to the driver when handling the
@@ -1915,6 +1917,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
  * @sdata: the sdata of the interface which has received the frame
  * @elems: parsed 802.11 elements received with the frame
  * @current_band: indicates the current band
+ * @vht_cap_info: VHT capabilities of the transmitter
  * @sta_flags: contains information about own capabilities and restrictions
  *     to decide which channel switch announcements can be accepted. Only the
  *     following subset of &enum ieee80211_sta_flags are evaluated:
@@ -1929,6 +1932,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                                 struct ieee802_11_elems *elems,
                                 enum nl80211_band current_band,
+                                u32 vht_cap_info,
                                 u32 sta_flags, u8 *bssid,
                                 struct ieee80211_csa_ie *csa_ie);
 
@@ -2194,7 +2198,7 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
 /* channel management */
 bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
                               struct cfg80211_chan_def *chandef);
-bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
+bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
                                const struct ieee80211_vht_operation *oper,
                                const struct ieee80211_ht_operation *htop,
                                struct cfg80211_chan_def *chandef);
index 79e0a90982dd10ba761d0efb1c11c0b0d74572ee..696d6fb322e65ee8736753b4127ea66894f824f8 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2008, 2009 open80211s Ltd.
- * Copyright (C) 2018 - 2019 Intel Corporation
+ * Copyright (C) 2018 - 2020 Intel Corporation
  * Authors:    Luis Carlos Cobo <luisca@cozybit.com>
  *            Javier Cardona <javier@cozybit.com>
  */
@@ -63,6 +63,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
        u32 basic_rates = 0;
        struct cfg80211_chan_def sta_chan_def;
        struct ieee80211_supported_band *sband;
+       u32 vht_cap_info = 0;
 
        /*
         * As support for each feature is added, check for matching
@@ -96,7 +97,11 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
        cfg80211_chandef_create(&sta_chan_def, sdata->vif.bss_conf.chandef.chan,
                                NL80211_CHAN_NO_HT);
        ieee80211_chandef_ht_oper(ie->ht_operation, &sta_chan_def);
-       ieee80211_chandef_vht_oper(&sdata->local->hw,
+
+       if (ie->vht_cap_elem)
+               vht_cap_info = le32_to_cpu(ie->vht_cap_elem->vht_cap_info);
+
+       ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
                                   ie->vht_operation, ie->ht_operation,
                                   &sta_chan_def);
 
@@ -1076,7 +1081,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        struct ieee80211_supported_band *sband;
        int err;
-       u32 sta_flags;
+       u32 sta_flags, vht_cap_info = 0;
 
        sdata_assert_lock(sdata);
 
@@ -1099,8 +1104,13 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
                break;
        }
 
+       if (elems->vht_cap_elem)
+               vht_cap_info =
+                       le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
+
        memset(&params, 0, sizeof(params));
        err = ieee80211_parse_ch_switch_ie(sdata, elems, sband->band,
+                                          vht_cap_info,
                                           sta_flags, sdata->vif.addr,
                                           &csa_ie);
        if (err < 0)
index f6ddce646f185d3053999528ded4baad493ee2f0..1f9c48414c857159340bd625b8392f6c1623bc7a 100644 (file)
@@ -145,6 +145,7 @@ static u32
 ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                             struct ieee80211_supported_band *sband,
                             struct ieee80211_channel *channel,
+                            u32 vht_cap_info,
                             const struct ieee80211_ht_operation *ht_oper,
                             const struct ieee80211_vht_operation *vht_oper,
                             const struct ieee80211_he_operation *he_oper,
@@ -223,7 +224,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                memcpy(&he_oper_vht_cap, he_oper->optional, 3);
                he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0);
 
-               if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
+               if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
                                                &he_oper_vht_cap, ht_oper,
                                                &vht_chandef)) {
                        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
@@ -232,8 +233,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                        ret = IEEE80211_STA_DISABLE_HE;
                        goto out;
                }
-       } else if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_oper,
-                                              ht_oper, &vht_chandef)) {
+       } else if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
+                                              vht_cap_info,
+                                              vht_oper, ht_oper,
+                                              &vht_chandef)) {
                if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
                        sdata_info(sdata,
                                   "AP VHT information is invalid, disable VHT\n");
@@ -329,6 +332,7 @@ out:
 static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
                               struct sta_info *sta,
                               const struct ieee80211_ht_cap *ht_cap,
+                              const struct ieee80211_vht_cap *vht_cap,
                               const struct ieee80211_ht_operation *ht_oper,
                               const struct ieee80211_vht_operation *vht_oper,
                               const struct ieee80211_he_operation *he_oper,
@@ -343,6 +347,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
        u16 ht_opmode;
        u32 flags;
        enum ieee80211_sta_rx_bandwidth new_sta_bw;
+       u32 vht_cap_info = 0;
        int ret;
 
        /* if HT was/is disabled, don't track any bandwidth changes */
@@ -371,8 +376,11 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
                sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
        }
 
+       if (vht_cap)
+               vht_cap_info = le32_to_cpu(vht_cap->vht_cap_info);
+
        /* calculate new channel (type) based on HT/VHT/HE operation IEs */
-       flags = ieee80211_determine_chantype(sdata, sband, chan,
+       flags = ieee80211_determine_chantype(sdata, sband, chan, vht_cap_info,
                                             ht_oper, vht_oper, he_oper,
                                             &chandef, true);
 
@@ -1327,6 +1335,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        enum nl80211_band current_band;
        struct ieee80211_csa_ie csa_ie;
        struct ieee80211_channel_switch ch_switch;
+       struct ieee80211_bss *bss;
        int res;
 
        sdata_assert_lock(sdata);
@@ -1338,7 +1347,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
 
        current_band = cbss->channel->band;
+       bss = (void *)cbss->priv;
        res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
+                                          bss->vht_cap_info,
                                           ifmgd->flags,
                                           ifmgd->associated->bssid, &csa_ie);
 
@@ -4097,8 +4108,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        changed |= ieee80211_recalc_twt_req(sdata, sta, &elems);
 
-       if (ieee80211_config_bw(sdata, sta,
-                               elems.ht_cap_elem, elems.ht_operation,
+       if (ieee80211_config_bw(sdata, sta, elems.ht_cap_elem,
+                               elems.vht_cap_elem, elems.ht_operation,
                                elems.vht_operation, elems.he_operation,
                                bssid, &changed)) {
                mutex_unlock(&local->sta_mtx);
@@ -4815,6 +4826,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
        const struct ieee80211_he_operation *he_oper = NULL;
        struct ieee80211_supported_band *sband;
        struct cfg80211_chan_def chandef;
+       struct ieee80211_bss *bss = (void *)cbss->priv;
        int ret;
        u32 i;
        bool have_80mhz;
@@ -4913,6 +4925,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
 
        ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
                                                     cbss->channel,
+                                                    bss->vht_cap_info,
                                                     ht_oper, vht_oper, he_oper,
                                                     &chandef, false);
 
index 5db15996524f8f0b7e154e0a0cc739c07c1bbd5b..d0c2e80121185e821670cb7874ddf8d293086cef 100644 (file)
@@ -132,6 +132,12 @@ ieee80211_update_bss_from_elems(struct ieee80211_local *local,
                        bss->beacon_rate =
                                &sband->bitrates[rx_status->rate_idx];
        }
+
+       if (elems->vht_cap_elem)
+               bss->vht_cap_info =
+                       le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
+       else
+               bss->vht_cap_info = 0;
 }
 
 struct ieee80211_bss *
index 5fe2b645912f6fc3aec117abad8c9c38aa317205..ae1cb2c6872245e03d7f9805cb4e46c2c6ad52b2 100644 (file)
@@ -9,7 +9,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2007-2008, Intel Corporation
  * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
- * Copyright (C) 2018        Intel Corporation
+ * Copyright (C) 2018, 2020 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -22,6 +22,7 @@
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                                 struct ieee802_11_elems *elems,
                                 enum nl80211_band current_band,
+                                u32 vht_cap_info,
                                 u32 sta_flags, u8 *bssid,
                                 struct ieee80211_csa_ie *csa_ie)
 {
@@ -150,6 +151,7 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 
                /* ignore if parsing fails */
                if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
+                                               vht_cap_info,
                                                &vht_oper, &ht_oper,
                                                &new_vht_chandef))
                        new_vht_chandef.chan = NULL;
index 87dd003dbdf2e802056c7a31da4ef50b7be29a4c..e6104829fa1ce4f495fd14f80af8a57daf4d91bb 100644 (file)
@@ -3120,7 +3120,7 @@ bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
        return true;
 }
 
-bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
+bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
                                const struct ieee80211_vht_operation *oper,
                                const struct ieee80211_ht_operation *htop,
                                struct cfg80211_chan_def *chandef)
@@ -3132,6 +3132,10 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
        u32 vht_cap;
        bool support_80_80 = false;
        bool support_160 = false;
+       u8 ext_nss_bw_supp = u32_get_bits(vht_cap_info,
+                                         IEEE80211_VHT_CAP_EXT_NSS_BW_MASK);
+       u8 supp_chwidth = u32_get_bits(vht_cap_info,
+                                      IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
 
        if (!oper || !htop)
                return false;
@@ -3151,11 +3155,48 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw,
                                IEEE80211_HT_OP_MODE_CCFS2_MASK)
                        >> IEEE80211_HT_OP_MODE_CCFS2_SHIFT;
 
-       /* when parsing (and we know how to) CCFS1 and CCFS2 are equivalent */
        ccf0 = ccfs0;
-       ccf1 = ccfs1;
-       if (!ccfs1 && ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW))
+
+       /* if not supported, parse as though we didn't understand it */
+       if (!ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW))
+               ext_nss_bw_supp = 0;
+
+       /*
+        * Cf. IEEE 802.11 Table 9-250
+        *
+        * We really just consider that because it's inefficient to connect
+        * at a higher bandwidth than we'll actually be able to use.
+        */
+       switch ((supp_chwidth << 4) | ext_nss_bw_supp) {
+       default:
+       case 0x00:
+               ccf1 = 0;
+               support_160 = false;
+               support_80_80 = false;
+               break;
+       case 0x01:
+               support_80_80 = false;
+               /* fall through */
+       case 0x02:
+       case 0x03:
                ccf1 = ccfs2;
+               break;
+       case 0x10:
+               ccf1 = ccfs1;
+               break;
+       case 0x11:
+       case 0x12:
+               if (!ccfs1)
+                       ccf1 = ccfs2;
+               else
+                       ccf1 = ccfs1;
+               break;
+       case 0x13:
+       case 0x20:
+       case 0x23:
+               ccf1 = ccfs1;
+               break;
+       }
 
        cf0 = ieee80211_channel_to_frequency(ccf0, chandef->chan->band);
        cf1 = ieee80211_channel_to_frequency(ccf1, chandef->chan->band);