xref: /xnu-11215.61.5/tests/net_bond.c (revision 4f1223e81cd707a65cc109d0b8ad6653699da3c4)
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_bond.c
31  * - test if_bond.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 <netinet/in.h>
45 #include <netinet6/in6_var.h>
46 #include <netinet6/nd6.h>
47 #include <netinet/in.h>
48 #include <netinet/ip.h>
49 #include <netinet/udp.h>
50 #include <netinet/tcp.h>
51 #include <netinet/if_ether.h>
52 #include <netinet/ip6.h>
53 #include <netinet/icmp6.h>
54 #include <net/if_arp.h>
55 #include <net/bpf.h>
56 #include <net/if_fake_var.h>
57 #include <net/if_vlan_var.h>
58 #include <net/if_bond_var.h>
59 #include <sys/ioctl.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <errno.h>
63 #include <pthread.h>
64 #include <stdbool.h>
65 #include <TargetConditionals.h>
66 #include <darwintest_utils.h>
67 
68 #include "net_test_lib.h"
69 #include "inet_transfer.h"
70 #include "bpflib.h"
71 #include "in_cksum.h"
72 
73 #define TEN_NET                 0x0a000000
74 #define TEN_1_NET               (TEN_NET | 0x010000)
75 
76 static void
get_ipv4_address(u_int unit,u_int addr_index,struct in_addr * ip)77 get_ipv4_address(u_int unit, u_int addr_index, struct in_addr *ip)
78 {
79 	/* up to 255 units, 255 addresses */
80 	ip->s_addr = htonl(TEN_1_NET | (unit << 8) | addr_index);
81 	return;
82 }
83 
84 /**
85 ** Test Main
86 **/
87 static network_interface_pair           S_bond_pair;
88 static network_interface_pair_list_t    S_feth_pairs;
89 static network_interface_pair_list_t    S_vlan_pairs;
90 
91 
92 #define VLAN_UNIT_START                 200
93 #define FAKE_SYSCTL                     "net.link.fake"
94 #define FAKE_SYSCTL_BSD_MODE            FAKE_SYSCTL ".bsd_mode"
95 #define FAKE_SYSCTL_VLAN_TAGGING        FAKE_SYSCTL ".vlan_tagging"
96 
97 static int fake_bsd_mode;
98 static bool fake_bsd_mode_was_set;
99 static int fake_vlan_tagging;
100 static bool fake_vlan_tagging_was_set;
101 
102 static void
sysctl_set_integer(const char * name,int val,int * restore_val,bool * was_set)103 sysctl_set_integer(const char * name, int val, int * restore_val,
104     bool * was_set)
105 {
106 	int     error;
107 	size_t  len;
108 
109 	T_LOG("%s\n", __func__);
110 	len = sizeof(int);
111 	error = sysctlbyname(name, restore_val, &len, &val, sizeof(int));
112 	T_ASSERT_EQ(error, 0, "sysctl %s %d -> %d", name, *restore_val, val);
113 	*was_set = (*restore_val != val);
114 }
115 
116 static void
sysctl_restore_integer(const char * name,int restore_val,bool was_set)117 sysctl_restore_integer(const char * name, int restore_val, bool was_set)
118 {
119 	if (was_set) {
120 		int     error;
121 
122 		error = sysctlbyname(name, NULL, 0, &restore_val, sizeof(int));
123 		T_ASSERT_EQ(error, 0, "sysctl %s %d", name, restore_val);
124 	} else {
125 		T_LOG("sysctl %s not modified", name);
126 	}
127 }
128 
129 static void
fake_set_bsd_mode(bool enable)130 fake_set_bsd_mode(bool enable)
131 {
132 	sysctl_set_integer(FAKE_SYSCTL_BSD_MODE, enable ? 1 : 0,
133 	    &fake_bsd_mode, &fake_bsd_mode_was_set);
134 }
135 
136 static void
fake_restore_bsd_mode(void)137 fake_restore_bsd_mode(void)
138 {
139 	sysctl_restore_integer(FAKE_SYSCTL_BSD_MODE,
140 	    fake_bsd_mode, fake_bsd_mode_was_set);
141 }
142 
143 static void
fake_set_vlan_tagging(bool enable)144 fake_set_vlan_tagging(bool enable)
145 {
146 	sysctl_set_integer(FAKE_SYSCTL_VLAN_TAGGING, enable ? 1 : 0,
147 	    &fake_vlan_tagging, &fake_vlan_tagging_was_set);
148 }
149 
150 static void
fake_restore_vlan_tagging(void)151 fake_restore_vlan_tagging(void)
152 {
153 	sysctl_restore_integer(FAKE_SYSCTL_VLAN_TAGGING, fake_vlan_tagging,
154 	    fake_vlan_tagging_was_set);
155 }
156 
157 static void
cleanup_common(void)158 cleanup_common(void)
159 {
160 	if (G_debug) {
161 		T_LOG("Sleeping for 5 seconds\n");
162 		sleep(5);
163 	}
164 	fake_restore_bsd_mode();
165 	fake_restore_vlan_tagging();
166 	network_interface_destroy(&S_bond_pair.one);
167 	network_interface_destroy(&S_bond_pair.two);
168 	network_interface_pair_list_destroy(S_feth_pairs);
169 	network_interface_pair_list_destroy(S_vlan_pairs);
170 	return;
171 }
172 
173 static void
cleanup(void)174 cleanup(void)
175 {
176 	cleanup_common();
177 	return;
178 }
179 
180 static void
sigint_handler(__unused int sig)181 sigint_handler(__unused int sig)
182 {
183 	cleanup_common();
184 	signal(SIGINT, SIG_DFL);
185 }
186 
187 static void
test_traffic_for_pair(network_interface_pair_t pair,uint8_t af)188 test_traffic_for_pair(network_interface_pair_t pair, uint8_t af)
189 {
190 	inet_address    server;
191 
192 	T_LOG("Testing %s -> %s\n",
193 	    pair->one.if_name, pair->two.if_name);
194 	if (af == AF_INET) {
195 		server.v4 = pair->one.ip;
196 	} else {
197 		server.v6 = pair->one.ip6;
198 	}
199 	inet_test_traffic(af, &server, pair->one.if_name, pair->one.if_index,
200 	    pair->two.if_name, pair->two.if_index);
201 }
202 
203 static void
test_traffic_for_af(uint8_t af)204 test_traffic_for_af(uint8_t af)
205 {
206 	test_traffic_for_pair(&S_bond_pair, af);
207 
208 	for (u_int i = 0; i < S_vlan_pairs->count; i++) {
209 		network_interface_pair_t        pair;
210 
211 		pair = &S_vlan_pairs->list[i];
212 		test_traffic_for_pair(pair, af);
213 	}
214 }
215 
216 static void
network_interface_init(network_interface_t netif,const char * name,unsigned int unit,unsigned int address_index)217 network_interface_init(network_interface_t netif,
218     const char * name, unsigned int unit,
219     unsigned int address_index)
220 {
221 	network_interface_create(netif, name);
222 	get_ipv4_address(unit, address_index, &netif->ip);
223 	ifnet_add_ip_address(netif->if_name, netif->ip,
224 	    inet_class_c_subnet_mask);
225 	route_add_inet_scoped_subnet(netif->if_name, netif->if_index,
226 	    netif->ip, inet_class_c_subnet_mask);
227 }
228 
229 
230 #define BOND100_NAME    BOND_NAME "100"
231 #define BOND101_NAME    BOND_NAME "101"
232 
233 static void
bond_add_member(if_name_t bond,if_name_t member)234 bond_add_member(if_name_t bond, if_name_t member)
235 {
236 	struct ifreq            ifr;
237 	struct if_bond_req      ibr;
238 	int                     result;
239 	int                     s = inet_dgram_socket_get();
240 
241 	bzero(&ibr, sizeof(ibr));
242 	ibr.ibr_op = IF_BOND_OP_ADD_INTERFACE;
243 	strlcpy(ibr.ibr_ibru.ibru_if_name, member,
244 	    sizeof(ibr.ibr_ibru.ibru_if_name));
245 
246 	bzero(&ifr, sizeof(ifr));
247 	strlcpy(ifr.ifr_name, bond, sizeof(ifr.ifr_name));
248 	ifr.ifr_data = (caddr_t)&ibr;
249 	result = ioctl(s, SIOCSIFBOND, &ifr);
250 	T_ASSERT_POSIX_SUCCESS(result, "SIOCSIFBOND(%s) %s",
251 	    bond, member);
252 }
253 
254 
255 static void
initialize_bond_pair(u_int n)256 initialize_bond_pair(u_int n)
257 {
258 	network_interface_t             one;
259 	network_interface_t             two;
260 	network_interface_pair_t        scan;
261 
262 	one = &S_bond_pair.one;
263 	network_interface_init(one, BOND100_NAME, 0, 1);
264 
265 	two = &S_bond_pair.two;
266 	network_interface_init(two, BOND101_NAME, 0, 2);
267 
268 	S_feth_pairs = network_interface_pair_list_alloc(n);
269 	scan = S_feth_pairs->list;
270 	for (size_t i = 0; i < n; i++, scan++) {
271 		network_interface_create(&scan->one, FETH_NAME);
272 		bond_add_member(one->if_name, scan->one.if_name);
273 
274 		network_interface_create(&scan->two, FETH_NAME);
275 		fake_set_peer(scan->one.if_name, scan->two.if_name);
276 		bond_add_member(two->if_name, scan->two.if_name);
277 	}
278 }
279 
280 
281 static void
vlan_interface_init(network_interface_t netif,const if_name_t phys,const char * name,uint16_t unit,unsigned int address_index)282 vlan_interface_init(network_interface_t netif, const if_name_t phys,
283     const char * name, uint16_t unit,
284     unsigned int address_index)
285 {
286 	network_interface_init(netif, name, unit, address_index);
287 	siocsifvlan(netif->if_name, phys, unit);
288 }
289 
290 static void
initialize_vlan_pairs(u_int n)291 initialize_vlan_pairs(u_int n)
292 {
293 	network_interface_pair_t        scan;
294 	int                             vlan_unit = VLAN_UNIT_START;
295 
296 	S_vlan_pairs = network_interface_pair_list_alloc(n);
297 	scan = S_vlan_pairs->list;
298 	for (size_t i = 0; i < n; i++, scan++) {
299 		if_name_t       name;
300 		uint16_t        tag = (uint16_t)(i + 1);
301 
302 		snprintf(name, sizeof(name), "%s%d", VLAN_NAME, vlan_unit++);
303 		vlan_interface_init(&scan->one, S_bond_pair.one.if_name,
304 		    name, tag, 1);
305 		snprintf(name, sizeof(name), "%s%d", VLAN_NAME, vlan_unit++);
306 		vlan_interface_init(&scan->two, S_bond_pair.two.if_name,
307 		    name, tag, 2);
308 	}
309 }
310 
311 static void
bond_test_traffic(bool hw_vlan)312 bond_test_traffic(bool hw_vlan)
313 {
314 #if !TARGET_OS_OSX
315 	T_SKIP("bond is only available on macOS");
316 #else /* TARGET_OS_OSX */
317 	signal(SIGINT, sigint_handler);
318 	T_ATEND(cleanup);
319 	T_LOG("Bond test %s\n",
320 	    hw_vlan ? "hardware tagging" : "software tagging");
321 	fake_set_bsd_mode(true);
322 	fake_set_vlan_tagging(hw_vlan);
323 	initialize_bond_pair(4);
324 	initialize_vlan_pairs(5);
325 	test_traffic_for_af(AF_INET6);
326 	test_traffic_for_af(AF_INET);
327 	if (G_debug) {
328 		T_LOG("Sleeping for 5 seconds\n");
329 		sleep(5);
330 	}
331 #endif /* TARGET_OS_OSX */
332 }
333 
334 T_DECL(net_if_bond_test_software_tagging,
335     "bond test traffic software tagging",
336     T_META_ASROOT(true))
337 {
338 	bond_test_traffic(false);
339 }
340 
341 T_DECL(net_if_bond_test_hardware_tagging,
342     "bond test hardware tagging",
343     T_META_ASROOT(true))
344 {
345 	bond_test_traffic(true);
346 }
347