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