1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * KUnit tests for element parsing 4 * 5 * Copyright (C) 2023-2025 Intel Corporation 6 */ 7 #include <kunit/test.h> 8 #include "../ieee80211_i.h" 9 10 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); 11 12 static const struct mesh_preq_parse_test_case { 13 const char *desc; 14 u8 len; 15 bool ae_enabled; 16 u8 target_count; 17 bool result; 18 } mesh_preq_parse_cases[] = { 19 { 20 .desc = "shorter than header", 21 .len = 16, 22 .ae_enabled = false, 23 .target_count = 1, 24 .result = false, 25 }, 26 { 27 .desc = "too short non AE, target count is not included", 28 .len = 29, 29 .ae_enabled = false, 30 .target_count = 1, 31 .result = false, 32 }, 33 { 34 .desc = "too short non AE, target count is 1", 35 .len = 36, 36 .ae_enabled = false, 37 .target_count = 1, 38 .result = false, 39 }, 40 { 41 .desc = "too short AE, target count is not included", 42 .len = 35, 43 .ae_enabled = true, 44 .target_count = 1, 45 .result = false, 46 }, 47 { 48 .desc = "too short AE, target count is 1", 49 .len = 42, 50 .ae_enabled = true, 51 .target_count = 1, 52 .result = false, 53 }, 54 { 55 .desc = "target count is zero", 56 .len = 26, 57 .ae_enabled = false, 58 .target_count = 0, 59 .result = false, 60 }, 61 { 62 .desc = "target count is 21", 63 .len = 255, 64 .ae_enabled = false, 65 .target_count = 21, 66 .result = false, 67 }, 68 { 69 .desc = "non AE, target count is 1", 70 .len = 37, 71 .ae_enabled = false, 72 .target_count = 1, 73 .result = true, 74 }, 75 { 76 .desc = "non AE, target count is 20", 77 .len = 246, 78 .ae_enabled = false, 79 .target_count = 20, 80 .result = true, 81 }, 82 { 83 .desc = "AE, target count is 1", 84 .len = 43, 85 .ae_enabled = true, 86 .target_count = 1, 87 .result = true, 88 }, 89 { 90 .desc = "AE, target count is 20", 91 .len = 252, 92 .ae_enabled = true, 93 .target_count = 20, 94 .result = true, 95 }, 96 }; 97 98 KUNIT_ARRAY_PARAM_DESC(mesh_preq_parse, mesh_preq_parse_cases, desc); 99 100 static const struct mesh_prep_parse_test_case { 101 const char *desc; 102 u8 len; 103 bool ae_enabled; 104 bool result; 105 } mesh_prep_parse_cases[] = { 106 { 107 .desc = "shorter than header", 108 .len = 12, 109 .ae_enabled = false, 110 .result = false, 111 }, 112 { 113 .desc = "non AE short", 114 .len = 30, 115 .ae_enabled = false, 116 .result = false, 117 }, 118 { 119 .desc = "non AE", 120 .len = 31, 121 .ae_enabled = false, 122 .result = true, 123 }, 124 { 125 .desc = "AE short", 126 .len = 36, 127 .ae_enabled = true, 128 .result = false, 129 }, 130 { 131 .desc = "AE", 132 .len = 37, 133 .ae_enabled = true, 134 .result = true, 135 }, 136 }; 137 138 KUNIT_ARRAY_PARAM_DESC(mesh_prep_parse, mesh_prep_parse_cases, desc); 139 140 static const struct mesh_perr_parse_test_case { 141 const char *desc; 142 u8 len; 143 u8 number_of_dst; 144 int ae_enabled_idx; 145 bool result; 146 } mesh_perr_parse_cases[] = { 147 { 148 .desc = "shorter than header", 149 .len = 1, 150 .number_of_dst = 1, 151 .ae_enabled_idx = -1, 152 .result = false, 153 }, 154 { 155 .desc = "number_of_dst is 0", 156 .len = 2, 157 .number_of_dst = 0, 158 .ae_enabled_idx = -1, 159 .result = true, 160 }, 161 { 162 .desc = "number_of_dst is 20", 163 .len = 255, 164 .number_of_dst = 20, 165 .ae_enabled_idx = -1, 166 .result = false, 167 }, 168 { 169 .desc = "number_of_dst is 1, non AE, short", 170 .len = 14, 171 .number_of_dst = 1, 172 .ae_enabled_idx = -1, 173 .result = false, 174 }, 175 { 176 .desc = "number_of_dst is 1, non AE", 177 .len = 15, 178 .number_of_dst = 1, 179 .ae_enabled_idx = -1, 180 .result = true, 181 }, 182 { 183 .desc = "number_of_dst is 1, non AE, extra short dst header", 184 .len = 25, 185 .number_of_dst = 1, 186 .ae_enabled_idx = -1, 187 .result = false, 188 }, 189 { 190 .desc = "number_of_dst is 1, non AE, extra dst header", 191 .len = 26, 192 .number_of_dst = 1, 193 .ae_enabled_idx = -1, 194 .result = false, 195 }, 196 { 197 .desc = "number_of_dst is 1, AE, short", 198 .len = 20, 199 .number_of_dst = 1, 200 .ae_enabled_idx = 0, 201 .result = false, 202 }, 203 { 204 .desc = "number_of_dst is 1, AE", 205 .len = 21, 206 .number_of_dst = 1, 207 .ae_enabled_idx = 0, 208 .result = true, 209 }, 210 { 211 .desc = "number_of_dst is 19, non AE, short", 212 .len = 2 + 13 * 19 - 1, 213 .number_of_dst = 19, 214 .ae_enabled_idx = -1, 215 .result = false, 216 }, 217 { 218 .desc = "number_of_dst is 19, non AE", 219 .len = 2 + 13 * 19, 220 .number_of_dst = 19, 221 .ae_enabled_idx = -1, 222 .result = true, 223 }, 224 { 225 .desc = "number_of_dst is 19, AE, short", 226 .len = 2 + 13 * 19 + 6 - 1, 227 .number_of_dst = 19, 228 .ae_enabled_idx = 18, 229 .result = false, 230 }, 231 { 232 .desc = "number_of_dst is 19, AE", 233 .len = 2 + 13 * 19 + 6, 234 .number_of_dst = 19, 235 .ae_enabled_idx = 18, 236 .result = true, 237 }, 238 }; 239 240 KUNIT_ARRAY_PARAM_DESC(mesh_perr_parse, mesh_perr_parse_cases, desc); 241 242 243 static void mle_defrag(struct kunit *test) 244 { 245 struct ieee80211_elems_parse_params parse_params = { 246 .link_id = 12, 247 .from_ap = true, 248 .mode = IEEE80211_CONN_MODE_EHT, 249 /* type is not really relevant here */ 250 .type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON, 251 }; 252 struct ieee802_11_elems *parsed; 253 struct sk_buff *skb; 254 u8 *len_mle, *len_prof; 255 int i; 256 257 skb = alloc_skb(1024, GFP_KERNEL); 258 KUNIT_ASSERT_NOT_NULL(test, skb); 259 260 if (skb_pad(skb, skb_tailroom(skb))) { 261 KUNIT_FAIL(test, "failed to pad skb"); 262 return; 263 } 264 265 /* build a multi-link element */ 266 skb_put_u8(skb, WLAN_EID_EXTENSION); 267 len_mle = skb_put(skb, 1); 268 skb_put_u8(skb, WLAN_EID_EXT_EHT_MULTI_LINK); 269 270 put_unaligned_le16(IEEE80211_ML_CONTROL_TYPE_BASIC, 271 skb_put(skb, 2)); 272 /* struct ieee80211_mle_basic_common_info */ 273 skb_put_u8(skb, 7); /* includes len field */ 274 skb_put_data(skb, "\x00\x00\x00\x00\x00\x00", ETH_ALEN); /* MLD addr */ 275 276 /* with a STA profile inside */ 277 skb_put_u8(skb, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE); 278 len_prof = skb_put(skb, 1); 279 put_unaligned_le16(IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE | 280 parse_params.link_id, 281 skb_put(skb, 2)); 282 skb_put_u8(skb, 1); /* fake sta_info_len - includes itself */ 283 /* put a bunch of useless elements into it */ 284 for (i = 0; i < 20; i++) { 285 skb_put_u8(skb, WLAN_EID_SSID); 286 skb_put_u8(skb, 20); 287 skb_put(skb, 20); 288 } 289 290 /* fragment STA profile */ 291 ieee80211_fragment_element(skb, len_prof, 292 IEEE80211_MLE_SUBELEM_FRAGMENT); 293 /* fragment MLE */ 294 ieee80211_fragment_element(skb, len_mle, WLAN_EID_FRAGMENT); 295 296 parse_params.start = skb->data; 297 parse_params.len = skb->len; 298 parsed = ieee802_11_parse_elems_full(&parse_params); 299 /* should return ERR_PTR or valid, not NULL */ 300 KUNIT_EXPECT_NOT_NULL(test, parsed); 301 302 if (IS_ERR_OR_NULL(parsed)) 303 goto free_skb; 304 305 KUNIT_EXPECT_NOT_NULL(test, parsed->ml_basic); 306 KUNIT_EXPECT_EQ(test, 307 parsed->ml_basic_len, 308 2 /* control */ + 309 7 /* common info */ + 310 2 /* sta profile element header */ + 311 3 /* sta profile header */ + 312 20 * 22 /* sta profile data */ + 313 2 /* sta profile fragment element */); 314 KUNIT_EXPECT_NOT_NULL(test, parsed->prof); 315 KUNIT_EXPECT_EQ(test, 316 parsed->sta_prof_len, 317 3 /* sta profile header */ + 318 20 * 22 /* sta profile data */); 319 320 kfree(parsed); 321 free_skb: 322 kfree_skb(skb); 323 } 324 325 static void mesh_preq_parse(struct kunit *test) 326 { 327 const struct mesh_preq_parse_test_case *params = test->param_value; 328 u8 data[64] = {}; 329 struct ieee80211_mesh_hwmp_preq_top *top = (void *)data; 330 struct ieee80211_mesh_hwmp_preq_bottom *bottom; 331 332 top->flags = params->ae_enabled ? AE_F : 0; 333 bottom = ieee80211_mesh_hwmp_preq_get_bottom(data); 334 bottom->target_count = params->target_count; 335 336 KUNIT_EXPECT_EQ(test, 337 ieee80211_mesh_preq_size_ok(data, params->len), 338 params->result); 339 } 340 341 static void mesh_prep_parse(struct kunit *test) 342 { 343 const struct mesh_prep_parse_test_case *params = test->param_value; 344 u8 data[64] = {}; 345 struct ieee80211_mesh_hwmp_prep_top *top = (void *)data; 346 top->flags = params->ae_enabled ? AE_F : 0; 347 348 KUNIT_EXPECT_EQ(test, 349 ieee80211_mesh_prep_size_ok(data, params->len), 350 params->result); 351 } 352 353 static void mesh_perr_parse(struct kunit *test) 354 { 355 const struct mesh_perr_parse_test_case *params = test->param_value; 356 u8 data[256] = {}; 357 struct ieee80211_mesh_hwmp_perr *perr = (void *)data; 358 359 perr->number_of_dst = params->number_of_dst; 360 if (params->ae_enabled_idx > -1) { 361 struct ieee80211_mesh_hwmp_perr_dst *dst = 362 ieee80211_mesh_hwmp_perr_get_dst( 363 data, params->ae_enabled_idx); 364 365 dst->flags = AE_F; 366 } 367 368 KUNIT_EXPECT_EQ(test, 369 ieee80211_mesh_perr_size_ok(data, params->len), 370 params->result); 371 } 372 373 static struct kunit_case element_parsing_test_cases[] = { 374 KUNIT_CASE(mle_defrag), 375 KUNIT_CASE_PARAM(mesh_preq_parse, mesh_preq_parse_gen_params), 376 KUNIT_CASE_PARAM(mesh_prep_parse, mesh_prep_parse_gen_params), 377 KUNIT_CASE_PARAM(mesh_perr_parse, mesh_perr_parse_gen_params), 378 {} 379 }; 380 381 static struct kunit_suite element_parsing = { 382 .name = "mac80211-element-parsing", 383 .test_cases = element_parsing_test_cases, 384 }; 385 386 kunit_test_suite(element_parsing); 387