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 Compnay 14 */ 15 16 /* 17 * Driver for mac ktests 18 * 19 * This generates input payloads for the packet-parsing tests in the mac_test 20 * module. Prior to calling this program, that module (`mac_test`) must be 21 * loaded so we can execute those tests with our payloads. Since that manual 22 * step of loading the module is required, this test is currently omitted from 23 * the default runfile. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <stdbool.h> 29 #include <unistd.h> 30 #include <fcntl.h> 31 #include <strings.h> 32 #include <spawn.h> 33 #include <wait.h> 34 #include <errno.h> 35 #include <err.h> 36 #include <sys/debug.h> 37 #include <sys/sysmacros.h> 38 39 #include <libnvpair.h> 40 #include <libktest.h> 41 #include <sys/ethernet.h> 42 #include <netinet/in.h> 43 #include <netinet/ip.h> 44 #include <netinet/ip6.h> 45 #include <netinet/tcp.h> 46 47 static ktest_hdl_t *kthdl = NULL; 48 49 /* 50 * Clones of in-kernel types to specify desired results. 51 * N.B. These must be kept in sync with those in mac_provider.h 52 */ 53 typedef enum mac_ether_offload_flags { 54 MEOI_L2INFO_SET = 1 << 0, 55 MEOI_L3INFO_SET = 1 << 1, 56 MEOI_L4INFO_SET = 1 << 2, 57 MEOI_VLAN_TAGGED = 1 << 3, 58 MEOI_L3_FRAGMENT = 1 << 4 59 } mac_ether_offload_flags_t; 60 61 typedef struct mac_ether_offload_info { 62 mac_ether_offload_flags_t meoi_flags; /* What's valid? */ 63 size_t meoi_len; /* Total message length */ 64 uint8_t meoi_l2hlen; /* How long is the Ethernet header? */ 65 uint16_t meoi_l3proto; /* What's the Ethertype */ 66 uint16_t meoi_l3hlen; /* How long is the header? */ 67 uint8_t meoi_l4proto; /* What is the payload type? */ 68 uint8_t meoi_l4hlen; /* How long is the L4 header */ 69 } mac_ether_offload_info_t; 70 71 72 typedef struct test_pkt { 73 size_t tp_sz; 74 uint8_t *tp_bytes; 75 } test_pkt_t; 76 77 static test_pkt_t * 78 tp_alloc(void) 79 { 80 void *buf = calloc(1, sizeof (test_pkt_t)); 81 VERIFY(buf != NULL); 82 return (buf); 83 } 84 85 static void 86 tp_free(test_pkt_t *tp) 87 { 88 if (tp->tp_bytes != NULL) { 89 free(tp->tp_bytes); 90 } 91 free(tp); 92 } 93 94 static void 95 tp_append(test_pkt_t *tp, const void *bytes, size_t sz) 96 { 97 if (tp->tp_bytes == NULL) { 98 VERIFY(tp->tp_sz == 0); 99 100 tp->tp_bytes = malloc(sz); 101 VERIFY(tp->tp_bytes != NULL); 102 bcopy(bytes, tp->tp_bytes, sz); 103 tp->tp_sz = sz; 104 } else { 105 const size_t new_sz = tp->tp_sz + sz; 106 107 tp->tp_bytes = realloc(tp->tp_bytes, new_sz); 108 VERIFY(tp->tp_bytes != NULL); 109 bcopy(bytes, &tp->tp_bytes[tp->tp_sz], sz); 110 tp->tp_sz = new_sz; 111 } 112 } 113 114 static void 115 append_ether(test_pkt_t *tp, uint16_t ethertype) 116 { 117 struct ether_header hdr_ether = { 118 .ether_type = htons(ethertype), 119 }; 120 121 tp_append(tp, &hdr_ether, sizeof (hdr_ether)); 122 } 123 124 static void 125 append_ip4(test_pkt_t *tp, uint8_t ipproto) 126 { 127 struct ip hdr_ip = { 128 .ip_v = 4, 129 .ip_hl = 5, 130 .ip_p = ipproto, 131 }; 132 133 tp_append(tp, &hdr_ip, sizeof (hdr_ip)); 134 } 135 136 static void 137 append_ip6(test_pkt_t *tp, uint8_t ipproto) 138 { 139 struct ip6_hdr hdr_ip6 = { 0 }; 140 hdr_ip6.ip6_vfc = 0x60; 141 hdr_ip6.ip6_nxt = ipproto; 142 143 tp_append(tp, &hdr_ip6, sizeof (hdr_ip6)); 144 } 145 146 static void 147 append_tcp(test_pkt_t *tp) 148 { 149 struct tcphdr hdr_tcp = { 150 .th_off = 5 151 }; 152 tp_append(tp, &hdr_tcp, sizeof (hdr_tcp)); 153 } 154 155 static test_pkt_t * 156 build_tcp4(mac_ether_offload_info_t *meoi) 157 { 158 test_pkt_t *tp = tp_alloc(); 159 append_ether(tp, ETHERTYPE_IP); 160 append_ip4(tp, IPPROTO_TCP); 161 append_tcp(tp); 162 163 mac_ether_offload_info_t expected = { 164 .meoi_flags = 165 MEOI_L2INFO_SET | MEOI_L3INFO_SET | MEOI_L4INFO_SET, 166 .meoi_len = tp->tp_sz, 167 .meoi_l2hlen = sizeof (struct ether_header), 168 .meoi_l3proto = ETHERTYPE_IP, 169 .meoi_l3hlen = sizeof (struct ip), 170 .meoi_l4proto = IPPROTO_TCP, 171 .meoi_l4hlen = sizeof (struct tcphdr), 172 }; 173 *meoi = expected; 174 175 return (tp); 176 } 177 178 static test_pkt_t * 179 build_tcp6(mac_ether_offload_info_t *meoi) 180 { 181 test_pkt_t *tp = tp_alloc(); 182 append_ether(tp, ETHERTYPE_IPV6); 183 append_ip6(tp, IPPROTO_TCP); 184 append_tcp(tp); 185 186 mac_ether_offload_info_t expected = { 187 .meoi_flags = 188 MEOI_L2INFO_SET | MEOI_L3INFO_SET | MEOI_L4INFO_SET, 189 .meoi_len = tp->tp_sz, 190 .meoi_l2hlen = sizeof (struct ether_header), 191 .meoi_l3proto = ETHERTYPE_IPV6, 192 .meoi_l3hlen = sizeof (struct ip6_hdr), 193 .meoi_l4proto = IPPROTO_TCP, 194 .meoi_l4hlen = sizeof (struct tcphdr), 195 }; 196 *meoi = expected; 197 198 return (tp); 199 } 200 201 static test_pkt_t * 202 build_frag_v4(mac_ether_offload_info_t *meoi) 203 { 204 test_pkt_t *tp = tp_alloc(); 205 append_ether(tp, ETHERTYPE_IP); 206 207 struct ip hdr_ip = { 208 .ip_v = 4, 209 .ip_hl = 5, 210 .ip_off = IP_MF, 211 .ip_p = IPPROTO_TCP, 212 }; 213 tp_append(tp, &hdr_ip, sizeof (hdr_ip)); 214 215 append_tcp(tp); 216 217 mac_ether_offload_info_t expected = { 218 .meoi_flags = MEOI_L2INFO_SET | MEOI_L3INFO_SET | 219 MEOI_L4INFO_SET | MEOI_L3_FRAGMENT, 220 .meoi_l2hlen = sizeof (struct ether_header), 221 .meoi_l3hlen = sizeof (struct ip), 222 .meoi_l4hlen = sizeof (struct tcphdr), 223 .meoi_l3proto = ETHERTYPE_IP, 224 .meoi_l4proto = IPPROTO_TCP 225 }; 226 *meoi = expected; 227 228 return (tp); 229 } 230 231 static test_pkt_t * 232 build_frag_v6(mac_ether_offload_info_t *meoi) 233 { 234 test_pkt_t *tp = tp_alloc(); 235 append_ether(tp, ETHERTYPE_IPV6); 236 237 struct ip6_hdr hdr_ip6 = { 0 }; 238 hdr_ip6.ip6_vfc = 0x60; 239 hdr_ip6.ip6_nxt = IPPROTO_ROUTING; 240 241 struct ip6_rthdr0 eh_route = { 242 .ip6r0_nxt = IPPROTO_FRAGMENT, 243 .ip6r0_len = 0, 244 /* Has padding for len=0 8-byte boundary */ 245 }; 246 struct ip6_frag eh_frag = { 247 .ip6f_nxt = IPPROTO_DSTOPTS, 248 }; 249 struct ip6_dstopt { 250 struct ip6_opt ip6dst_hdr; 251 /* pad out to required 8-byte boundary */ 252 uint8_t ip6dst_data[6]; 253 } eh_dstopts = { 254 .ip6dst_hdr = { 255 .ip6o_type = IPPROTO_TCP, 256 .ip6o_len = 0, 257 } 258 }; 259 260 /* 261 * Mark the packet for fragmentation, but do so in the middle of the EHs 262 * as a more contrived case. 263 */ 264 VERIFY(tp->tp_sz == sizeof (struct ether_header)); 265 tp_append(tp, &hdr_ip6, sizeof (hdr_ip6)); 266 tp_append(tp, &eh_route, sizeof (eh_route)); 267 tp_append(tp, &eh_frag, sizeof (eh_frag)); 268 tp_append(tp, &eh_dstopts, sizeof (eh_dstopts)); 269 const size_t l3sz = tp->tp_sz - sizeof (struct ether_header); 270 271 append_tcp(tp); 272 273 mac_ether_offload_info_t expected = { 274 .meoi_flags = MEOI_L2INFO_SET | MEOI_L3INFO_SET | 275 MEOI_L4INFO_SET | MEOI_L3_FRAGMENT, 276 .meoi_l2hlen = sizeof (struct ether_header), 277 .meoi_l3hlen = l3sz, 278 .meoi_l4hlen = sizeof (struct tcphdr), 279 .meoi_l3proto = ETHERTYPE_IPV6, 280 .meoi_l4proto = IPPROTO_TCP 281 }; 282 *meoi = expected; 283 284 return (tp); 285 } 286 287 static nvlist_t * 288 meoi_to_nvlist(const mac_ether_offload_info_t *meoi) 289 { 290 nvlist_t *out = fnvlist_alloc(); 291 fnvlist_add_int32(out, "meoi_flags", meoi->meoi_flags); 292 fnvlist_add_uint64(out, "meoi_len", meoi->meoi_len); 293 fnvlist_add_uint8(out, "meoi_l2hlen", meoi->meoi_l2hlen); 294 fnvlist_add_uint16(out, "meoi_l3proto", meoi->meoi_l3proto); 295 fnvlist_add_uint16(out, "meoi_l3hlen", meoi->meoi_l3hlen); 296 fnvlist_add_uint8(out, "meoi_l4proto", meoi->meoi_l4proto); 297 fnvlist_add_uint8(out, "meoi_l4hlen", meoi->meoi_l4hlen); 298 299 return (out); 300 } 301 302 static nvlist_t * 303 build_meoi_payload(test_pkt_t *tp, const mac_ether_offload_info_t *results, 304 uint32_t *splits, uint_t num_splits) 305 { 306 nvlist_t *nvl_results = meoi_to_nvlist(results); 307 308 nvlist_t *payload = fnvlist_alloc(); 309 fnvlist_add_byte_array(payload, "pkt_bytes", tp->tp_bytes, tp->tp_sz); 310 if (num_splits != 0 && splits != NULL) { 311 fnvlist_add_uint32_array(payload, "splits", splits, 312 num_splits); 313 } 314 fnvlist_add_nvlist(payload, "results", nvl_results); 315 316 nvlist_free(nvl_results); 317 318 return (payload); 319 } 320 321 static nvlist_t * 322 build_partial_payload(test_pkt_t *tp, uint_t offset, 323 const mac_ether_offload_info_t *partial, 324 const mac_ether_offload_info_t *results, 325 uint32_t *splits, uint_t num_splits) 326 { 327 nvlist_t *nvl_partial = meoi_to_nvlist(partial); 328 nvlist_t *nvl_results = meoi_to_nvlist(results); 329 330 nvlist_t *payload = fnvlist_alloc(); 331 fnvlist_add_byte_array(payload, "pkt_bytes", tp->tp_bytes, tp->tp_sz); 332 if (num_splits != 0 && splits != NULL) { 333 fnvlist_add_uint32_array(payload, "splits", splits, 334 num_splits); 335 } 336 fnvlist_add_nvlist(payload, "results", nvl_results); 337 fnvlist_add_nvlist(payload, "partial", nvl_partial); 338 fnvlist_add_uint32(payload, "offset", offset); 339 340 nvlist_free(nvl_partial); 341 nvlist_free(nvl_results); 342 343 return (payload); 344 } 345 346 static nvlist_t * 347 build_ether_payload(test_pkt_t *tp, uint8_t *dstaddr, uint32_t tci, 348 uint32_t *splits, uint_t num_splits) 349 { 350 nvlist_t *payload = fnvlist_alloc(); 351 fnvlist_add_byte_array(payload, "pkt_bytes", tp->tp_bytes, tp->tp_sz); 352 if (num_splits != 0 && splits != NULL) { 353 fnvlist_add_uint32_array(payload, "splits", splits, 354 num_splits); 355 } 356 fnvlist_add_byte_array(payload, "dstaddr", dstaddr, ETHERADDRL); 357 fnvlist_add_uint32(payload, "tci", tci); 358 359 return (payload); 360 } 361 362 struct test_tuple { 363 const char *tt_module; 364 const char *tt_suite; 365 const char *tt_test; 366 }; 367 const struct test_tuple tuple_meoi = { 368 .tt_module = "mac", 369 .tt_suite = "parsing", 370 .tt_test = "mac_ether_offload_info_test" 371 }; 372 const struct test_tuple tuple_partial_meoi = { 373 .tt_module = "mac", 374 .tt_suite = "parsing", 375 .tt_test = "mac_partial_offload_info_test" 376 }; 377 const struct test_tuple tuple_l2info = { 378 .tt_module = "mac", 379 .tt_suite = "parsing", 380 .tt_test = "mac_ether_l2_info_test" 381 }; 382 383 static bool 384 run_test(nvlist_t *payload, const struct test_tuple *tuple) 385 { 386 size_t payload_sz; 387 char *payload_packed = fnvlist_pack(payload, &payload_sz); 388 VERIFY(payload_packed != NULL); 389 nvlist_free(payload); 390 391 ktest_run_req_t req = { 392 .krq_module = tuple->tt_module, 393 .krq_suite = tuple->tt_suite, 394 .krq_test = tuple->tt_test, 395 .krq_input = (uchar_t *)payload_packed, 396 .krq_input_len = payload_sz, 397 }; 398 ktest_run_result_t result = { 0 }; 399 400 if (!ktest_run(kthdl, &req, &result)) { 401 err(EXIT_FAILURE, "error while attempting ktest_run()"); 402 } 403 404 const char *cname = ktest_code_name(result.krr_code); 405 if (result.krr_code == KTEST_CODE_PASS) { 406 (void) printf("%s: %s\n", tuple->tt_test, cname); 407 free(result.krr_msg); 408 return (true); 409 } else { 410 (void) printf("%s: %s @ line %u\n", 411 tuple->tt_test, cname, result.krr_line); 412 (void) printf("\tmsg: %s", result.krr_msg); 413 free(result.krr_msg); 414 return (false); 415 } 416 } 417 418 static uint32_t * 419 split_gen_single(uint_t num_bytes) 420 { 421 uint32_t *splits = calloc(num_bytes, sizeof (uint32_t)); 422 VERIFY(splits != NULL); 423 for (uint_t i = 0; i < num_bytes; i++) { 424 splits[i] = 1; 425 } 426 return (splits); 427 } 428 static uint32_t * 429 split_gen_random(uint_t num_bytes, uint_t *num_splits) 430 { 431 /* 432 * Generate split points between 0-10 bytes in size. Assuming an 433 * average size of 5 when allocating a fixed buffer, with any remaining 434 * bytes going into one large trailing mblk. 435 */ 436 *num_splits = num_bytes / 5; 437 438 uint32_t *splits = calloc(*num_splits, sizeof (uint32_t)); 439 VERIFY(splits != NULL); 440 for (uint_t i = 0; i < *num_splits; i++) { 441 /* 442 * This uses random() rather than something like 443 * arc4random_uniform() so we can have deterministic splits for 444 * the test case. This is achieved with a prior srand() call 445 * with a fixed seed. 446 */ 447 splits[i] = random() % 11; 448 } 449 450 return (splits); 451 } 452 static void 453 split_print(const uint32_t *splits, uint_t num_splits) 454 { 455 if (num_splits == 0) { 456 (void) printf("\tsplits: []\n"); 457 } else { 458 (void) printf("\tsplits: ["); 459 for (uint_t i = 0; i < num_splits; i++) { 460 (void) printf("%s%u", i == 0 ? "" : ", ", splits[i]); 461 } 462 (void) printf("]\n"); 463 } 464 } 465 466 /* 467 * Run variations of mac_ether_offload_info() test against packet/meoi pair. 468 * Returns true if any variation failed. 469 */ 470 static bool 471 run_meoi_variants(const char *prefix, test_pkt_t *tp, 472 const mac_ether_offload_info_t *meoi) 473 { 474 nvlist_t *payload; 475 bool any_failed = false; 476 uint32_t *splits = NULL; 477 uint_t num_splits; 478 479 (void) printf("%s - simple - ", prefix); 480 payload = build_meoi_payload(tp, meoi, NULL, 0); 481 any_failed |= !run_test(payload, &tuple_meoi); 482 483 (void) printf("%s - split-single-bytes - ", prefix); 484 splits = split_gen_single(tp->tp_sz); 485 payload = build_meoi_payload(tp, meoi, splits, tp->tp_sz); 486 any_failed |= !run_test(payload, &tuple_meoi); 487 free(splits); 488 489 (void) printf("%s - split-random - ", prefix); 490 splits = split_gen_random(tp->tp_sz, &num_splits); 491 payload = build_meoi_payload(tp, meoi, splits, num_splits); 492 any_failed |= !run_test(payload, &tuple_meoi); 493 split_print(splits, num_splits); 494 free(splits); 495 496 return (any_failed); 497 } 498 499 /* 500 * Run variations of mac_partial_offload_info() test against packet/meoi pair. 501 * Returns true if any variation failed. 502 */ 503 static bool 504 run_partial_variants(const char *prefix, test_pkt_t *tp, 505 const mac_ether_offload_info_t *meoi) 506 { 507 nvlist_t *payload; 508 bool any_failed = false; 509 uint32_t *splits = NULL; 510 uint_t num_splits; 511 512 /* skip over the l2 header but ask for the rest to be filled */ 513 uint32_t offset = meoi->meoi_l2hlen; 514 mac_ether_offload_info_t partial = { 515 .meoi_flags = MEOI_L2INFO_SET, 516 .meoi_l3proto = meoi->meoi_l3proto, 517 }; 518 /* And the result should reflect that ignored l2 header */ 519 mac_ether_offload_info_t result; 520 bcopy(meoi, &result, sizeof (result)); 521 result.meoi_l2hlen = 0; 522 523 (void) printf("%s - simple - ", prefix); 524 payload = build_partial_payload(tp, offset, &partial, &result, NULL, 0); 525 any_failed |= !run_test(payload, &tuple_partial_meoi); 526 527 (void) printf("%s - split-single-bytes - ", prefix); 528 splits = split_gen_single(tp->tp_sz); 529 payload = build_partial_payload(tp, offset, &partial, &result, splits, 530 tp->tp_sz); 531 any_failed |= !run_test(payload, &tuple_partial_meoi); 532 free(splits); 533 534 (void) printf("%s - split-random - ", prefix); 535 splits = split_gen_random(tp->tp_sz, &num_splits); 536 payload = build_partial_payload(tp, offset, &partial, &result, splits, 537 num_splits); 538 any_failed |= !run_test(payload, &tuple_partial_meoi); 539 split_print(splits, num_splits); 540 free(splits); 541 542 return (any_failed); 543 } 544 545 /* 546 * Run variations of mac_ether_l2_info() test against packet/data pairing. 547 * Returns true if any variation failed. 548 */ 549 static bool 550 run_ether_variants(const char *prefix, test_pkt_t *tp, uint8_t *dstaddr, 551 uint32_t tci) 552 { 553 nvlist_t *payload; 554 bool any_failed = false; 555 uint32_t *splits = NULL; 556 557 (void) printf("%s - simple - ", prefix); 558 payload = build_ether_payload(tp, dstaddr, tci, NULL, 0); 559 any_failed |= !run_test(payload, &tuple_l2info); 560 561 (void) printf("%s - split-single-bytes - ", prefix); 562 splits = split_gen_single(tp->tp_sz); 563 payload = build_ether_payload(tp, dstaddr, tci, splits, tp->tp_sz); 564 any_failed |= !run_test(payload, &tuple_l2info); 565 free(splits); 566 567 /* intentionally split dstaddr, tpid, tci, and ethertype */ 568 uint32_t intentional_splits[] = { 4, 9, 2, 2 }; 569 (void) printf("%s - split-intentional - ", prefix); 570 payload = build_ether_payload(tp, dstaddr, tci, intentional_splits, 571 ARRAY_SIZE(intentional_splits)); 572 any_failed |= !run_test(payload, &tuple_l2info); 573 split_print(intentional_splits, ARRAY_SIZE(intentional_splits)); 574 575 return (any_failed); 576 } 577 578 int 579 main(int argc, char *argv[]) 580 { 581 if (!ktest_mod_load("mac")) { 582 err(EXIT_FAILURE, "could not load mac ktest module"); 583 } 584 if ((kthdl = ktest_init()) == NULL) { 585 err(EXIT_FAILURE, "could not initialize libktest"); 586 } 587 588 bool any_failed = false; 589 590 /* Use fixed seed for deterministic "random" output */ 591 srandom(0x1badbeef); 592 593 mac_ether_offload_info_t meoi_tcp4 = { 0 }; 594 test_pkt_t *tp_tcp4 = build_tcp4(&meoi_tcp4); 595 596 mac_ether_offload_info_t meoi_tcp6 = { 0 }; 597 test_pkt_t *tp_tcp6 = build_tcp6(&meoi_tcp6); 598 599 any_failed |= 600 run_meoi_variants("basic tcp4", tp_tcp4, &meoi_tcp4); 601 any_failed |= 602 run_meoi_variants("basic tcp6", tp_tcp6, &meoi_tcp6); 603 any_failed |= run_partial_variants("basic tcp4", tp_tcp4, &meoi_tcp4); 604 any_failed |= run_partial_variants("basic tcp6", tp_tcp6, &meoi_tcp6); 605 606 /* 607 * Truncate the tcp header to induce a parse failure, but expect that 608 * the packet info is still populated 609 */ 610 tp_tcp4->tp_sz -= 4; 611 tp_tcp6->tp_sz -= 4; 612 meoi_tcp4.meoi_flags &= ~MEOI_L4INFO_SET; 613 meoi_tcp6.meoi_flags &= ~MEOI_L4INFO_SET; 614 615 any_failed |= 616 run_meoi_variants("truncated tcp4", tp_tcp4, &meoi_tcp4); 617 any_failed |= 618 run_meoi_variants("truncated tcp6", tp_tcp6, &meoi_tcp6); 619 620 mac_ether_offload_info_t meoi_frag_v4 = { 0 }; 621 mac_ether_offload_info_t meoi_frag_v6 = { 0 }; 622 test_pkt_t *tp_frag_v4 = build_frag_v4(&meoi_frag_v4); 623 test_pkt_t *tp_frag_v6 = build_frag_v6(&meoi_frag_v6); 624 625 any_failed |= run_meoi_variants("fragment ipv4", tp_frag_v4, 626 &meoi_frag_v4); 627 any_failed |= run_meoi_variants("fragment ipv6", tp_frag_v6, 628 &meoi_frag_v6); 629 630 test_pkt_t *tp_ether_plain = tp_alloc(); 631 struct ether_header hdr_l2_plain = { 632 .ether_dhost = { 0x86, 0x1d, 0xe0, 0x11, 0x22, 0x33}, 633 .ether_type = htons(ETHERTYPE_IP), 634 }; 635 tp_append(tp_ether_plain, &hdr_l2_plain, sizeof (hdr_l2_plain)); 636 637 test_pkt_t *tp_ether_vlan = tp_alloc(); 638 const uint16_t arb_vlan = 201; 639 struct ether_vlan_header hdr_l2_vlan = { 640 .ether_dhost = { 0x86, 0x1d, 0xe0, 0x11, 0x22, 0x33}, 641 .ether_tpid = htons(ETHERTYPE_VLAN), 642 .ether_tci = htons(arb_vlan), 643 .ether_type = htons(ETHERTYPE_IP), 644 }; 645 tp_append(tp_ether_vlan, &hdr_l2_vlan, sizeof (hdr_l2_vlan)); 646 647 any_failed |= run_ether_variants("ether plain", tp_ether_plain, 648 hdr_l2_plain.ether_dhost.ether_addr_octet, UINT32_MAX); 649 any_failed |= run_ether_variants("ether vlan", tp_ether_vlan, 650 hdr_l2_vlan.ether_dhost.ether_addr_octet, arb_vlan); 651 652 tp_free(tp_tcp4); 653 tp_free(tp_tcp6); 654 tp_free(tp_frag_v4); 655 tp_free(tp_frag_v6); 656 tp_free(tp_ether_plain); 657 tp_free(tp_ether_vlan); 658 659 ktest_fini(kthdl); 660 return (any_failed ? EXIT_FAILURE : EXIT_SUCCESS); 661 } 662