xref: /xnu-10063.141.1/tests/bpf_write.c (revision d8b80295118ef25ac3a784134bcf95cd8e88109f)
1 /*
2  * Copyright (c) 2023 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <darwintest.h>
30 
31 #include <sys/ioctl.h>
32 #include <sys/sysctl.h>
33 
34 #include <net/if.h>
35 #include <net/if_arp.h>
36 #include <net/if_fake_var.h>
37 #include <net/bpf.h>
38 #include <net/ethernet.h>
39 
40 #include <netinet/ip.h>
41 
42 #include <stdlib.h>
43 #include <string.h>
44 #include <strings.h>
45 
46 #include "net_test_lib.h"
47 #include "bpflib.h"
48 #include "in_cksum.h"
49 
50 T_GLOBAL_META(
51 	T_META_NAMESPACE("xnu.net"),
52 	T_META_ASROOT(true),
53 	T_META_RADAR_COMPONENT_NAME("xnu"),
54 	T_META_RADAR_COMPONENT_VERSION("networking"),
55 	T_META_CHECK_LEAKS(false));
56 
57 
58 #define MAXBUF 32
59 static void
HexDump(void * data,size_t len)60 HexDump(void *data, size_t len)
61 {
62 	size_t i, j, k;
63 	unsigned char *ptr = (unsigned char *)data;
64 	unsigned char buf[3 * MAXBUF + 1];
65 
66 	for (i = 0; i < len; i += MAXBUF) {
67 		for (j = i, k = 0; j < i + MAXBUF && j < len; j++) {
68 			unsigned char msnbl = ptr[j] >> 4;
69 			unsigned char lsnbl = ptr[j] & 0x0f;
70 
71 			buf[k++] = msnbl < 10 ? msnbl + '0' : msnbl + 'a' - 10;
72 			buf[k++] = lsnbl < 10 ? lsnbl + '0' : lsnbl + 'a' - 10;
73 			if ((j % 2) == 1) {
74 				buf[k++] = ' ';
75 			}
76 			if ((j % MAXBUF) == MAXBUF - 1) {
77 				buf[k++] = ' ';
78 			}
79 		}
80 		buf[k] = 0;
81 		T_LOG("%5zd: %s\n", i, buf);
82 	}
83 }
84 
85 static int udp_fd = -1;
86 static char ifname1[IF_NAMESIZE];
87 static char ifname2[IF_NAMESIZE];
88 static int default_fake_max_mtu = 0;
89 
90 static void
cleanup(void)91 cleanup(void)
92 {
93 	if (udp_fd != -1) {
94 		(void)ifnet_destroy(udp_fd, ifname1, false);
95 		T_LOG("ifnet_destroy %s", ifname1);
96 
97 		(void)ifnet_destroy(udp_fd, ifname2, false);
98 		T_LOG("ifnet_destroy %s", ifname2);
99 	}
100 
101 	if (default_fake_max_mtu != 0) {
102 		T_LOG("sysctl net.link.fake.max_mtu=%d", default_fake_max_mtu);
103 		(void) sysctlbyname("net.link.fake.max_mtu", NULL, NULL, &default_fake_max_mtu, sizeof(int));
104 	}
105 
106 	if (udp_fd != -1) {
107 		(void) close(udp_fd);
108 	}
109 }
110 
111 static void
init(int mtu)112 init(int mtu)
113 {
114 	T_ATEND(cleanup);
115 
116 	udp_fd = inet_dgram_socket();
117 
118 	if (mtu > 0) {
119 		size_t oldlen = sizeof(int);
120 		T_ASSERT_POSIX_SUCCESS(sysctlbyname("net.link.fake.max_mtu", &default_fake_max_mtu, &oldlen, &mtu, sizeof(int)),
121 		    "sysctl net.link.fake.max_mtu %d -> %d", default_fake_max_mtu, mtu);
122 	}
123 }
124 
125 static int
set_if_mtu(const char * ifname,int mtu)126 set_if_mtu(const char *ifname, int mtu)
127 {
128 	int error = 0;
129 	struct ifreq ifr = {};
130 
131 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
132 	ifr.ifr_mtu = mtu;
133 
134 	T_ASSERT_POSIX_SUCCESS(ioctl(udp_fd, SIOCSIFMTU, (caddr_t)&ifr), NULL);
135 
136 	return error;
137 }
138 
139 static int
setup_feth_pair(int mtu)140 setup_feth_pair(int mtu)
141 {
142 	int error = 0;
143 
144 	strlcpy(ifname1, FETH_NAME, sizeof(ifname1));
145 	error = ifnet_create_2(udp_fd, ifname1, sizeof(ifname1));
146 	if (error != 0) {
147 		goto done;
148 	}
149 	T_LOG("created %s", ifname1);
150 
151 	strlcpy(ifname2, FETH_NAME, sizeof(ifname2));
152 	error = ifnet_create_2(udp_fd, ifname2, sizeof(ifname2));
153 	if (error != 0) {
154 		goto done;
155 	}
156 	T_LOG("created %s", ifname2);
157 
158 	ifnet_attach_ip(udp_fd, ifname1);
159 
160 	if ((error = fake_set_peer(udp_fd, ifname1, ifname2)) != 0) {
161 		goto done;
162 	}
163 	if (mtu != 0) {
164 		set_if_mtu(ifname1, mtu);
165 		set_if_mtu(ifname2, mtu);
166 	}
167 done:
168 	return error;
169 }
170 
171 static int
create_bpf_on_interface(const char * ifname,int * out_fd,int * out_bdlen,u_int write_size_max)172 create_bpf_on_interface(const char *ifname, int *out_fd, int *out_bdlen, u_int write_size_max)
173 {
174 	int bpf_fd = -1;
175 	int error = 0;
176 	int bdlen = 0;
177 
178 	bpf_fd = bpf_new();
179 	if (bpf_fd < 0) {
180 		error = errno;
181 		T_LOG("bpf_new");
182 		goto done;
183 	}
184 	T_ASSERT_POSIX_SUCCESS(bpf_set_blen(bpf_fd, 128 * 1024), NULL);
185 
186 	T_ASSERT_POSIX_SUCCESS(bpf_get_blen(bpf_fd, &bdlen), NULL);
187 
188 	T_ASSERT_POSIX_SUCCESS(bpf_set_immediate(bpf_fd, 1), NULL);
189 
190 	T_ASSERT_POSIX_SUCCESS(bpf_setif(bpf_fd, ifname), "bpf set if %s",
191 	    ifname1);
192 
193 	T_ASSERT_POSIX_SUCCESS(bpf_set_see_sent(bpf_fd, 1), NULL);
194 
195 	T_ASSERT_POSIX_SUCCESS(bpf_set_header_complete(bpf_fd, 0), NULL);
196 
197 #ifdef BIOCSWRITEMAX
198 	T_ASSERT_POSIX_SUCCESS(bpf_set_write_size_max(bpf_fd, write_size_max), NULL);
199 
200 	u_int value;
201 	T_ASSERT_POSIX_SUCCESS(bpf_get_write_size_max(bpf_fd, &value), NULL);
202 
203 	T_LOG("write_size_max %u %s value %u", write_size_max, write_size_max != value ? "!=" : "==", value);
204 #else
205 	if (write_size_max > 0) {
206 		T_SKIP("BIOCSWRITEMAX not supported");
207 	}
208 #endif
209 	struct timeval five_seconds = { .tv_sec = 5, .tv_usec = 0 };
210 	T_ASSERT_POSIX_SUCCESS(bpf_set_timeout(bpf_fd, &five_seconds), NULL);
211 
212 done:
213 	*out_bdlen = bdlen;
214 	*out_fd = bpf_fd;
215 	return error;
216 }
217 
218 static void
do_bpf_write(const char * ifname,u_int ip_len,bool expect_success,u_int write_size_max)219 do_bpf_write(const char *ifname, u_int ip_len, bool expect_success, u_int write_size_max)
220 {
221 	int bpf_fd = -1;
222 	int bdlen = 0;
223 	u_int payload_len;
224 
225 	if (ip_len == 0) {
226 		payload_len = (u_int)sizeof(dhcp_min_payload);
227 	} else {
228 		T_ASSERT_GE((size_t)ip_len, sizeof(struct ip) + sizeof(struct udphdr) + sizeof(dhcp_min_payload),
229 		    "ip_len");
230 		payload_len = ip_len - (sizeof(struct ip) + sizeof(struct udphdr));
231 	}
232 
233 	T_ASSERT_POSIX_ZERO(create_bpf_on_interface(ifname, &bpf_fd, &bdlen, write_size_max), NULL);
234 	T_LOG("bpf bdlen %d", bdlen);
235 
236 	struct ether_addr src_eaddr = {};
237 	ifnet_get_lladdr(udp_fd, ifname1, &src_eaddr);
238 
239 	struct in_addr src_ip = { .s_addr = INADDR_ANY };
240 	uint16_t src_port = 68;
241 
242 	struct ether_addr dst_eaddr = {};
243 	memset(dst_eaddr.octet, 255, ETHER_ADDR_LEN);
244 
245 	struct in_addr dst_ip = { .s_addr = INADDR_BROADCAST };
246 
247 	uint16_t dst_port = 67;
248 
249 	char *payload = calloc(1, payload_len);
250 
251 	make_dhcp_payload((dhcp_min_payload_t)(void *)payload, &src_eaddr);
252 
253 	u_int pkt_size = ETHER_HDR_LEN + IP_MAXPACKET;
254 	unsigned char *pkt = calloc(1, pkt_size);
255 
256 	u_int frame_length = ethernet_udp4_frame_populate((void *)pkt,
257 	    pkt_size,
258 	    &src_eaddr,
259 	    src_ip,
260 	    src_port,
261 	    &dst_eaddr,
262 	    dst_ip,
263 	    dst_port,
264 	    payload,
265 	    payload_len);
266 
267 	T_LOG("frame_length %u ip_len %u payload_len %u", frame_length, ip_len, payload_len);
268 
269 	T_ASSERT_GT((size_t)frame_length, (size_t)0, "frame_length must greater than zero");
270 
271 
272 	ssize_t nwritten;
273 	nwritten = write(bpf_fd, pkt, frame_length);
274 
275 	T_LOG("bpf write returned %ld", nwritten);
276 
277 	if (expect_success) {
278 		T_ASSERT_POSIX_SUCCESS(nwritten, "write bpf");
279 	} else {
280 		T_ASSERT_POSIX_FAILURE(nwritten, EMSGSIZE, "write bpf");
281 		goto done;
282 	}
283 
284 	T_LOG("bpf written %ld bytes over %u", nwritten, frame_length);
285 	HexDump(pkt, MIN((size_t)nwritten, 512));
286 
287 	unsigned char *buffer = calloc(1, (size_t)bdlen);
288 	T_ASSERT_NOTNULL(buffer, "malloc()");
289 
290 	ssize_t nread = read(bpf_fd, buffer, (size_t)bdlen);
291 
292 	T_ASSERT_POSIX_SUCCESS(nread, "read bpf");
293 
294 	T_LOG("bpf read %ld bytes", nread);
295 
296 	/*
297 	 * We need at least the BPF header
298 	 */
299 	T_ASSERT_GT((size_t)nread, sizeof(sizeof(struct bpf_hdr)), NULL);
300 
301 	/*
302 	 * Note: The following will fail if there is parasitic traffic and that should not happen
303 	 */
304 	struct bpf_hdr *hp = (struct bpf_hdr *)(void *)buffer;
305 	T_LOG("tv_sec %u tv_usec %u caplen %u datalen %u hdrlen %u",
306 	    hp->bh_tstamp.tv_sec, hp->bh_tstamp.tv_usec,
307 	    hp->bh_caplen, hp->bh_datalen, hp->bh_hdrlen);
308 
309 	HexDump(buffer, MIN((size_t)(hp->bh_hdrlen + hp->bh_caplen), 512));
310 
311 	T_ASSERT_EQ_LONG(nwritten, (long)hp->bh_caplen, "bpf read same size as written");
312 
313 	T_ASSERT_EQ_INT(bcmp(buffer + hp->bh_hdrlen, pkt, (size_t)nwritten), 0, "bpf read same bytes as written");
314 
315 	if (buffer != NULL) {
316 		free(buffer);
317 	}
318 done:
319 	if (bpf_fd != -1) {
320 		close(bpf_fd);
321 	}
322 }
323 
324 static void
test_bpf_write(u_int data_len,int mtu,bool expect_success,u_int write_size_max)325 test_bpf_write(u_int data_len, int mtu, bool expect_success, u_int write_size_max)
326 {
327 	init(mtu);
328 
329 	T_ASSERT_POSIX_ZERO(setup_feth_pair(mtu), NULL);
330 
331 	do_bpf_write(ifname1, data_len, expect_success, write_size_max);
332 }
333 
334 T_DECL(bpf_write_dhcp, "BPF write DHCP feth MTU 1500")
335 {
336 	test_bpf_write(0, 1500, true, 0);
337 }
338 
339 T_DECL(bpf_write_1024, "BPF write 1024 feth MTU 1500")
340 {
341 	test_bpf_write(1024, 1500, true, 0);
342 }
343 
344 T_DECL(bpf_write_1514, "BPF write 1500 feth MTU 1500")
345 {
346 	test_bpf_write(1514, 1500, true, 0);
347 }
348 
349 T_DECL(bpf_write_65482, "BPF write 65482 feth MTU 1500")
350 {
351 	test_bpf_write(65482, 1500, false, 0);
352 }
353 
354 T_DECL(bpf_write_2048, "BPF write 2048 feth MTU 1500")
355 {
356 	test_bpf_write(2048, 1500, false, 0);
357 }
358 
359 T_DECL(bpf_write_2048_mtu_4096, "BPF write 2048 feth MTU 4096 ")
360 {
361 	test_bpf_write(2048, 4096, true, 0);
362 }
363 
364 T_DECL(bpf_write_4110_mtu_4096, "BPF write 4110 feth MTU 4096 ")
365 {
366 	test_bpf_write(4110, 4096, true, 0);
367 }
368 
369 T_DECL(bpf_write_4096_mtu_9000, "BPF write 4096 feth MTU 9000")
370 {
371 	test_bpf_write(4096, 9000, true, 0);
372 }
373 
374 T_DECL(bpf_write_8192_mtu_9000, "BPF write 8192 feth MTU 9000")
375 {
376 	test_bpf_write(8192, 9000, true, 0);
377 }
378 
379 T_DECL(bpf_write_9000_mtu_9000, "BPF write 9000 feth MTU 9000")
380 {
381 	test_bpf_write(9000, 9000, true, 0);
382 }
383 
384 T_DECL(bpf_write_9018_mtu_9000, "BPF write 9018 feth MTU 9000")
385 {
386 	test_bpf_write(9018, 9000, true, 0);
387 }
388 
389 T_DECL(bpf_write_16370_mtu_9000, "BPF write 16370 feth MTU 9000")
390 {
391 	test_bpf_write(16370, 9000, false, 0);
392 }
393 
394 T_DECL(bpf_write_16370_mtu_9000_max_16370, "BPF write 16370 feth MTU 9000 max 16370")
395 {
396 	test_bpf_write(16370, 9000, true, 16370);
397 }
398