1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * KUnit tests for inform_bss functions |
4 | * |
5 | * Copyright (C) 2023-2024 Intel Corporation |
6 | */ |
7 | #include <linux/ieee80211.h> |
8 | #include <net/cfg80211.h> |
9 | #include <kunit/test.h> |
10 | #include <kunit/skbuff.h> |
11 | #include "../core.h" |
12 | #include "util.h" |
13 | |
14 | /* mac80211 helpers for element building */ |
15 | #include "../../mac80211/ieee80211_i.h" |
16 | |
17 | MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); |
18 | |
19 | struct test_elem { |
20 | u8 id; |
21 | u8 len; |
22 | union { |
23 | u8 data[255]; |
24 | struct { |
25 | u8 eid; |
26 | u8 edata[254]; |
27 | }; |
28 | }; |
29 | }; |
30 | |
31 | static struct gen_new_ie_case { |
32 | const char *desc; |
33 | struct test_elem parent_ies[16]; |
34 | struct test_elem child_ies[16]; |
35 | struct test_elem result_ies[16]; |
36 | } gen_new_ie_cases[] = { |
37 | { |
38 | .desc = "ML not inherited" , |
39 | .parent_ies = { |
40 | { .id = WLAN_EID_EXTENSION, .len = 255, |
41 | .eid = WLAN_EID_EXT_EHT_MULTI_LINK }, |
42 | }, |
43 | .child_ies = { |
44 | { .id = WLAN_EID_SSID, .len = 2 }, |
45 | }, |
46 | .result_ies = { |
47 | { .id = WLAN_EID_SSID, .len = 2 }, |
48 | }, |
49 | }, |
50 | { |
51 | .desc = "fragments are ignored if previous len not 255" , |
52 | .parent_ies = { |
53 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 254, }, |
54 | { .id = WLAN_EID_FRAGMENT, .len = 125, }, |
55 | }, |
56 | .child_ies = { |
57 | { .id = WLAN_EID_SSID, .len = 2 }, |
58 | { .id = WLAN_EID_FRAGMENT, .len = 125, }, |
59 | }, |
60 | .result_ies = { |
61 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 254, }, |
62 | { .id = WLAN_EID_SSID, .len = 2 }, |
63 | }, |
64 | }, |
65 | { |
66 | .desc = "fragments inherited" , |
67 | .parent_ies = { |
68 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, }, |
69 | { .id = WLAN_EID_FRAGMENT, .len = 125, }, |
70 | }, |
71 | .child_ies = { |
72 | { .id = WLAN_EID_SSID, .len = 2 }, |
73 | }, |
74 | .result_ies = { |
75 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, }, |
76 | { .id = WLAN_EID_FRAGMENT, .len = 125, }, |
77 | { .id = WLAN_EID_SSID, .len = 2 }, |
78 | }, |
79 | }, |
80 | { |
81 | .desc = "fragments copied" , |
82 | .parent_ies = { |
83 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, }, |
84 | { .id = WLAN_EID_FRAGMENT, .len = 125, }, |
85 | }, |
86 | .child_ies = { |
87 | { .id = WLAN_EID_SSID, .len = 2 }, |
88 | }, |
89 | .result_ies = { |
90 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, }, |
91 | { .id = WLAN_EID_FRAGMENT, .len = 125, }, |
92 | { .id = WLAN_EID_SSID, .len = 2 }, |
93 | }, |
94 | }, |
95 | { |
96 | .desc = "multiple elements inherit" , |
97 | .parent_ies = { |
98 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, }, |
99 | { .id = WLAN_EID_FRAGMENT, .len = 125, }, |
100 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 123, }, |
101 | }, |
102 | .child_ies = { |
103 | { .id = WLAN_EID_SSID, .len = 2 }, |
104 | }, |
105 | .result_ies = { |
106 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, }, |
107 | { .id = WLAN_EID_FRAGMENT, .len = 125, }, |
108 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 123, }, |
109 | { .id = WLAN_EID_SSID, .len = 2 }, |
110 | }, |
111 | }, |
112 | { |
113 | .desc = "one child element overrides" , |
114 | .parent_ies = { |
115 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, }, |
116 | { .id = WLAN_EID_FRAGMENT, .len = 125, }, |
117 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 123, }, |
118 | }, |
119 | .child_ies = { |
120 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 127, }, |
121 | { .id = WLAN_EID_SSID, .len = 2 }, |
122 | }, |
123 | .result_ies = { |
124 | { .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 127, }, |
125 | { .id = WLAN_EID_SSID, .len = 2 }, |
126 | }, |
127 | }, |
128 | { |
129 | .desc = "empty elements from parent" , |
130 | .parent_ies = { |
131 | { .id = 0x1, .len = 0, }, |
132 | { .id = WLAN_EID_EXTENSION, .len = 1, .eid = 0x10 }, |
133 | }, |
134 | .child_ies = { |
135 | }, |
136 | .result_ies = { |
137 | { .id = 0x1, .len = 0, }, |
138 | { .id = WLAN_EID_EXTENSION, .len = 1, .eid = 0x10 }, |
139 | }, |
140 | }, |
141 | { |
142 | .desc = "empty elements from child" , |
143 | .parent_ies = { |
144 | }, |
145 | .child_ies = { |
146 | { .id = 0x1, .len = 0, }, |
147 | { .id = WLAN_EID_EXTENSION, .len = 1, .eid = 0x10 }, |
148 | }, |
149 | .result_ies = { |
150 | { .id = 0x1, .len = 0, }, |
151 | { .id = WLAN_EID_EXTENSION, .len = 1, .eid = 0x10 }, |
152 | }, |
153 | }, |
154 | { |
155 | .desc = "invalid extended elements ignored" , |
156 | .parent_ies = { |
157 | { .id = WLAN_EID_EXTENSION, .len = 0 }, |
158 | }, |
159 | .child_ies = { |
160 | { .id = WLAN_EID_EXTENSION, .len = 0 }, |
161 | }, |
162 | .result_ies = { |
163 | }, |
164 | }, |
165 | { |
166 | .desc = "multiple extended elements" , |
167 | .parent_ies = { |
168 | { .id = WLAN_EID_EXTENSION, .len = 3, |
169 | .eid = WLAN_EID_EXT_HE_CAPABILITY }, |
170 | { .id = WLAN_EID_EXTENSION, .len = 5, |
171 | .eid = WLAN_EID_EXT_ASSOC_DELAY_INFO }, |
172 | { .id = WLAN_EID_EXTENSION, .len = 7, |
173 | .eid = WLAN_EID_EXT_HE_OPERATION }, |
174 | { .id = WLAN_EID_EXTENSION, .len = 11, |
175 | .eid = WLAN_EID_EXT_FILS_REQ_PARAMS }, |
176 | }, |
177 | .child_ies = { |
178 | { .id = WLAN_EID_SSID, .len = 13 }, |
179 | { .id = WLAN_EID_EXTENSION, .len = 17, |
180 | .eid = WLAN_EID_EXT_HE_CAPABILITY }, |
181 | { .id = WLAN_EID_EXTENSION, .len = 11, |
182 | .eid = WLAN_EID_EXT_FILS_KEY_CONFIRM }, |
183 | { .id = WLAN_EID_EXTENSION, .len = 19, |
184 | .eid = WLAN_EID_EXT_HE_OPERATION }, |
185 | }, |
186 | .result_ies = { |
187 | { .id = WLAN_EID_EXTENSION, .len = 17, |
188 | .eid = WLAN_EID_EXT_HE_CAPABILITY }, |
189 | { .id = WLAN_EID_EXTENSION, .len = 5, |
190 | .eid = WLAN_EID_EXT_ASSOC_DELAY_INFO }, |
191 | { .id = WLAN_EID_EXTENSION, .len = 19, |
192 | .eid = WLAN_EID_EXT_HE_OPERATION }, |
193 | { .id = WLAN_EID_EXTENSION, .len = 11, |
194 | .eid = WLAN_EID_EXT_FILS_REQ_PARAMS }, |
195 | { .id = WLAN_EID_SSID, .len = 13 }, |
196 | { .id = WLAN_EID_EXTENSION, .len = 11, |
197 | .eid = WLAN_EID_EXT_FILS_KEY_CONFIRM }, |
198 | }, |
199 | }, |
200 | { |
201 | .desc = "non-inherit element" , |
202 | .parent_ies = { |
203 | { .id = 0x1, .len = 7, }, |
204 | { .id = 0x2, .len = 11, }, |
205 | { .id = 0x3, .len = 13, }, |
206 | { .id = WLAN_EID_EXTENSION, .len = 17, .eid = 0x10 }, |
207 | { .id = WLAN_EID_EXTENSION, .len = 19, .eid = 0x11 }, |
208 | { .id = WLAN_EID_EXTENSION, .len = 23, .eid = 0x12 }, |
209 | { .id = WLAN_EID_EXTENSION, .len = 29, .eid = 0x14 }, |
210 | }, |
211 | .child_ies = { |
212 | { .id = WLAN_EID_EXTENSION, |
213 | .eid = WLAN_EID_EXT_NON_INHERITANCE, |
214 | .len = 10, |
215 | .edata = { 0x3, 0x1, 0x2, 0x3, |
216 | 0x4, 0x10, 0x11, 0x13, 0x14 } }, |
217 | { .id = WLAN_EID_SSID, .len = 2 }, |
218 | }, |
219 | .result_ies = { |
220 | { .id = WLAN_EID_EXTENSION, .len = 23, .eid = 0x12 }, |
221 | { .id = WLAN_EID_SSID, .len = 2 }, |
222 | }, |
223 | }, |
224 | }; |
225 | KUNIT_ARRAY_PARAM_DESC(gen_new_ie, gen_new_ie_cases, desc) |
226 | |
227 | static void test_gen_new_ie(struct kunit *test) |
228 | { |
229 | const struct gen_new_ie_case *params = test->param_value; |
230 | struct sk_buff *parent = kunit_zalloc_skb(test, len: 1024, GFP_KERNEL); |
231 | struct sk_buff *child = kunit_zalloc_skb(test, len: 1024, GFP_KERNEL); |
232 | struct sk_buff *reference = kunit_zalloc_skb(test, len: 1024, GFP_KERNEL); |
233 | u8 *out = kunit_kzalloc(test, IEEE80211_MAX_DATA_LEN, GFP_KERNEL); |
234 | size_t len; |
235 | int i; |
236 | |
237 | KUNIT_ASSERT_NOT_NULL(test, parent); |
238 | KUNIT_ASSERT_NOT_NULL(test, child); |
239 | KUNIT_ASSERT_NOT_NULL(test, reference); |
240 | KUNIT_ASSERT_NOT_NULL(test, out); |
241 | |
242 | for (i = 0; i < ARRAY_SIZE(params->parent_ies); i++) { |
243 | if (params->parent_ies[i].len != 0) { |
244 | skb_put_u8(skb: parent, val: params->parent_ies[i].id); |
245 | skb_put_u8(skb: parent, val: params->parent_ies[i].len); |
246 | skb_put_data(skb: parent, data: params->parent_ies[i].data, |
247 | len: params->parent_ies[i].len); |
248 | } |
249 | |
250 | if (params->child_ies[i].len != 0) { |
251 | skb_put_u8(skb: child, val: params->child_ies[i].id); |
252 | skb_put_u8(skb: child, val: params->child_ies[i].len); |
253 | skb_put_data(skb: child, data: params->child_ies[i].data, |
254 | len: params->child_ies[i].len); |
255 | } |
256 | |
257 | if (params->result_ies[i].len != 0) { |
258 | skb_put_u8(skb: reference, val: params->result_ies[i].id); |
259 | skb_put_u8(skb: reference, val: params->result_ies[i].len); |
260 | skb_put_data(skb: reference, data: params->result_ies[i].data, |
261 | len: params->result_ies[i].len); |
262 | } |
263 | } |
264 | |
265 | len = cfg80211_gen_new_ie(ie: parent->data, ielen: parent->len, |
266 | subie: child->data, subie_len: child->len, |
267 | new_ie: out, IEEE80211_MAX_DATA_LEN); |
268 | KUNIT_EXPECT_EQ(test, len, reference->len); |
269 | KUNIT_EXPECT_MEMEQ(test, out, reference->data, reference->len); |
270 | memset(out, 0, IEEE80211_MAX_DATA_LEN); |
271 | |
272 | /* Exactly enough space */ |
273 | len = cfg80211_gen_new_ie(ie: parent->data, ielen: parent->len, |
274 | subie: child->data, subie_len: child->len, |
275 | new_ie: out, new_ie_len: reference->len); |
276 | KUNIT_EXPECT_EQ(test, len, reference->len); |
277 | KUNIT_EXPECT_MEMEQ(test, out, reference->data, reference->len); |
278 | memset(out, 0, IEEE80211_MAX_DATA_LEN); |
279 | |
280 | /* Not enough space (or expected zero length) */ |
281 | len = cfg80211_gen_new_ie(ie: parent->data, ielen: parent->len, |
282 | subie: child->data, subie_len: child->len, |
283 | new_ie: out, new_ie_len: reference->len - 1); |
284 | KUNIT_EXPECT_EQ(test, len, 0); |
285 | } |
286 | |
287 | static void test_gen_new_ie_malformed(struct kunit *test) |
288 | { |
289 | struct sk_buff *malformed = kunit_zalloc_skb(test, len: 1024, GFP_KERNEL); |
290 | u8 *out = kunit_kzalloc(test, IEEE80211_MAX_DATA_LEN, GFP_KERNEL); |
291 | size_t len; |
292 | |
293 | KUNIT_ASSERT_NOT_NULL(test, malformed); |
294 | KUNIT_ASSERT_NOT_NULL(test, out); |
295 | |
296 | skb_put_u8(skb: malformed, val: WLAN_EID_SSID); |
297 | skb_put_u8(skb: malformed, val: 3); |
298 | skb_put(skb: malformed, len: 3); |
299 | skb_put_u8(skb: malformed, val: WLAN_EID_REDUCED_NEIGHBOR_REPORT); |
300 | skb_put_u8(skb: malformed, val: 10); |
301 | skb_put(skb: malformed, len: 9); |
302 | |
303 | len = cfg80211_gen_new_ie(ie: malformed->data, ielen: malformed->len, |
304 | subie: out, subie_len: 0, |
305 | new_ie: out, IEEE80211_MAX_DATA_LEN); |
306 | KUNIT_EXPECT_EQ(test, len, 5); |
307 | |
308 | len = cfg80211_gen_new_ie(ie: out, ielen: 0, |
309 | subie: malformed->data, subie_len: malformed->len, |
310 | new_ie: out, IEEE80211_MAX_DATA_LEN); |
311 | KUNIT_EXPECT_EQ(test, len, 5); |
312 | } |
313 | |
314 | struct inform_bss { |
315 | struct kunit *test; |
316 | |
317 | int inform_bss_count; |
318 | }; |
319 | |
320 | static void inform_bss_inc_counter(struct wiphy *wiphy, |
321 | struct cfg80211_bss *bss, |
322 | const struct cfg80211_bss_ies *ies, |
323 | void *drv_data) |
324 | { |
325 | struct inform_bss *ctx = t_wiphy_ctx(wiphy); |
326 | |
327 | ctx->inform_bss_count++; |
328 | |
329 | rcu_read_lock(); |
330 | KUNIT_EXPECT_PTR_EQ(ctx->test, drv_data, ctx); |
331 | KUNIT_EXPECT_PTR_EQ(ctx->test, ies, rcu_dereference(bss->ies)); |
332 | rcu_read_unlock(); |
333 | } |
334 | |
335 | static void test_inform_bss_ssid_only(struct kunit *test) |
336 | { |
337 | struct inform_bss ctx = { |
338 | .test = test, |
339 | }; |
340 | struct wiphy *wiphy = T_WIPHY(test, ctx); |
341 | struct t_wiphy_priv *w_priv = wiphy_priv(wiphy); |
342 | struct cfg80211_inform_bss inform_bss = { |
343 | .signal = 50, |
344 | .drv_data = &ctx, |
345 | }; |
346 | const u8 bssid[ETH_ALEN] = { 0x10, 0x22, 0x33, 0x44, 0x55, 0x66 }; |
347 | u64 tsf = 0x1000000000000000ULL; |
348 | int beacon_int = 100; |
349 | u16 capability = 0x1234; |
350 | static const u8 input[] = { |
351 | [0] = WLAN_EID_SSID, |
352 | [1] = 4, |
353 | [2] = 'T', 'E', 'S', 'T' |
354 | }; |
355 | struct cfg80211_bss *bss, *other; |
356 | const struct cfg80211_bss_ies *ies; |
357 | |
358 | w_priv->ops->inform_bss = inform_bss_inc_counter; |
359 | |
360 | inform_bss.chan = ieee80211_get_channel_khz(wiphy, MHZ_TO_KHZ(2412)); |
361 | KUNIT_ASSERT_NOT_NULL(test, inform_bss.chan); |
362 | |
363 | bss = cfg80211_inform_bss_data(wiphy, data: &inform_bss, |
364 | ftype: CFG80211_BSS_FTYPE_PRESP, bssid, tsf, |
365 | capability, beacon_interval: beacon_int, |
366 | ie: input, ielen: sizeof(input), |
367 | GFP_KERNEL); |
368 | KUNIT_EXPECT_NOT_NULL(test, bss); |
369 | KUNIT_EXPECT_EQ(test, ctx.inform_bss_count, 1); |
370 | |
371 | /* Check values in returned bss are correct */ |
372 | KUNIT_EXPECT_EQ(test, bss->signal, inform_bss.signal); |
373 | KUNIT_EXPECT_EQ(test, bss->beacon_interval, beacon_int); |
374 | KUNIT_EXPECT_EQ(test, bss->capability, capability); |
375 | KUNIT_EXPECT_EQ(test, bss->bssid_index, 0); |
376 | KUNIT_EXPECT_PTR_EQ(test, bss->channel, inform_bss.chan); |
377 | KUNIT_EXPECT_MEMEQ(test, bssid, bss->bssid, sizeof(bssid)); |
378 | |
379 | /* Check the IEs have the expected value */ |
380 | rcu_read_lock(); |
381 | ies = rcu_dereference(bss->ies); |
382 | KUNIT_EXPECT_NOT_NULL(test, ies); |
383 | KUNIT_EXPECT_EQ(test, ies->tsf, tsf); |
384 | KUNIT_EXPECT_EQ(test, ies->len, sizeof(input)); |
385 | KUNIT_EXPECT_MEMEQ(test, ies->data, input, sizeof(input)); |
386 | rcu_read_unlock(); |
387 | |
388 | /* Check we can look up the BSS - by SSID */ |
389 | other = cfg80211_get_bss(wiphy, NULL, NULL, ssid: "TEST" , ssid_len: 4, |
390 | bss_type: IEEE80211_BSS_TYPE_ANY, |
391 | privacy: IEEE80211_PRIVACY_ANY); |
392 | KUNIT_EXPECT_PTR_EQ(test, bss, other); |
393 | cfg80211_put_bss(wiphy, bss: other); |
394 | |
395 | /* Check we can look up the BSS - by BSSID */ |
396 | other = cfg80211_get_bss(wiphy, NULL, bssid, NULL, ssid_len: 0, |
397 | bss_type: IEEE80211_BSS_TYPE_ANY, |
398 | privacy: IEEE80211_PRIVACY_ANY); |
399 | KUNIT_EXPECT_PTR_EQ(test, bss, other); |
400 | cfg80211_put_bss(wiphy, bss: other); |
401 | |
402 | cfg80211_put_bss(wiphy, bss); |
403 | } |
404 | |
405 | static struct inform_bss_ml_sta_case { |
406 | const char *desc; |
407 | int mld_id; |
408 | bool sta_prof_vendor_elems; |
409 | bool include_oper_class; |
410 | bool nstr; |
411 | } inform_bss_ml_sta_cases[] = { |
412 | { |
413 | .desc = "zero_mld_id" , |
414 | .mld_id = 0, |
415 | .sta_prof_vendor_elems = false, |
416 | }, { |
417 | .desc = "zero_mld_id_with_oper_class" , |
418 | .mld_id = 0, |
419 | .sta_prof_vendor_elems = false, |
420 | .include_oper_class = true, |
421 | }, { |
422 | .desc = "mld_id_eq_1" , |
423 | .mld_id = 1, |
424 | .sta_prof_vendor_elems = true, |
425 | }, { |
426 | .desc = "mld_id_eq_1_with_oper_class" , |
427 | .mld_id = 1, |
428 | .sta_prof_vendor_elems = true, |
429 | .include_oper_class = true, |
430 | }, { |
431 | .desc = "nstr" , |
432 | .mld_id = 0, |
433 | .nstr = true, |
434 | }, |
435 | }; |
436 | KUNIT_ARRAY_PARAM_DESC(inform_bss_ml_sta, inform_bss_ml_sta_cases, desc) |
437 | |
438 | static void test_inform_bss_ml_sta(struct kunit *test) |
439 | { |
440 | const struct inform_bss_ml_sta_case *params = test->param_value; |
441 | struct inform_bss ctx = { |
442 | .test = test, |
443 | }; |
444 | struct wiphy *wiphy = T_WIPHY(test, ctx); |
445 | struct t_wiphy_priv *w_priv = wiphy_priv(wiphy); |
446 | struct cfg80211_inform_bss inform_bss = { |
447 | .signal = 50, |
448 | .drv_data = &ctx, |
449 | }; |
450 | struct cfg80211_bss *bss, *link_bss; |
451 | const struct cfg80211_bss_ies *ies; |
452 | |
453 | /* sending station */ |
454 | const u8 bssid[ETH_ALEN] = { 0x10, 0x22, 0x33, 0x44, 0x55, 0x66 }; |
455 | u64 tsf = 0x1000000000000000ULL; |
456 | int beacon_int = 100; |
457 | u16 capability = 0x1234; |
458 | |
459 | /* Building the frame *************************************************/ |
460 | struct sk_buff *input = kunit_zalloc_skb(test, len: 1024, GFP_KERNEL); |
461 | u8 *len_mle, *len_prof; |
462 | u8 link_id = 2; |
463 | struct { |
464 | struct ieee80211_neighbor_ap_info info; |
465 | struct ieee80211_tbtt_info_ge_11 ap; |
466 | } __packed rnr_normal = { |
467 | .info = { |
468 | .tbtt_info_hdr = u8_encode_bits(v: 0, IEEE80211_AP_INFO_TBTT_HDR_COUNT), |
469 | .tbtt_info_len = sizeof(struct ieee80211_tbtt_info_ge_11), |
470 | .op_class = 81, |
471 | .channel = 11, |
472 | }, |
473 | .ap = { |
474 | .tbtt_offset = 0xff, |
475 | .bssid = { 0x10, 0x22, 0x33, 0x44, 0x55, 0x67 }, |
476 | .short_ssid = 0, /* unused */ |
477 | .bss_params = 0, |
478 | .psd_20 = 0, |
479 | .mld_params.mld_id = params->mld_id, |
480 | .mld_params.params = |
481 | le16_encode_bits(v: link_id, |
482 | IEEE80211_RNR_MLD_PARAMS_LINK_ID), |
483 | } |
484 | }; |
485 | struct { |
486 | struct ieee80211_neighbor_ap_info info; |
487 | struct ieee80211_rnr_mld_params mld_params; |
488 | } __packed rnr_nstr = { |
489 | .info = { |
490 | .tbtt_info_hdr = |
491 | u8_encode_bits(v: 0, IEEE80211_AP_INFO_TBTT_HDR_COUNT) | |
492 | u8_encode_bits(IEEE80211_TBTT_INFO_TYPE_MLD, |
493 | IEEE80211_AP_INFO_TBTT_HDR_TYPE), |
494 | .tbtt_info_len = sizeof(struct ieee80211_rnr_mld_params), |
495 | .op_class = 81, |
496 | .channel = 11, |
497 | }, |
498 | .mld_params = { |
499 | .mld_id = params->mld_id, |
500 | .params = |
501 | le16_encode_bits(v: link_id, |
502 | IEEE80211_RNR_MLD_PARAMS_LINK_ID), |
503 | } |
504 | }; |
505 | size_t rnr_len = params->nstr ? sizeof(rnr_nstr) : sizeof(rnr_normal); |
506 | void *rnr = params->nstr ? (void *)&rnr_nstr : (void *)&rnr_normal; |
507 | struct { |
508 | __le16 control; |
509 | u8 var_len; |
510 | u8 mld_mac_addr[ETH_ALEN]; |
511 | u8 link_id_info; |
512 | u8 params_change_count; |
513 | __le16 mld_caps_and_ops; |
514 | u8 mld_id; |
515 | __le16 ext_mld_caps_and_ops; |
516 | } __packed mle_basic_common_info = { |
517 | .control = |
518 | cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC | |
519 | IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT | |
520 | IEEE80211_MLC_BASIC_PRES_LINK_ID | |
521 | (params->mld_id ? IEEE80211_MLC_BASIC_PRES_MLD_ID : 0) | |
522 | IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP), |
523 | .mld_id = params->mld_id, |
524 | .mld_caps_and_ops = cpu_to_le16(0x0102), |
525 | .ext_mld_caps_and_ops = cpu_to_le16(0x0304), |
526 | .var_len = sizeof(mle_basic_common_info) - 2 - |
527 | (params->mld_id ? 0 : 1), |
528 | .mld_mac_addr = { 0x10, 0x22, 0x33, 0x44, 0x55, 0x60 }, |
529 | }; |
530 | struct { |
531 | __le16 control; |
532 | u8 var_len; |
533 | u8 bssid[ETH_ALEN]; |
534 | __le16 beacon_int; |
535 | __le64 tsf_offset; |
536 | __le16 capabilities; /* already part of payload */ |
537 | } __packed sta_prof = { |
538 | .control = |
539 | cpu_to_le16(IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE | |
540 | IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT | |
541 | IEEE80211_MLE_STA_CONTROL_BEACON_INT_PRESENT | |
542 | IEEE80211_MLE_STA_CONTROL_TSF_OFFS_PRESENT | |
543 | u16_encode_bits(link_id, |
544 | IEEE80211_MLE_STA_CONTROL_LINK_ID)), |
545 | .var_len = sizeof(sta_prof) - 2 - 2, |
546 | .bssid = { *rnr_normal.ap.bssid }, |
547 | .beacon_int = cpu_to_le16(101), |
548 | .tsf_offset = cpu_to_le64(-123ll), |
549 | .capabilities = cpu_to_le16(0xdead), |
550 | }; |
551 | |
552 | KUNIT_ASSERT_NOT_NULL(test, input); |
553 | |
554 | w_priv->ops->inform_bss = inform_bss_inc_counter; |
555 | |
556 | inform_bss.chan = ieee80211_get_channel_khz(wiphy, MHZ_TO_KHZ(2412)); |
557 | KUNIT_ASSERT_NOT_NULL(test, inform_bss.chan); |
558 | |
559 | skb_put_u8(skb: input, val: WLAN_EID_SSID); |
560 | skb_put_u8(skb: input, val: 4); |
561 | skb_put_data(skb: input, data: "TEST" , len: 4); |
562 | |
563 | if (params->include_oper_class) { |
564 | skb_put_u8(skb: input, val: WLAN_EID_SUPPORTED_REGULATORY_CLASSES); |
565 | skb_put_u8(skb: input, val: 1); |
566 | skb_put_u8(skb: input, val: 81); |
567 | } |
568 | |
569 | skb_put_u8(skb: input, val: WLAN_EID_REDUCED_NEIGHBOR_REPORT); |
570 | skb_put_u8(skb: input, val: rnr_len); |
571 | skb_put_data(skb: input, data: rnr, len: rnr_len); |
572 | |
573 | /* build a multi-link element */ |
574 | skb_put_u8(skb: input, val: WLAN_EID_EXTENSION); |
575 | len_mle = skb_put(skb: input, len: 1); |
576 | skb_put_u8(skb: input, val: WLAN_EID_EXT_EHT_MULTI_LINK); |
577 | skb_put_data(skb: input, data: &mle_basic_common_info, len: sizeof(mle_basic_common_info)); |
578 | if (!params->mld_id) |
579 | t_skb_remove_member(input, typeof(mle_basic_common_info), mld_id); |
580 | /* with a STA profile inside */ |
581 | skb_put_u8(skb: input, val: IEEE80211_MLE_SUBELEM_PER_STA_PROFILE); |
582 | len_prof = skb_put(skb: input, len: 1); |
583 | skb_put_data(skb: input, data: &sta_prof, len: sizeof(sta_prof)); |
584 | |
585 | if (params->sta_prof_vendor_elems) { |
586 | /* Put two (vendor) element into sta_prof */ |
587 | skb_put_u8(skb: input, val: WLAN_EID_VENDOR_SPECIFIC); |
588 | skb_put_u8(skb: input, val: 160); |
589 | skb_put(skb: input, len: 160); |
590 | |
591 | skb_put_u8(skb: input, val: WLAN_EID_VENDOR_SPECIFIC); |
592 | skb_put_u8(skb: input, val: 165); |
593 | skb_put(skb: input, len: 165); |
594 | } |
595 | |
596 | /* fragment STA profile */ |
597 | ieee80211_fragment_element(skb: input, len_pos: len_prof, |
598 | frag_id: IEEE80211_MLE_SUBELEM_FRAGMENT); |
599 | /* fragment MLE */ |
600 | ieee80211_fragment_element(skb: input, len_pos: len_mle, frag_id: WLAN_EID_FRAGMENT); |
601 | |
602 | /* Put a (vendor) element after the ML element */ |
603 | skb_put_u8(skb: input, val: WLAN_EID_VENDOR_SPECIFIC); |
604 | skb_put_u8(skb: input, val: 155); |
605 | skb_put(skb: input, len: 155); |
606 | |
607 | /* Submit *************************************************************/ |
608 | bss = cfg80211_inform_bss_data(wiphy, data: &inform_bss, |
609 | ftype: CFG80211_BSS_FTYPE_PRESP, bssid, tsf, |
610 | capability, beacon_interval: beacon_int, |
611 | ie: input->data, ielen: input->len, |
612 | GFP_KERNEL); |
613 | KUNIT_EXPECT_NOT_NULL(test, bss); |
614 | KUNIT_EXPECT_EQ(test, ctx.inform_bss_count, 2); |
615 | |
616 | /* Check link_bss *****************************************************/ |
617 | link_bss = __cfg80211_get_bss(wiphy, NULL, bssid: sta_prof.bssid, NULL, ssid_len: 0, |
618 | bss_type: IEEE80211_BSS_TYPE_ANY, |
619 | privacy: IEEE80211_PRIVACY_ANY, |
620 | use_for: 0); |
621 | KUNIT_ASSERT_NOT_NULL(test, link_bss); |
622 | KUNIT_EXPECT_EQ(test, link_bss->signal, 0); |
623 | KUNIT_EXPECT_EQ(test, link_bss->beacon_interval, |
624 | le16_to_cpu(sta_prof.beacon_int)); |
625 | KUNIT_EXPECT_EQ(test, link_bss->capability, |
626 | le16_to_cpu(sta_prof.capabilities)); |
627 | KUNIT_EXPECT_EQ(test, link_bss->bssid_index, 0); |
628 | KUNIT_EXPECT_PTR_EQ(test, link_bss->channel, |
629 | ieee80211_get_channel_khz(wiphy, MHZ_TO_KHZ(2462))); |
630 | |
631 | /* Test wiphy does not set WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY */ |
632 | if (params->nstr) { |
633 | KUNIT_EXPECT_EQ(test, link_bss->use_for, 0); |
634 | KUNIT_EXPECT_EQ(test, link_bss->cannot_use_reasons, |
635 | NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY); |
636 | KUNIT_EXPECT_NULL(test, |
637 | cfg80211_get_bss(wiphy, NULL, sta_prof.bssid, |
638 | NULL, 0, |
639 | IEEE80211_BSS_TYPE_ANY, |
640 | IEEE80211_PRIVACY_ANY)); |
641 | } else { |
642 | KUNIT_EXPECT_EQ(test, link_bss->use_for, |
643 | NL80211_BSS_USE_FOR_ALL); |
644 | KUNIT_EXPECT_EQ(test, link_bss->cannot_use_reasons, 0); |
645 | } |
646 | |
647 | rcu_read_lock(); |
648 | ies = rcu_dereference(link_bss->ies); |
649 | KUNIT_EXPECT_NOT_NULL(test, ies); |
650 | KUNIT_EXPECT_EQ(test, ies->tsf, tsf + le64_to_cpu(sta_prof.tsf_offset)); |
651 | /* Resulting length should be: |
652 | * SSID (inherited) + RNR (inherited) + vendor element(s) + |
653 | * operating class (if requested) + |
654 | * generated RNR (if MLD ID == 0 and not NSTR) + |
655 | * MLE common info + MLE header and control |
656 | */ |
657 | if (params->sta_prof_vendor_elems) |
658 | KUNIT_EXPECT_EQ(test, ies->len, |
659 | 6 + 2 + rnr_len + 2 + 160 + 2 + 165 + |
660 | (params->include_oper_class ? 3 : 0) + |
661 | (!params->mld_id && !params->nstr ? 22 : 0) + |
662 | mle_basic_common_info.var_len + 5); |
663 | else |
664 | KUNIT_EXPECT_EQ(test, ies->len, |
665 | 6 + 2 + rnr_len + 2 + 155 + |
666 | (params->include_oper_class ? 3 : 0) + |
667 | (!params->mld_id && !params->nstr ? 22 : 0) + |
668 | mle_basic_common_info.var_len + 5); |
669 | rcu_read_unlock(); |
670 | |
671 | cfg80211_put_bss(wiphy, bss); |
672 | cfg80211_put_bss(wiphy, bss: link_bss); |
673 | } |
674 | |
675 | static struct cfg80211_parse_colocated_ap_case { |
676 | const char *desc; |
677 | u8 op_class; |
678 | u8 channel; |
679 | struct ieee80211_neighbor_ap_info info; |
680 | union { |
681 | struct ieee80211_tbtt_info_ge_11 tbtt_long; |
682 | struct ieee80211_tbtt_info_7_8_9 tbtt_short; |
683 | }; |
684 | bool add_junk; |
685 | bool same_ssid; |
686 | bool valid; |
687 | } cfg80211_parse_colocated_ap_cases[] = { |
688 | { |
689 | .desc = "wrong_band" , |
690 | .info.op_class = 81, |
691 | .info.channel = 11, |
692 | .tbtt_long = { |
693 | .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, |
694 | .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, |
695 | }, |
696 | .valid = false, |
697 | }, |
698 | { |
699 | .desc = "wrong_type" , |
700 | /* IEEE80211_AP_INFO_TBTT_HDR_TYPE is in the least significant bits */ |
701 | .info.tbtt_info_hdr = IEEE80211_TBTT_INFO_TYPE_MLD, |
702 | .tbtt_long = { |
703 | .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, |
704 | .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, |
705 | }, |
706 | .valid = false, |
707 | }, |
708 | { |
709 | .desc = "colocated_invalid_len_short" , |
710 | .info.tbtt_info_len = 6, |
711 | .tbtt_short = { |
712 | .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, |
713 | .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP | |
714 | IEEE80211_RNR_TBTT_PARAMS_SAME_SSID, |
715 | }, |
716 | .valid = false, |
717 | }, |
718 | { |
719 | .desc = "colocated_invalid_len_short_mld" , |
720 | .info.tbtt_info_len = 10, |
721 | .tbtt_long = { |
722 | .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, |
723 | .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, |
724 | }, |
725 | .valid = false, |
726 | }, |
727 | { |
728 | .desc = "colocated_non_mld" , |
729 | .info.tbtt_info_len = sizeof(struct ieee80211_tbtt_info_7_8_9), |
730 | .tbtt_short = { |
731 | .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, |
732 | .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP | |
733 | IEEE80211_RNR_TBTT_PARAMS_SAME_SSID, |
734 | }, |
735 | .same_ssid = true, |
736 | .valid = true, |
737 | }, |
738 | { |
739 | .desc = "colocated_non_mld_invalid_bssid" , |
740 | .info.tbtt_info_len = sizeof(struct ieee80211_tbtt_info_7_8_9), |
741 | .tbtt_short = { |
742 | .bssid = { 0xff, 0x11, 0x22, 0x33, 0x44, 0x55 }, |
743 | .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP | |
744 | IEEE80211_RNR_TBTT_PARAMS_SAME_SSID, |
745 | }, |
746 | .same_ssid = true, |
747 | .valid = false, |
748 | }, |
749 | { |
750 | .desc = "colocated_mld" , |
751 | .tbtt_long = { |
752 | .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, |
753 | .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, |
754 | }, |
755 | .valid = true, |
756 | }, |
757 | { |
758 | .desc = "colocated_mld" , |
759 | .tbtt_long = { |
760 | .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, |
761 | .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, |
762 | }, |
763 | .add_junk = true, |
764 | .valid = false, |
765 | }, |
766 | { |
767 | .desc = "colocated_disabled_mld" , |
768 | .tbtt_long = { |
769 | .bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, |
770 | .bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP, |
771 | .mld_params.params = cpu_to_le16(IEEE80211_RNR_MLD_PARAMS_DISABLED_LINK), |
772 | }, |
773 | .valid = false, |
774 | }, |
775 | }; |
776 | KUNIT_ARRAY_PARAM_DESC(cfg80211_parse_colocated_ap, cfg80211_parse_colocated_ap_cases, desc) |
777 | |
778 | static void test_cfg80211_parse_colocated_ap(struct kunit *test) |
779 | { |
780 | const struct cfg80211_parse_colocated_ap_case *params = test->param_value; |
781 | struct sk_buff *input = kunit_zalloc_skb(test, len: 1024, GFP_KERNEL); |
782 | struct cfg80211_bss_ies *ies; |
783 | struct ieee80211_neighbor_ap_info info; |
784 | LIST_HEAD(coloc_ap_list); |
785 | int count; |
786 | |
787 | KUNIT_ASSERT_NOT_NULL(test, input); |
788 | |
789 | info = params->info; |
790 | |
791 | /* Reasonable values for a colocated AP */ |
792 | if (!info.tbtt_info_len) |
793 | info.tbtt_info_len = sizeof(params->tbtt_long); |
794 | if (!info.op_class) |
795 | info.op_class = 131; |
796 | if (!info.channel) |
797 | info.channel = 33; |
798 | /* Zero is the correct default for .btt_info_hdr (one entry, TBTT type) */ |
799 | |
800 | skb_put_u8(skb: input, val: WLAN_EID_SSID); |
801 | skb_put_u8(skb: input, val: 4); |
802 | skb_put_data(skb: input, data: "TEST" , len: 4); |
803 | |
804 | skb_put_u8(skb: input, val: WLAN_EID_REDUCED_NEIGHBOR_REPORT); |
805 | skb_put_u8(skb: input, val: sizeof(info) + info.tbtt_info_len + (params->add_junk ? 3 : 0)); |
806 | skb_put_data(skb: input, data: &info, len: sizeof(info)); |
807 | skb_put_data(skb: input, data: ¶ms->tbtt_long, len: info.tbtt_info_len); |
808 | |
809 | if (params->add_junk) |
810 | skb_put_data(skb: input, data: "123" , len: 3); |
811 | |
812 | ies = kunit_kzalloc(test, struct_size(ies, data, input->len), GFP_KERNEL); |
813 | ies->len = input->len; |
814 | memcpy(ies->data, input->data, input->len); |
815 | |
816 | count = cfg80211_parse_colocated_ap(ies, list: &coloc_ap_list); |
817 | |
818 | KUNIT_EXPECT_EQ(test, count, params->valid); |
819 | KUNIT_EXPECT_EQ(test, list_count_nodes(&coloc_ap_list), params->valid); |
820 | |
821 | if (params->valid && !list_empty(head: &coloc_ap_list)) { |
822 | struct cfg80211_colocated_ap *ap; |
823 | |
824 | ap = list_first_entry(&coloc_ap_list, typeof(*ap), list); |
825 | if (info.tbtt_info_len <= sizeof(params->tbtt_short)) |
826 | KUNIT_EXPECT_MEMEQ(test, ap->bssid, params->tbtt_short.bssid, ETH_ALEN); |
827 | else |
828 | KUNIT_EXPECT_MEMEQ(test, ap->bssid, params->tbtt_long.bssid, ETH_ALEN); |
829 | |
830 | if (params->same_ssid) { |
831 | KUNIT_EXPECT_EQ(test, ap->ssid_len, 4); |
832 | KUNIT_EXPECT_MEMEQ(test, ap->ssid, "TEST" , 4); |
833 | } else { |
834 | KUNIT_EXPECT_EQ(test, ap->ssid_len, 0); |
835 | } |
836 | } |
837 | |
838 | cfg80211_free_coloc_ap_list(coloc_ap_list: &coloc_ap_list); |
839 | } |
840 | |
841 | static struct kunit_case gen_new_ie_test_cases[] = { |
842 | KUNIT_CASE_PARAM(test_gen_new_ie, gen_new_ie_gen_params), |
843 | KUNIT_CASE(test_gen_new_ie_malformed), |
844 | {} |
845 | }; |
846 | |
847 | static struct kunit_suite gen_new_ie = { |
848 | .name = "cfg80211-ie-generation" , |
849 | .test_cases = gen_new_ie_test_cases, |
850 | }; |
851 | |
852 | kunit_test_suite(gen_new_ie); |
853 | |
854 | static struct kunit_case inform_bss_test_cases[] = { |
855 | KUNIT_CASE(test_inform_bss_ssid_only), |
856 | KUNIT_CASE_PARAM(test_inform_bss_ml_sta, inform_bss_ml_sta_gen_params), |
857 | {} |
858 | }; |
859 | |
860 | static struct kunit_suite inform_bss = { |
861 | .name = "cfg80211-inform-bss" , |
862 | .test_cases = inform_bss_test_cases, |
863 | }; |
864 | |
865 | kunit_test_suite(inform_bss); |
866 | |
867 | static struct kunit_case scan_6ghz_cases[] = { |
868 | KUNIT_CASE_PARAM(test_cfg80211_parse_colocated_ap, |
869 | cfg80211_parse_colocated_ap_gen_params), |
870 | {} |
871 | }; |
872 | |
873 | static struct kunit_suite scan_6ghz = { |
874 | .name = "cfg80211-scan-6ghz" , |
875 | .test_cases = scan_6ghz_cases, |
876 | }; |
877 | |
878 | kunit_test_suite(scan_6ghz); |
879 | |