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