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 2023 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/tcp.h> 23 #include <sys/dlpi.h> 24 #include <sys/ethernet.h> 25 #include <sys/ktest.h> 26 27 static uint32_t 28 mt_pseudo_sum(const uint8_t proto, ipha_t *ip) 29 { 30 const uint32_t ip_hdr_sz = IPH_HDR_LENGTH(ip); 31 const ipaddr_t src = ip->ipha_src; 32 const ipaddr_t dst = ip->ipha_dst; 33 uint16_t len; 34 uint32_t sum = 0; 35 36 switch (proto) { 37 case IPPROTO_TCP: 38 sum = IP_TCP_CSUM_COMP; 39 break; 40 41 case IPPROTO_UDP: 42 sum = IP_UDP_CSUM_COMP; 43 break; 44 } 45 46 len = ntohs(ip->ipha_length) - ip_hdr_sz; 47 sum += (dst >> 16) + (dst & 0xFFFF) + (src >> 16) + (src & 0xFFFF); 48 sum += htons(len); 49 return (sum); 50 } 51 52 /* 53 * An implementation of the internet checksum inspired by RFC 1071. 54 * This implementation is as naive as possible. It serves as the 55 * reference point for testing the optimized versions in the rest of 56 * our stack. This is no place for optimization or cleverness. 57 * 58 * Arguments 59 * 60 * initial: The initial sum value. 61 * 62 * addr: Pointer to the beginning of the byte stream to sum. 63 * 64 * len: The number of bytes to sum. 65 * 66 * Return 67 * 68 * The resulting internet checksum. 69 */ 70 static uint32_t 71 mt_rfc1071_sum(uint32_t initial, uint16_t *addr, size_t len) 72 { 73 uint32_t sum = initial; 74 75 while (len > 1) { 76 sum += *addr; 77 addr++; 78 len -= 2; 79 } 80 81 if (len == 1) { 82 sum += *((uint8_t *)addr); 83 } 84 85 while ((sum >> 16) != 0) { 86 sum = (sum >> 16) + (sum & 0xFFFF); 87 } 88 89 return (~sum & 0xFFFF); 90 } 91 92 typedef boolean_t (*mac_sw_cksum_ipv4_t)(mblk_t *, uint32_t, ipha_t *, 93 const char **); 94 95 /* 96 * Fill out a basic TCP header in the given mblk at the given offset. 97 * A TCP header should never straddle an mblk boundary. 98 */ 99 static tcpha_t * 100 mt_tcp_basic_hdr(mblk_t *mp, uint16_t offset, uint16_t lport, uint16_t fport, 101 uint32_t seq, uint32_t ack, uint8_t flags, uint16_t win) 102 { 103 tcpha_t *tcp = (tcpha_t *)(mp->b_rptr + offset); 104 105 VERIFY3U((uintptr_t)tcp + sizeof (*tcp), <=, mp->b_wptr); 106 tcp->tha_lport = htons(lport); 107 tcp->tha_fport = htons(fport); 108 tcp->tha_seq = htonl(seq); 109 tcp->tha_ack = htonl(ack); 110 tcp->tha_offset_and_reserved = 0x5 << 4; 111 tcp->tha_flags = flags; 112 tcp->tha_win = htons(win); 113 tcp->tha_sum = 0x0; 114 tcp->tha_urp = 0x0; 115 116 return (tcp); 117 } 118 119 static ipha_t * 120 mt_ipv4_simple_hdr(mblk_t *mp, uint16_t offset, uint16_t datum_length, 121 uint16_t ident, uint8_t proto, char *src, char *dst) 122 { 123 uint32_t srcaddr, dstaddr; 124 ipha_t *ip = (ipha_t *)(mp->b_rptr + offset); 125 126 VERIFY3U((uintptr_t)ip + sizeof (*ip), <=, mp->b_wptr); 127 128 VERIFY(inet_pton(AF_INET, src, &srcaddr)); 129 VERIFY(inet_pton(AF_INET, dst, &dstaddr)); 130 ip->ipha_version_and_hdr_length = IP_SIMPLE_HDR_VERSION; 131 ip->ipha_type_of_service = 0x0; 132 ip->ipha_length = htons(sizeof (*ip) + datum_length); 133 ip->ipha_ident = htons(ident); 134 ip->ipha_fragment_offset_and_flags = IPH_DF_HTONS; 135 ip->ipha_ttl = 255; 136 ip->ipha_protocol = proto; 137 ip->ipha_hdr_checksum = 0x0; 138 ip->ipha_src = srcaddr; 139 ip->ipha_dst = dstaddr; 140 141 return (ip); 142 } 143 144 static struct ether_header * 145 mt_ether_hdr(mblk_t *mp, uint16_t offset, char *dst, char *src, uint16_t etype) 146 { 147 char *byte = dst; 148 unsigned long tmp; 149 struct ether_header *eh = (struct ether_header *)(mp->b_rptr + offset); 150 151 VERIFY3U((uintptr_t)eh + sizeof (*eh), <=, mp->b_wptr); 152 153 /* No strtok in these here parts. */ 154 for (uint_t i = 0; i < 6; i++) { 155 char *end = strchr(dst, ':'); 156 VERIFY3P(end, !=, NULL); 157 VERIFY0(ddi_strtoul(byte, NULL, 16, &tmp)); 158 VERIFY3U(tmp, <=, 255); 159 eh->ether_dhost.ether_addr_octet[i] = tmp; 160 byte = end + 1; 161 } 162 163 byte = src; 164 for (uint_t i = 0; i < 6; i++) { 165 char *end = strchr(dst, ':'); 166 VERIFY3P(end, !=, NULL); 167 VERIFY0(ddi_strtoul(byte, NULL, 16, &tmp)); 168 VERIFY3U(tmp, <=, 255); 169 eh->ether_shost.ether_addr_octet[i] = tmp; 170 byte = end + 1; 171 } 172 173 eh->ether_type = etype; 174 return (eh); 175 } 176 177 void 178 mac_sw_cksum_ipv4_tcp_test(ktest_ctx_hdl_t *ctx) 179 { 180 ddi_modhandle_t hdl = NULL; 181 mac_sw_cksum_ipv4_t mac_sw_cksum_ipv4 = NULL; 182 tcpha_t *tcp; 183 ipha_t *ip; 184 struct ether_header *eh; 185 mblk_t *mp = NULL; 186 char *msg = "...when it's not your turn"; 187 size_t msglen = strlen(msg) + 1; 188 size_t mplen; 189 const char *err = ""; 190 uint32_t sum; 191 size_t ehsz = sizeof (*eh); 192 size_t ipsz = sizeof (*ip); 193 size_t tcpsz = sizeof (*tcp); 194 195 if (ktest_hold_mod("mac", &hdl) != 0) { 196 KT_ERROR(ctx, "failed to hold 'mac' module"); 197 return; 198 } 199 200 if (ktest_get_fn(hdl, "mac_sw_cksum_ipv4", 201 (void **)&mac_sw_cksum_ipv4) != 0) { 202 KT_ERROR(ctx, "failed to resolve symbol %s`%s", "mac", 203 "mac_sw_cksum_ipv4"); 204 goto cleanup; 205 } 206 207 mplen = ehsz + ipsz + tcpsz + msglen; 208 mp = allocb(mplen, 0); 209 KT_EASSERT3P(mp, !=, NULL, ctx); 210 mp->b_wptr = mp->b_rptr + mplen; 211 tcp = mt_tcp_basic_hdr(mp, ehsz + ipsz, 2002, 2008, 1, 166, 0, 32000); 212 ip = mt_ipv4_simple_hdr(mp, ehsz, tcpsz + msglen, 410, IPPROTO_TCP, 213 "192.168.2.4", "192.168.2.5"); 214 eh = mt_ether_hdr(mp, 0, "f2:35:c2:72:26:57", "92:ce:5a:29:46:9d", 215 ETHERTYPE_IP); 216 217 bcopy(msg, mp->b_rptr + ehsz + ipsz + tcpsz, msglen); 218 219 /* 220 * It's important that we calculate the reference checksum 221 * first, because mac_sw_cksum_ipv4() populates the checksum 222 * field. 223 */ 224 sum = mt_pseudo_sum(IPPROTO_TCP, ip); 225 sum = mt_rfc1071_sum(sum, (uint16_t *)(mp->b_rptr + ehsz + ipsz), 226 tcpsz + msglen); 227 228 /* 229 * The internet checksum can never be 0xFFFF, as that would 230 * indicate an input of all zeros. 231 */ 232 KT_ASSERT3UG(sum, !=, 0xFFFF, ctx, cleanup); 233 KT_ASSERTG(mac_sw_cksum_ipv4(mp, ehsz, ip, &err), ctx, cleanup); 234 KT_ASSERT3UG(tcp->tha_sum, !=, 0xFFFF, ctx, cleanup); 235 KT_ASSERT3UG(sum, ==, tcp->tha_sum, ctx, cleanup); 236 KT_PASS(ctx); 237 238 cleanup: 239 if (hdl != NULL) { 240 ktest_release_mod(hdl); 241 } 242 243 if (mp != NULL) { 244 freeb(mp); 245 } 246 } 247 248 /* 249 * Verify that an unexpected IP protocol results in the expect 250 * failure. 251 */ 252 void 253 mac_sw_cksum_ipv4_bad_proto_test(ktest_ctx_hdl_t *ctx) 254 { 255 ddi_modhandle_t hdl = NULL; 256 mac_sw_cksum_ipv4_t mac_sw_cksum_ipv4 = NULL; 257 tcpha_t *tcp; 258 ipha_t *ip; 259 struct ether_header *eh; 260 mblk_t *mp = NULL; 261 char *msg = "...when it's not your turn"; 262 size_t msglen = strlen(msg) + 1; 263 size_t mplen; 264 const char *err = ""; 265 size_t ehsz = sizeof (*eh); 266 size_t ipsz = sizeof (*ip); 267 size_t tcpsz = sizeof (*tcp); 268 269 if (ktest_hold_mod("mac", &hdl) != 0) { 270 KT_ERROR(ctx, "failed to hold 'mac' module"); 271 return; 272 } 273 274 if (ktest_get_fn(hdl, "mac_sw_cksum_ipv4", 275 (void **)&mac_sw_cksum_ipv4) != 0) { 276 KT_ERROR(ctx, "failed to resolve symbol mac`mac_sw_cksum_ipv4"); 277 goto cleanup; 278 } 279 280 mplen = ehsz + ipsz + tcpsz + msglen; 281 mp = allocb(mplen, 0); 282 KT_EASSERT3P(mp, !=, NULL, ctx); 283 mp->b_wptr = mp->b_rptr + mplen; 284 tcp = mt_tcp_basic_hdr(mp, ehsz + ipsz, 2002, 2008, 1, 166, 0, 32000); 285 ip = mt_ipv4_simple_hdr(mp, ehsz, tcpsz + msglen, 410, IPPROTO_ENCAP, 286 "192.168.2.4", "192.168.2.5"); 287 eh = mt_ether_hdr(mp, 0, "f2:35:c2:72:26:57", "92:ce:5a:29:46:9d", 288 ETHERTYPE_IP); 289 bcopy(msg, mp->b_rptr + ehsz + ipsz + tcpsz, msglen); 290 KT_ASSERT0G(mac_sw_cksum_ipv4(mp, ehsz, ip, &err), ctx, cleanup); 291 KT_PASS(ctx); 292 293 cleanup: 294 if (hdl != NULL) { 295 ktest_release_mod(hdl); 296 } 297 298 if (mp != NULL) { 299 freeb(mp); 300 } 301 } 302 303 typedef struct snoop_pkt_record_hdr { 304 uint32_t spr_orig_len; 305 uint32_t spr_include_len; 306 uint32_t spr_record_len; 307 uint32_t spr_cumulative_drops; 308 uint32_t spr_ts_secs; 309 uint32_t spr_ts_micros; 310 } snoop_pkt_record_hdr_t; 311 312 typedef struct snoop_pkt { 313 uchar_t *sp_bytes; 314 uint16_t sp_len; 315 } snoop_pkt_t; 316 317 typedef struct snoop_iter { 318 uchar_t *sic_input; /* beginning of stream */ 319 uintptr_t sic_end; /* end of stream */ 320 uchar_t *sic_pos; /* current position in stream */ 321 uint_t sic_pkt_num; /* current packet number, 1-based */ 322 snoop_pkt_record_hdr_t *sic_pkt_hdr; /* current packet record header */ 323 } snoop_iter_t; 324 325 #define PAST_END(itr, len) \ 326 (((uintptr_t)(itr)->sic_pos + len) > itr->sic_end) 327 328 /* 329 * Get the next packet in the snoop stream iterator returned by 330 * mt_snoop_iter_get(). A copy of the packet is returned via the pkt 331 * pointer. The caller provides the snoop_pkt_t, and this function 332 * allocates a new buffer inside it to hold a copy of the packet's 333 * bytes. It is the responsibility of the caller to free the copy. It 334 * is recommended the caller make use of desballoc(9F) along with the 335 * snoop_pkt_free() callback. When all the packets in the stream have 336 * been read all subsequent calls to this function will set sp_bytes 337 * to NULL and sp_len to 0. 338 * 339 * The caller may optionally specify an rhdr argument in order to 340 * receive a pointer to the packet record header (unlike the packet 341 * bytes this is a pointer into the stream, not a copy). 342 */ 343 static int 344 mt_snoop_iter_next(ktest_ctx_hdl_t *ctx, snoop_iter_t *itr, snoop_pkt_t *pkt, 345 snoop_pkt_record_hdr_t **rhdr) 346 { 347 uchar_t *pkt_start; 348 349 /* 350 * We've read exactly the number of bytes expected, this is 351 * the end. 352 */ 353 if ((uintptr_t)(itr->sic_pos) == itr->sic_end) { 354 pkt->sp_bytes = NULL; 355 pkt->sp_len = 0; 356 357 if (rhdr != NULL) 358 *rhdr = NULL; 359 360 return (0); 361 } 362 363 /* 364 * A corrupted record or truncated stream could point us past 365 * the end of the stream. 366 */ 367 if (PAST_END(itr, sizeof (snoop_pkt_record_hdr_t))) { 368 KT_ERROR(ctx, "record corrupted or stream truncated, read past " 369 "end of stream for record header #%d: 0x%p + %u > 0x%p", 370 itr->sic_pkt_num, itr->sic_pos, 371 sizeof (snoop_pkt_record_hdr_t), itr->sic_end); 372 return (EIO); 373 } 374 375 itr->sic_pkt_hdr = (snoop_pkt_record_hdr_t *)itr->sic_pos; 376 pkt_start = itr->sic_pos + sizeof (snoop_pkt_record_hdr_t); 377 378 /* 379 * A corrupted record or truncated stream could point us past 380 * the end of the stream. 381 */ 382 if (PAST_END(itr, ntohl(itr->sic_pkt_hdr->spr_record_len))) { 383 KT_ERROR(ctx, "record corrupted or stream truncated, read past " 384 "end of stream for record #%d: 0x%p + %u > 0x%p", 385 itr->sic_pkt_num, itr->sic_pos, 386 ntohl(itr->sic_pkt_hdr->spr_record_len), itr->sic_end); 387 return (EIO); 388 } 389 390 pkt->sp_len = ntohl(itr->sic_pkt_hdr->spr_include_len); 391 pkt->sp_bytes = kmem_zalloc(pkt->sp_len, KM_SLEEP); 392 bcopy(pkt_start, pkt->sp_bytes, pkt->sp_len); 393 itr->sic_pos += ntohl(itr->sic_pkt_hdr->spr_record_len); 394 itr->sic_pkt_num++; 395 396 if (rhdr != NULL) { 397 *rhdr = itr->sic_pkt_hdr; 398 } 399 400 return (0); 401 } 402 403 /* 404 * Parse a snoop data stream (RFC 1761) provided by input and return 405 * a packet iterator to be used by mt_snoop_iter_next(). 406 */ 407 static int 408 mt_snoop_iter_get(ktest_ctx_hdl_t *ctx, uchar_t *input, const uint_t input_len, 409 snoop_iter_t **itr_out) 410 { 411 const uchar_t id[8] = { 's', 'n', 'o', 'o', 'p', '\0', '\0', '\0' }; 412 uint32_t version; 413 uint32_t datalink; 414 snoop_iter_t *itr; 415 416 *itr_out = NULL; 417 418 if (input_len < 16) { 419 KT_ERROR(ctx, "snoop stream truncated at file header: %u < %u ", 420 input_len, 16); 421 return (ENOBUFS); 422 } 423 424 if (memcmp(input, &id, sizeof (id)) != 0) { 425 KT_ERROR(ctx, "snoop stream malformed identification: %x %x %x " 426 "%x %x %x %x %x", input[0], input[1], input[2], input[3], 427 input[4], input[5], input[6], input[7]); 428 return (EINVAL); 429 } 430 431 itr = kmem_zalloc(sizeof (*itr), KM_SLEEP); 432 itr->sic_input = input; 433 itr->sic_end = (uintptr_t)input + input_len; 434 itr->sic_pos = input + sizeof (id); 435 itr->sic_pkt_num = 1; 436 itr->sic_pkt_hdr = NULL; 437 version = ntohl(*(uint32_t *)itr->sic_pos); 438 439 if (version != 2) { 440 KT_ERROR(ctx, "snoop stream bad version: %u != %u", version, 2); 441 return (EINVAL); 442 } 443 444 itr->sic_pos += sizeof (version); 445 datalink = ntohl(*(uint32_t *)itr->sic_pos); 446 447 /* We expect only Ethernet. */ 448 if (datalink != DL_ETHER) { 449 KT_ERROR(ctx, "snoop stream bad datalink type: %u != %u", 450 datalink, DL_ETHER); 451 kmem_free(itr, sizeof (*itr)); 452 return (EINVAL); 453 } 454 455 itr->sic_pos += sizeof (datalink); 456 *itr_out = itr; 457 return (0); 458 } 459 460 static void 461 snoop_pkt_free(snoop_pkt_t *pkt) 462 { 463 kmem_free(pkt->sp_bytes, pkt->sp_len); 464 } 465 466 /* 467 * Verify mac_sw_cksum_ipv4() against an arbitrary TCP stream read 468 * from the snoop capture given as input. In order to verify the 469 * checksum all TCP/IPv4 packets must be captured in full. The snoop 470 * capture may contain non-TCP/IPv4 packets, which will be skipped 471 * over. If not a single TCP/IPv4 packet is found, the test will 472 * report an error. 473 */ 474 void 475 mac_sw_cksum_ipv4_snoop_test(ktest_ctx_hdl_t *ctx) 476 { 477 ddi_modhandle_t hdl = NULL; 478 mac_sw_cksum_ipv4_t mac_sw_cksum_ipv4 = NULL; 479 uchar_t *bytes; 480 size_t num_bytes = 0; 481 uint_t pkt_num = 0; 482 tcpha_t *tcp; 483 ipha_t *ip; 484 struct ether_header *eh; 485 mblk_t *mp = NULL; 486 const char *err = ""; 487 uint32_t csum; 488 size_t ehsz, ipsz, tcpsz, msglen; 489 snoop_iter_t *itr = NULL; 490 snoop_pkt_record_hdr_t *hdr = NULL; 491 boolean_t at_least_one = B_FALSE; 492 snoop_pkt_t pkt; 493 int ret; 494 495 if (ktest_hold_mod("mac", &hdl) != 0) { 496 KT_ERROR(ctx, "failed to hold 'mac' module"); 497 return; 498 } 499 500 if (ktest_get_fn(hdl, "mac_sw_cksum_ipv4", 501 (void **)&mac_sw_cksum_ipv4) != 0) { 502 KT_ERROR(ctx, "failed to resolve symbol mac`mac_sw_cksum_ipv4"); 503 return; 504 } 505 506 ktest_get_input(ctx, &bytes, &num_bytes); 507 ret = mt_snoop_iter_get(ctx, bytes, num_bytes, &itr); 508 if (ret != 0) { 509 /* mt_snoop_iter_get() already set error context. */ 510 goto cleanup; 511 } 512 513 bzero(&pkt, sizeof (pkt)); 514 515 while ((ret = mt_snoop_iter_next(ctx, itr, &pkt, &hdr)) == 0) { 516 frtn_t frtn; 517 518 if (pkt.sp_len == 0) { 519 break; 520 } 521 522 pkt_num++; 523 524 /* 525 * Prepend the packet record number to any 526 * fail/skip/error message so the user knows which 527 * record in the snoop stream to inspect. 528 */ 529 ktest_msg_prepend(ctx, "pkt #%u: ", pkt_num); 530 531 /* IPv4 only */ 532 if (hdr->spr_include_len < (sizeof (*eh) + sizeof (*ip))) { 533 continue; 534 } 535 536 /* fully recorded packets only */ 537 if (hdr->spr_include_len != hdr->spr_orig_len) { 538 continue; 539 } 540 541 frtn.free_func = snoop_pkt_free; 542 frtn.free_arg = (caddr_t)&pkt; 543 mp = desballoc(pkt.sp_bytes, pkt.sp_len, 0, &frtn); 544 KT_EASSERT3PG(mp, !=, NULL, ctx, cleanup); 545 mp->b_wptr += pkt.sp_len; 546 eh = (struct ether_header *)mp->b_rptr; 547 ehsz = sizeof (*eh); 548 549 /* IPv4 only */ 550 if (ntohs(eh->ether_type) != ETHERTYPE_IP) { 551 freeb(mp); 552 mp = NULL; 553 continue; 554 } 555 556 ip = (ipha_t *)(mp->b_rptr + ehsz); 557 ipsz = sizeof (*ip); 558 559 if (ip->ipha_protocol == IPPROTO_TCP) { 560 tcp = (tcpha_t *)(mp->b_rptr + sizeof (*eh) + 561 sizeof (*ip)); 562 tcpsz = TCP_HDR_LENGTH(tcp); 563 msglen = ntohs(ip->ipha_length) - (ipsz + tcpsz); 564 565 /* Let's make sure we don't run off into space. */ 566 if ((tcpsz + msglen) > (pkt.sp_len - (ehsz + ipsz))) { 567 KT_ERROR(ctx, "(tcpsz=%lu + msglen=%lu) > " 568 "(pkt_len=%lu - (ehsz=%lu + ipsz=%lu))", 569 tcpsz, msglen, pkt.sp_len, ehsz, ipsz); 570 goto cleanup; 571 } 572 573 /* 574 * As we are reading a snoop input stream we 575 * need to make sure to zero out any existing 576 * checksum. 577 */ 578 tcp->tha_sum = 0; 579 csum = mt_pseudo_sum(IPPROTO_TCP, ip); 580 csum = mt_rfc1071_sum(csum, 581 (uint16_t *)(mp->b_rptr + ehsz + ipsz), 582 tcpsz + msglen); 583 } else { 584 freeb(mp); 585 mp = NULL; 586 continue; 587 } 588 589 /* 590 * The internet checksum can never be 0xFFFF, as that 591 * would indicate an input of all zeros. 592 */ 593 KT_ASSERT3UG(csum, !=, 0xFFFF, ctx, cleanup); 594 KT_ASSERTG(mac_sw_cksum_ipv4(mp, ehsz, ip, &err), ctx, cleanup); 595 KT_ASSERT3UG(tcp->tha_sum, !=, 0xFFFF, ctx, cleanup); 596 KT_ASSERT3UG(tcp->tha_sum, ==, csum, ctx, cleanup); 597 at_least_one = B_TRUE; 598 freeb(mp); 599 mp = NULL; 600 601 /* 602 * Clear the prepended message for the iterator call 603 * as it already includes the current record number 604 * (and pkt_num is not incremented, thus incorrect, 605 * until after a successful call). 606 */ 607 ktest_msg_clear(ctx); 608 } 609 610 if (ret != 0) { 611 /* mt_snoop_next() already set error context. */ 612 goto cleanup; 613 } 614 615 if (at_least_one) { 616 KT_PASS(ctx); 617 } else { 618 ktest_msg_clear(ctx); 619 KT_ERROR(ctx, "at least one TCP/IPv4 packet expected"); 620 } 621 622 cleanup: 623 if (hdl != NULL) { 624 ktest_release_mod(hdl); 625 } 626 627 if (mp != NULL) { 628 freeb(mp); 629 } 630 631 if (itr != NULL) { 632 kmem_free(itr, sizeof (*itr)); 633 } 634 } 635 636 static struct modlmisc mac_test_modlmisc = { 637 .misc_modops = &mod_miscops, 638 .misc_linkinfo = "mac ktest module" 639 }; 640 641 static struct modlinkage mac_test_modlinkage = { 642 .ml_rev = MODREV_1, 643 .ml_linkage = { &mac_test_modlmisc, NULL } 644 }; 645 646 int 647 _init() 648 { 649 int ret; 650 ktest_module_hdl_t *km = NULL; 651 ktest_suite_hdl_t *ks = NULL; 652 653 VERIFY0(ktest_create_module("mac", &km)); 654 VERIFY0(ktest_add_suite(km, "checksum", &ks)); 655 VERIFY0(ktest_add_test(ks, "mac_sw_cksum_ipv4_tcp_test", 656 mac_sw_cksum_ipv4_tcp_test, KTEST_FLAG_NONE)); 657 VERIFY0(ktest_add_test(ks, "mac_sw_cksum_ipv4_bad_proto_test", 658 mac_sw_cksum_ipv4_bad_proto_test, KTEST_FLAG_NONE)); 659 VERIFY0(ktest_add_test(ks, "mac_sw_cksum_ipv4_snoop_test", 660 mac_sw_cksum_ipv4_snoop_test, KTEST_FLAG_INPUT)); 661 662 if ((ret = ktest_register_module(km)) != 0) { 663 ktest_free_module(km); 664 return (ret); 665 } 666 667 if ((ret = mod_install(&mac_test_modlinkage)) != 0) { 668 ktest_unregister_module("mac"); 669 return (ret); 670 } 671 672 return (0); 673 } 674 675 int 676 _fini(void) 677 { 678 ktest_unregister_module("mac"); 679 return (mod_remove(&mac_test_modlinkage)); 680 } 681 682 int 683 _info(struct modinfo *modinfop) 684 { 685 return (mod_info(&mac_test_modlinkage, modinfop)); 686 } 687