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 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 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