xref: /illumos-gate/usr/src/test/os-tests/tests/mac/mac_lso.c (revision 10597944279b73141546abca67a8e947810e5bb2)
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_lso() ktests.
18  *
19  * This program builds up the packed nvlist payloads expected by the ktest for
20  * mac_sw_lso().  The caller provides a snoop(1) with one packet, which may not
21  * have valid checksums. This operates the runner similarly to mac_cksum:
22  * required checksum types are specified via option flags, and the output mblk
23  * chain is compared byte-for-byte with equality against a separate output
24  * snoop file.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <libgen.h>
33 
34 #include <libktest.h>
35 
36 #include "mac_ktest_common.h"
37 
38 static ktest_hdl_t *kthdl = NULL;
39 const char *mac_lso_cmd = "";
40 
41 static void __NORETURN
mac_lso_usage(void)42 mac_lso_usage(void)
43 {
44 	(void) fprintf(stderr, "Usage: %s [flags] [opts] <cap_file_in> "
45 	    "<cap_file_out>\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\t\tOne of -f/-p *must* be provided.\n"
51 	    "\t-e\tsplit mblk after Ethernet header\n"
52 	    "Options:\n"
53 	    "\t-b <len>\tpad mblk with <len> bytes (must be even)\n"
54 	    "\t-s <len>\tsplit mblk after len bytes (must be even)\n"
55 	    "\t\t\tif -e is specified, will be applied after that split\n"
56 	    "\t-m <len>\tmaximum segment size for LSO (default 1448)\n"
57 	    "Arguments:\n"
58 	    "\t<cap_file_in> is a snoop capture containing one test packet.\n"
59 	    "\t<cap_file_out> is a snoop capture of expected output packets.\n"
60 	    "\tInput packets may or may not have filled L3/L4 checksums, as\n"
61 	    "\tclients will have different expectations about which\n",
62 	    "\tchecksum offloads are available.\n",
63 	    mac_lso_cmd);
64 	exit(EXIT_FAILURE);
65 }
66 
67 int
main(int argc,char * argv[])68 main(int argc, char *argv[])
69 {
70 	/* Peel off command name for usage */
71 	mac_lso_cmd = basename(argv[0]);
72 	argc--;
73 	argv++;
74 	optind = 0;
75 	const char *errstr = NULL;
76 
77 	struct payload_opts popts = {
78 		.po_mss = 1448
79 	};
80 	int c;
81 	while ((c = getopt(argc, argv, "4fpeb:s:m:")) != -1) {
82 		switch (c) {
83 		case 'p':
84 			popts.po_cksum_partial = B_TRUE;
85 			break;
86 		case 'f':
87 			popts.po_cksum_full = B_TRUE;
88 			break;
89 		case '4':
90 			popts.po_cksum_ipv4 = B_TRUE;
91 			break;
92 		case 'b':
93 			popts.po_padding =
94 			    strtonumx(optarg, 0, UINT16_MAX, &errstr, 0);
95 			if (errstr != NULL) {
96 				errx(EXIT_FAILURE,
97 				    "invalid padding value %s: %s",
98 				    optarg, errstr);
99 			}
100 			break;
101 		case 'e':
102 			popts.po_split_ether = B_TRUE;
103 			break;
104 		case 's':
105 			popts.po_split_manual =
106 			    strtonumx(optarg, 0, UINT16_MAX, &errstr, 0);
107 			if (errstr != NULL) {
108 				errx(EXIT_FAILURE,
109 				    "invalid split value %s: %s",
110 				    optarg, errstr);
111 			}
112 			break;
113 		case 'm':
114 			popts.po_mss =
115 			    strtonumx(optarg, 0, UINT32_MAX, &errstr, 0);
116 			if (errstr != NULL) {
117 				errx(EXIT_FAILURE,
118 				    "invalid MSS value %s: %s",
119 				    optarg, errstr);
120 			}
121 			break;
122 
123 		case '?':
124 			warnx("unknown option: -%c", optopt);
125 			mac_lso_usage();
126 		}
127 	}
128 	argc -= optind;
129 	argv += optind;
130 
131 	if (argc != 2) {
132 		(void) fprintf(stderr,
133 		    "cap_file_in and cap_file_out are required arguments\n");
134 		mac_lso_usage();
135 	}
136 
137 	if (popts.po_cksum_full == popts.po_cksum_partial) {
138 		errx(EXIT_FAILURE, "specify exactly one of -f/-p");
139 	}
140 
141 	int in_fd = open(argv[0], O_RDONLY);
142 	if (in_fd < 0) {
143 		err(EXIT_FAILURE, "could not open input cap file %s", argv[0]);
144 	}
145 
146 	int out_fd = open(argv[1], O_RDONLY);
147 	if (out_fd < 0) {
148 		err(EXIT_FAILURE, "could not open output cap file %s", argv[0]);
149 	}
150 
151 	pkt_cap_iter_t *in_iter = pkt_cap_open(in_fd);
152 	if (in_iter == NULL) {
153 		err(EXIT_FAILURE, "unrecognized cap file %s", argv[0]);
154 	}
155 
156 	pkt_cap_iter_t *out_iter = pkt_cap_open(out_fd);
157 	if (out_iter == NULL) {
158 		err(EXIT_FAILURE, "unrecognized cap file %s", argv[1]);
159 	}
160 
161 	const void *pkt_buf = NULL;
162 	uint_t pkt_sz;
163 	if (!pkt_cap_next(in_iter, &pkt_buf, &pkt_sz)) {
164 		err(EXIT_FAILURE, "no packets in input capture");
165 	}
166 
167 	uint_t out_pkt_sz;
168 	char *out_pkt_buf = serialize_pkt_chain(out_iter, &out_pkt_sz);
169 	if (out_pkt_buf == NULL) {
170 		err(EXIT_FAILURE, "failed to read output packet stream");
171 	}
172 
173 	if ((kthdl = ktest_init()) == NULL) {
174 		err(EXIT_FAILURE, "could not initialize libktest");
175 	}
176 	if (!ktest_mod_load("mac")) {
177 		err(EXIT_FAILURE, "could not load mac ktest module");
178 	}
179 
180 	ktest_run_req_t req = {
181 		.krq_module = "mac",
182 		.krq_suite = "lso",
183 		.krq_test = "mac_sw_lso_test",
184 	};
185 	size_t payload_sz;
186 	char *payload =
187 	    build_payload(pkt_buf, pkt_sz, out_pkt_buf, out_pkt_sz, &popts,
188 	    &payload_sz);
189 	req.krq_input = (uchar_t *)payload;
190 	req.krq_input_len = (uint_t)payload_sz;
191 
192 	ktest_run_result_t result = { 0 };
193 	if (!ktest_run(kthdl, &req, &result)) {
194 		err(EXIT_FAILURE, "failure while attempting ktest run");
195 	}
196 	free(payload);
197 	free(out_pkt_buf);
198 
199 	const char *code_name = ktest_code_name(result.krr_code);
200 	(void) printf("%s\t(len: %u)\n", code_name, pkt_sz);
201 	if (result.krr_msg != NULL) {
202 		if (result.krr_code != KTEST_CODE_PASS) {
203 			(void) printf("MSG: %s\n", result.krr_msg);
204 		}
205 		free(result.krr_msg);
206 	}
207 
208 	pkt_cap_close(in_iter);
209 	pkt_cap_close(out_iter);
210 	ktest_fini(kthdl);
211 
212 	return (result.krr_code == KTEST_CODE_PASS ? EXIT_SUCCESS :
213 	    EXIT_FAILURE);
214 }
215