xref: /illumos-gate/usr/src/test/os-tests/tests/mac/mac_ktest_common.c (revision 10597944279b73141546abca67a8e947810e5bb2)
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