Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wifi: mac80211: add HT and VHT basic set verification

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>

authored by

Benjamin Berg and committed by
Johannes Berg
574faa0e 7364a468

+129
+129
net/mac80211/mlme.c
··· 346 346 } 347 347 348 348 static bool 349 + ieee80211_verify_sta_ht_mcs_support(struct ieee80211_sub_if_data *sdata, 350 + struct ieee80211_supported_band *sband, 351 + const struct ieee80211_ht_operation *ht_op) 352 + { 353 + struct ieee80211_sta_ht_cap sta_ht_cap; 354 + int i; 355 + 356 + if (sband->band == NL80211_BAND_6GHZ) 357 + return true; 358 + 359 + if (!ht_op) 360 + return false; 361 + 362 + memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); 363 + ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); 364 + 365 + /* 366 + * P802.11REVme/D7.0 - 6.5.4.2.4 367 + * ... 368 + * If the MLME of an HT STA receives an MLME-JOIN.request primitive 369 + * with the SelectedBSS parameter containing a Basic HT-MCS Set field 370 + * in the HT Operation parameter that contains any unsupported MCSs, 371 + * the MLME response in the resulting MLME-JOIN.confirm primitive shall 372 + * contain a ResultCode parameter that is not set to the value SUCCESS. 373 + * ... 374 + */ 375 + 376 + /* Simply check that all basic rates are in the STA RX mask */ 377 + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { 378 + if ((ht_op->basic_set[i] & sta_ht_cap.mcs.rx_mask[i]) != 379 + ht_op->basic_set[i]) 380 + return false; 381 + } 382 + 383 + return true; 384 + } 385 + 386 + static bool 387 + ieee80211_verify_sta_vht_mcs_support(struct ieee80211_sub_if_data *sdata, 388 + int link_id, 389 + struct ieee80211_supported_band *sband, 390 + const struct ieee80211_vht_operation *vht_op) 391 + { 392 + struct ieee80211_sta_vht_cap sta_vht_cap; 393 + u16 ap_min_req_set, sta_rx_mcs_map, sta_tx_mcs_map; 394 + int nss; 395 + 396 + if (sband->band != NL80211_BAND_5GHZ) 397 + return true; 398 + 399 + if (!vht_op) 400 + return false; 401 + 402 + memcpy(&sta_vht_cap, &sband->vht_cap, sizeof(sta_vht_cap)); 403 + ieee80211_apply_vhtcap_overrides(sdata, &sta_vht_cap); 404 + 405 + ap_min_req_set = le16_to_cpu(vht_op->basic_mcs_set); 406 + sta_rx_mcs_map = le16_to_cpu(sta_vht_cap.vht_mcs.rx_mcs_map); 407 + sta_tx_mcs_map = le16_to_cpu(sta_vht_cap.vht_mcs.tx_mcs_map); 408 + 409 + /* 410 + * Many APs are incorrectly advertising an all-zero value here, 411 + * which really means MCS 0-7 are required for 1-8 streams, but 412 + * they don't really mean it that way. 413 + * Some other APs are incorrectly advertising 3 spatial streams 414 + * with MCS 0-7 are required, but don't really mean it that way 415 + * and we'll connect only with HT, rather than even HE. 416 + * As a result, unfortunately the VHT basic MCS/NSS set cannot 417 + * be used at all, so check it only in strict mode. 418 + */ 419 + if (!ieee80211_hw_check(&sdata->local->hw, STRICT)) 420 + return true; 421 + 422 + /* 423 + * P802.11REVme/D7.0 - 6.5.4.2.4 424 + * ... 425 + * If the MLME of a VHT STA receives an MLME-JOIN.request primitive 426 + * with a SelectedBSS parameter containing a Basic VHT-MCS And NSS Set 427 + * field in the VHT Operation parameter that contains any unsupported 428 + * <VHT-MCS, NSS> tuple, the MLME response in the resulting 429 + * MLME-JOIN.confirm primitive shall contain a ResultCode parameter 430 + * that is not set to the value SUCCESS. 431 + * ... 432 + */ 433 + for (nss = 8; nss > 0; nss--) { 434 + u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; 435 + u8 sta_rx_val; 436 + u8 sta_tx_val; 437 + 438 + if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED) 439 + continue; 440 + 441 + sta_rx_val = (sta_rx_mcs_map >> (2 * (nss - 1))) & 3; 442 + sta_tx_val = (sta_tx_mcs_map >> (2 * (nss - 1))) & 3; 443 + 444 + if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || 445 + sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || 446 + sta_rx_val < ap_op_val || sta_tx_val < ap_op_val) { 447 + link_id_info(sdata, link_id, 448 + "Missing mandatory rates for %d Nss, rx %d, tx %d oper %d, disable VHT\n", 449 + nss, sta_rx_val, sta_tx_val, ap_op_val); 450 + return false; 451 + } 452 + } 453 + 454 + return true; 455 + } 456 + 457 + static bool 349 458 ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata, 350 459 int link_id, 351 460 const struct ieee80211_he_cap_elem *he_cap, ··· 1150 1041 if (chanreq->oper.width != ap_chandef->width || ap_mode != conn->mode) 1151 1042 link_id_info(sdata, link_id, 1152 1043 "regulatory prevented using AP config, downgraded\n"); 1044 + 1045 + if (conn->mode >= IEEE80211_CONN_MODE_HT && 1046 + !ieee80211_verify_sta_ht_mcs_support(sdata, sband, 1047 + elems->ht_operation)) { 1048 + conn->mode = IEEE80211_CONN_MODE_LEGACY; 1049 + conn->bw_limit = IEEE80211_CONN_BW_LIMIT_20; 1050 + link_id_info(sdata, link_id, 1051 + "required MCSes not supported, disabling HT\n"); 1052 + } 1053 + 1054 + if (conn->mode >= IEEE80211_CONN_MODE_VHT && 1055 + !ieee80211_verify_sta_vht_mcs_support(sdata, link_id, sband, 1056 + elems->vht_operation)) { 1057 + conn->mode = IEEE80211_CONN_MODE_HT; 1058 + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, 1059 + conn->bw_limit, 1060 + IEEE80211_CONN_BW_LIMIT_40); 1061 + link_id_info(sdata, link_id, 1062 + "required MCSes not supported, disabling VHT\n"); 1063 + } 1153 1064 1154 1065 if (conn->mode >= IEEE80211_CONN_MODE_HE && 1155 1066 (!ieee80211_verify_peer_he_mcs_support(sdata, link_id,