xref: /xnu-11215/tests/net_vlan.c (revision 8d741a5d)
1 /*
2  * Copyright (c) 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 /*
30  * net_vlan.c
31  * - test if_vlan.c functionality
32  */
33 
34 #include <darwintest.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <stddef.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/socket.h>
41 #include <arpa/inet.h>
42 #include <sys/event.h>
43 #include <net/if.h>
44 #include <net/if_vlan_var.h>
45 #include <netinet/in.h>
46 #include <netinet6/in6_var.h>
47 #include <netinet6/nd6.h>
48 #include <netinet/in.h>
49 #include <netinet/ip.h>
50 #include <netinet/udp.h>
51 #include <netinet/tcp.h>
52 #include <netinet/if_ether.h>
53 #include <netinet/ip6.h>
54 #include <netinet/icmp6.h>
55 #include <net/if_arp.h>
56 #include <net/bpf.h>
57 #include <sys/ioctl.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <errno.h>
61 #include <pthread.h>
62 #include <stdbool.h>
63 #include <TargetConditionals.h>
64 #include <darwintest_utils.h>
65 #include <os/variant_private.h>
66 
67 #include "net_test_lib.h"
68 #include "inet_transfer.h"
69 #include "bpflib.h"
70 #include "in_cksum.h"
71 
72 #define VLAN_UNIT_START         200 /* start at vlan200 to avoid conflicts */
73 #define TEN_NET                 0x0a000000
74 #define TEN_1_NET               (TEN_NET | 0x010000)
75 #define VLAN_TAG_START          1
76 
77 static void
get_ipv4_address(u_int unit,u_int addr_index,struct in_addr * ip)78 get_ipv4_address(u_int unit, u_int addr_index, struct in_addr *ip)
79 {
80 	/* up to 255 units, 255 addresses */
81 	ip->s_addr = htonl(TEN_1_NET | (unit << 8) | addr_index);
82 	return;
83 }
84 
85 /**
86 ** interface management
87 **/
88 
89 /**
90 ** Test Main
91 **/
92 static network_interface_pair_list_t    S_feth_pairs;
93 static network_interface_pair_list_t    S_vlan_pairs;
94 static const char *                     S_bridge;
95 
96 #define VLAN_SYSCTL                     "net.link.vlan"
97 #define VLAN_SYSCTL_ENABLED             VLAN_SYSCTL ".enabled"
98 
99 #define FAKE_SYSCTL                     "net.link.fake"
100 #define FAKE_SYSCTL_BSD_MODE            FAKE_SYSCTL ".bsd_mode"
101 #define FAKE_SYSCTL_VLAN_TAGGING        FAKE_SYSCTL ".vlan_tagging"
102 
103 static int fake_bsd_mode;
104 static bool fake_bsd_mode_was_set;
105 static int fake_vlan_tagging;
106 static bool fake_vlan_tagging_was_set;
107 
108 static void
sysctl_set_integer(const char * name,int val,int * restore_val,bool * was_set)109 sysctl_set_integer(const char * name, int val, int * restore_val,
110     bool * was_set)
111 {
112 	int     error;
113 	size_t  len;
114 
115 	T_LOG("%s\n", __func__);
116 	len = sizeof(int);
117 	error = sysctlbyname(name, restore_val, &len, &val, sizeof(int));
118 	T_ASSERT_EQ(error, 0, "sysctl %s %d -> %d", name, *restore_val, val);
119 	*was_set = (*restore_val != val);
120 }
121 
122 static void
sysctl_restore_integer(const char * name,int restore_val,bool was_set)123 sysctl_restore_integer(const char * name, int restore_val, bool was_set)
124 {
125 	if (was_set) {
126 		int     error;
127 
128 		error = sysctlbyname(name, NULL, 0, &restore_val, sizeof(int));
129 		T_ASSERT_EQ(error, 0, "sysctl %s %d", name, restore_val);
130 	} else {
131 		T_LOG("sysctl %s not modified", name);
132 	}
133 }
134 
135 static void
fake_set_bsd_mode(bool enable)136 fake_set_bsd_mode(bool enable)
137 {
138 	sysctl_set_integer(FAKE_SYSCTL_BSD_MODE, enable ? 1 : 0,
139 	    &fake_bsd_mode, &fake_bsd_mode_was_set);
140 }
141 
142 static void
fake_restore_bsd_mode(void)143 fake_restore_bsd_mode(void)
144 {
145 	sysctl_restore_integer(FAKE_SYSCTL_BSD_MODE,
146 	    fake_bsd_mode, fake_bsd_mode_was_set);
147 }
148 
149 static void
fake_set_vlan_tagging(bool enable)150 fake_set_vlan_tagging(bool enable)
151 {
152 	sysctl_set_integer(FAKE_SYSCTL_VLAN_TAGGING, enable ? 1 : 0,
153 	    &fake_vlan_tagging, &fake_vlan_tagging_was_set);
154 }
155 
156 static void
fake_restore_vlan_tagging(void)157 fake_restore_vlan_tagging(void)
158 {
159 	sysctl_restore_integer(FAKE_SYSCTL_VLAN_TAGGING, fake_vlan_tagging,
160 	    fake_vlan_tagging_was_set);
161 }
162 
163 #if !TARGET_OS_BRIDGE
164 #if !TARGET_OS_OSX
165 static bool
vlan_is_enabled(void)166 vlan_is_enabled(void)
167 {
168 	size_t  len = sizeof(int);
169 	int     val = 0;
170 
171 	(void)sysctlbyname(VLAN_SYSCTL_ENABLED, &val, &len, NULL, 0);
172 	return val != 0;
173 }
174 #endif /* !TARGET_OS_OSX */
175 #endif /* !TARGET_OS_BRIDGE */
176 
177 static void
cleanup_common(void)178 cleanup_common(void)
179 {
180 	if (G_debug) {
181 		T_LOG("Sleeping for 5 seconds\n");
182 		sleep(5);
183 	}
184 	if (S_bridge != NULL) {
185 		ifnet_destroy(S_bridge, false);
186 	}
187 	fake_restore_bsd_mode();
188 	fake_restore_vlan_tagging();
189 	network_interface_pair_list_destroy(S_feth_pairs);
190 	network_interface_pair_list_destroy(S_vlan_pairs);
191 	return;
192 }
193 
194 static void
cleanup(void)195 cleanup(void)
196 {
197 	cleanup_common();
198 	return;
199 }
200 
201 static void
sigint_handler(__unused int sig)202 sigint_handler(__unused int sig)
203 {
204 	cleanup_common();
205 	signal(SIGINT, SIG_DFL);
206 }
207 
208 static void
test_traffic_for_network_interfaces(network_interface_t one,network_interface_t two,uint8_t af)209 test_traffic_for_network_interfaces(network_interface_t one,
210     network_interface_t two,
211     uint8_t af)
212 {
213 	inet_address    server;
214 
215 	T_LOG("Testing %s -> %s\n",
216 	    one->if_name, two->if_name);
217 	if (af == AF_INET) {
218 		server.v4 = one->ip;
219 	} else {
220 		server.v6 = two->ip6;
221 	}
222 	inet_test_traffic(af, &server, one->if_name, one->if_index,
223 	    two->if_name, two->if_index);
224 }
225 
226 static void
test_traffic_for_pair(network_interface_pair_t pair,uint8_t af)227 test_traffic_for_pair(network_interface_pair_t pair, uint8_t af)
228 {
229 	test_traffic_for_network_interfaces(&pair->one, &pair->two, af);
230 }
231 
232 static void
test_traffic_for_af(uint8_t af)233 test_traffic_for_af(uint8_t af)
234 {
235 	test_traffic_for_pair(S_feth_pairs->list, af);
236 
237 	for (u_int i = 0; i < S_vlan_pairs->count; i++) {
238 		network_interface_pair_t        pair;
239 
240 		pair = &S_vlan_pairs->list[i];
241 		test_traffic_for_pair(pair, af);
242 	}
243 }
244 
245 static void
network_interface_assign_address(network_interface_t netif,unsigned int unit,unsigned int address_index)246 network_interface_assign_address(network_interface_t netif,
247     unsigned int unit, unsigned int address_index)
248 {
249 	get_ipv4_address(unit, address_index, &netif->ip);
250 	ifnet_add_ip_address(netif->if_name, netif->ip,
251 	    inet_class_c_subnet_mask);
252 	route_add_inet_scoped_subnet(netif->if_name, netif->if_index,
253 	    netif->ip, inet_class_c_subnet_mask);
254 }
255 
256 static void
initialize_feth_pairs(u_int n,bool need_address)257 initialize_feth_pairs(u_int n, bool need_address)
258 {
259 	network_interface_pair_t        scan;
260 
261 	S_feth_pairs = network_interface_pair_list_alloc(n);
262 	scan = S_feth_pairs->list;
263 	for (unsigned int i = 0; i < n; i++, scan++) {
264 		network_interface_create(&scan->one, FETH_NAME);
265 		network_interface_create(&scan->two, FETH_NAME);
266 		if (need_address) {
267 			network_interface_assign_address(&scan->one, i, 1);
268 			network_interface_assign_address(&scan->two, i, 2);
269 		}
270 		fake_set_peer(scan->one.if_name, scan->two.if_name);
271 	}
272 }
273 
274 static void
initialize_vlan_pairs(u_int n,bool need_address)275 initialize_vlan_pairs(u_int n, bool need_address)
276 {
277 	network_interface_pair_t        feth_pair;
278 	network_interface_pair_t        scan;
279 	int                             vlan_unit = VLAN_UNIT_START;
280 
281 	feth_pair = S_feth_pairs->list;
282 	S_vlan_pairs = network_interface_pair_list_alloc(n);
283 	scan = S_vlan_pairs->list;
284 	for (size_t i = 0; i < n; i++, scan++) {
285 		if_name_t       name;
286 		uint16_t        tag = (uint16_t)(i + VLAN_TAG_START);
287 
288 		snprintf(name, sizeof(name), "%s%d", VLAN_NAME, vlan_unit++);
289 		network_interface_create(&scan->one, name);
290 		siocsifvlan(scan->one.if_name, feth_pair->one.if_name, tag);
291 		if (need_address) {
292 			network_interface_assign_address(&scan->one, tag, 1);
293 		}
294 
295 		snprintf(name, sizeof(name), "%s%d", VLAN_NAME, vlan_unit++);
296 		network_interface_create(&scan->two, name);
297 		siocsifvlan(scan->two.if_name, feth_pair->two.if_name, tag);
298 		if (need_address) {
299 			network_interface_assign_address(&scan->two, tag, 2);
300 		}
301 	}
302 }
303 
304 static void
vlan_send_short_packet(void)305 vlan_send_short_packet(void)
306 {
307 	ether_addr_t                    eaddr;
308 	struct ether_vlan_header *      evl_p;
309 	int                             fd;
310 	size_t                          frame_length;
311 	const char *                    ifname;
312 	uint16_t *                      length;
313 	uint8_t *                       data;
314 	ssize_t                         n;
315 	int                             opt;
316 	ether_packet                    pkt;
317 
318 	ifname = S_feth_pairs->list->one.if_name;
319 	fd = bpf_new();
320 	T_ASSERT_GE(fd, 0, "bpf_new() %d", fd);
321 	bpf_set_traffic_class(fd, SO_TC_CTL);
322 	opt = 1;
323 	T_QUIET;
324 	T_ASSERT_POSIX_SUCCESS(ioctl(fd, FIONBIO, &opt), NULL);
325 	T_QUIET;
326 	T_ASSERT_POSIX_SUCCESS(bpf_set_immediate(fd, 1), NULL);
327 	T_ASSERT_POSIX_SUCCESS(bpf_setif(fd, ifname), "bpf set if %s",
328 	    ifname);
329 	T_QUIET;
330 	T_ASSERT_POSIX_SUCCESS(bpf_set_see_sent(fd, 0), NULL);
331 	T_QUIET;
332 	T_ASSERT_POSIX_SUCCESS(bpf_set_header_complete(fd, 1), NULL);
333 	T_QUIET;
334 	ifnet_get_lladdr(ifname, &eaddr);
335 
336 #define VLAN_SHORT_PAYLOAD_LENGTH       6
337 	/* send broadcast 802.3 LLC frame of length 6 (rdar://129012466) */
338 	bzero(&pkt, sizeof(pkt));
339 	frame_length = sizeof(*evl_p) + VLAN_SHORT_PAYLOAD_LENGTH;
340 	evl_p = (struct ether_vlan_header *)&pkt;
341 	bcopy(&ether_broadcast, evl_p->evl_dhost, sizeof(evl_p->evl_dhost));
342 	bcopy(&eaddr, evl_p->evl_shost, sizeof(evl_p->evl_shost));
343 	evl_p->evl_tag = htons(VLAN_TAG_START);
344 	evl_p->evl_encap_proto = htons(ETHERTYPE_VLAN);
345 	length = (uint16_t *)&evl_p->evl_proto;
346 	*length = htons(VLAN_SHORT_PAYLOAD_LENGTH);
347 	/* make it an LLC frame so that it will decode as a valid packet */
348 	data = (uint8_t *)(length + 1);
349 	*data++ = 0x00;
350 	*data++ = 0x0a;
351 	*data++ = 0x00;
352 	*data++ = 0x06;
353 	*data++ = 0x00;
354 	*data++ = 0x01;
355 	n = write(fd, &pkt, frame_length);
356 	T_ASSERT_EQ((size_t)n, frame_length, "write");
357 	close(fd);
358 }
359 
360 static void
vlan_test_check_skip(void)361 vlan_test_check_skip(void)
362 {
363 #if TARGET_OS_BRIDGE
364 	T_SKIP("Skipping test on bridgeOS");
365 #else /* TARGET_OS_BRIDGE */
366 #if !TARGET_OS_OSX
367 #define XNU_TEST_NET_VLAN       "com.apple.xnu.test.net_vlan"
368 	if (!vlan_is_enabled()) {
369 		if (os_variant_is_darwinos(XNU_TEST_NET_VLAN)) {
370 			T_FAIL("darwinos should support VLAN");
371 		}
372 		if (os_variant_has_factory_content(XNU_TEST_NET_VLAN)) {
373 			T_FAIL("non-ui should support VLAN");
374 		}
375 		T_SKIP("VLAN is not available on this os variant");
376 	}
377 #endif /* !TARGET_OS_OSX */
378 #endif /* TARGET_OS_BRIDGE */
379 }
380 
381 
382 static void
vlan_test_traffic(bool hw_vlan)383 vlan_test_traffic(bool hw_vlan)
384 {
385 	vlan_test_check_skip();
386 
387 	signal(SIGINT, sigint_handler);
388 	T_ATEND(cleanup);
389 	T_LOG("VLAN test %s\n",
390 	    hw_vlan ? "hardware tagging" : "software tagging");
391 	fake_set_bsd_mode(true);
392 	fake_set_vlan_tagging(hw_vlan);
393 	initialize_feth_pairs(1, true);
394 	initialize_vlan_pairs(5, true);
395 	test_traffic_for_af(AF_INET6);
396 	test_traffic_for_af(AF_INET);
397 	if (G_debug) {
398 		T_LOG("Sleeping for 5 seconds\n");
399 		sleep(5);
400 	}
401 }
402 
403 static void
vlan_test_bridged_traffic(bool hw_vlan)404 vlan_test_bridged_traffic(bool hw_vlan)
405 {
406 	errno_t                         err;
407 	network_interface_pair_t        feth_pair;
408 	network_interface_pair_t        vlan_pair;
409 
410 	vlan_test_check_skip();
411 
412 	signal(SIGINT, sigint_handler);
413 	T_ATEND(cleanup);
414 	T_LOG("VLAN test bridged %s\n",
415 	    hw_vlan ? "hardware tagging" : "software tagging");
416 	fake_set_bsd_mode(true);
417 	fake_set_vlan_tagging(hw_vlan);
418 	initialize_feth_pairs(2, false);
419 	initialize_vlan_pairs(1, false);
420 
421 	/* get the single VLAN pair */
422 	vlan_pair = S_vlan_pairs->list;
423 
424 	/* get the second FETH pair */
425 	feth_pair = S_feth_pairs->list + 1;
426 
427 	/* create a bridge */
428 	S_bridge = BRIDGE200;
429 	err = ifnet_create(S_bridge);
430 	T_ASSERT_EQ(err, 0, "ifnet_create %s", S_bridge);
431 
432 	/* add first VLAN to bridge */
433 	err = bridge_add_member(S_bridge, vlan_pair->one.if_name);
434 	T_ASSERT_EQ(err, 0, "bridge_add_member(%s, %s)", S_bridge,
435 	    vlan_pair->one.if_name);
436 
437 	/* assign address to second VLAN */
438 	network_interface_assign_address(&vlan_pair->two, 0, 1);
439 
440 	/* add feth in second pair to bridge */
441 	err = bridge_add_member(S_bridge, feth_pair->one.if_name);
442 	T_ASSERT_EQ(err, 0, "bridge_add_member(%s, %s)", S_bridge,
443 	    feth_pair->one.if_name);
444 
445 	/* assign address to second feth in second pair */
446 	network_interface_assign_address(&feth_pair->two, 0, 2);
447 
448 	test_traffic_for_network_interfaces(&vlan_pair->two, &feth_pair->two,
449 	    AF_INET);
450 	test_traffic_for_network_interfaces(&vlan_pair->two, &feth_pair->two,
451 	    AF_INET6);
452 }
453 
454 static void
vlan_test_short_packet(void)455 vlan_test_short_packet(void)
456 {
457 	vlan_test_check_skip();
458 
459 	signal(SIGINT, sigint_handler);
460 	T_ATEND(cleanup);
461 	T_LOG("VLAN test short packet\n");
462 	fake_set_bsd_mode(true);
463 	fake_set_vlan_tagging(false);
464 
465 	initialize_feth_pairs(1, true);
466 	initialize_vlan_pairs(1, true);
467 	/* send VLAN packet over feth */
468 	vlan_send_short_packet();
469 	if (G_debug) {
470 		T_LOG("Sleeping for 5 seconds\n");
471 		sleep(5);
472 	}
473 }
474 
475 T_DECL(net_if_vlan_test_software_tagging,
476     "vlan test traffic software tagging",
477     T_META_ASROOT(true))
478 {
479 	vlan_test_traffic(false);
480 }
481 
482 T_DECL(net_if_vlan_test_hardware_tagging,
483     "vlan test hardware tagging",
484     T_META_ASROOT(true))
485 {
486 	vlan_test_traffic(true);
487 }
488 
489 T_DECL(net_if_vlan_test_software_tagging_bridged,
490     "vlan test traffic software tagging bridged",
491     T_META_ASROOT(true))
492 {
493 	vlan_test_bridged_traffic(false);
494 }
495 
496 T_DECL(net_if_vlan_test_hardware_tagging_bridged,
497     "vlan test hardware tagging bridged",
498     T_META_ASROOT(true))
499 {
500 	vlan_test_bridged_traffic(true);
501 }
502 
503 T_DECL(net_if_vlan_test_short_packet,
504     "vlan test short packet",
505     T_META_ASROOT(true))
506 {
507 	vlan_test_short_packet();
508 }
509