Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2
3#include <kunit/test.h>
4
5#include "utils.h"
6
7struct mctp_test_route {
8 struct mctp_route rt;
9 struct sk_buff_head pkts;
10};
11
12static int mctp_test_route_output(struct mctp_route *rt, struct sk_buff *skb)
13{
14 struct mctp_test_route *test_rt = container_of(rt, struct mctp_test_route, rt);
15
16 skb_queue_tail(&test_rt->pkts, skb);
17
18 return 0;
19}
20
21/* local version of mctp_route_alloc() */
22static struct mctp_test_route *mctp_route_test_alloc(void)
23{
24 struct mctp_test_route *rt;
25
26 rt = kzalloc(sizeof(*rt), GFP_KERNEL);
27 if (!rt)
28 return NULL;
29
30 INIT_LIST_HEAD(&rt->rt.list);
31 refcount_set(&rt->rt.refs, 1);
32 rt->rt.output = mctp_test_route_output;
33
34 skb_queue_head_init(&rt->pkts);
35
36 return rt;
37}
38
39static struct mctp_test_route *mctp_test_create_route(struct net *net,
40 struct mctp_dev *dev,
41 mctp_eid_t eid,
42 unsigned int mtu)
43{
44 struct mctp_test_route *rt;
45
46 rt = mctp_route_test_alloc();
47 if (!rt)
48 return NULL;
49
50 rt->rt.min = eid;
51 rt->rt.max = eid;
52 rt->rt.mtu = mtu;
53 rt->rt.type = RTN_UNSPEC;
54 if (dev)
55 mctp_dev_hold(dev);
56 rt->rt.dev = dev;
57
58 list_add_rcu(&rt->rt.list, &net->mctp.routes);
59
60 return rt;
61}
62
63static void mctp_test_route_destroy(struct kunit *test,
64 struct mctp_test_route *rt)
65{
66 unsigned int refs;
67
68 rtnl_lock();
69 list_del_rcu(&rt->rt.list);
70 rtnl_unlock();
71
72 skb_queue_purge(&rt->pkts);
73 if (rt->rt.dev)
74 mctp_dev_put(rt->rt.dev);
75
76 refs = refcount_read(&rt->rt.refs);
77 KUNIT_ASSERT_EQ_MSG(test, refs, 1, "route ref imbalance");
78
79 kfree_rcu(&rt->rt, rcu);
80}
81
82static struct sk_buff *mctp_test_create_skb(const struct mctp_hdr *hdr,
83 unsigned int data_len)
84{
85 size_t hdr_len = sizeof(*hdr);
86 struct sk_buff *skb;
87 unsigned int i;
88 u8 *buf;
89
90 skb = alloc_skb(hdr_len + data_len, GFP_KERNEL);
91 if (!skb)
92 return NULL;
93
94 memcpy(skb_put(skb, hdr_len), hdr, hdr_len);
95
96 buf = skb_put(skb, data_len);
97 for (i = 0; i < data_len; i++)
98 buf[i] = i & 0xff;
99
100 return skb;
101}
102
103static struct sk_buff *__mctp_test_create_skb_data(const struct mctp_hdr *hdr,
104 const void *data,
105 size_t data_len)
106{
107 size_t hdr_len = sizeof(*hdr);
108 struct sk_buff *skb;
109
110 skb = alloc_skb(hdr_len + data_len, GFP_KERNEL);
111 if (!skb)
112 return NULL;
113
114 memcpy(skb_put(skb, hdr_len), hdr, hdr_len);
115 memcpy(skb_put(skb, data_len), data, data_len);
116
117 return skb;
118}
119
120#define mctp_test_create_skb_data(h, d) \
121 __mctp_test_create_skb_data(h, d, sizeof(*d))
122
123struct mctp_frag_test {
124 unsigned int mtu;
125 unsigned int msgsize;
126 unsigned int n_frags;
127};
128
129static void mctp_test_fragment(struct kunit *test)
130{
131 const struct mctp_frag_test *params;
132 int rc, i, n, mtu, msgsize;
133 struct mctp_test_route *rt;
134 struct sk_buff *skb;
135 struct mctp_hdr hdr;
136 u8 seq;
137
138 params = test->param_value;
139 mtu = params->mtu;
140 msgsize = params->msgsize;
141
142 hdr.ver = 1;
143 hdr.src = 8;
144 hdr.dest = 10;
145 hdr.flags_seq_tag = MCTP_HDR_FLAG_TO;
146
147 skb = mctp_test_create_skb(&hdr, msgsize);
148 KUNIT_ASSERT_TRUE(test, skb);
149
150 rt = mctp_test_create_route(&init_net, NULL, 10, mtu);
151 KUNIT_ASSERT_TRUE(test, rt);
152
153 rc = mctp_do_fragment_route(&rt->rt, skb, mtu, MCTP_TAG_OWNER);
154 KUNIT_EXPECT_FALSE(test, rc);
155
156 n = rt->pkts.qlen;
157
158 KUNIT_EXPECT_EQ(test, n, params->n_frags);
159
160 for (i = 0;; i++) {
161 struct mctp_hdr *hdr2;
162 struct sk_buff *skb2;
163 u8 tag_mask, seq2;
164 bool first, last;
165
166 first = i == 0;
167 last = i == (n - 1);
168
169 skb2 = skb_dequeue(&rt->pkts);
170
171 if (!skb2)
172 break;
173
174 hdr2 = mctp_hdr(skb2);
175
176 tag_mask = MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO;
177
178 KUNIT_EXPECT_EQ(test, hdr2->ver, hdr.ver);
179 KUNIT_EXPECT_EQ(test, hdr2->src, hdr.src);
180 KUNIT_EXPECT_EQ(test, hdr2->dest, hdr.dest);
181 KUNIT_EXPECT_EQ(test, hdr2->flags_seq_tag & tag_mask,
182 hdr.flags_seq_tag & tag_mask);
183
184 KUNIT_EXPECT_EQ(test,
185 !!(hdr2->flags_seq_tag & MCTP_HDR_FLAG_SOM), first);
186 KUNIT_EXPECT_EQ(test,
187 !!(hdr2->flags_seq_tag & MCTP_HDR_FLAG_EOM), last);
188
189 seq2 = (hdr2->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) &
190 MCTP_HDR_SEQ_MASK;
191
192 if (first) {
193 seq = seq2;
194 } else {
195 seq++;
196 KUNIT_EXPECT_EQ(test, seq2, seq & MCTP_HDR_SEQ_MASK);
197 }
198
199 if (!last)
200 KUNIT_EXPECT_EQ(test, skb2->len, mtu);
201 else
202 KUNIT_EXPECT_LE(test, skb2->len, mtu);
203
204 kfree_skb(skb2);
205 }
206
207 mctp_test_route_destroy(test, rt);
208}
209
210static const struct mctp_frag_test mctp_frag_tests[] = {
211 {.mtu = 68, .msgsize = 63, .n_frags = 1},
212 {.mtu = 68, .msgsize = 64, .n_frags = 1},
213 {.mtu = 68, .msgsize = 65, .n_frags = 2},
214 {.mtu = 68, .msgsize = 66, .n_frags = 2},
215 {.mtu = 68, .msgsize = 127, .n_frags = 2},
216 {.mtu = 68, .msgsize = 128, .n_frags = 2},
217 {.mtu = 68, .msgsize = 129, .n_frags = 3},
218 {.mtu = 68, .msgsize = 130, .n_frags = 3},
219};
220
221static void mctp_frag_test_to_desc(const struct mctp_frag_test *t, char *desc)
222{
223 sprintf(desc, "mtu %d len %d -> %d frags",
224 t->msgsize, t->mtu, t->n_frags);
225}
226
227KUNIT_ARRAY_PARAM(mctp_frag, mctp_frag_tests, mctp_frag_test_to_desc);
228
229struct mctp_rx_input_test {
230 struct mctp_hdr hdr;
231 bool input;
232};
233
234static void mctp_test_rx_input(struct kunit *test)
235{
236 const struct mctp_rx_input_test *params;
237 struct mctp_test_route *rt;
238 struct mctp_test_dev *dev;
239 struct sk_buff *skb;
240
241 params = test->param_value;
242
243 dev = mctp_test_create_dev();
244 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
245
246 rt = mctp_test_create_route(&init_net, dev->mdev, 8, 68);
247 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt);
248
249 skb = mctp_test_create_skb(¶ms->hdr, 1);
250 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
251
252 __mctp_cb(skb);
253
254 mctp_pkttype_receive(skb, dev->ndev, &mctp_packet_type, NULL);
255
256 KUNIT_EXPECT_EQ(test, !!rt->pkts.qlen, params->input);
257
258 mctp_test_route_destroy(test, rt);
259 mctp_test_destroy_dev(dev);
260}
261
262#define RX_HDR(_ver, _src, _dest, _fst) \
263 { .ver = _ver, .src = _src, .dest = _dest, .flags_seq_tag = _fst }
264
265/* we have a route for EID 8 only */
266static const struct mctp_rx_input_test mctp_rx_input_tests[] = {
267 { .hdr = RX_HDR(1, 10, 8, 0), .input = true },
268 { .hdr = RX_HDR(1, 10, 9, 0), .input = false }, /* no input route */
269 { .hdr = RX_HDR(2, 10, 8, 0), .input = false }, /* invalid version */
270};
271
272static void mctp_rx_input_test_to_desc(const struct mctp_rx_input_test *t,
273 char *desc)
274{
275 sprintf(desc, "{%x,%x,%x,%x}", t->hdr.ver, t->hdr.src, t->hdr.dest,
276 t->hdr.flags_seq_tag);
277}
278
279KUNIT_ARRAY_PARAM(mctp_rx_input, mctp_rx_input_tests,
280 mctp_rx_input_test_to_desc);
281
282/* set up a local dev, route on EID 8, and a socket listening on type 0 */
283static void __mctp_route_test_init(struct kunit *test,
284 struct mctp_test_dev **devp,
285 struct mctp_test_route **rtp,
286 struct socket **sockp)
287{
288 struct sockaddr_mctp addr = {0};
289 struct mctp_test_route *rt;
290 struct mctp_test_dev *dev;
291 struct socket *sock;
292 int rc;
293
294 dev = mctp_test_create_dev();
295 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
296
297 rt = mctp_test_create_route(&init_net, dev->mdev, 8, 68);
298 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rt);
299
300 rc = sock_create_kern(&init_net, AF_MCTP, SOCK_DGRAM, 0, &sock);
301 KUNIT_ASSERT_EQ(test, rc, 0);
302
303 addr.smctp_family = AF_MCTP;
304 addr.smctp_network = MCTP_NET_ANY;
305 addr.smctp_addr.s_addr = 8;
306 addr.smctp_type = 0;
307 rc = kernel_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
308 KUNIT_ASSERT_EQ(test, rc, 0);
309
310 *rtp = rt;
311 *devp = dev;
312 *sockp = sock;
313}
314
315static void __mctp_route_test_fini(struct kunit *test,
316 struct mctp_test_dev *dev,
317 struct mctp_test_route *rt,
318 struct socket *sock)
319{
320 sock_release(sock);
321 mctp_test_route_destroy(test, rt);
322 mctp_test_destroy_dev(dev);
323}
324
325struct mctp_route_input_sk_test {
326 struct mctp_hdr hdr;
327 u8 type;
328 bool deliver;
329};
330
331static void mctp_test_route_input_sk(struct kunit *test)
332{
333 const struct mctp_route_input_sk_test *params;
334 struct sk_buff *skb, *skb2;
335 struct mctp_test_route *rt;
336 struct mctp_test_dev *dev;
337 struct socket *sock;
338 int rc;
339
340 params = test->param_value;
341
342 __mctp_route_test_init(test, &dev, &rt, &sock);
343
344 skb = mctp_test_create_skb_data(¶ms->hdr, ¶ms->type);
345 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
346
347 skb->dev = dev->ndev;
348 __mctp_cb(skb);
349
350 rc = mctp_route_input(&rt->rt, skb);
351
352 if (params->deliver) {
353 KUNIT_EXPECT_EQ(test, rc, 0);
354
355 skb2 = skb_recv_datagram(sock->sk, 0, 1, &rc);
356 KUNIT_EXPECT_NOT_ERR_OR_NULL(test, skb2);
357 KUNIT_EXPECT_EQ(test, skb->len, 1);
358
359 skb_free_datagram(sock->sk, skb2);
360
361 } else {
362 KUNIT_EXPECT_NE(test, rc, 0);
363 skb2 = skb_recv_datagram(sock->sk, 0, 1, &rc);
364 KUNIT_EXPECT_PTR_EQ(test, skb2, NULL);
365 }
366
367 __mctp_route_test_fini(test, dev, rt, sock);
368}
369
370#define FL_S (MCTP_HDR_FLAG_SOM)
371#define FL_E (MCTP_HDR_FLAG_EOM)
372#define FL_T (MCTP_HDR_FLAG_TO)
373
374static const struct mctp_route_input_sk_test mctp_route_input_sk_tests[] = {
375 { .hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_T), .type = 0, .deliver = true },
376 { .hdr = RX_HDR(1, 10, 8, FL_S | FL_E | FL_T), .type = 1, .deliver = false },
377 { .hdr = RX_HDR(1, 10, 8, FL_S | FL_E), .type = 0, .deliver = false },
378 { .hdr = RX_HDR(1, 10, 8, FL_E | FL_T), .type = 0, .deliver = false },
379 { .hdr = RX_HDR(1, 10, 8, FL_T), .type = 0, .deliver = false },
380 { .hdr = RX_HDR(1, 10, 8, 0), .type = 0, .deliver = false },
381};
382
383static void mctp_route_input_sk_to_desc(const struct mctp_route_input_sk_test *t,
384 char *desc)
385{
386 sprintf(desc, "{%x,%x,%x,%x} type %d", t->hdr.ver, t->hdr.src,
387 t->hdr.dest, t->hdr.flags_seq_tag, t->type);
388}
389
390KUNIT_ARRAY_PARAM(mctp_route_input_sk, mctp_route_input_sk_tests,
391 mctp_route_input_sk_to_desc);
392
393struct mctp_route_input_sk_reasm_test {
394 const char *name;
395 struct mctp_hdr hdrs[4];
396 int n_hdrs;
397 int rx_len;
398};
399
400static void mctp_test_route_input_sk_reasm(struct kunit *test)
401{
402 const struct mctp_route_input_sk_reasm_test *params;
403 struct sk_buff *skb, *skb2;
404 struct mctp_test_route *rt;
405 struct mctp_test_dev *dev;
406 struct socket *sock;
407 int i, rc;
408 u8 c;
409
410 params = test->param_value;
411
412 __mctp_route_test_init(test, &dev, &rt, &sock);
413
414 for (i = 0; i < params->n_hdrs; i++) {
415 c = i;
416 skb = mctp_test_create_skb_data(¶ms->hdrs[i], &c);
417 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, skb);
418
419 skb->dev = dev->ndev;
420 __mctp_cb(skb);
421
422 rc = mctp_route_input(&rt->rt, skb);
423 }
424
425 skb2 = skb_recv_datagram(sock->sk, 0, 1, &rc);
426
427 if (params->rx_len) {
428 KUNIT_EXPECT_NOT_ERR_OR_NULL(test, skb2);
429 KUNIT_EXPECT_EQ(test, skb2->len, params->rx_len);
430 skb_free_datagram(sock->sk, skb2);
431
432 } else {
433 KUNIT_EXPECT_PTR_EQ(test, skb2, NULL);
434 }
435
436 __mctp_route_test_fini(test, dev, rt, sock);
437}
438
439#define RX_FRAG(f, s) RX_HDR(1, 10, 8, FL_T | (f) | ((s) << MCTP_HDR_SEQ_SHIFT))
440
441static const struct mctp_route_input_sk_reasm_test mctp_route_input_sk_reasm_tests[] = {
442 {
443 .name = "single packet",
444 .hdrs = {
445 RX_FRAG(FL_S | FL_E, 0),
446 },
447 .n_hdrs = 1,
448 .rx_len = 1,
449 },
450 {
451 .name = "single packet, offset seq",
452 .hdrs = {
453 RX_FRAG(FL_S | FL_E, 1),
454 },
455 .n_hdrs = 1,
456 .rx_len = 1,
457 },
458 {
459 .name = "start & end packets",
460 .hdrs = {
461 RX_FRAG(FL_S, 0),
462 RX_FRAG(FL_E, 1),
463 },
464 .n_hdrs = 2,
465 .rx_len = 2,
466 },
467 {
468 .name = "start & end packets, offset seq",
469 .hdrs = {
470 RX_FRAG(FL_S, 1),
471 RX_FRAG(FL_E, 2),
472 },
473 .n_hdrs = 2,
474 .rx_len = 2,
475 },
476 {
477 .name = "start & end packets, out of order",
478 .hdrs = {
479 RX_FRAG(FL_E, 1),
480 RX_FRAG(FL_S, 0),
481 },
482 .n_hdrs = 2,
483 .rx_len = 0,
484 },
485 {
486 .name = "start, middle & end packets",
487 .hdrs = {
488 RX_FRAG(FL_S, 0),
489 RX_FRAG(0, 1),
490 RX_FRAG(FL_E, 2),
491 },
492 .n_hdrs = 3,
493 .rx_len = 3,
494 },
495 {
496 .name = "missing seq",
497 .hdrs = {
498 RX_FRAG(FL_S, 0),
499 RX_FRAG(FL_E, 2),
500 },
501 .n_hdrs = 2,
502 .rx_len = 0,
503 },
504 {
505 .name = "seq wrap",
506 .hdrs = {
507 RX_FRAG(FL_S, 3),
508 RX_FRAG(FL_E, 0),
509 },
510 .n_hdrs = 2,
511 .rx_len = 2,
512 },
513};
514
515static void mctp_route_input_sk_reasm_to_desc(
516 const struct mctp_route_input_sk_reasm_test *t,
517 char *desc)
518{
519 sprintf(desc, "%s", t->name);
520}
521
522KUNIT_ARRAY_PARAM(mctp_route_input_sk_reasm, mctp_route_input_sk_reasm_tests,
523 mctp_route_input_sk_reasm_to_desc);
524
525static struct kunit_case mctp_test_cases[] = {
526 KUNIT_CASE_PARAM(mctp_test_fragment, mctp_frag_gen_params),
527 KUNIT_CASE_PARAM(mctp_test_rx_input, mctp_rx_input_gen_params),
528 KUNIT_CASE_PARAM(mctp_test_route_input_sk, mctp_route_input_sk_gen_params),
529 KUNIT_CASE_PARAM(mctp_test_route_input_sk_reasm,
530 mctp_route_input_sk_reasm_gen_params),
531 {}
532};
533
534static struct kunit_suite mctp_test_suite = {
535 .name = "mctp",
536 .test_cases = mctp_test_cases,
537};
538
539kunit_test_suite(mctp_test_suite);