wifi: mac80211: add HT and VHT basic set verification
authorBenjamin Berg <benjamin.berg@intel.com>
Wed, 5 Feb 2025 09:39:18 +0000 (11:39 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 11 Feb 2025 10:59:07 +0000 (11:59 +0100)
So far we did not verify the HT and VHT basic MCS set. However, in
P802.11REVme/D7.0 (6.5.4.2.4) says that the MLME-JOIN.request shall
return an error if the VHT and HT basic set requirements are not met.

Given broken APs, apply VHT basic MCS/NSS set checks only in
strict mode.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250205110958.e2d8d4095f6b.I66bcf6c2de3b9d3325e4ffd9f573f4cd26ce5685@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/mlme.c

index a963e020a2492f7776adee06759e89caac01e70c..3749b3e23bb7ac57e1091491e115a216cb40e4c4 100644 (file)
@@ -345,6 +345,115 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
        return IEEE80211_CONN_MODE_EHT;
 }
 
+static bool
+ieee80211_verify_sta_ht_mcs_support(struct ieee80211_sub_if_data *sdata,
+                                   struct ieee80211_supported_band *sband,
+                                   const struct ieee80211_ht_operation *ht_op)
+{
+       struct ieee80211_sta_ht_cap sta_ht_cap;
+       int i;
+
+       if (sband->band == NL80211_BAND_6GHZ)
+               return true;
+
+       if (!ht_op)
+               return false;
+
+       memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+       ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
+       /*
+        * P802.11REVme/D7.0 - 6.5.4.2.4
+        * ...
+        * If the MLME of an HT STA receives an MLME-JOIN.request primitive
+        * with the SelectedBSS parameter containing a Basic HT-MCS Set field
+        * in the HT Operation parameter that contains any unsupported MCSs,
+        * the MLME response in the resulting MLME-JOIN.confirm primitive shall
+        * contain a ResultCode parameter that is not set to the value SUCCESS.
+        * ...
+        */
+
+       /* Simply check that all basic rates are in the STA RX mask */
+       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+               if ((ht_op->basic_set[i] & sta_ht_cap.mcs.rx_mask[i]) !=
+                   ht_op->basic_set[i])
+                       return false;
+       }
+
+       return true;
+}
+
+static bool
+ieee80211_verify_sta_vht_mcs_support(struct ieee80211_sub_if_data *sdata,
+                                    int link_id,
+                                    struct ieee80211_supported_band *sband,
+                                    const struct ieee80211_vht_operation *vht_op)
+{
+       struct ieee80211_sta_vht_cap sta_vht_cap;
+       u16 ap_min_req_set, sta_rx_mcs_map, sta_tx_mcs_map;
+       int nss;
+
+       if (sband->band != NL80211_BAND_5GHZ)
+               return true;
+
+       if (!vht_op)
+               return false;
+
+       memcpy(&sta_vht_cap, &sband->vht_cap, sizeof(sta_vht_cap));
+       ieee80211_apply_vhtcap_overrides(sdata, &sta_vht_cap);
+
+       ap_min_req_set = le16_to_cpu(vht_op->basic_mcs_set);
+       sta_rx_mcs_map = le16_to_cpu(sta_vht_cap.vht_mcs.rx_mcs_map);
+       sta_tx_mcs_map = le16_to_cpu(sta_vht_cap.vht_mcs.tx_mcs_map);
+
+       /*
+        * Many APs are incorrectly advertising an all-zero value here,
+        * which really means MCS 0-7 are required for 1-8 streams, but
+        * they don't really mean it that way.
+        * Some other APs are incorrectly advertising 3 spatial streams
+        * with MCS 0-7 are required, but don't really mean it that way
+        * and we'll connect only with HT, rather than even HE.
+        * As a result, unfortunately the VHT basic MCS/NSS set cannot
+        * be used at all, so check it only in strict mode.
+        */
+       if (!ieee80211_hw_check(&sdata->local->hw, STRICT))
+               return true;
+
+       /*
+        * P802.11REVme/D7.0 - 6.5.4.2.4
+        * ...
+        * If the MLME of a VHT STA receives an MLME-JOIN.request primitive
+        * with a SelectedBSS parameter containing a Basic VHT-MCS And NSS Set
+        * field in the VHT Operation parameter that contains any unsupported
+        * <VHT-MCS, NSS> tuple, the MLME response in the resulting
+        * MLME-JOIN.confirm primitive shall contain a ResultCode parameter
+        * that is not set to the value SUCCESS.
+        * ...
+        */
+       for (nss = 8; nss > 0; nss--) {
+               u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3;
+               u8 sta_rx_val;
+               u8 sta_tx_val;
+
+               if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED)
+                       continue;
+
+               sta_rx_val = (sta_rx_mcs_map >> (2 * (nss - 1))) & 3;
+               sta_tx_val = (sta_tx_mcs_map >> (2 * (nss - 1))) & 3;
+
+               if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
+                   sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
+                   sta_rx_val < ap_op_val || sta_tx_val < ap_op_val) {
+                       link_id_info(sdata, link_id,
+                                    "Missing mandatory rates for %d Nss, rx %d, tx %d oper %d, disable VHT\n",
+                                    nss, sta_rx_val, sta_tx_val, ap_op_val);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 static bool
 ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata,
                                     int link_id,
@@ -1042,6 +1151,26 @@ again:
                link_id_info(sdata, link_id,
                             "regulatory prevented using AP config, downgraded\n");
 
+       if (conn->mode >= IEEE80211_CONN_MODE_HT &&
+           !ieee80211_verify_sta_ht_mcs_support(sdata, sband,
+                                                elems->ht_operation)) {
+               conn->mode = IEEE80211_CONN_MODE_LEGACY;
+               conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20;
+               link_id_info(sdata, link_id,
+                            "required MCSes not supported, disabling HT\n");
+       }
+
+       if (conn->mode >= IEEE80211_CONN_MODE_VHT &&
+           !ieee80211_verify_sta_vht_mcs_support(sdata, link_id, sband,
+                                                 elems->vht_operation)) {
+               conn->mode = IEEE80211_CONN_MODE_HT;
+               conn->bw_limit = min_t(enum ieee80211_conn_bw_limit,
+                                      conn->bw_limit,
+                                      IEEE80211_CONN_BW_LIMIT_40);
+               link_id_info(sdata, link_id,
+                            "required MCSes not supported, disabling VHT\n");
+       }
+
        if (conn->mode >= IEEE80211_CONN_MODE_HE &&
            (!ieee80211_verify_peer_he_mcs_support(sdata, link_id,
                                                   (void *)elems->he_cap,