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