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 #include <stdio.h> 17 #include <stdlib.h> 18 #include <fcntl.h> 19 #include <unistd.h> 20 #include <err.h> 21 #include <errno.h> 22 #include <strings.h> 23 #include <netinet/in.h> 24 #include <sys/mman.h> 25 #include <sys/dlpi.h> 26 #include <sys/stat.h> 27 #include <sys/sysmacros.h> 28 #include <sys/ethernet.h> 29 30 #include <libnvpair.h> 31 32 #include "mac_ktest_common.h" 33 34 static const char snoop_magic[8] = "snoop\0\0\0"; 35 static const uint_t snoop_acceptable_vers = 2; 36 37 pkt_cap_iter_t * 38 pkt_cap_open(int fd) 39 { 40 struct stat info; 41 if (fstat(fd, &info) != 0) { 42 return (NULL); 43 } 44 if (info.st_size < sizeof (snoop_file_hdr_t)) { 45 errno = EINVAL; 46 return (NULL); 47 } 48 49 const size_t page_sz = (size_t)sysconf(_SC_PAGESIZE); 50 const size_t map_sz = P2ROUNDUP(info.st_size, page_sz); 51 void *map = mmap(NULL, map_sz, PROT_READ, MAP_PRIVATE, fd, 0); 52 if (map == NULL) { 53 return (NULL); 54 } 55 56 const snoop_file_hdr_t *hdr = (const snoop_file_hdr_t *)map; 57 if (bcmp(&hdr->sfh_magic, snoop_magic, sizeof (hdr->sfh_magic)) != 0 || 58 ntohl(hdr->sfh_vers) != snoop_acceptable_vers || 59 ntohl(hdr->sfh_mac_type) != DL_ETHER) { 60 (void) munmap(map, map_sz); 61 errno = EINVAL; 62 return (NULL); 63 } 64 65 struct pkt_cap_iter *iter = malloc(sizeof (struct pkt_cap_iter)); 66 if (iter == NULL) { 67 (void) munmap(map, map_sz); 68 errno = ENOMEM; 69 return (NULL); 70 } 71 72 iter->pci_fd = fd; 73 iter->pci_base = (const char *)map; 74 iter->pci_map_sz = map_sz; 75 iter->pci_sz = info.st_size; 76 iter->pci_offset = sizeof (*hdr); 77 78 return (iter); 79 } 80 81 void 82 pkt_cap_close(pkt_cap_iter_t *iter) 83 { 84 (void) munmap((void *)iter->pci_base, iter->pci_map_sz); 85 (void) close(iter->pci_fd); 86 free(iter); 87 } 88 89 void 90 pkt_cap_reset(pkt_cap_iter_t *iter) 91 { 92 iter->pci_offset = sizeof (snoop_file_hdr_t); 93 } 94 95 bool 96 pkt_cap_next(pkt_cap_iter_t *iter, const void **pkt_buf, uint_t *sizep) 97 { 98 size_t remain = iter->pci_sz - iter->pci_offset; 99 100 if (remain < sizeof (snoop_pkt_hdr_t)) { 101 return (false); 102 } 103 104 const snoop_pkt_hdr_t *hdr = 105 (const snoop_pkt_hdr_t *)&iter->pci_base[iter->pci_offset]; 106 107 const uint_t msg_sz = ntohl(hdr->sph_msglen); 108 const uint_t total_sz = ntohl(hdr->sph_totlen); 109 if (remain < total_sz || remain < msg_sz) { 110 return (false); 111 } 112 113 *pkt_buf = (const void *)&hdr[1]; 114 *sizep = msg_sz; 115 iter->pci_offset += total_sz; 116 return (true); 117 } 118 119 char * 120 serialize_pkt_chain(pkt_cap_iter_t *iter, uint_t *sizep) 121 { 122 /* 123 * First, figure out how many bytes are needed. We're serializing 124 * down to `uint32_t` (len) + <bytes> for each packet. 125 */ 126 const void *pkt_buf = NULL; 127 *sizep = 0; 128 uint_t pkt_sz; 129 while (pkt_cap_next(iter, &pkt_buf, &pkt_sz)) { 130 *sizep += sizeof (uint32_t) + pkt_sz; 131 } 132 133 /* 134 * Rewalk, and copy all the bytes out. 135 */ 136 char *out = malloc(*sizep); 137 pkt_cap_reset(iter); 138 139 if (out == NULL) 140 return (out); 141 char *cur = out; 142 while (pkt_cap_next(iter, &pkt_buf, &pkt_sz)) { 143 uint32_t ps = pkt_sz; 144 bcopy(&ps, cur, sizeof (ps)); 145 cur += sizeof (ps); 146 bcopy(pkt_buf, cur, pkt_sz); 147 cur += pkt_sz; 148 } 149 150 return (out); 151 } 152 153 char * 154 build_payload(const void *pkt_buf, uint_t pkt_sz, 155 const void *out_pkt_buf, uint_t out_pkt_sz, 156 const struct payload_opts *popts, size_t *payload_sz) 157 { 158 nvlist_t *payload = fnvlist_alloc(); 159 fnvlist_add_byte_array(payload, "pkt_bytes", 160 (uchar_t *)pkt_buf, pkt_sz); 161 if (out_pkt_buf != NULL) { 162 fnvlist_add_byte_array(payload, "out_pkt_bytes", 163 (uchar_t *)out_pkt_buf, out_pkt_sz); 164 } 165 if (popts->po_mss) { 166 fnvlist_add_uint32(payload, "mss", popts->po_mss); 167 } 168 if (popts->po_padding) { 169 fnvlist_add_uint32(payload, "padding", popts->po_padding); 170 } 171 if (popts->po_cksum_partial) { 172 fnvlist_add_boolean(payload, "cksum_partial"); 173 } 174 if (popts->po_cksum_full) { 175 fnvlist_add_boolean(payload, "cksum_full"); 176 } 177 if (popts->po_cksum_ipv4) { 178 fnvlist_add_boolean(payload, "cksum_ipv4"); 179 } 180 181 uint_t nsplit = 0; 182 uint32_t splits[2]; 183 if (popts->po_split_ether) { 184 splits[nsplit++] = sizeof (struct ether_header); 185 } 186 if (popts->po_split_manual != 0) { 187 splits[nsplit++] = popts->po_split_manual; 188 } 189 if (nsplit > 0) { 190 fnvlist_add_uint32_array(payload, "cksum_splits", splits, 191 nsplit); 192 } 193 194 char *packed = fnvlist_pack(payload, payload_sz); 195 nvlist_free(payload); 196 197 return (packed); 198 } 199