xref: /linux/net/mac80211/tests/elems.c (revision 3e9201e4fe8bd78f4601a51212562505bbb60e3a)
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