xref: /illumos-gate/usr/src/test/os-tests/tests/mac/mac_cksum.c (revision 1f8b8a0145321ca42ee324565958ceb82a14ee7a)
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 <unistd.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <strings.h>
35 #include <netinet/in.h>
36 #include <sys/mman.h>
37 #include <sys/dlpi.h>
38 #include <sys/types.h>
39 #include <sys/types32.h>
40 #include <sys/stat.h>
41 #include <sys/sysmacros.h>
42 #include <sys/ethernet.h>
43 
44 #include <libnvpair.h>
45 #include <libktest.h>
46 
47 typedef struct snoop_pkt_hdr {
48 	uint_t			sph_origlen;
49 	uint_t			sph_msglen;
50 	uint_t			sph_totlen;
51 	uint_t			sph_drops;
52 #if defined(_LP64)
53 	struct timeval32	sph_timestamp;
54 #else
55 #error	"ktest is expected to be 64-bit for now"
56 #endif
57 } snoop_pkt_hdr_t;
58 
59 typedef struct snoop_file_hdr {
60 	char		sfh_magic[8];
61 	uint32_t	sfh_vers;
62 	uint32_t	sfh_mac_type;
63 } snoop_file_hdr_t;
64 
65 static const char snoop_magic[8] = "snoop\0\0\0";
66 static const uint_t snoop_acceptable_vers = 2;
67 
68 typedef struct pkt_cap_iter {
69 	int		pci_fd;
70 	const char	*pci_base;
71 	size_t		pci_map_sz;
72 	size_t		pci_sz;
73 	size_t		pci_offset;
74 } pkt_cap_iter_t;
75 
76 static pkt_cap_iter_t *
pkt_cap_open(int fd)77 pkt_cap_open(int fd)
78 {
79 	struct stat info;
80 	if (fstat(fd, &info) != 0) {
81 		return (NULL);
82 	}
83 	if (info.st_size < sizeof (snoop_file_hdr_t)) {
84 		errno = EINVAL;
85 		return (NULL);
86 	}
87 
88 	const size_t page_sz = (size_t)sysconf(_SC_PAGESIZE);
89 	const size_t map_sz = P2ROUNDUP(info.st_size, page_sz);
90 	void *map = mmap(NULL, map_sz, PROT_READ, MAP_PRIVATE, fd, 0);
91 	if (map == NULL) {
92 		return (NULL);
93 	}
94 
95 	const snoop_file_hdr_t *hdr = (const snoop_file_hdr_t *)map;
96 	if (bcmp(&hdr->sfh_magic, snoop_magic, sizeof (hdr->sfh_magic)) != 0 ||
97 	    ntohl(hdr->sfh_vers) != snoop_acceptable_vers ||
98 	    ntohl(hdr->sfh_mac_type) != DL_ETHER) {
99 		(void) munmap(map, map_sz);
100 		errno = EINVAL;
101 		return (NULL);
102 	}
103 
104 	struct pkt_cap_iter *iter = malloc(sizeof (struct pkt_cap_iter));
105 	if (iter == NULL) {
106 		(void) munmap(map, map_sz);
107 		errno = ENOMEM;
108 		return (NULL);
109 	}
110 
111 	iter->pci_fd = fd;
112 	iter->pci_base = (const char *)map;
113 	iter->pci_map_sz = map_sz;
114 	iter->pci_sz = info.st_size;
115 	iter->pci_offset = sizeof (*hdr);
116 
117 	return (iter);
118 }
119 
120 static void
pkt_cap_close(pkt_cap_iter_t * iter)121 pkt_cap_close(pkt_cap_iter_t *iter)
122 {
123 	(void) munmap((void *)iter->pci_base, iter->pci_map_sz);
124 	(void) close(iter->pci_fd);
125 	free(iter);
126 }
127 
128 static bool
pkt_cap_next(pkt_cap_iter_t * iter,const void ** pkt_buf,uint_t * sizep)129 pkt_cap_next(pkt_cap_iter_t *iter, const void **pkt_buf, uint_t *sizep)
130 {
131 	size_t remain = iter->pci_sz - iter->pci_offset;
132 
133 	if (remain < sizeof (snoop_pkt_hdr_t)) {
134 		return (false);
135 	}
136 
137 	const snoop_pkt_hdr_t *hdr =
138 	    (const snoop_pkt_hdr_t *)&iter->pci_base[iter->pci_offset];
139 
140 	const uint_t msg_sz = ntohl(hdr->sph_msglen);
141 	const uint_t total_sz = ntohl(hdr->sph_totlen);
142 	if (remain < total_sz || remain < msg_sz) {
143 		return (false);
144 	}
145 
146 	*pkt_buf = (const void *)&hdr[1];
147 	*sizep = msg_sz;
148 	iter->pci_offset += total_sz;
149 	return (true);
150 }
151 
152 static ktest_hdl_t *kthdl = NULL;
153 const char *mac_cksum_cmd = "";
154 
155 struct payload_opts {
156 	uint_t		po_padding;
157 	boolean_t	po_cksum_partial;
158 	boolean_t	po_cksum_full;
159 	boolean_t	po_cksum_ipv4;
160 	boolean_t	po_split_ether;
161 	uint_t		po_split_manual;
162 };
163 
164 static char *
build_payload(const void * pkt_buf,uint_t pkt_sz,const struct payload_opts * popts,size_t * payload_sz)165 build_payload(const void *pkt_buf, uint_t pkt_sz,
166     const struct payload_opts *popts, size_t *payload_sz)
167 {
168 	nvlist_t *payload = fnvlist_alloc();
169 	fnvlist_add_byte_array(payload, "pkt_bytes",
170 	    (uchar_t *)pkt_buf, pkt_sz);
171 	if (popts->po_padding) {
172 		fnvlist_add_uint32(payload, "padding", popts->po_padding);
173 	}
174 	if (popts->po_cksum_partial) {
175 		fnvlist_add_boolean(payload, "cksum_partial");
176 	}
177 	if (popts->po_cksum_full) {
178 		fnvlist_add_boolean(payload, "cksum_full");
179 	}
180 	if (popts->po_cksum_ipv4) {
181 		fnvlist_add_boolean(payload, "cksum_ipv4");
182 	}
183 
184 	uint_t nsplit = 0;
185 	uint32_t splits[2];
186 	if (popts->po_split_ether) {
187 		splits[nsplit++] = sizeof (struct ether_header);
188 	}
189 	if (popts->po_split_manual != 0) {
190 		splits[nsplit++] = popts->po_split_manual;
191 	}
192 	if (nsplit > 0) {
193 		fnvlist_add_uint32_array(payload, "cksum_splits", splits,
194 		    nsplit);
195 	}
196 
197 	char *packed = fnvlist_pack(payload, payload_sz);
198 	nvlist_free(payload);
199 
200 	return (packed);
201 }
202 
203 static void
mac_cksum_usage(void)204 mac_cksum_usage(void)
205 {
206 	(void) fprintf(stderr, "usage: %s [flags] [opts] <cap_file>\n\n"
207 	    "Flags:\n"
208 	    "\t-4\temulate HCK_IPV4_HDRCKSUM\n"
209 	    "\t-f\temulate HCK_FULLCKSUM\t(cannot be used with -p)\n"
210 	    "\t-p\temulate HCK_PARTIALCKSUM\t(cannot be used with -f)\n"
211 	    "\t-e\tsplit mblk after Ethernet header\n"
212 	    "Options:\n"
213 	    "\t-b <len>\tpad mblk with <len> bytes (must be even)\n"
214 	    "\t-s <len>\tsplit mblk after len bytes (must be even)\n"
215 	    "\t\t\tif -e is specified, will be applied after that split\n"
216 	    "Arguments:\n"
217 	    "\t<cap_file> is a snoop capture of packets to test.\n"
218 	    "\tAny TCP or UDP packets (or plain IPv4) are expected to have\n"
219 	    "\tcorrect checksums.  The emulated results will be compared\n"
220 	    "\tagainst those sums in the packet (assuming them proper)\n",
221 	    mac_cksum_cmd);
222 	exit(EXIT_FAILURE);
223 }
224 
225 int
main(int argc,char * argv[])226 main(int argc, char *argv[])
227 {
228 	/* Peel off command name for usage */
229 	mac_cksum_cmd = argv[0];
230 	argc--;
231 	argv++;
232 	optind = 0;
233 
234 	struct payload_opts popts = { 0 };
235 	int c;
236 	while ((c = getopt(argc, argv, "4fpeb:s:")) != -1) {
237 		switch (c) {
238 		case 'p':
239 			popts.po_cksum_partial = B_TRUE;
240 			break;
241 		case 'f':
242 			popts.po_cksum_full = B_TRUE;
243 			break;
244 		case '4':
245 			popts.po_cksum_ipv4 = B_TRUE;
246 			break;
247 		case 'b':
248 			errno = 0;
249 			popts.po_padding = strtoul(optarg, NULL, 0);
250 			if (errno != 0) {
251 				err(EXIT_FAILURE,
252 				    "invalid padding value %s", optarg);
253 			}
254 			break;
255 		case 'e':
256 			popts.po_split_ether = B_TRUE;
257 			break;
258 		case 's':
259 			errno = 0;
260 			popts.po_split_manual = strtoul(optarg, NULL, 0);
261 			if (errno != 0) {
262 				err(EXIT_FAILURE,
263 				    "invalid split value %s", optarg);
264 			}
265 			break;
266 
267 		case '?':
268 			warnx("unknown run option: -%c", optopt);
269 			mac_cksum_usage();
270 		}
271 	}
272 	argc -= optind;
273 	argv += optind;
274 
275 	if (argc != 1) {
276 		mac_cksum_usage();
277 	}
278 
279 	int fd = open(argv[0], O_RDONLY, 0);
280 	if (fd < 0) {
281 		err(EXIT_FAILURE, "could not open cap file %s", argv[0]);
282 	}
283 
284 	pkt_cap_iter_t *iter = pkt_cap_open(fd);
285 	if (iter == NULL) {
286 		err(EXIT_FAILURE, "unrecognized cap file %s", argv[0]);
287 	}
288 
289 	if (!ktest_mod_load("mac")) {
290 		err(EXIT_FAILURE, "could not load mac ktest module");
291 	}
292 	if ((kthdl = ktest_init()) == NULL) {
293 		err(EXIT_FAILURE, "could not initialize libktest");
294 	}
295 
296 	const void *pkt_buf;
297 	uint_t pkt_sz;
298 	uint_t count_pass = 0, count_fail = 0, count_skip = 0, idx = 0;
299 	while (pkt_cap_next(iter, &pkt_buf, &pkt_sz)) {
300 		ktest_run_req_t req = {
301 			.krq_module = "mac",
302 			.krq_suite = "checksum",
303 			.krq_test = "mac_sw_cksum_test",
304 		};
305 		size_t payload_sz;
306 		char *payload =
307 		    build_payload(pkt_buf, pkt_sz, &popts, &payload_sz);
308 		req.krq_input = (uchar_t *)payload;
309 		req.krq_input_len = (uint_t)payload_sz;
310 
311 		ktest_run_result_t result = { 0 };
312 		if (!ktest_run(kthdl, &req, &result)) {
313 			err(EXIT_FAILURE, "failure while attempting ktest run");
314 		}
315 		free(payload);
316 
317 		const char *code_name = ktest_code_name(result.krr_code);
318 		switch (result.krr_code) {
319 		case KTEST_CODE_PASS:
320 			count_pass++;
321 			break;
322 		case KTEST_CODE_SKIP:
323 			count_skip++;
324 			break;
325 		default:
326 			count_fail++;
327 			break;
328 		}
329 		(void) printf("%4u\t%s\t(len: %u)\n", idx, code_name, pkt_sz);
330 		if (result.krr_msg != NULL) {
331 			if (result.krr_code != KTEST_CODE_PASS) {
332 				(void) printf("MSG: %s\n", result.krr_msg);
333 			}
334 			free(result.krr_msg);
335 		}
336 		idx++;
337 	}
338 	if (idx == 0) {
339 		errx(EXIT_FAILURE, "No valid packets found");
340 	} else if (idx != 1) {
341 		/* Summarize for > 1 packet */
342 		(void) printf("SUMMARY: %u PASS, %u SKIP, %u FAIL\n",
343 		    count_pass, count_skip, count_fail);
344 	}
345 
346 	pkt_cap_close(iter);
347 	ktest_fini(kthdl);
348 
349 	return (idx == count_pass ? EXIT_SUCCESS : EXIT_FAILURE);
350 }
351