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

wifi: mac80211: tests: add tests for ieee80211_determine_chan_mode

Add a few tests for ieee80211_determine_chan_mode that check that
mac80211 will not try to connect to an AP if an advertised basic rate is
not supported.

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.530c81eb7fdc.Ia77f5efdf9efb70d2766a3d6bf425553bcb308e8@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Benjamin Berg and committed by
Johannes Berg
b46524b5 574faa0e

+265 -7
+7
net/mac80211/ieee80211_i.h
··· 2804 2804 void ieee80211_rearrange_tpe_psd(struct ieee80211_parsed_tpe_psd *psd, 2805 2805 const struct cfg80211_chan_def *ap, 2806 2806 const struct cfg80211_chan_def *used); 2807 + struct ieee802_11_elems * 2808 + ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, 2809 + struct ieee80211_conn_settings *conn, 2810 + struct cfg80211_bss *cbss, int link_id, 2811 + struct ieee80211_chan_req *chanreq, 2812 + struct cfg80211_chan_def *ap_chandef, 2813 + unsigned long *userspace_selectors); 2807 2814 #else 2808 2815 #define EXPORT_SYMBOL_IF_MAC80211_KUNIT(sym) 2809 2816 #define VISIBLE_IF_MAC80211_KUNIT static
+2 -1
net/mac80211/mlme.c
··· 989 989 chanreq->ap = *ap_chandef; 990 990 } 991 991 992 - static struct ieee802_11_elems * 992 + VISIBLE_IF_MAC80211_KUNIT struct ieee802_11_elems * 993 993 ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, 994 994 struct ieee80211_conn_settings *conn, 995 995 struct cfg80211_bss *cbss, int link_id, ··· 1214 1214 kfree(elems); 1215 1215 return ERR_PTR(ret); 1216 1216 } 1217 + EXPORT_SYMBOL_IF_MAC80211_KUNIT(ieee80211_determine_chan_mode); 1217 1218 1218 1219 static int ieee80211_config_bw(struct ieee80211_link_data *link, 1219 1220 struct ieee802_11_elems *elems,
+1 -1
net/mac80211/tests/Makefile
··· 1 - mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o 1 + mac80211-tests-y += module.o util.o elems.o mfp.o tpe.o chan-mode.o 2 2 3 3 obj-$(CONFIG_MAC80211_KUNIT_TEST) += mac80211-tests.o
+254
net/mac80211/tests/chan-mode.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * KUnit tests for channel mode functions 4 + * 5 + * Copyright (C) 2024 Intel Corporation 6 + */ 7 + #include <net/cfg80211.h> 8 + #include <kunit/test.h> 9 + 10 + #include "util.h" 11 + 12 + MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); 13 + 14 + static const struct determine_chan_mode_case { 15 + const char *desc; 16 + u8 extra_supp_rate; 17 + enum ieee80211_conn_mode conn_mode; 18 + enum ieee80211_conn_mode expected_mode; 19 + bool strict; 20 + u8 userspace_selector; 21 + struct ieee80211_ht_cap ht_capa_mask; 22 + struct ieee80211_vht_cap vht_capa; 23 + struct ieee80211_vht_cap vht_capa_mask; 24 + u8 vht_basic_mcs_1_4_set:1, 25 + vht_basic_mcs_5_8_set:1, 26 + he_basic_mcs_1_4_set:1, 27 + he_basic_mcs_5_8_set:1; 28 + u8 vht_basic_mcs_1_4, vht_basic_mcs_5_8; 29 + u8 he_basic_mcs_1_4, he_basic_mcs_5_8; 30 + u8 eht_mcs7_min_nss; 31 + int error; 32 + } determine_chan_mode_cases[] = { 33 + { 34 + .desc = "Normal case, EHT is working", 35 + .conn_mode = IEEE80211_CONN_MODE_EHT, 36 + .expected_mode = IEEE80211_CONN_MODE_EHT, 37 + }, { 38 + .desc = "Requiring EHT support is fine", 39 + .conn_mode = IEEE80211_CONN_MODE_EHT, 40 + .expected_mode = IEEE80211_CONN_MODE_EHT, 41 + .extra_supp_rate = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY, 42 + }, { 43 + .desc = "Lowering the mode limits us", 44 + .conn_mode = IEEE80211_CONN_MODE_VHT, 45 + .expected_mode = IEEE80211_CONN_MODE_VHT, 46 + }, { 47 + .desc = "Requesting a basic rate/selector that we do not support", 48 + .conn_mode = IEEE80211_CONN_MODE_EHT, 49 + .extra_supp_rate = 0x80 | (BSS_MEMBERSHIP_SELECTOR_MIN - 1), 50 + .error = EINVAL, 51 + }, { 52 + .desc = "As before, but userspace says it is taking care of it", 53 + .conn_mode = IEEE80211_CONN_MODE_EHT, 54 + .userspace_selector = BSS_MEMBERSHIP_SELECTOR_MIN - 1, 55 + .extra_supp_rate = 0x80 | (BSS_MEMBERSHIP_SELECTOR_MIN - 1), 56 + .expected_mode = IEEE80211_CONN_MODE_EHT, 57 + }, { 58 + .desc = "Masking out a supported rate in HT capabilities", 59 + .conn_mode = IEEE80211_CONN_MODE_EHT, 60 + .expected_mode = IEEE80211_CONN_MODE_LEGACY, 61 + .ht_capa_mask = { 62 + .mcs.rx_mask[0] = 0xf7, 63 + }, 64 + }, { 65 + .desc = "Masking out a RX rate in VHT capabilities", 66 + .conn_mode = IEEE80211_CONN_MODE_EHT, 67 + .expected_mode = IEEE80211_CONN_MODE_HT, 68 + /* Only one RX stream at MCS 0-7 */ 69 + .vht_capa = { 70 + .supp_mcs.rx_mcs_map = 71 + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_7), 72 + }, 73 + .vht_capa_mask = { 74 + .supp_mcs.rx_mcs_map = cpu_to_le16(0xffff), 75 + }, 76 + .strict = true, 77 + }, { 78 + .desc = "Masking out a TX rate in VHT capabilities", 79 + .conn_mode = IEEE80211_CONN_MODE_EHT, 80 + .expected_mode = IEEE80211_CONN_MODE_HT, 81 + /* Only one TX stream at MCS 0-7 */ 82 + .vht_capa = { 83 + .supp_mcs.tx_mcs_map = 84 + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_7), 85 + }, 86 + .vht_capa_mask = { 87 + .supp_mcs.tx_mcs_map = cpu_to_le16(0xffff), 88 + }, 89 + .strict = true, 90 + }, { 91 + .desc = "AP has higher VHT requirement than client", 92 + .conn_mode = IEEE80211_CONN_MODE_EHT, 93 + .expected_mode = IEEE80211_CONN_MODE_HT, 94 + .vht_basic_mcs_5_8_set = 1, 95 + .vht_basic_mcs_5_8 = 0xFE, /* require 5th stream */ 96 + .strict = true, 97 + }, { 98 + .desc = "all zero VHT basic rates are ignored (many APs broken)", 99 + .conn_mode = IEEE80211_CONN_MODE_VHT, 100 + .expected_mode = IEEE80211_CONN_MODE_VHT, 101 + .vht_basic_mcs_1_4_set = 1, 102 + .vht_basic_mcs_5_8_set = 1, 103 + }, { 104 + .desc = "AP requires 3 HE streams but client only has two", 105 + .conn_mode = IEEE80211_CONN_MODE_EHT, 106 + .expected_mode = IEEE80211_CONN_MODE_VHT, 107 + .he_basic_mcs_1_4 = 0b11001010, 108 + .he_basic_mcs_1_4_set = 1, 109 + }, { 110 + .desc = "all zero HE basic rates are ignored (iPhone workaround)", 111 + .conn_mode = IEEE80211_CONN_MODE_HE, 112 + .expected_mode = IEEE80211_CONN_MODE_HE, 113 + .he_basic_mcs_1_4_set = 1, 114 + .he_basic_mcs_5_8_set = 1, 115 + }, { 116 + .desc = "AP requires too many RX streams with EHT MCS 7", 117 + .conn_mode = IEEE80211_CONN_MODE_EHT, 118 + .expected_mode = IEEE80211_CONN_MODE_HE, 119 + .eht_mcs7_min_nss = 0x15, 120 + }, { 121 + .desc = "AP requires too many TX streams with EHT MCS 7", 122 + .conn_mode = IEEE80211_CONN_MODE_EHT, 123 + .expected_mode = IEEE80211_CONN_MODE_HE, 124 + .eht_mcs7_min_nss = 0x51, 125 + }, { 126 + .desc = "AP requires too many RX streams with EHT MCS 7 and EHT is required", 127 + .extra_supp_rate = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY, 128 + .conn_mode = IEEE80211_CONN_MODE_EHT, 129 + .eht_mcs7_min_nss = 0x15, 130 + .error = EINVAL, 131 + } 132 + }; 133 + KUNIT_ARRAY_PARAM_DESC(determine_chan_mode, determine_chan_mode_cases, desc) 134 + 135 + static void test_determine_chan_mode(struct kunit *test) 136 + { 137 + const struct determine_chan_mode_case *params = test->param_value; 138 + struct t_sdata *t_sdata = T_SDATA(test); 139 + struct ieee80211_conn_settings conn = { 140 + .mode = params->conn_mode, 141 + .bw_limit = IEEE80211_CONN_BW_LIMIT_20, 142 + }; 143 + struct cfg80211_bss cbss = { 144 + .channel = &t_sdata->band_5ghz.channels[0], 145 + }; 146 + unsigned long userspace_selectors[BITS_TO_LONGS(128)] = {}; 147 + u8 bss_ies[] = { 148 + /* Supported Rates */ 149 + WLAN_EID_SUPP_RATES, 0x08, 150 + 0x82, 0x84, 0x8b, 0x96, 0xc, 0x12, 0x18, 0x24, 151 + /* Extended Supported Rates */ 152 + WLAN_EID_EXT_SUPP_RATES, 0x05, 153 + 0x30, 0x48, 0x60, 0x6c, params->extra_supp_rate, 154 + /* HT Capabilities */ 155 + WLAN_EID_HT_CAPABILITY, 0x1a, 156 + 0x0c, 0x00, 0x1b, 0xff, 0xff, 0x00, 0x00, 0x00, 157 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 158 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 159 + 0x00, 0x00, 160 + /* HT Information (0xff for 1 stream) */ 161 + WLAN_EID_HT_OPERATION, 0x16, 162 + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 163 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 165 + /* VHT Capabilities */ 166 + WLAN_EID_VHT_CAPABILITY, 0xc, 167 + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 168 + 0xff, 0xff, 0x00, 0x00, 169 + /* VHT Operation */ 170 + WLAN_EID_VHT_OPERATION, 0x05, 171 + 0x00, 0x00, 0x00, 172 + params->vht_basic_mcs_1_4_set ? 173 + params->vht_basic_mcs_1_4 : 174 + le16_get_bits(t_sdata->band_5ghz.vht_cap.vht_mcs.rx_mcs_map, 0xff), 175 + params->vht_basic_mcs_5_8_set ? 176 + params->vht_basic_mcs_5_8 : 177 + le16_get_bits(t_sdata->band_5ghz.vht_cap.vht_mcs.rx_mcs_map, 0xff00), 178 + /* HE Capabilities */ 179 + WLAN_EID_EXTENSION, 0x16, WLAN_EID_EXT_HE_CAPABILITY, 180 + 0x01, 0x78, 0xc8, 0x1a, 0x40, 0x00, 0x00, 0xbf, 181 + 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 182 + 0x00, 0xfa, 0xff, 0xfa, 0xff, 183 + /* HE Operation (permit overriding values) */ 184 + WLAN_EID_EXTENSION, 0x07, WLAN_EID_EXT_HE_OPERATION, 185 + 0xf0, 0x3f, 0x00, 0xb0, 186 + params->he_basic_mcs_1_4_set ? params->he_basic_mcs_1_4 : 0xfc, 187 + params->he_basic_mcs_5_8_set ? params->he_basic_mcs_5_8 : 0xff, 188 + /* EHT Capabilities */ 189 + WLAN_EID_EXTENSION, 0x12, WLAN_EID_EXT_EHT_CAPABILITY, 190 + 0x07, 0x00, 0x1c, 0x00, 0x00, 0xfe, 0xff, 0xff, 191 + 0x7f, 0x01, 0x00, 0x88, 0x88, 0x88, 0x00, 0x00, 192 + 0x00, 193 + /* EHT Operation */ 194 + WLAN_EID_EXTENSION, 0x09, WLAN_EID_EXT_EHT_OPERATION, 195 + 0x01, params->eht_mcs7_min_nss ? params->eht_mcs7_min_nss : 0x11, 196 + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 197 + }; 198 + struct ieee80211_chan_req chanreq = {}; 199 + struct cfg80211_chan_def ap_chandef = {}; 200 + struct ieee802_11_elems *elems; 201 + 202 + if (params->strict) 203 + set_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags); 204 + else 205 + clear_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags); 206 + 207 + t_sdata->sdata->u.mgd.ht_capa_mask = params->ht_capa_mask; 208 + t_sdata->sdata->u.mgd.vht_capa = params->vht_capa; 209 + t_sdata->sdata->u.mgd.vht_capa_mask = params->vht_capa_mask; 210 + 211 + if (params->userspace_selector) 212 + set_bit(params->userspace_selector, userspace_selectors); 213 + 214 + rcu_assign_pointer(cbss.ies, 215 + kunit_kzalloc(test, 216 + sizeof(cbss) + sizeof(bss_ies), 217 + GFP_KERNEL)); 218 + KUNIT_ASSERT_NOT_NULL(test, rcu_access_pointer(cbss.ies)); 219 + ((struct cfg80211_bss_ies *)rcu_access_pointer(cbss.ies))->len = sizeof(bss_ies); 220 + 221 + memcpy((void *)rcu_access_pointer(cbss.ies)->data, bss_ies, 222 + sizeof(bss_ies)); 223 + 224 + rcu_read_lock(); 225 + elems = ieee80211_determine_chan_mode(t_sdata->sdata, &conn, &cbss, 226 + 0, &chanreq, &ap_chandef, 227 + userspace_selectors); 228 + rcu_read_unlock(); 229 + 230 + /* We do not need elems, free them if they are valid. */ 231 + if (!IS_ERR_OR_NULL(elems)) 232 + kfree(elems); 233 + 234 + if (params->error) { 235 + KUNIT_ASSERT_TRUE(test, IS_ERR(elems)); 236 + KUNIT_ASSERT_EQ(test, PTR_ERR(elems), -params->error); 237 + } else { 238 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, elems); 239 + KUNIT_ASSERT_EQ(test, conn.mode, params->expected_mode); 240 + } 241 + } 242 + 243 + static struct kunit_case chan_mode_cases[] = { 244 + KUNIT_CASE_PARAM(test_determine_chan_mode, 245 + determine_chan_mode_gen_params), 246 + {} 247 + }; 248 + 249 + static struct kunit_suite chan_mode = { 250 + .name = "mac80211-mlme-chan-mode", 251 + .test_cases = chan_mode_cases, 252 + }; 253 + 254 + kunit_test_suite(chan_mode);
+1 -5
net/mac80211/tests/util.c
··· 266 266 cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | 267 267 IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | 268 268 IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | 269 - IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | 270 - IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | 271 - IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | 272 - IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | 273 - IEEE80211_VHT_MCS_SUPPORT_0_9 << 14); 269 + IEEE80211_VHT_MCS_SUPPORT_0_9 << 6); 274 270 sband->vht_cap.vht_mcs.tx_mcs_map = 275 271 sband->vht_cap.vht_mcs.rx_mcs_map; 276 272 break;