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 #include <sys/uio.h>
34
35 #include <net/if.h>
36 #include <net/if_arp.h>
37 #include <net/if_fake_var.h>
38 #include <net/bpf.h>
39 #include <net/ethernet.h>
40
41 #include <netinet/ip.h>
42
43 #include <stdlib.h>
44 #include <string.h>
45 #include <strings.h>
46
47 #include "net_test_lib.h"
48 #include "bpflib.h"
49 #include "in_cksum.h"
50
51 T_GLOBAL_META(
52 T_META_NAMESPACE("xnu.net"),
53 T_META_ASROOT(true),
54 T_META_RADAR_COMPONENT_NAME("xnu"),
55 T_META_RADAR_COMPONENT_VERSION("networking"),
56 T_META_CHECK_LEAKS(false));
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 u_int value;
178
179 bpf_fd = bpf_new();
180 if (bpf_fd < 0) {
181 error = errno;
182 T_LOG("bpf_new");
183 goto done;
184 }
185 T_ASSERT_POSIX_SUCCESS(bpf_set_blen(bpf_fd, 128 * 1024), NULL);
186
187 T_ASSERT_POSIX_SUCCESS(bpf_get_blen(bpf_fd, &bdlen), NULL);
188
189 T_ASSERT_POSIX_SUCCESS(bpf_set_immediate(bpf_fd, 1), NULL);
190
191 T_ASSERT_POSIX_SUCCESS(bpf_setif(bpf_fd, ifname), "bpf set if %s",
192 ifname1);
193
194 T_ASSERT_POSIX_SUCCESS(bpf_set_see_sent(bpf_fd, 1), NULL);
195
196 T_ASSERT_POSIX_SUCCESS(bpf_set_header_complete(bpf_fd, 1), NULL);
197
198 #ifdef BIOCSWRITEMAX
199 T_ASSERT_POSIX_SUCCESS(bpf_set_write_size_max(bpf_fd, write_size_max), NULL);
200
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
210 #ifdef BIOCSBATCHWRITE
211 T_ASSERT_POSIX_SUCCESS(bpf_set_batch_write(bpf_fd, 1), NULL);
212
213 T_ASSERT_POSIX_SUCCESS(bpf_get_batch_write(bpf_fd, &value), NULL);
214
215 T_LOG("batch_write %u %s 1", value, value != 1 ? "!=" : "==");
216 #else
217 T_SKIP("BIOCSBATCHWRITE not supported");
218 #endif
219
220 struct timeval five_seconds = { .tv_sec = 5, .tv_usec = 0 };
221 T_ASSERT_POSIX_SUCCESS(bpf_set_timeout(bpf_fd, &five_seconds), NULL);
222
223 done:
224 *out_bdlen = bdlen;
225 *out_fd = bpf_fd;
226 return error;
227 }
228
229 static void
make_bootp_packet(struct iovec * iov,u_int ip_len,int id)230 make_bootp_packet(struct iovec *iov, u_int ip_len, int id)
231 {
232 u_int payload_len;
233
234 if (ip_len == 0) {
235 payload_len = (u_int)sizeof(dhcp_min_payload);
236 } else {
237 T_ASSERT_GE((size_t)ip_len, sizeof(struct ip) + sizeof(struct udphdr) + sizeof(dhcp_min_payload),
238 "ip_len");
239 payload_len = ip_len - (sizeof(struct ip) + sizeof(struct udphdr));
240 }
241
242 struct ether_addr src_eaddr = {};
243 ifnet_get_lladdr(udp_fd, ifname1, &src_eaddr);
244
245 struct in_addr src_ip = { .s_addr = INADDR_ANY };
246 uint16_t src_port = 68;
247
248 struct ether_addr dst_eaddr = {};
249 memset(dst_eaddr.octet, 255, ETHER_ADDR_LEN);
250
251 struct in_addr dst_ip = { .s_addr = INADDR_BROADCAST };
252
253 uint16_t dst_port = 67;
254
255 void *payload = calloc(1, payload_len);
256
257 make_dhcp_payload((dhcp_min_payload_t)payload, &src_eaddr);
258
259 struct bootp *dhcp = (struct bootp *)payload;
260 dhcp->bp_xid = (uint32_t)id;
261
262 u_int pkt_size = ETHER_HDR_LEN + IP_MAXPACKET;
263 unsigned char *pkt = calloc(1, pkt_size);
264
265 u_int frame_length = ethernet_udp4_frame_populate((void *)pkt,
266 pkt_size,
267 &src_eaddr,
268 src_ip,
269 src_port,
270 &dst_eaddr,
271 dst_ip,
272 dst_port,
273 payload,
274 payload_len);
275
276 T_ASSERT_EQ(sizeof(struct bpf_hdr), BPF_WORDALIGN(sizeof(struct bpf_hdr)), "bpfhdr.bh_hdrlen == BPF_WORDALIGN(sizeof(struct bpf_hdr))");
277
278 struct bpf_hdr bpfhdr = {};
279 bpfhdr.bh_caplen = frame_length;
280 bpfhdr.bh_datalen = frame_length;
281 bpfhdr.bh_hdrlen = sizeof(struct bpf_hdr);
282
283 iov->iov_len = BPF_WORDALIGN(bpfhdr.bh_hdrlen + frame_length);
284 iov->iov_base = calloc(1, iov->iov_len);
285
286 T_LOG("iov_len %lu bh_hdrlen %u frame_length %u ip_len %u payload_len %u",
287 iov->iov_len, bpfhdr.bh_hdrlen, frame_length, ip_len, payload_len);
288
289 bcopy(&bpfhdr, iov->iov_base, bpfhdr.bh_hdrlen);
290 bcopy(pkt, (char *)iov->iov_base + bpfhdr.bh_hdrlen, frame_length);
291
292 free(pkt);
293 free(payload);
294 }
295
296 static void
do_bpf_write_batch(int count,const char * ifname,u_int ip_len,bool expect_success,u_int write_size_max)297 do_bpf_write_batch(int count, const char *ifname, u_int ip_len, bool expect_success, u_int write_size_max)
298 {
299 int bpf_fd = -1;
300 int bdlen = 0;
301 struct iovec *iovs = NULL;
302 int i;
303
304 T_ASSERT_POSIX_ZERO(create_bpf_on_interface(ifname, &bpf_fd, &bdlen, write_size_max), NULL);
305 T_LOG("bpf bdlen %d", bdlen);
306
307 /*
308 * Allocate an iovec for each packet that contains the BPF header + the data
309 */
310 iovs = calloc((size_t)count, sizeof(struct iovec));
311
312 ssize_t total_len = 0;
313 for (i = 0; i < count; i++) {
314 make_bootp_packet(&iovs[i], ip_len, i + 1);
315 total_len += iovs[i].iov_len;
316
317 struct bpf_hdr *h0 = (struct bpf_hdr *)iovs[i].iov_base;;
318
319 T_LOG("tv_sec %u tv_usec %u caplen %u datalen %u hdrlen %u",
320 h0->bh_tstamp.tv_sec, h0->bh_tstamp.tv_usec,
321 h0->bh_caplen, h0->bh_datalen, h0->bh_hdrlen);
322
323 HexDump(iovs[i].iov_base, MIN((size_t)iovs[i].iov_len, 512));
324
325 T_ASSERT_EQ((int)(iovs[i].iov_len % BPF_ALIGNMENT), 0, "iovs[i].iov_len %% BPF_ALIGNMENT == 0");
326 }
327 T_LOG("total_len %ld", total_len);
328
329 ssize_t nwritten;
330 nwritten = writev(bpf_fd, iovs, count);
331
332 T_LOG("bpf write returned %ld", nwritten);
333
334 if (expect_success) {
335 T_ASSERT_POSIX_SUCCESS(nwritten, "write bpf");
336 } else {
337 T_ASSERT_POSIX_FAILURE(nwritten, EMSGSIZE, "write bpf");
338 goto done;
339 }
340
341 T_LOG("bpf written %ld bytes over %lu", nwritten, total_len);
342
343 T_ASSERT_GE((size_t)nwritten, iovs[0].iov_len, "nwritten %lu >= iovs[0].iov_len %lu", nwritten, iovs[0].iov_len);
344
345 /*
346 * Give 100 ms for the packets to be captured
347 */
348 usleep(100000);
349
350 /*
351 * Read the batch and verify the content matches what we just sent.
352 */
353 unsigned char *buffer = calloc(1, (size_t)bdlen);
354 T_ASSERT_NOTNULL(buffer, "malloc()");
355
356 ssize_t nread = read(bpf_fd, buffer, (size_t)bdlen);
357
358 T_ASSERT_POSIX_SUCCESS(nread, "read bpf");
359
360 T_LOG("bpf read %ld bytes", nread);
361
362 /*
363 * We need at least the BPF header
364 */
365 T_ASSERT_GT((size_t)nread, sizeof(sizeof(struct bpf_hdr)), NULL);
366
367 unsigned char *bp = buffer;
368 unsigned char *ep = buffer + nread;
369
370 for (i = 0; i < count && bp < ep; i++) {
371 struct bpf_hdr *hp = (struct bpf_hdr *)(void *)bp;
372
373 T_LOG("tv_sec %u tv_usec %u caplen %u datalen %u hdrlen %u",
374 hp->bh_tstamp.tv_sec, hp->bh_tstamp.tv_usec,
375 hp->bh_caplen, hp->bh_datalen, hp->bh_hdrlen);
376
377 HexDump(bp, MIN((size_t)BPF_WORDALIGN(hp->bh_hdrlen + hp->bh_caplen), 512));
378
379 /*
380 * Note: The following will fail if there is parasitic traffic and that should not happen over feth
381 */
382 unsigned char *p0 = iovs[i].iov_base;
383 struct bpf_hdr *h0 = (struct bpf_hdr *)iovs[i].iov_base;;
384
385 T_ASSERT_EQ(h0->bh_caplen, hp->bh_caplen, "h0->bh_caplen %d == hp->bh_caplen %d", h0->bh_caplen, hp->bh_caplen);
386 T_ASSERT_EQ(h0->bh_datalen, hp->bh_datalen, "h0->bh_datalen %d == hp->bh_datalen %d", h0->bh_datalen, hp->bh_datalen);
387
388 T_ASSERT_EQ_INT(bcmp(h0->bh_hdrlen + p0, hp->bh_hdrlen + bp, hp->bh_caplen), 0, "bpf read same bytes as written iov %d", i);
389
390 bp += BPF_WORDALIGN(hp->bh_hdrlen + hp->bh_caplen);
391 }
392
393 if (buffer != NULL) {
394 free(buffer);
395 }
396 done:
397 if (bpf_fd != -1) {
398 close(bpf_fd);
399 }
400 }
401
402 static void
test_bpf_write_batch(int count,u_int data_len,int mtu,bool expect_success,u_int write_size_max)403 test_bpf_write_batch(int count, u_int data_len, int mtu, bool expect_success, u_int write_size_max)
404 {
405 init(mtu);
406
407 T_ASSERT_POSIX_ZERO(setup_feth_pair(mtu), NULL);
408
409 do_bpf_write_batch(count, ifname1, data_len, expect_success, write_size_max);
410 }
411
412 T_DECL(bpf_write_batch_dhcp, "BPF write DHCP feth MTU 1500")
413 {
414 test_bpf_write_batch(1, 0, 1500, true, 0);
415 }
416
417 T_DECL(bpf_write_batch_dhcp_x2, "BPF write DHCP feth MTU 1500 x 2")
418 {
419 test_bpf_write_batch(2, 0, 1500, true, 0);
420 }
421
422 T_DECL(bpf_write_batch_dhcp_x3, "BPF write DHCP feth MTU 1500 x 3")
423 {
424 test_bpf_write_batch(3, 0, 1500, true, 0);
425 }
426
427 T_DECL(bpf_write_batch_1020, "BPF write 1020 feth MTU 1500 x 2")
428 {
429 test_bpf_write_batch(2, 1020, 1500, true, 0);
430 }
431
432 T_DECL(bpf_write_batch_1021, "BPF write 1021 feth MTU 1500 x 2")
433 {
434 test_bpf_write_batch(2, 1021, 1500, true, 0);
435 }
436
437 T_DECL(bpf_write_batch_1022, "BPF write 1022 feth MTU 1500 x 2")
438 {
439 test_bpf_write_batch(2, 1022, 1500, true, 0);
440 }
441
442 T_DECL(bpf_write_batch_1023, "BPF write 1023 feth MTU 1500 x2 ")
443 {
444 test_bpf_write_batch(2, 1023, 1500, true, 0);
445 }
446