xref: /illumos-gate/usr/src/test/os-tests/tests/mac/mac_cksum.c (revision fcdb3229a31dd4ff700c69238814e326aad49098)
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  * Executor for mac_sw_cksum() ktests.
18  *
19  * This program builds up the packed nvlist payloads expected by the ktest for
20  * mac_sw_cksum().  The caller provides a snoop(1) with one or more packets
21  * bearing valid checksums.  Along with the checksum types selected (via option
22  * flags), it is passed into the ktest, where it is stripped of its checksums
23  * and then run through mac_sw_cksum().  The resulting mblk is compared
24  * byte-for-byte with the original input to determine if the emulation generated
25  * the correct checksums.
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <libgen.h>
34 
35 #include <libktest.h>
36 
37 #include "mac_ktest_common.h"
38 
39 static ktest_hdl_t *kthdl = NULL;
40 const char *mac_cksum_cmd = "";
41 
42 static void __NORETURN
43 mac_cksum_usage(void)
44 {
45 	(void) fprintf(stderr, "Usage: %s [flags] [opts] <cap_file>\n\n"
46 	    "Flags:\n"
47 	    "\t-4\temulate HCK_IPV4_HDRCKSUM\n"
48 	    "\t-f\temulate HCK_FULLCKSUM\t(cannot be used with -p)\n"
49 	    "\t-p\temulate HCK_PARTIALCKSUM\t(cannot be used with -f)\n"
50 	    "\t-e\tsplit mblk after Ethernet header\n"
51 	    "Options:\n"
52 	    "\t-b <len>\tpad mblk with <len> bytes (must be even)\n"
53 	    "\t-s <len>\tsplit mblk after len bytes (must be even)\n"
54 	    "\t\t\tif -e is specified, will be applied after that split\n"
55 	    "Arguments:\n"
56 	    "\t<cap_file> is a snoop capture of packets to test.\n"
57 	    "\tAny TCP or UDP packets (or plain IPv4) are expected to have\n"
58 	    "\tcorrect checksums.  The emulated results will be compared\n"
59 	    "\tagainst those sums in the packet (assuming them proper)\n",
60 	    mac_cksum_cmd);
61 	exit(EXIT_FAILURE);
62 }
63 
64 int
65 main(int argc, char *argv[])
66 {
67 	/* Peel off command name for usage */
68 	mac_cksum_cmd = basename(argv[0]);
69 	argc--;
70 	argv++;
71 	optind = 0;
72 	const char *errstr = NULL;
73 
74 	struct payload_opts popts = { 0 };
75 	int c;
76 	while ((c = getopt(argc, argv, "4fpeb:s:")) != -1) {
77 		switch (c) {
78 		case 'p':
79 			popts.po_cksum_partial = B_TRUE;
80 			break;
81 		case 'f':
82 			popts.po_cksum_full = B_TRUE;
83 			break;
84 		case '4':
85 			popts.po_cksum_ipv4 = B_TRUE;
86 			break;
87 		case 'b':
88 			popts.po_padding =
89 			    strtonumx(optarg, 0, UINT16_MAX, &errstr, 0);
90 			if (errstr != NULL) {
91 				errx(EXIT_FAILURE,
92 				    "invalid padding value %s: %s",
93 				    optarg, errstr);
94 			}
95 			break;
96 		case 'e':
97 			popts.po_split_ether = B_TRUE;
98 			break;
99 		case 's':
100 			popts.po_split_manual =
101 			    strtonumx(optarg, 0, UINT16_MAX, &errstr, 0);
102 			if (errstr != NULL) {
103 				errx(EXIT_FAILURE,
104 				    "invalid split value %s: %s",
105 				    optarg, errstr);
106 			}
107 			break;
108 
109 		case '?':
110 			warnx("unknown option: -%c", optopt);
111 			mac_cksum_usage();
112 		}
113 	}
114 	argc -= optind;
115 	argv += optind;
116 
117 	if (argc != 1) {
118 		(void) fprintf(stderr, "cap_file is a required argument\n");
119 		mac_cksum_usage();
120 	}
121 
122 	int fd = open(argv[0], O_RDONLY);
123 	if (fd < 0) {
124 		err(EXIT_FAILURE, "could not open cap file %s", argv[0]);
125 	}
126 
127 	pkt_cap_iter_t *iter = pkt_cap_open(fd);
128 	if (iter == NULL) {
129 		err(EXIT_FAILURE, "unrecognized cap file %s", argv[0]);
130 	}
131 
132 	if ((kthdl = ktest_init()) == NULL) {
133 		err(EXIT_FAILURE, "could not initialize libktest");
134 	}
135 	if (!ktest_mod_load("mac")) {
136 		err(EXIT_FAILURE, "could not load mac ktest module");
137 	}
138 
139 	const void *pkt_buf;
140 	uint_t pkt_sz;
141 	uint_t count_pass = 0, count_fail = 0, count_skip = 0, idx = 0;
142 	while (pkt_cap_next(iter, &pkt_buf, &pkt_sz)) {
143 		ktest_run_req_t req = {
144 			.krq_module = "mac",
145 			.krq_suite = "checksum",
146 			.krq_test = "mac_sw_cksum_test",
147 		};
148 		size_t payload_sz;
149 		char *payload = build_payload(pkt_buf, pkt_sz, NULL, 0,
150 		    &popts, &payload_sz);
151 		req.krq_input = (uchar_t *)payload;
152 		req.krq_input_len = (uint_t)payload_sz;
153 
154 		ktest_run_result_t result = { 0 };
155 		if (!ktest_run(kthdl, &req, &result)) {
156 			err(EXIT_FAILURE, "failure while attempting ktest run");
157 		}
158 		free(payload);
159 
160 		const char *code_name = ktest_code_name(result.krr_code);
161 		switch (result.krr_code) {
162 		case KTEST_CODE_PASS:
163 			count_pass++;
164 			break;
165 		case KTEST_CODE_SKIP:
166 			count_skip++;
167 			break;
168 		default:
169 			count_fail++;
170 			break;
171 		}
172 		(void) printf("%4u\t%s\t(len: %u)\n", idx, code_name, pkt_sz);
173 		if (result.krr_msg != NULL) {
174 			if (result.krr_code != KTEST_CODE_PASS) {
175 				(void) printf("MSG: %s\n", result.krr_msg);
176 			}
177 			free(result.krr_msg);
178 		}
179 		idx++;
180 	}
181 	if (idx == 0) {
182 		errx(EXIT_FAILURE, "No valid packets found");
183 	} else if (idx != 1) {
184 		/* Summarize for > 1 packet */
185 		(void) printf("SUMMARY: %u PASS, %u SKIP, %u FAIL\n",
186 		    count_pass, count_skip, count_fail);
187 	}
188 
189 	pkt_cap_close(iter);
190 	ktest_fini(kthdl);
191 
192 	return (idx == count_pass ? EXIT_SUCCESS : EXIT_FAILURE);
193 }
194