1 /*
2 * Copyright (c) 2023-2024 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 char ifname1[IF_NAMESIZE];
86 static char ifname2[IF_NAMESIZE];
87 static int default_fake_max_mtu = 0;
88
89 static void
cleanup(void)90 cleanup(void)
91 {
92 if (ifname1[0] != '\0') {
93 (void)ifnet_destroy(ifname1, false);
94 T_LOG("ifnet_destroy %s", ifname1);
95 }
96
97 if (ifname2[0] != '\0') {
98 (void)ifnet_destroy(ifname2, false);
99 T_LOG("ifnet_destroy %s", ifname2);
100 }
101
102 if (default_fake_max_mtu != 0) {
103 T_LOG("sysctl net.link.fake.max_mtu=%d", default_fake_max_mtu);
104 (void) sysctlbyname("net.link.fake.max_mtu", NULL, NULL, &default_fake_max_mtu, sizeof(int));
105 }
106 }
107
108 static void
init(int mtu)109 init(int mtu)
110 {
111 T_ATEND(cleanup);
112
113 if (mtu > 0) {
114 size_t oldlen = sizeof(int);
115 T_ASSERT_POSIX_SUCCESS(sysctlbyname("net.link.fake.max_mtu", &default_fake_max_mtu, &oldlen, &mtu, sizeof(int)),
116 "sysctl net.link.fake.max_mtu %d -> %d", default_fake_max_mtu, mtu);
117 }
118 }
119
120 static int
setup_feth_pair(int mtu)121 setup_feth_pair(int mtu)
122 {
123 int error = 0;
124
125 strlcpy(ifname1, FETH_NAME, sizeof(ifname1));
126 error = ifnet_create_2(ifname1, sizeof(ifname1));
127 if (error != 0) {
128 ifname1[0] = '\0';
129 goto done;
130 }
131 T_LOG("created %s", ifname1);
132
133 strlcpy(ifname2, FETH_NAME, sizeof(ifname2));
134 error = ifnet_create_2(ifname2, sizeof(ifname2));
135 if (error != 0) {
136 ifname2[0] = '\0';
137 goto done;
138 }
139 T_LOG("created %s", ifname2);
140
141 ifnet_attach_ip(ifname1);
142
143 fake_set_peer(ifname1, ifname2);
144 if (mtu != 0) {
145 ifnet_set_mtu(ifname1, mtu);
146 ifnet_set_mtu(ifname2, mtu);
147 }
148 done:
149 return error;
150 }
151
152 static int
create_bpf_on_interface(const char * ifname,int * out_fd,int * out_bdlen,u_int write_size_max)153 create_bpf_on_interface(const char *ifname, int *out_fd, int *out_bdlen, u_int write_size_max)
154 {
155 int bpf_fd = -1;
156 int error = 0;
157 int bdlen = 0;
158
159 bpf_fd = bpf_new();
160 if (bpf_fd < 0) {
161 error = errno;
162 T_LOG("bpf_new");
163 goto done;
164 }
165 T_ASSERT_POSIX_SUCCESS(bpf_set_blen(bpf_fd, 128 * 1024), NULL);
166
167 T_ASSERT_POSIX_SUCCESS(bpf_get_blen(bpf_fd, &bdlen), NULL);
168
169 T_ASSERT_POSIX_SUCCESS(bpf_set_immediate(bpf_fd, 1), NULL);
170
171 T_ASSERT_POSIX_SUCCESS(bpf_setif(bpf_fd, ifname), "bpf set if %s",
172 ifname1);
173
174 T_ASSERT_POSIX_SUCCESS(bpf_set_see_sent(bpf_fd, 1), NULL);
175
176 T_ASSERT_POSIX_SUCCESS(bpf_set_header_complete(bpf_fd, 0), NULL);
177
178 #ifdef BIOCSWRITEMAX
179 T_ASSERT_POSIX_SUCCESS(bpf_set_write_size_max(bpf_fd, write_size_max), NULL);
180
181 u_int value;
182 T_ASSERT_POSIX_SUCCESS(bpf_get_write_size_max(bpf_fd, &value), NULL);
183
184 T_LOG("write_size_max %u %s value %u", write_size_max, write_size_max != value ? "!=" : "==", value);
185 #else
186 if (write_size_max > 0) {
187 T_SKIP("BIOCSWRITEMAX not supported");
188 }
189 #endif
190 struct timeval five_seconds = { .tv_sec = 5, .tv_usec = 0 };
191 T_ASSERT_POSIX_SUCCESS(bpf_set_timeout(bpf_fd, &five_seconds), NULL);
192
193 done:
194 *out_bdlen = bdlen;
195 *out_fd = bpf_fd;
196 return error;
197 }
198
199 static void
do_bpf_write(const char * ifname,u_int ip_len,bool expect_success,u_int write_size_max)200 do_bpf_write(const char *ifname, u_int ip_len, bool expect_success, u_int write_size_max)
201 {
202 int bpf_fd = -1;
203 int bdlen = 0;
204 u_int payload_len;
205
206 if (ip_len == 0) {
207 payload_len = (u_int)sizeof(dhcp_min_payload);
208 } else {
209 T_ASSERT_GE((size_t)ip_len, sizeof(struct ip) + sizeof(struct udphdr) + sizeof(dhcp_min_payload),
210 "ip_len");
211 payload_len = ip_len - (sizeof(struct ip) + sizeof(struct udphdr));
212 }
213
214 T_ASSERT_POSIX_ZERO(create_bpf_on_interface(ifname, &bpf_fd, &bdlen, write_size_max), NULL);
215 T_LOG("bpf bdlen %d", bdlen);
216
217 struct ether_addr src_eaddr = { 0 };
218 ifnet_get_lladdr(ifname1, &src_eaddr);
219
220 struct in_addr src_ip = { .s_addr = INADDR_ANY };
221 uint16_t src_port = 68;
222
223 struct ether_addr dst_eaddr = { 0 };
224 memset(dst_eaddr.octet, 255, ETHER_ADDR_LEN);
225
226 struct in_addr dst_ip = { .s_addr = INADDR_BROADCAST };
227
228 uint16_t dst_port = 67;
229
230 char *payload = calloc(1, payload_len);
231
232 make_dhcp_payload((dhcp_min_payload_t)(void *)payload, &src_eaddr);
233
234 u_int pkt_size = ETHER_HDR_LEN + IP_MAXPACKET;
235 unsigned char *pkt = calloc(1, pkt_size);
236
237 u_int frame_length = ethernet_udp4_frame_populate((void *)pkt,
238 pkt_size,
239 &src_eaddr,
240 src_ip,
241 src_port,
242 &dst_eaddr,
243 dst_ip,
244 dst_port,
245 payload,
246 payload_len);
247
248 T_LOG("frame_length %u ip_len %u payload_len %u", frame_length, ip_len, payload_len);
249
250 T_ASSERT_GT((size_t)frame_length, (size_t)0, "frame_length must greater than zero");
251
252
253 ssize_t nwritten;
254 nwritten = write(bpf_fd, pkt, frame_length);
255
256 T_LOG("bpf write returned %ld", nwritten);
257
258 if (expect_success) {
259 T_ASSERT_POSIX_SUCCESS(nwritten, "write bpf");
260 } else {
261 T_ASSERT_POSIX_FAILURE(nwritten, EMSGSIZE, "write bpf");
262 goto done;
263 }
264
265 T_LOG("bpf written %ld bytes over %u", nwritten, frame_length);
266 HexDump(pkt, MIN((size_t)nwritten, 512));
267
268 unsigned char *buffer = calloc(1, (size_t)bdlen);
269 T_ASSERT_NOTNULL(buffer, "malloc()");
270
271 ssize_t nread = read(bpf_fd, buffer, (size_t)bdlen);
272
273 T_ASSERT_POSIX_SUCCESS(nread, "read bpf");
274
275 T_LOG("bpf read %ld bytes", nread);
276
277 /*
278 * We need at least the BPF header
279 */
280 T_ASSERT_GT((size_t)nread, sizeof(sizeof(struct bpf_hdr)), NULL);
281
282 /*
283 * Note: The following will fail if there is parasitic traffic and that should not happen
284 */
285 struct bpf_hdr *hp = (struct bpf_hdr *)(void *)buffer;
286 T_LOG("tv_sec %u tv_usec %u caplen %u datalen %u hdrlen %u",
287 hp->bh_tstamp.tv_sec, hp->bh_tstamp.tv_usec,
288 hp->bh_caplen, hp->bh_datalen, hp->bh_hdrlen);
289
290 HexDump(buffer, MIN((size_t)(hp->bh_hdrlen + hp->bh_caplen), 512));
291
292 T_ASSERT_EQ_LONG(nwritten, (long)hp->bh_caplen, "bpf read same size as written");
293
294 T_ASSERT_EQ_INT(bcmp(buffer + hp->bh_hdrlen, pkt, (size_t)nwritten), 0, "bpf read same bytes as written");
295
296 if (buffer != NULL) {
297 free(buffer);
298 }
299 done:
300 if (bpf_fd != -1) {
301 close(bpf_fd);
302 }
303 }
304
305 static void
test_bpf_write(u_int data_len,int mtu,bool expect_success,u_int write_size_max)306 test_bpf_write(u_int data_len, int mtu, bool expect_success, u_int write_size_max)
307 {
308 init(mtu);
309
310 T_ASSERT_POSIX_ZERO(setup_feth_pair(mtu), NULL);
311
312 do_bpf_write(ifname1, data_len, expect_success, write_size_max);
313 }
314
315 T_DECL(bpf_write_dhcp, "BPF write DHCP feth MTU 1500", T_META_TAG_VM_PREFERRED)
316 {
317 test_bpf_write(0, 1500, true, 0);
318 }
319
320 T_DECL(bpf_write_1024, "BPF write 1024 feth MTU 1500", T_META_TAG_VM_PREFERRED)
321 {
322 test_bpf_write(1024, 1500, true, 0);
323 }
324
325 T_DECL(bpf_write_1514, "BPF write 1500 feth MTU 1500", T_META_TAG_VM_PREFERRED)
326 {
327 test_bpf_write(1514, 1500, true, 0);
328 }
329
330 T_DECL(bpf_write_65482, "BPF write 65482 feth MTU 1500", T_META_TAG_VM_PREFERRED)
331 {
332 test_bpf_write(65482, 1500, false, 0);
333 }
334
335 T_DECL(bpf_write_2048, "BPF write 2048 feth MTU 1500", T_META_TAG_VM_PREFERRED)
336 {
337 test_bpf_write(2048, 1500, false, 0);
338 }
339
340 T_DECL(bpf_write_2048_mtu_4096, "BPF write 2048 feth MTU 4096 ", T_META_TAG_VM_PREFERRED)
341 {
342 test_bpf_write(2048, 4096, true, 0);
343 }
344
345 T_DECL(bpf_write_4110_mtu_4096, "BPF write 4110 feth MTU 4096 ", T_META_TAG_VM_PREFERRED)
346 {
347 test_bpf_write(4110, 4096, true, 0);
348 }
349
350 T_DECL(bpf_write_4096_mtu_9000, "BPF write 4096 feth MTU 9000", T_META_TAG_VM_PREFERRED)
351 {
352 test_bpf_write(4096, 9000, true, 0);
353 }
354
355 T_DECL(bpf_write_8192_mtu_9000, "BPF write 8192 feth MTU 9000", T_META_TAG_VM_PREFERRED)
356 {
357 test_bpf_write(8192, 9000, true, 0);
358 }
359
360 T_DECL(bpf_write_9000_mtu_9000, "BPF write 9000 feth MTU 9000", T_META_TAG_VM_PREFERRED)
361 {
362 test_bpf_write(9000, 9000, true, 0);
363 }
364
365 T_DECL(bpf_write_9018_mtu_9000, "BPF write 9018 feth MTU 9000", T_META_TAG_VM_PREFERRED)
366 {
367 test_bpf_write(9018, 9000, true, 0);
368 }
369
370 T_DECL(bpf_write_16370_mtu_9000, "BPF write 16370 feth MTU 9000", T_META_TAG_VM_PREFERRED)
371 {
372 test_bpf_write(16370, 9000, false, 0);
373 }
374
375 T_DECL(bpf_write_16370_mtu_9000_max_16370, "BPF write 16370 feth MTU 9000 max 16370", T_META_TAG_VM_PREFERRED)
376 {
377 test_bpf_write(16370, 9000, true, 16370);
378 }
379