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 *
pkt_cap_open(int fd)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
pkt_cap_close(pkt_cap_iter_t * iter)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
pkt_cap_reset(pkt_cap_iter_t * iter)90 pkt_cap_reset(pkt_cap_iter_t *iter)
91 {
92 iter->pci_offset = sizeof (snoop_file_hdr_t);
93 }
94
95 bool
pkt_cap_next(pkt_cap_iter_t * iter,const void ** pkt_buf,uint_t * sizep)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 *
serialize_pkt_chain(pkt_cap_iter_t * iter,uint_t * sizep)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 *
build_payload(const void * pkt_buf,uint_t pkt_sz,const void * out_pkt_buf,uint_t out_pkt_sz,const struct payload_opts * popts,size_t * payload_sz)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