1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 * Copyright 2024 Ryan Zezeski
15 */
16
17 /*
18 * A test module for various mac routines.
19 */
20 #include <inet/ip.h>
21 #include <inet/ip_impl.h>
22 #include <inet/sctp_ip.h>
23 #include <inet/tcp.h>
24 #include <inet/udp_impl.h>
25 #include <sys/dlpi.h>
26 #include <sys/ethernet.h>
27 #include <sys/ktest.h>
28 #include <sys/mac_provider.h>
29 #include <sys/pattr.h>
30 #include <sys/strsun.h>
31
32 typedef mblk_t *(*mac_sw_cksum_t)(mblk_t *, mac_emul_t);
33
34 typedef struct cksum_test_params {
35 mblk_t *ctp_mp;
36 uchar_t *ctp_raw;
37 uint_t ctp_raw_sz;
38 boolean_t ctp_do_partial;
39 boolean_t ctp_do_full;
40 boolean_t ctp_do_ipv4;
41 } cksum_test_params_t;
42
43 /* Arbitrary limit to padding allowed for cksum tests */
44 #define PADDING_MAX 32
45
46 static boolean_t
cksum_test_parse_input(ktest_ctx_hdl_t * ctx,cksum_test_params_t * ctp)47 cksum_test_parse_input(ktest_ctx_hdl_t *ctx, cksum_test_params_t *ctp)
48 {
49 uchar_t *bytes;
50 size_t num_bytes = 0;
51
52 ktest_get_input(ctx, &bytes, &num_bytes);
53 bzero(ctp, sizeof (*ctp));
54
55 nvlist_t *params = NULL;
56 if (nvlist_unpack((char *)bytes, num_bytes, ¶ms, KM_SLEEP) != 0) {
57 KT_ERROR(ctx, "Invalid nvlist input");
58 return (B_FALSE);
59 }
60
61 uchar_t *pkt_bytes;
62 uint_t pkt_sz;
63
64 if (nvlist_lookup_byte_array(params, "pkt_bytes", &pkt_bytes,
65 &pkt_sz) != 0) {
66 KT_ERROR(ctx, "Input missing pkt_bytes field");
67 return (B_FALSE);
68 }
69 if (pkt_sz == 0) {
70 KT_ERROR(ctx, "Packet must not be 0-length");
71 return (B_FALSE);
72 }
73
74 uint32_t padding = 0;
75 (void) nvlist_lookup_uint32(params, "padding", &padding);
76 if (padding & 1) {
77 KT_ERROR(ctx, "padding must be even");
78 return (B_FALSE);
79 } else if (padding > PADDING_MAX) {
80 KT_ERROR(ctx, "padding greater than max of %u", PADDING_MAX);
81 return (B_FALSE);
82 }
83
84 ctp->ctp_do_ipv4 = fnvlist_lookup_boolean(params, "cksum_ipv4");
85 ctp->ctp_do_partial = fnvlist_lookup_boolean(params, "cksum_partial");
86 ctp->ctp_do_full = fnvlist_lookup_boolean(params, "cksum_full");
87
88 if (ctp->ctp_do_partial && ctp->ctp_do_full) {
89 KT_ERROR(ctx, "Cannot request full and partial cksum");
90 return (B_FALSE);
91 }
92
93 mblk_t *mp = allocb(pkt_sz + padding, 0);
94 if (mp == NULL) {
95 KT_ERROR(ctx, "Could not allocate mblk");
96 return (B_FALSE);
97 }
98 if (padding != 0) {
99 bzero(mp->b_rptr, padding);
100 mp->b_rptr += padding;
101 mp->b_wptr += padding;
102 }
103 bcopy(pkt_bytes, mp->b_rptr, pkt_sz);
104 mp->b_wptr += pkt_sz;
105 ctp->ctp_mp = mp;
106
107 ctp->ctp_raw = kmem_alloc(pkt_sz, KM_SLEEP);
108 bcopy(pkt_bytes, ctp->ctp_raw, pkt_sz);
109 ctp->ctp_raw_sz = pkt_sz;
110
111 nvlist_free(params);
112 return (B_TRUE);
113 }
114
115 /* Calculate pseudo-header checksum for a packet */
116 static uint16_t
cksum_calc_pseudo(ktest_ctx_hdl_t * ctx,mblk_t * mp,const mac_ether_offload_info_t * meoi)117 cksum_calc_pseudo(ktest_ctx_hdl_t *ctx, mblk_t *mp,
118 const mac_ether_offload_info_t *meoi)
119 {
120 if ((meoi->meoi_flags & MEOI_L4INFO_SET) == 0) {
121 KT_ERROR(ctx, "MEOI lacks L4 info");
122 return (0);
123 }
124
125 const uint16_t *iphs =
126 (const uint16_t *)(mp->b_rptr + meoi->meoi_l2hlen);
127 uint32_t cksum = 0;
128
129 /* Copied from ip_input_cksum_pseudo_v[46]() */
130 if (meoi->meoi_l3proto == ETHERTYPE_IP) {
131 cksum += iphs[6] + iphs[7] + iphs[8] + iphs[9];
132 } else if (meoi->meoi_l3proto == ETHERTYPE_IPV6) {
133 cksum += iphs[4] + iphs[5] + iphs[6] + iphs[7] +
134 iphs[8] + iphs[9] + iphs[10] + iphs[11] +
135 iphs[12] + iphs[13] + iphs[14] + iphs[15] +
136 iphs[16] + iphs[17] + iphs[18] + iphs[19];
137 } else {
138 KT_ERROR(ctx, "unexpected proto %u", meoi->meoi_l3proto);
139 return (0);
140 }
141
142 switch (meoi->meoi_l4proto) {
143 case IPPROTO_TCP:
144 cksum += IP_TCP_CSUM_COMP;
145 break;
146 case IPPROTO_UDP:
147 cksum += IP_UDP_CSUM_COMP;
148 break;
149 case IPPROTO_ICMPV6:
150 cksum += IP_ICMPV6_CSUM_COMP;
151 break;
152 default:
153 KT_ERROR(ctx, "unexpected l4 proto %u", meoi->meoi_l4proto);
154 return (0);
155 }
156
157 uint16_t ulp_len =
158 meoi->meoi_len - ((uint16_t)meoi->meoi_l2hlen + meoi->meoi_l3hlen);
159 if (meoi->meoi_l3proto == ETHERTYPE_IP) {
160 /*
161 * IPv4 packets can fall below the 60-byte minimum for ethernet,
162 * resulting in padding which makes the "easy" means of
163 * determining ULP length potentially inaccurate.
164 *
165 * Reach into the v4 header to make that calculation.
166 */
167 const ipha_t *ipha =
168 (const ipha_t *)(mp->b_rptr + meoi->meoi_l2hlen);
169 ulp_len = ntohs(ipha->ipha_length) - meoi->meoi_l3hlen;
170 }
171
172 cksum += htons(ulp_len);
173
174 cksum = (cksum >> 16) + (cksum & 0xffff);
175 cksum = (cksum >> 16) + (cksum & 0xffff);
176 return (cksum);
177 }
178
179 /*
180 * Verify mac_sw_cksum() emulation against an arbitrary input packet. If the
181 * packet is of a support protocol, any L3 and L4 checksums are cleared, and
182 * then mac_sw_cksum() is called to perform the offload emulation. Afterwards,
183 * the packet is compared to see if it equasl the input, which is assumed to
184 * have correct checksums.
185 *
186 * This can request either emulation via HCK_PARTIALCKSUM or HCK_FULLCKSUM.
187 */
188 static void
mac_sw_cksum_test(ktest_ctx_hdl_t * ctx)189 mac_sw_cksum_test(ktest_ctx_hdl_t *ctx)
190 {
191 ddi_modhandle_t hdl = NULL;
192 mac_sw_cksum_t mac_sw_cksum = NULL;
193
194 if (ktest_hold_mod("mac", &hdl) != 0) {
195 KT_ERROR(ctx, "failed to hold 'mac' module");
196 return;
197 }
198 if (ktest_get_fn(hdl, "mac_sw_cksum", (void **)&mac_sw_cksum) != 0) {
199 KT_ERROR(ctx, "failed to resolve symbol mac`mac_sw_cksum");
200 goto cleanup;
201 }
202
203 cksum_test_params_t ctp;
204 if (!cksum_test_parse_input(ctx, &ctp)) {
205 goto cleanup;
206 }
207 mblk_t *mp = ctp.ctp_mp;
208
209 mac_ether_offload_info_t meoi;
210 mac_ether_offload_info(mp, &meoi);
211
212 if ((meoi.meoi_flags & MEOI_L3INFO_SET) == 0 ||
213 (meoi.meoi_l3proto != ETHERTYPE_IP &&
214 meoi.meoi_l3proto != ETHERTYPE_IPV6)) {
215 KT_SKIP(ctx, "l3 protocol not recognized/supported");
216 goto cleanup;
217 }
218
219 mac_emul_t emul_flags = 0;
220 uint_t hck_flags = 0, hck_start = 0, hck_stuff = 0, hck_end = 0;
221 if (meoi.meoi_l3proto == ETHERTYPE_IP && ctp.ctp_do_ipv4) {
222 ipha_t *ipha = (ipha_t *)(mp->b_rptr + meoi.meoi_l2hlen);
223
224 ipha->ipha_hdr_checksum = 0;
225 emul_flags |= MAC_IPCKSUM_EMUL;
226 hck_flags |= HCK_IPV4_HDRCKSUM;
227 }
228
229 const boolean_t do_l4 = ctp.ctp_do_partial || ctp.ctp_do_full;
230 if ((meoi.meoi_flags & MEOI_L4INFO_SET) != 0 && do_l4) {
231 boolean_t valid_proto = B_FALSE;
232 hck_start = meoi.meoi_l2hlen + meoi.meoi_l3hlen;
233 hck_stuff = hck_start;
234 hck_end = meoi.meoi_len;
235
236 switch (meoi.meoi_l4proto) {
237 case IPPROTO_TCP:
238 hck_stuff += offsetof(tcpha_t, tha_sum);
239 valid_proto = B_TRUE;
240 break;
241 case IPPROTO_UDP:
242 hck_stuff += offsetof(udpha_t, uha_checksum);
243 valid_proto = B_TRUE;
244 break;
245 case IPPROTO_ICMPV6:
246 hck_stuff += offsetof(icmp6_t, icmp6_cksum);
247 valid_proto = B_TRUE;
248 break;
249 case IPPROTO_SCTP:
250 /*
251 * Only full checksums are supported for SCTP, and the
252 * test logic for clearing the existing sum needs to
253 * account for its increased width.
254 */
255 hck_stuff += offsetof(sctp_hdr_t, sh_chksum);
256 if (ctp.ctp_do_full) {
257 uint32_t *lckp =
258 (uint32_t *)(mp->b_rptr + hck_stuff);
259 *lckp = 0;
260 /*
261 * Set the emulation flags ourself, and skip the
262 * logic below by leaving valid_proto as false.
263 */
264 hck_flags |= HCK_FULLCKSUM;
265 emul_flags |= MAC_HWCKSUM_EMUL;
266 }
267 break;
268 default:
269 break;
270 }
271
272 if (valid_proto) {
273 uint16_t *ckp = (uint16_t *)(mp->b_rptr + hck_stuff);
274
275 emul_flags |= MAC_HWCKSUM_EMUL;
276 if (ctp.ctp_do_partial) {
277 hck_flags |= HCK_PARTIALCKSUM;
278 /* Populate L4 pseudo-header cksum */
279 *ckp = cksum_calc_pseudo(ctx, mp, &meoi);
280 } else {
281 hck_flags |= HCK_FULLCKSUM;
282 /* Zero out the L4 cksum */
283 *ckp = 0;
284 }
285 }
286 }
287 if (do_l4 && (hck_flags & (HCK_FULLCKSUM|HCK_PARTIALCKSUM)) == 0) {
288 KT_SKIP(ctx, "L4 checksum not support for packet");
289 goto cleanup;
290 }
291
292 if (emul_flags != 0) {
293 if ((hck_flags & HCK_PARTIALCKSUM) == 0) {
294 hck_start = hck_stuff = hck_end = 0;
295 } else {
296 /*
297 * The offsets for mac_hcksum_set are all relative to
298 * the start of the L3 header. Prior to here, these
299 * values were relative to the start of the packet.
300 */
301 hck_start -= meoi.meoi_l2hlen;
302 hck_stuff -= meoi.meoi_l2hlen;
303 hck_end -= meoi.meoi_l2hlen;
304 }
305 mac_hcksum_set(mp, hck_start, hck_stuff, hck_end, 0, hck_flags);
306 ctp.ctp_mp = mp = mac_sw_cksum(mp, emul_flags);
307
308 KT_ASSERT3UG(mp, !=, NULL, ctx, cleanup);
309 const uint_t input_pkt_sz = ctp.ctp_raw_sz;
310 KT_ASSERT3UG(MBLKL(mp), ==, input_pkt_sz, ctx, cleanup);
311
312 for (uint_t i = 0; i < input_pkt_sz; i++) {
313 /*
314 * If there is any mismatch from the expected output,
315 * print the contents of the first "bad" byte.
316 */
317 if (mp->b_rptr[i] != ctp.ctp_raw[i]) {
318 if (i + 2 < input_pkt_sz) {
319 KT_FAIL(ctx, "mp[%x] %04X != %04X",
320 i,
321 *(uint16_t *)&mp->b_rptr[i],
322 *(uint16_t *)&ctp.ctp_raw[i]);
323 } else {
324 KT_FAIL(ctx, "mp[%x] %02X != %02X",
325 i, mp->b_rptr[i], ctp.ctp_raw[i]);
326 }
327 goto cleanup;
328 }
329 }
330 } else {
331 KT_SKIP(ctx, "no checksums supported for packet");
332 goto cleanup;
333 }
334
335 KT_PASS(ctx);
336
337 cleanup:
338 if (hdl != NULL) {
339 ktest_release_mod(hdl);
340 }
341 if (ctp.ctp_mp != NULL) {
342 freeb(ctp.ctp_mp);
343 }
344 if (ctp.ctp_raw != NULL) {
345 kmem_free(ctp.ctp_raw, ctp.ctp_raw_sz);
346 }
347 }
348
349 typedef struct meoi_test_params {
350 mblk_t *mtp_mp;
351 mac_ether_offload_info_t mtp_partial;
352 mac_ether_offload_info_t mtp_results;
353 uint_t mtp_offset;
354 } meoi_test_params_t;
355
356 static void
nvlist_to_meoi(nvlist_t * results,mac_ether_offload_info_t * meoi)357 nvlist_to_meoi(nvlist_t *results, mac_ether_offload_info_t *meoi)
358 {
359 uint64_t u64_val;
360 int int_val;
361 uint16_t u16_val;
362 uint8_t u8_val;
363
364 bzero(meoi, sizeof (*meoi));
365 if (nvlist_lookup_int32(results, "meoi_flags", &int_val) == 0) {
366 meoi->meoi_flags = int_val;
367 }
368 if (nvlist_lookup_uint64(results, "meoi_len", &u64_val) == 0) {
369 meoi->meoi_len = u64_val;
370 }
371 if (nvlist_lookup_uint8(results, "meoi_l2hlen", &u8_val) == 0) {
372 meoi->meoi_l2hlen = u8_val;
373 }
374 if (nvlist_lookup_uint16(results, "meoi_l3proto", &u16_val) == 0) {
375 meoi->meoi_l3proto = u16_val;
376 }
377 if (nvlist_lookup_uint16(results, "meoi_l3hlen", &u16_val) == 0) {
378 meoi->meoi_l3hlen = u16_val;
379 }
380 if (nvlist_lookup_uint8(results, "meoi_l4proto", &u8_val) == 0) {
381 meoi->meoi_l4proto = u8_val;
382 }
383 if (nvlist_lookup_uint8(results, "meoi_l4hlen", &u8_val) == 0) {
384 meoi->meoi_l4hlen = u8_val;
385 }
386 }
387
388 static mblk_t *
alloc_split_pkt(ktest_ctx_hdl_t * ctx,nvlist_t * nvl,const char * pkt_field)389 alloc_split_pkt(ktest_ctx_hdl_t *ctx, nvlist_t *nvl, const char *pkt_field)
390 {
391 uchar_t *pkt_bytes;
392 uint_t pkt_sz;
393
394 if (nvlist_lookup_byte_array(nvl, pkt_field, &pkt_bytes,
395 &pkt_sz) != 0) {
396 KT_ERROR(ctx, "Input missing %s field", pkt_field);
397 return (NULL);
398 }
399
400 const uint32_t *splits = NULL;
401 uint_t num_splits = 0;
402 (void) nvlist_lookup_uint32_array(nvl, "splits", (uint32_t **)&splits,
403 &num_splits);
404
405 uint_t split_idx = 0;
406 mblk_t *result = NULL, *tail = NULL;
407
408 do {
409 uint_t block_sz = pkt_sz;
410 if (split_idx < num_splits) {
411 block_sz = MIN(block_sz, splits[split_idx]);
412 }
413
414 mblk_t *mp = allocb(block_sz, 0);
415 if (mp == NULL) {
416 KT_ERROR(ctx, "mblk alloc failure");
417 freemsg(result);
418 return (NULL);
419 }
420
421 if (result == NULL) {
422 result = mp;
423 } else {
424 tail->b_cont = mp;
425 }
426 tail = mp;
427
428 if (block_sz != 0) {
429 bcopy(pkt_bytes, mp->b_wptr, block_sz);
430 mp->b_wptr += block_sz;
431 }
432 pkt_sz -= block_sz;
433 pkt_bytes += block_sz;
434 split_idx++;
435 } while (pkt_sz > 0);
436
437 return (result);
438 }
439
440 /*
441 * mac_ether_offload_info tests expect the following as input (via packed
442 * nvlist)
443 *
444 * - pkt_bytes (byte array): packet bytes to parse
445 * - splits (uint32 array, optional): byte sizes to split packet into mblks
446 * - results (nvlist): mac_ether_offload_info result struct to compare
447 * - Field names and types should match those in the mac_ether_offload_info
448 * struct. Any fields not specified will be assumed to be zero.
449 *
450 * For mac_partial_offload_info tests, two additional fields are parsed:
451 *
452 * - offset (uint32, optional): offset into the packet at which the parsing
453 * should begin
454 * - partial (nvlist): mac_ether_offload_info input struct to be used as
455 * starting point for partial parsing
456 */
457 static boolean_t
meoi_test_parse_input(ktest_ctx_hdl_t * ctx,meoi_test_params_t * mtp,boolean_t test_partial)458 meoi_test_parse_input(ktest_ctx_hdl_t *ctx, meoi_test_params_t *mtp,
459 boolean_t test_partial)
460 {
461 uchar_t *bytes;
462 size_t num_bytes = 0;
463
464 ktest_get_input(ctx, &bytes, &num_bytes);
465 bzero(mtp, sizeof (*mtp));
466
467 nvlist_t *params = NULL;
468 if (nvlist_unpack((char *)bytes, num_bytes, ¶ms, KM_SLEEP) != 0) {
469 KT_ERROR(ctx, "Invalid nvlist input");
470 return (B_FALSE);
471 }
472
473 nvlist_t *results;
474 if (nvlist_lookup_nvlist(params, "results", &results) != 0) {
475 KT_ERROR(ctx, "Input missing results field");
476 nvlist_free(params);
477 return (B_FALSE);
478 }
479
480 if (test_partial) {
481 nvlist_t *partial;
482 if (nvlist_lookup_nvlist(params, "partial", &partial) != 0) {
483 KT_ERROR(ctx, "Input missing partial field");
484 nvlist_free(params);
485 return (B_FALSE);
486 } else {
487 nvlist_to_meoi(partial, &mtp->mtp_partial);
488 }
489
490 (void) nvlist_lookup_uint32(params, "offset", &mtp->mtp_offset);
491 }
492
493 mtp->mtp_mp = alloc_split_pkt(ctx, params, "pkt_bytes");
494 if (mtp->mtp_mp == NULL) {
495 nvlist_free(params);
496 return (B_FALSE);
497 }
498
499 nvlist_to_meoi(results, &mtp->mtp_results);
500
501 nvlist_free(params);
502 return (B_TRUE);
503 }
504
505 void
mac_ether_offload_info_test(ktest_ctx_hdl_t * ctx)506 mac_ether_offload_info_test(ktest_ctx_hdl_t *ctx)
507 {
508 meoi_test_params_t mtp = { 0 };
509
510 if (!meoi_test_parse_input(ctx, &mtp, B_FALSE)) {
511 return;
512 }
513
514 mac_ether_offload_info_t result;
515 mac_ether_offload_info(mtp.mtp_mp, &result);
516
517 const mac_ether_offload_info_t *expect = &mtp.mtp_results;
518 KT_ASSERT3UG(result.meoi_flags, ==, expect->meoi_flags, ctx, done);
519 KT_ASSERT3UG(result.meoi_l2hlen, ==, expect->meoi_l2hlen, ctx, done);
520 KT_ASSERT3UG(result.meoi_l3proto, ==, expect->meoi_l3proto, ctx, done);
521 KT_ASSERT3UG(result.meoi_l3hlen, ==, expect->meoi_l3hlen, ctx, done);
522 KT_ASSERT3UG(result.meoi_l4proto, ==, expect->meoi_l4proto, ctx, done);
523 KT_ASSERT3UG(result.meoi_l4hlen, ==, expect->meoi_l4hlen, ctx, done);
524
525 KT_PASS(ctx);
526
527 done:
528 freemsg(mtp.mtp_mp);
529 }
530
531 void
mac_partial_offload_info_test(ktest_ctx_hdl_t * ctx)532 mac_partial_offload_info_test(ktest_ctx_hdl_t *ctx)
533 {
534 meoi_test_params_t mtp = { 0 };
535
536 if (!meoi_test_parse_input(ctx, &mtp, B_TRUE)) {
537 return;
538 }
539
540 mac_ether_offload_info_t *result = &mtp.mtp_partial;
541 mac_partial_offload_info(mtp.mtp_mp, mtp.mtp_offset, result);
542
543 const mac_ether_offload_info_t *expect = &mtp.mtp_results;
544 KT_ASSERT3UG(result->meoi_flags, ==, expect->meoi_flags, ctx, done);
545 KT_ASSERT3UG(result->meoi_l2hlen, ==, expect->meoi_l2hlen, ctx, done);
546 KT_ASSERT3UG(result->meoi_l3proto, ==, expect->meoi_l3proto, ctx, done);
547 KT_ASSERT3UG(result->meoi_l3hlen, ==, expect->meoi_l3hlen, ctx, done);
548 KT_ASSERT3UG(result->meoi_l4proto, ==, expect->meoi_l4proto, ctx, done);
549 KT_ASSERT3UG(result->meoi_l4hlen, ==, expect->meoi_l4hlen, ctx, done);
550
551 KT_PASS(ctx);
552
553 done:
554 freemsg(mtp.mtp_mp);
555 }
556
557 typedef struct ether_test_params {
558 mblk_t *etp_mp;
559 uint32_t etp_tci;
560 uint8_t etp_dstaddr[ETHERADDRL];
561 boolean_t etp_is_err;
562 } ether_test_params_t;
563
564 /*
565 * mac_ether_l2_info tests expect the following as input (via packed nvlist)
566 *
567 * - pkt_bytes (byte array): packet bytes to parse
568 * - splits (uint32 array, optional): byte sizes to split packet into mblks
569 * - tci (uint32): VLAN TCI result value to compare
570 * - dstaddr (byte array): MAC addr result value to compare
571 * - is_err (boolean): if test function should return error
572 */
573 static boolean_t
ether_parse_input(ktest_ctx_hdl_t * ctx,ether_test_params_t * etp)574 ether_parse_input(ktest_ctx_hdl_t *ctx, ether_test_params_t *etp)
575 {
576 uchar_t *bytes;
577 size_t num_bytes = 0;
578
579 ktest_get_input(ctx, &bytes, &num_bytes);
580 bzero(etp, sizeof (*etp));
581
582 nvlist_t *params = NULL;
583 if (nvlist_unpack((char *)bytes, num_bytes, ¶ms, KM_SLEEP) != 0) {
584 KT_ERROR(ctx, "Invalid nvlist input");
585 return (B_FALSE);
586 }
587
588 etp->etp_mp = alloc_split_pkt(ctx, params, "pkt_bytes");
589 if (etp->etp_mp == NULL) {
590 nvlist_free(params);
591 return (B_FALSE);
592 }
593
594 if (nvlist_lookup_uint32(params, "tci", &etp->etp_tci) != 0) {
595 KT_ERROR(ctx, "Input missing tci field");
596 nvlist_free(params);
597 return (B_FALSE);
598 }
599
600 uchar_t *dstaddr;
601 uint_t dstaddr_sz;
602 if (nvlist_lookup_byte_array(params, "dstaddr", &dstaddr,
603 &dstaddr_sz) != 0) {
604 KT_ERROR(ctx, "Input missing dstaddr field");
605 nvlist_free(params);
606 return (B_FALSE);
607 } else if (dstaddr_sz != ETHERADDRL) {
608 KT_ERROR(ctx, "bad dstaddr size %u != %u", dstaddr_sz,
609 ETHERADDRL);
610 nvlist_free(params);
611 return (B_FALSE);
612 }
613 bcopy(dstaddr, &etp->etp_dstaddr, ETHERADDRL);
614
615 etp->etp_is_err = nvlist_lookup_boolean(params, "is_err") == 0;
616
617 nvlist_free(params);
618 return (B_TRUE);
619 }
620
621 void
mac_ether_l2_info_test(ktest_ctx_hdl_t * ctx)622 mac_ether_l2_info_test(ktest_ctx_hdl_t *ctx)
623 {
624 ether_test_params_t etp = { 0 };
625
626 if (!ether_parse_input(ctx, &etp)) {
627 return;
628 }
629
630 uint8_t dstaddr[ETHERADDRL];
631 uint32_t vlan_tci = 0;
632 const boolean_t is_err =
633 !mac_ether_l2_info(etp.etp_mp, dstaddr, &vlan_tci);
634
635 KT_ASSERTG(is_err == etp.etp_is_err, ctx, done);
636 KT_ASSERTG(bcmp(dstaddr, etp.etp_dstaddr, ETHERADDRL) == 0, ctx,
637 done);
638 KT_ASSERT3UG(vlan_tci, ==, etp.etp_tci, ctx, done);
639
640 KT_PASS(ctx);
641
642 done:
643 freemsg(etp.etp_mp);
644 }
645
646
647 static struct modlmisc mac_ktest_modlmisc = {
648 .misc_modops = &mod_miscops,
649 .misc_linkinfo = "mac ktest module"
650 };
651
652 static struct modlinkage mac_ktest_modlinkage = {
653 .ml_rev = MODREV_1,
654 .ml_linkage = { &mac_ktest_modlmisc, NULL }
655 };
656
657 int
_init()658 _init()
659 {
660 int ret;
661 ktest_module_hdl_t *km = NULL;
662 ktest_suite_hdl_t *ks = NULL;
663
664 VERIFY0(ktest_create_module("mac", &km));
665 VERIFY0(ktest_add_suite(km, "checksum", &ks));
666 VERIFY0(ktest_add_test(ks, "mac_sw_cksum_test",
667 mac_sw_cksum_test, KTEST_FLAG_INPUT));
668
669 ks = NULL;
670 VERIFY0(ktest_add_suite(km, "parsing", &ks));
671 VERIFY0(ktest_add_test(ks, "mac_ether_offload_info_test",
672 mac_ether_offload_info_test, KTEST_FLAG_INPUT));
673 VERIFY0(ktest_add_test(ks, "mac_partial_offload_info_test",
674 mac_partial_offload_info_test, KTEST_FLAG_INPUT));
675 VERIFY0(ktest_add_test(ks, "mac_ether_l2_info_test",
676 mac_ether_l2_info_test, KTEST_FLAG_INPUT));
677
678 if ((ret = ktest_register_module(km)) != 0) {
679 ktest_free_module(km);
680 return (ret);
681 }
682
683 if ((ret = mod_install(&mac_ktest_modlinkage)) != 0) {
684 ktest_unregister_module("mac");
685 return (ret);
686 }
687
688 return (0);
689 }
690
691 int
_fini(void)692 _fini(void)
693 {
694 ktest_unregister_module("mac");
695 return (mod_remove(&mac_ktest_modlinkage));
696 }
697
698 int
_info(struct modinfo * modinfop)699 _info(struct modinfo *modinfop)
700 {
701 return (mod_info(&mac_ktest_modlinkage, modinfop));
702 }
703