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
mt_pseudo_sum(const uint8_t proto,ipha_t * ip)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
mt_rfc1071_sum(uint32_t initial,uint16_t * addr,size_t len)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 *
mt_tcp_basic_hdr(mblk_t * mp,uint16_t offset,uint16_t lport,uint16_t fport,uint32_t seq,uint32_t ack,uint8_t flags,uint16_t win)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 *
mt_ipv4_simple_hdr(mblk_t * mp,uint16_t offset,uint16_t datum_length,uint16_t ident,uint8_t proto,char * src,char * dst)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 *
mt_ether_hdr(mblk_t * mp,uint16_t offset,char * dst,char * src,uint16_t etype)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
mac_sw_cksum_ipv4_tcp_test(ktest_ctx_hdl_t * ctx)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
mac_sw_cksum_ipv4_bad_proto_test(ktest_ctx_hdl_t * ctx)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
mt_snoop_iter_next(ktest_ctx_hdl_t * ctx,snoop_iter_t * itr,snoop_pkt_t * pkt,snoop_pkt_record_hdr_t ** rhdr)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
mt_snoop_iter_get(ktest_ctx_hdl_t * ctx,uchar_t * input,const uint_t input_len,snoop_iter_t ** itr_out)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
snoop_pkt_free(snoop_pkt_t * pkt)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
mac_sw_cksum_ipv4_snoop_test(ktest_ctx_hdl_t * ctx)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
_init()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
_fini(void)676 _fini(void)
677 {
678 ktest_unregister_module("mac");
679 return (mod_remove(&mac_test_modlinkage));
680 }
681
682 int
_info(struct modinfo * modinfop)683 _info(struct modinfo *modinfop)
684 {
685 return (mod_info(&mac_test_modlinkage, modinfop));
686 }
687