xref: /xnu-12377.61.12/tests/net_test_lib.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
1 /*
2  * Copyright (c) 2019-2025 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 "net_test_lib.h"
30 #include "inet_transfer.h"
31 #include "bpflib.h"
32 #include "in_cksum.h"
33 #include <net/if_fake_var.h>
34 #include <net/if_vlan_var.h>
35 #include <net/if_bridgevar.h>
36 
37 #define RTM_BUFLEN (sizeof(struct rt_msghdr) + 6 * SOCK_MAXADDRLEN)
38 
39 #define ROUNDUP(a) \
40 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
41 
42 bool G_debug;
43 
44 struct in_addr inet_class_c_subnet_mask = {
45 	.s_addr = htonl(IN_CLASSC_NET)
46 };
47 
48 ether_addr_t ether_broadcast = {
49 	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
50 };
51 
52 /*
53  * local utility functions
54  */
55 static void
siocll_start(int s,const char * ifname)56 siocll_start(int s, const char * ifname)
57 {
58 	struct in6_aliasreq     ifra_in6;
59 	int                     result;
60 
61 	bzero(&ifra_in6, sizeof(ifra_in6));
62 	strncpy(ifra_in6.ifra_name, ifname, sizeof(ifra_in6.ifra_name));
63 	result = ioctl(s, SIOCLL_START, &ifra_in6);
64 	T_QUIET;
65 	T_ASSERT_POSIX_SUCCESS(result, "SIOCLL_START %s", ifname);
66 }
67 
68 static void
nd_flags_set(int s,const char * if_name,uint32_t set_flags,uint32_t clear_flags)69 nd_flags_set(int s, const char * if_name,
70     uint32_t set_flags, uint32_t clear_flags)
71 {
72 	uint32_t                new_flags;
73 	struct in6_ndireq       nd;
74 	int                     result;
75 
76 	bzero(&nd, sizeof(nd));
77 	strncpy(nd.ifname, if_name, sizeof(nd.ifname));
78 	result = ioctl(s, SIOCGIFINFO_IN6, &nd);
79 	T_ASSERT_POSIX_SUCCESS(result, "SIOCGIFINFO_IN6(%s)", if_name);
80 	new_flags = nd.ndi.flags;
81 	if (set_flags) {
82 		new_flags |= set_flags;
83 	}
84 	if (clear_flags) {
85 		new_flags &= ~clear_flags;
86 	}
87 	if (new_flags != nd.ndi.flags) {
88 		nd.ndi.flags = new_flags;
89 		result = ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd);
90 		T_ASSERT_POSIX_SUCCESS(result,
91 		    "SIOCSIFINFO_FLAGS(%s) 0x%x",
92 		    if_name, nd.ndi.flags);
93 	}
94 }
95 
96 
97 static void
siocprotoattach_in6(int s,const char * name)98 siocprotoattach_in6(int s, const char * name)
99 {
100 	struct in6_aliasreq ifra;
101 	int                 result;
102 
103 	bzero(&ifra, sizeof(ifra));
104 	strncpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
105 	result = ioctl(s, SIOCPROTOATTACH_IN6, &ifra);
106 	T_ASSERT_POSIX_SUCCESS(result, "SIOCPROTOATTACH_IN6(%s)", name);
107 }
108 
109 static void
sioc_a_or_d_ifaddr(int s,char * ifname,struct in_addr addr,struct in_addr mask,bool add)110 sioc_a_or_d_ifaddr(int s, char *ifname, struct in_addr addr, struct in_addr mask,
111     bool add)
112 {
113 	struct ifaliasreq       ifra;
114 	char                    ntopbuf_ip[INET_ADDRSTRLEN];
115 	char                    ntopbuf_mask[INET_ADDRSTRLEN];
116 	unsigned long           request;
117 	const char *            request_str;
118 	int                     ret;
119 	struct sockaddr_in *    sin;
120 
121 	bzero(&ifra, sizeof(ifra));
122 	strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
123 
124 	sin = (struct sockaddr_in *)(void *)&ifra.ifra_addr;
125 	sin->sin_len = sizeof(*sin);
126 	sin->sin_family = AF_INET;
127 	sin->sin_addr = addr;
128 
129 	sin = (struct sockaddr_in *)(void *)&ifra.ifra_mask;
130 	sin->sin_len = sizeof(*sin);
131 	sin->sin_family = AF_INET;
132 	sin->sin_addr = mask;
133 
134 	if (add) {
135 		request = SIOCAIFADDR;
136 		request_str = "SIOCAIFADDR";
137 	} else {
138 		request = SIOCDIFADDR;
139 		request_str = "SIOCDIFADDR";
140 	}
141 	ret = ioctl(s, request, &ifra);
142 	inet_ntop(AF_INET, &addr, ntopbuf_ip, sizeof(ntopbuf_ip));
143 	inet_ntop(AF_INET, &sin->sin_addr, ntopbuf_mask, sizeof(ntopbuf_mask));
144 	T_ASSERT_POSIX_SUCCESS(ret, "%s %s %s %s", request_str,
145 	    ifname, ntopbuf_ip, ntopbuf_mask);
146 }
147 
148 static void
siocaifaddr(int s,char * ifname,struct in_addr addr,struct in_addr mask)149 siocaifaddr(int s, char *ifname, struct in_addr addr, struct in_addr mask)
150 {
151 	sioc_a_or_d_ifaddr(s, ifname, addr, mask, true);
152 }
153 
154 static void
siocdifaddr(int s,char * ifname,struct in_addr addr,struct in_addr mask)155 siocdifaddr(int s, char *ifname, struct in_addr addr, struct in_addr mask)
156 {
157 	sioc_a_or_d_ifaddr(s, ifname, addr, mask, false);
158 }
159 
160 
161 /*
162  * utility functions
163  */
164 
165 #define NO_SOCKET       (-1)
166 
167 static int
_dgram_socket_get(int * sock_p,int af)168 _dgram_socket_get(int * sock_p, int af)
169 {
170 	int     sock = *sock_p;
171 
172 	if (sock != NO_SOCKET) {
173 		goto done;
174 	}
175 	sock = *sock_p = socket(af, SOCK_DGRAM, 0);
176 	T_QUIET;
177 	T_ASSERT_POSIX_SUCCESS(sock,
178 	    "socket(SOCK_DGRAM, %s, 0)",
179 	    af == AF_INET ? "AF_INET" : "AF_INET6");
180 done:
181 	return sock;
182 }
183 
184 static void
_socket_close(int * sock_p)185 _socket_close(int * sock_p)
186 {
187 	int sock = *sock_p;
188 
189 	if (sock != NO_SOCKET) {
190 		close(sock);
191 		*sock_p = NO_SOCKET;
192 	}
193 }
194 
195 static int inet_dgram_socket = NO_SOCKET;
196 
197 int
inet_dgram_socket_get(void)198 inet_dgram_socket_get(void)
199 {
200 	return _dgram_socket_get(&inet_dgram_socket, AF_INET);
201 }
202 
203 void
inet_dgram_socket_close(void)204 inet_dgram_socket_close(void)
205 {
206 	_socket_close(&inet_dgram_socket);
207 }
208 
209 
210 static int inet6_dgram_socket = NO_SOCKET;
211 
212 int
inet6_dgram_socket_get(void)213 inet6_dgram_socket_get(void)
214 {
215 	return _dgram_socket_get(&inet6_dgram_socket, AF_INET6);
216 }
217 
218 void
inet6_dgram_socket_close(void)219 inet6_dgram_socket_close(void)
220 {
221 	_socket_close(&inet6_dgram_socket);
222 }
223 
224 u_int
ethernet_udp4_frame_populate(void * buf,size_t buf_len,const ether_addr_t * src,struct in_addr src_ip,uint16_t src_port,const ether_addr_t * dst,struct in_addr dst_ip,uint16_t dst_port,const void * data,u_int data_len)225 ethernet_udp4_frame_populate(void * buf, size_t buf_len,
226     const ether_addr_t * src,
227     struct in_addr src_ip,
228     uint16_t src_port,
229     const ether_addr_t * dst,
230     struct in_addr dst_ip,
231     uint16_t dst_port,
232     const void * data, u_int data_len)
233 {
234 	ether_header_t *        eh_p;
235 	u_int                   frame_length;
236 	static int              ip_id;
237 	ip_udp_header_t *       ip_udp;
238 	char *                  payload;
239 	udp_pseudo_hdr_t *      udp_pseudo;
240 
241 	frame_length = (u_int)(sizeof(*eh_p) + sizeof(*ip_udp)) + data_len;
242 	if (buf_len < frame_length) {
243 		return 0;
244 	}
245 
246 	/* determine frame offsets */
247 	eh_p = (ether_header_t *)buf;
248 	ip_udp = (ip_udp_header_t *)(void *)(eh_p + 1);
249 	udp_pseudo = (udp_pseudo_hdr_t *)(void *)
250 	    (((char *)&ip_udp->udp) - sizeof(*udp_pseudo));
251 	payload = (char *)(eh_p + 1) + sizeof(*ip_udp);
252 
253 	/* ethernet_header */
254 	bcopy(src, eh_p->ether_shost, ETHER_ADDR_LEN);
255 	bcopy(dst, eh_p->ether_dhost, ETHER_ADDR_LEN);
256 	eh_p->ether_type = htons(ETHERTYPE_IP);
257 
258 	/* copy the data */
259 	bcopy(data, payload, data_len);
260 
261 	/* fill in UDP pseudo header (gets overwritten by IP header below) */
262 	bcopy(&src_ip, &udp_pseudo->src_ip, sizeof(src_ip));
263 	bcopy(&dst_ip, &udp_pseudo->dst_ip, sizeof(dst_ip));
264 	udp_pseudo->zero = 0;
265 	udp_pseudo->proto = IPPROTO_UDP;
266 	udp_pseudo->length = htons(sizeof(ip_udp->udp) + data_len);
267 
268 	/* fill in UDP header */
269 	ip_udp->udp.uh_sport = htons(src_port);
270 	ip_udp->udp.uh_dport = htons(dst_port);
271 	ip_udp->udp.uh_ulen = htons(sizeof(ip_udp->udp) + data_len);
272 	ip_udp->udp.uh_sum = 0;
273 	ip_udp->udp.uh_sum = in_cksum(udp_pseudo, (int)(sizeof(*udp_pseudo)
274 	    + sizeof(ip_udp->udp) + data_len));
275 
276 	/* fill in IP header */
277 	bzero(ip_udp, sizeof(ip_udp->ip));
278 	ip_udp->ip.ip_v = IPVERSION;
279 	ip_udp->ip.ip_hl = sizeof(struct ip) >> 2;
280 	ip_udp->ip.ip_ttl = MAXTTL;
281 	ip_udp->ip.ip_p = IPPROTO_UDP;
282 	bcopy(&src_ip, &ip_udp->ip.ip_src, sizeof(src_ip));
283 	bcopy(&dst_ip, &ip_udp->ip.ip_dst, sizeof(dst_ip));
284 	ip_udp->ip.ip_len = htons(sizeof(*ip_udp) + data_len);
285 	ip_udp->ip.ip_id = htons(ip_id++);
286 
287 	/* compute the IP checksum */
288 	ip_udp->ip.ip_sum = 0; /* needs to be zero for checksum */
289 	ip_udp->ip.ip_sum = in_cksum(&ip_udp->ip, sizeof(ip_udp->ip));
290 
291 	return frame_length;
292 }
293 
294 u_int
ethernet_udp6_frame_populate(void * buf,size_t buf_len,const ether_addr_t * src,struct in6_addr * src_ip,uint16_t src_port,const ether_addr_t * dst,struct in6_addr * dst_ip,uint16_t dst_port,const void * data,u_int data_len)295 ethernet_udp6_frame_populate(void * buf, size_t buf_len,
296     const ether_addr_t * src,
297     struct in6_addr *src_ip,
298     uint16_t src_port,
299     const ether_addr_t * dst,
300     struct in6_addr * dst_ip,
301     uint16_t dst_port,
302     const void * data, u_int data_len)
303 {
304 	ether_header_t *        eh_p;
305 	u_int                   frame_length;
306 	ip6_udp_header_t *      ip6_udp;
307 	char *                  payload;
308 	udp6_pseudo_hdr_t *     udp6_pseudo;
309 
310 	frame_length = (u_int)(sizeof(*eh_p) + sizeof(*ip6_udp)) + data_len;
311 	if (buf_len < frame_length) {
312 		return 0;
313 	}
314 
315 	/* determine frame offsets */
316 	eh_p = (ether_header_t *)buf;
317 	ip6_udp = (ip6_udp_header_t *)(void *)(eh_p + 1);
318 	udp6_pseudo = (udp6_pseudo_hdr_t *)(void *)
319 	    (((char *)&ip6_udp->udp) - sizeof(*udp6_pseudo));
320 	payload = (char *)(eh_p + 1) + sizeof(*ip6_udp);
321 
322 	/* ethernet_header */
323 	bcopy(src, eh_p->ether_shost, ETHER_ADDR_LEN);
324 	bcopy(dst, eh_p->ether_dhost, ETHER_ADDR_LEN);
325 	eh_p->ether_type = htons(ETHERTYPE_IPV6);
326 
327 	/* copy the data */
328 	bcopy(data, payload, data_len);
329 
330 	/* fill in UDP pseudo header (gets overwritten by IP header below) */
331 	bcopy(src_ip, &udp6_pseudo->src_ip, sizeof(*src_ip));
332 	bcopy(dst_ip, &udp6_pseudo->dst_ip, sizeof(*dst_ip));
333 	udp6_pseudo->zero = 0;
334 	udp6_pseudo->proto = IPPROTO_UDP;
335 	udp6_pseudo->length = htons(sizeof(ip6_udp->udp) + data_len);
336 
337 	/* fill in UDP header */
338 	ip6_udp->udp.uh_sport = htons(src_port);
339 	ip6_udp->udp.uh_dport = htons(dst_port);
340 	ip6_udp->udp.uh_ulen = htons(sizeof(ip6_udp->udp) + data_len);
341 	ip6_udp->udp.uh_sum = 0;
342 	ip6_udp->udp.uh_sum = in_cksum(udp6_pseudo, (int)(sizeof(*udp6_pseudo)
343 	    + sizeof(ip6_udp->udp) + data_len));
344 
345 	/* fill in IP header */
346 	bzero(&ip6_udp->ip6, sizeof(ip6_udp->ip6));
347 	ip6_udp->ip6.ip6_vfc = IPV6_VERSION;
348 	ip6_udp->ip6.ip6_nxt = IPPROTO_UDP;
349 	bcopy(src_ip, &ip6_udp->ip6.ip6_src, sizeof(*src_ip));
350 	bcopy(dst_ip, &ip6_udp->ip6.ip6_dst, sizeof(*dst_ip));
351 	ip6_udp->ip6.ip6_plen = htons(sizeof(struct udphdr) + data_len);
352 	/* ip6_udp->ip6.ip6_flow = ? */
353 	return frame_length;
354 }
355 
356 /**
357 ** interface management
358 **/
359 
360 void
ifnet_get_lladdr(const char * ifname,ether_addr_t * eaddr)361 ifnet_get_lladdr(const char * ifname, ether_addr_t * eaddr)
362 {
363 	int err;
364 	struct ifreq ifr;
365 	int s = inet_dgram_socket_get();
366 
367 	bzero(&ifr, sizeof(ifr));
368 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
369 	ifr.ifr_addr.sa_family = AF_LINK;
370 	ifr.ifr_addr.sa_len = ETHER_ADDR_LEN;
371 	err = ioctl(s, SIOCGIFLLADDR, &ifr);
372 	T_QUIET;
373 	T_ASSERT_POSIX_SUCCESS(err, "SIOCGIFLLADDR %s", ifname);
374 	bcopy(ifr.ifr_addr.sa_data, eaddr->octet, ETHER_ADDR_LEN);
375 	return;
376 }
377 
378 int
ifnet_set_lladdr(const char * ifname,ether_addr_t * eaddr)379 ifnet_set_lladdr(const char * ifname, ether_addr_t * eaddr)
380 {
381 	int err;
382 	int this_errno = 0;
383 	struct ifreq ifr;
384 	int s = inet_dgram_socket_get();
385 
386 	bzero(&ifr, sizeof(ifr));
387 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
388 	ifr.ifr_addr.sa_family = AF_LINK;
389 	ifr.ifr_addr.sa_len = ETHER_ADDR_LEN;
390 	bcopy(eaddr->octet, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN);
391 	err = ioctl(s, SIOCSIFLLADDR, &ifr);
392 	if (err != 0) {
393 		this_errno = errno;
394 		T_LOG("SIOCSIFLLADDR %s %s (%d)", ifname,
395 		    strerror(this_errno), this_errno);
396 	} else {
397 		T_LOG("SIOCSIFLLADDR %s success", ifname);
398 	}
399 	return err;
400 }
401 
402 
403 void
ifnet_attach_ip(char * name)404 ifnet_attach_ip(char * name)
405 {
406 	int             err;
407 	struct ifreq    ifr;
408 	int             s = inet_dgram_socket_get();
409 
410 	bzero(&ifr, sizeof(ifr));
411 	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
412 	err = ioctl(s, SIOCPROTOATTACH, &ifr);
413 	T_QUIET;
414 	T_ASSERT_POSIX_SUCCESS(err, "SIOCPROTOATTACH %s", ifr.ifr_name);
415 	return;
416 }
417 
418 void
ifnet_start_ipv6(const char * ifname)419 ifnet_start_ipv6(const char * ifname)
420 {
421 	int             s6 = inet6_dgram_socket_get();
422 
423 	/* attach IPv6 */
424 	siocprotoattach_in6(s6, ifname);
425 
426 	/* disable DAD to avoid 1 second delay (rdar://problem/73270401) */
427 	nd_flags_set(s6, ifname, 0, ND6_IFF_DAD);
428 
429 	/* start IPv6LL */
430 	siocll_start(s6, ifname);
431 
432 	return;
433 }
434 
435 int
ifnet_destroy(const char * ifname,bool fail_on_error)436 ifnet_destroy(const char * ifname, bool fail_on_error)
437 {
438 	int             err;
439 	struct ifreq    ifr;
440 	int             s = inet_dgram_socket_get();
441 
442 	bzero(&ifr, sizeof(ifr));
443 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
444 	err = ioctl(s, SIOCIFDESTROY, &ifr);
445 	if (fail_on_error) {
446 		T_QUIET;
447 		T_ASSERT_POSIX_SUCCESS(err, "SIOCSIFDESTROY %s", ifr.ifr_name);
448 	}
449 	if (err < 0) {
450 		T_LOG("SIOCSIFDESTROY %s", ifr.ifr_name);
451 	}
452 	return err;
453 }
454 
455 int
ifnet_set_flags(const char * ifname,uint16_t flags_set,uint16_t flags_clear)456 ifnet_set_flags(const char * ifname, uint16_t flags_set, uint16_t flags_clear)
457 {
458 	uint16_t        flags_after;
459 	uint16_t        flags_before;
460 	struct ifreq    ifr;
461 	int             ret;
462 	int             s = inet_dgram_socket_get();
463 
464 	bzero(&ifr, sizeof(ifr));
465 	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
466 	ret = ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr);
467 	if (ret != 0) {
468 		T_LOG("SIOCGIFFLAGS %s", ifr.ifr_name);
469 		return ret;
470 	}
471 	flags_before = (uint16_t)ifr.ifr_flags;
472 	ifr.ifr_flags |= flags_set;
473 	ifr.ifr_flags &= ~(flags_clear);
474 	flags_after = (uint16_t)ifr.ifr_flags;
475 	if (flags_before == flags_after) {
476 		/* nothing to do */
477 		ret = 0;
478 	} else {
479 		/* issue the ioctl */
480 		T_QUIET;
481 		T_ASSERT_POSIX_SUCCESS(ioctl(s, SIOCSIFFLAGS, &ifr),
482 		    "SIOCSIFFLAGS %s 0x%x",
483 		    ifr.ifr_name, (uint16_t)ifr.ifr_flags);
484 		if (G_debug) {
485 			T_LOG("setflags(%s set 0x%x clear 0x%x) 0x%x => 0x%x",
486 			    ifr.ifr_name, flags_set, flags_clear,
487 			    flags_before, flags_after);
488 		}
489 	}
490 	return ret;
491 }
492 
493 /* On some platforms with DEBUG kernel, we need to wait a while */
494 #define SIFCREATE_RETRY 600
495 
496 static int
ifnet_create_common(const char * ifname,char * ifname_out,size_t ifname_out_len)497 ifnet_create_common(const char * ifname, char *ifname_out, size_t ifname_out_len)
498 {
499 	int             error = 0;
500 	struct ifreq    ifr;
501 	int             s = inet_dgram_socket_get();
502 
503 	bzero(&ifr, sizeof(ifr));
504 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
505 
506 	for (int i = 0; i < SIFCREATE_RETRY; i++) {
507 		if (ioctl(s, SIOCIFCREATE, &ifr) < 0) {
508 			error = errno;
509 			T_LOG("SIOCSIFCREATE %s: %s", ifname,
510 			    strerror(error));
511 			if (error == EBUSY) {
512 				/* interface is tearing down, try again */
513 				usleep(10000);
514 			} else if (error == EEXIST) {
515 				/* interface exists, try destroying it */
516 				(void)ifnet_destroy(ifname, false);
517 			} else {
518 				/* unexpected failure */
519 				break;
520 			}
521 		} else {
522 			if (ifname_out != NULL) {
523 				strlcpy(ifname_out, ifr.ifr_name, ifname_out_len);
524 			}
525 			error = 0;
526 			break;
527 		}
528 	}
529 	if (error == 0) {
530 		error = ifnet_set_flags(ifname, IFF_UP, 0);
531 	}
532 	return error;
533 }
534 
535 int
ifnet_create(const char * ifname)536 ifnet_create(const char * ifname)
537 {
538 	return ifnet_create_common(ifname, NULL, 0);
539 }
540 
541 int
ifnet_create_2(char * ifname,size_t len)542 ifnet_create_2(char * ifname, size_t len)
543 {
544 	return ifnet_create_common(ifname, ifname, len);
545 }
546 
547 void
ifnet_add_ip_address(char * ifname,struct in_addr addr,struct in_addr mask)548 ifnet_add_ip_address(char *ifname, struct in_addr addr, struct in_addr mask)
549 {
550 	int             s = inet_dgram_socket_get();
551 
552 	siocaifaddr(s, ifname, addr, mask);
553 }
554 
555 void
ifnet_remove_ip_address(char * ifname,struct in_addr addr,struct in_addr mask)556 ifnet_remove_ip_address(char *ifname, struct in_addr addr, struct in_addr mask)
557 {
558 	int             s = inet_dgram_socket_get();
559 
560 	siocdifaddr(s, ifname, addr, mask);
561 }
562 
563 int
ifnet_set_mtu(const char * ifname,int mtu)564 ifnet_set_mtu(const char *ifname, int mtu)
565 {
566 	int error = 0;
567 	struct ifreq ifr = { 0 };
568 	int s = inet_dgram_socket_get();
569 
570 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
571 	ifr.ifr_mtu = mtu;
572 
573 	T_ASSERT_POSIX_SUCCESS(ioctl(s, SIOCSIFMTU, (caddr_t)&ifr), "set MTU");
574 
575 	return error;
576 }
577 
578 int
siocdrvspec(const char * ifname,u_long op,void * arg,size_t argsize,bool set)579 siocdrvspec(const char * ifname, u_long op, void *arg, size_t argsize, bool set)
580 {
581 	struct ifdrv    ifd;
582 	int             s = inet_dgram_socket_get();
583 
584 	memset(&ifd, 0, sizeof(ifd));
585 	strlcpy(ifd.ifd_name, ifname, sizeof(ifd.ifd_name));
586 	ifd.ifd_cmd = op;
587 	ifd.ifd_len = argsize;
588 	ifd.ifd_data = arg;
589 	return ioctl(s, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd);
590 }
591 
592 void
fake_set_peer(const char * feth,const char * feth_peer)593 fake_set_peer(const char * feth, const char * feth_peer)
594 {
595 	struct if_fake_request  iffr;
596 	int                     ret;
597 
598 	bzero((char *)&iffr, sizeof(iffr));
599 	if (feth_peer != NULL) {
600 		strlcpy(iffr.iffr_peer_name, feth_peer,
601 		    sizeof(iffr.iffr_peer_name));
602 	}
603 	ret = siocdrvspec(feth, IF_FAKE_S_CMD_SET_PEER,
604 	    &iffr, sizeof(iffr), true);
605 	T_QUIET;
606 	T_ASSERT_POSIX_SUCCESS(ret,
607 	    "SIOCDRVSPEC(%s, IF_FAKE_S_CMD_SET_PEER, %s)",
608 	    feth, (feth_peer != NULL) ? feth_peer : "<none>");
609 	T_LOG("%s peer %s\n", feth, feth_peer);
610 	return;
611 }
612 
613 void
siocsifvlan(const char * vlan,const char * phys,uint16_t tag)614 siocsifvlan(const char * vlan, const char * phys, uint16_t tag)
615 {
616 	int             result;
617 	struct ifreq    ifr;
618 	int             s = inet_dgram_socket_get();
619 	struct vlanreq  vlr;
620 
621 	bzero(&ifr, sizeof(ifr));
622 	strlcpy(ifr.ifr_name, vlan, sizeof(ifr.ifr_name));
623 	ifr.ifr_data = (caddr_t)&vlr;
624 	strlcpy(vlr.vlr_parent, phys, sizeof(vlr.vlr_parent));
625 	vlr.vlr_tag = tag;
626 	result = ioctl(s, SIOCSIFVLAN, &ifr);
627 	T_ASSERT_POSIX_SUCCESS(result, "SIOCSIFVLAN(%s) %s %d",
628 	    vlan, phys, tag);
629 }
630 
631 u_int
make_dhcp_payload(dhcp_min_payload_t payload,ether_addr_t * eaddr)632 make_dhcp_payload(dhcp_min_payload_t payload, ether_addr_t *eaddr)
633 {
634 	struct bootp *  dhcp;
635 	u_int           payload_length;
636 
637 	/* create a minimal BOOTP packet */
638 	payload_length = sizeof(*payload);
639 	dhcp = (struct bootp *)payload;
640 	bzero(dhcp, payload_length);
641 	dhcp->bp_op = BOOTREQUEST;
642 	dhcp->bp_htype = ARPHRD_ETHER;
643 	dhcp->bp_hlen = sizeof(*eaddr);
644 	bcopy(eaddr->octet, dhcp->bp_chaddr, sizeof(eaddr->octet));
645 	return payload_length;
646 }
647 
648 
649 
650 /*
651  * routing table
652  */
653 
654 /*
655  * Stolen/modified from IPMonitor/ip_plugin.c
656  */
657 /*
658  * Define: ROUTE_MSG_ADDRS_SPACE
659  * Purpose:
660  *   Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
661  *   3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
662  *   someone changes the code and doesn't think to modify this.
663  */
664 #define ROUTE_MSG_ADDRS_SPACE   (3 * sizeof(struct sockaddr_in) \
665 	                         + 2 * sizeof(struct sockaddr_dl) \
666 	                         + 128)
667 typedef struct {
668 	struct rt_msghdr    hdr;
669 	char                addrs[ROUTE_MSG_ADDRS_SPACE];
670 } route_msg;
671 
672 typedef unsigned short  IFIndex;
673 
674 typedef enum {
675 	kRouteFlagsIsScoped         = 0x0001,
676 	kRouteFlagsHasGateway       = 0x0002,
677 	kRouteFlagsIsHost           = 0x0004,
678 } RouteFlags;
679 
680 typedef struct {
681 	IFIndex         ifindex;
682 	RouteFlags      flags;
683 	struct in_addr  dest;
684 	struct in_addr  mask;
685 	struct in_addr  gateway;
686 	struct in_addr  ifa;
687 } IPv4Route, * IPv4RouteRef;
688 
689 /*
690  * Function: IPv4RouteApply
691  * Purpose:
692  *   Add or remove the specified route to/from the kernel routing table.
693  */
694 static int
IPv4RouteApply(IPv4RouteRef route,uint8_t cmd,int s)695 IPv4RouteApply(IPv4RouteRef route, uint8_t cmd, int s)
696 {
697 	size_t          len;
698 	int             ret = 0;
699 	route_msg       rtmsg;
700 	union {
701 		struct sockaddr_in *    in_p;
702 		struct sockaddr_dl *    dl_p;
703 		char *                  ptr;
704 	} rtaddr;
705 	static int      rtm_seq;
706 	static bool     rtm_seq_inited;
707 
708 	if (!rtm_seq_inited) {
709 		rtm_seq_inited = true;
710 		rtm_seq = (int)arc4random();
711 	}
712 	if (route->ifindex == 0) {
713 		T_LOG("no interface specified, ignoring %s",
714 		    inet_ntoa(route->dest));
715 		return ENXIO;
716 	}
717 	if (s < 0) {
718 		T_LOG("invalid routing socket");
719 		return EBADF;
720 	}
721 	memset(&rtmsg, 0, sizeof(rtmsg));
722 	rtmsg.hdr.rtm_type = cmd;
723 	rtmsg.hdr.rtm_version = RTM_VERSION;
724 	rtmsg.hdr.rtm_seq = rtm_seq++;
725 	rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_IFP;
726 	if (route->ifa.s_addr != 0) {
727 		rtmsg.hdr.rtm_addrs |= RTA_IFA;
728 	}
729 	rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC;
730 	if ((route->flags & kRouteFlagsIsHost) != 0) {
731 		rtmsg.hdr.rtm_flags |= RTF_HOST;
732 	} else {
733 		rtmsg.hdr.rtm_addrs |= RTA_NETMASK;
734 		if ((route->flags & kRouteFlagsHasGateway) == 0) {
735 			rtmsg.hdr.rtm_flags |= RTF_CLONING;
736 		}
737 	}
738 	if ((route->flags & kRouteFlagsHasGateway) != 0) {
739 		rtmsg.hdr.rtm_flags |= RTF_GATEWAY;
740 	}
741 	if ((route->flags & kRouteFlagsIsScoped) != 0) {
742 		rtmsg.hdr.rtm_index = route->ifindex;
743 		rtmsg.hdr.rtm_flags |= RTF_IFSCOPE;
744 	}
745 
746 	rtaddr.ptr = rtmsg.addrs;
747 
748 	/* dest */
749 	rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
750 	rtaddr.in_p->sin_family = AF_INET;
751 	rtaddr.in_p->sin_addr = route->dest;
752 	rtaddr.ptr += sizeof(*rtaddr.in_p);
753 
754 	/* gateway */
755 	if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) {
756 		/* gateway is an IP address */
757 		rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
758 		rtaddr.in_p->sin_family = AF_INET;
759 		rtaddr.in_p->sin_addr = route->gateway;
760 		rtaddr.ptr += sizeof(*rtaddr.in_p);
761 	} else {
762 		/* gateway is the interface itself */
763 		rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
764 		rtaddr.dl_p->sdl_family = AF_LINK;
765 		rtaddr.dl_p->sdl_index = route->ifindex;
766 		rtaddr.ptr += sizeof(*rtaddr.dl_p);
767 	}
768 
769 	/* mask */
770 	if ((rtmsg.hdr.rtm_addrs & RTA_NETMASK) != 0) {
771 		rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
772 		rtaddr.in_p->sin_family = AF_INET;
773 		rtaddr.in_p->sin_addr = route->mask;
774 		rtaddr.ptr += sizeof(*rtaddr.in_p);
775 	}
776 
777 	/* interface */
778 	if ((rtmsg.hdr.rtm_addrs & RTA_IFP) != 0) {
779 		rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
780 		rtaddr.dl_p->sdl_family = AF_LINK;
781 		rtaddr.dl_p->sdl_index = route->ifindex;
782 		rtaddr.ptr += sizeof(*rtaddr.dl_p);
783 	}
784 	/* interface address */
785 	if ((rtmsg.hdr.rtm_addrs & RTA_IFA) != 0) {
786 		rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
787 		rtaddr.in_p->sin_family = AF_INET;
788 		rtaddr.in_p->sin_addr = route->ifa;
789 		rtaddr.ptr += sizeof(*rtaddr.in_p);
790 	}
791 
792 	/* apply the route */
793 	len = (sizeof(rtmsg.hdr)
794 	    + (unsigned long)(rtaddr.ptr - (char *)rtmsg.addrs));
795 	rtmsg.hdr.rtm_msglen = (u_short)len;
796 	if (write(s, &rtmsg, len) == -1) {
797 		ret = errno;
798 		T_LOG("write routing socket failed, (%d) %s",
799 		    errno, strerror(errno));
800 	}
801 	return ret;
802 }
803 
804 static int routing_socket = -1;
805 
806 static int
routing_socket_get(void)807 routing_socket_get(void)
808 {
809 	if (routing_socket != NO_SOCKET) {
810 		goto done;
811 	}
812 	routing_socket = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE);
813 	T_QUIET;
814 	T_ASSERT_POSIX_SUCCESS(routing_socket,
815 	    "socket(PF_ROUTE, SOCK_RAW, PF_ROUTE)");
816 done:
817 	return routing_socket;
818 }
819 
820 void
route_add_inet_scoped_subnet(char * ifname,u_short if_index,struct in_addr ifa,struct in_addr mask)821 route_add_inet_scoped_subnet(char * ifname, u_short if_index,
822     struct in_addr ifa, struct in_addr mask)
823 {
824 	int             error;
825 	IPv4Route       route;
826 	int             rs = routing_socket_get();
827 
828 	bzero(&route, sizeof(route));
829 	route.flags |= kRouteFlagsIsScoped;
830 	route.ifa = ifa;
831 	route.ifindex = if_index;
832 	route.mask = mask;
833 	route.dest.s_addr = route.ifa.s_addr & route.mask.s_addr;
834 	T_QUIET;
835 	T_ASSERT_NE((int)route.ifindex, 0, "if_nametoindex(%s)", ifname);
836 	error = IPv4RouteApply(&route, RTM_ADD, rs);
837 	T_ASSERT_EQ(error, 0, "add scoped subnet route %s %s/24", ifname,
838 	    inet_ntoa(route.dest));
839 	return;
840 }
841 
842 /**
843 ** network_interface
844 **/
845 
846 void
network_interface_create(network_interface_t if_p,const if_name_t name)847 network_interface_create(network_interface_t if_p, const if_name_t name)
848 {
849 	int             error;
850 	size_t          len = sizeof(if_p->if_name);
851 
852 	strlcpy(if_p->if_name, name, len);
853 	error = ifnet_create_2(if_p->if_name, len);
854 	T_ASSERT_POSIX_SUCCESS(error, "ifnet_create_2 %s", if_p->if_name);
855 
856 	if_p->if_index = (u_short)if_nametoindex(if_p->if_name);
857 	T_QUIET;
858 	T_ASSERT_TRUE(if_p->if_index != 0, NULL);
859 	T_LOG("%s: created %s index %d\n",
860 	    __func__, if_p->if_name, if_p->if_index);
861 }
862 
863 void
network_interface_destroy(network_interface_t if_p)864 network_interface_destroy(network_interface_t if_p)
865 {
866 	if (if_p->if_index != 0) {
867 		ifnet_destroy(if_p->if_name, false);
868 		T_LOG("%s: destroyed %s\n", __func__, if_p->if_name);
869 	}
870 }
871 
872 static inline size_t
network_interface_pair_list_size(size_t count)873 network_interface_pair_list_size(size_t count)
874 {
875 	return offsetof(network_interface_pair_list, list[count]);
876 }
877 
878 network_interface_pair_list_t
network_interface_pair_list_alloc(u_int n)879 network_interface_pair_list_alloc(u_int n)
880 {
881 	network_interface_pair_list_t   list;
882 
883 	list = (network_interface_pair_list_t)
884 	    calloc(1, network_interface_pair_list_size(n));
885 	list->count = n;
886 	return list;
887 }
888 
889 void
network_interface_pair_list_destroy(network_interface_pair_list_t list)890 network_interface_pair_list_destroy(network_interface_pair_list_t list)
891 {
892 	network_interface_pair_t        scan;
893 
894 	if (list == NULL) {
895 		return;
896 	}
897 	scan = list->list;
898 	for (size_t i = 0; i < list->count; i++, scan++) {
899 		network_interface_destroy(&scan->one);
900 		network_interface_destroy(&scan->two);
901 	}
902 }
903 
904 bool
has_ipv4_default_route(void)905 has_ipv4_default_route(void)
906 {
907 	bool result = false;
908 	struct rt_msghdr *rtm = NULL;
909 	struct sockaddr_in sin = { 0 };
910 
911 	sin.sin_len = sizeof(struct sockaddr_in);
912 	sin.sin_family = AF_INET;
913 	sin.sin_addr.s_addr = INADDR_ANY;
914 
915 	T_QUIET; T_ASSERT_NOTNULL(rtm = (struct rt_msghdr *)calloc(1, RTM_BUFLEN), NULL);
916 
917 	rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
918 	rtm->rtm_version = RTM_VERSION;
919 	rtm->rtm_type = RTM_GET;
920 	rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY | RTF_HOST;
921 	rtm->rtm_addrs = RTA_DST;
922 	rtm->rtm_pid = getpid();
923 	rtm->rtm_seq = 1;
924 
925 	uint8_t *cp = (unsigned char *)(rtm + 1);
926 
927 	bcopy(&sin, cp, sin.sin_len);
928 	cp += ROUNDUP(sin.sin_len);
929 
930 	u_short len = (u_short)(cp - (uint8_t *)rtm);
931 
932 	rtm->rtm_msglen = len;
933 
934 	int fd;
935 	T_QUIET; T_ASSERT_POSIX_SUCCESS(fd = socket(PF_ROUTE, SOCK_RAW, 0), NULL);
936 
937 	ssize_t sent = send(fd, rtm, len, 0);
938 	if (sent == len) {
939 		result = true;
940 	} else {
941 		result = false;
942 	}
943 
944 	(void) close(fd);
945 	free(rtm);
946 
947 	return result;
948 }
949 
950 bool
has_ipv6_default_route(void)951 has_ipv6_default_route(void)
952 {
953 	bool result = false;
954 	struct rt_msghdr *rtm = NULL;
955 	struct sockaddr_in6 sin6 = { 0 };
956 
957 	sin6.sin6_len = sizeof(struct sockaddr_in6);
958 	sin6.sin6_family = AF_INET6;
959 
960 	T_QUIET; T_ASSERT_NOTNULL(rtm = (struct rt_msghdr *)calloc(1, RTM_BUFLEN), NULL);
961 
962 	rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in6);
963 	rtm->rtm_version = RTM_VERSION;
964 	rtm->rtm_type = RTM_GET;
965 	rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY | RTF_HOST;
966 	rtm->rtm_addrs = RTA_DST;
967 	rtm->rtm_pid = getpid();
968 	rtm->rtm_seq = 1;
969 
970 	uint8_t *cp = (unsigned char *)(rtm + 1);
971 
972 	bcopy(&sin6, cp, sin6.sin6_len);
973 	cp += ROUNDUP(sin6.sin6_len);
974 
975 	u_short len = (u_short)(cp - (uint8_t *)rtm);
976 
977 	rtm->rtm_msglen = len;
978 
979 	int fd;
980 	T_QUIET; T_ASSERT_POSIX_SUCCESS(fd = socket(PF_ROUTE, SOCK_RAW, 0), NULL);
981 
982 	ssize_t sent = send(fd, rtm, len, 0);
983 	if (sent == len) {
984 		result = true;
985 	} else {
986 		result = false;
987 	}
988 
989 	(void) close(fd);
990 	free(rtm);
991 
992 	return result;
993 }
994 
995 /*
996  * Bridge management
997  */
998 int
bridge_add_member(const char * bridge,const char * member)999 bridge_add_member(const char * bridge, const char * member)
1000 {
1001 	struct ifbreq           req;
1002 	int                     ret;
1003 
1004 	memset(&req, 0, sizeof(req));
1005 	strlcpy(req.ifbr_ifsname, member, sizeof(req.ifbr_ifsname));
1006 	ret = siocdrvspec(bridge, BRDGADD, &req, sizeof(req), true);
1007 	T_QUIET;
1008 	T_ASSERT_POSIX_SUCCESS(ret, "%s %s %s", __func__, bridge, member);
1009 	return ret;
1010 }
1011 
1012 /*
1013 **  stolen from bootp/bootplib/util.c
1014 **
1015 **/
1016 
1017 static int
rt_xaddrs(char * cp,const char * cplim,struct rt_addrinfo * rtinfo)1018 rt_xaddrs(char * cp, const char * cplim, struct rt_addrinfo * rtinfo)
1019 {
1020 	int         i;
1021 	struct sockaddr *   sa;
1022 
1023 	bzero(rtinfo->rti_info, sizeof(rtinfo->rti_info));
1024 	for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
1025 		if ((rtinfo->rti_addrs & (1 << i)) == 0) {
1026 			continue;
1027 		}
1028 		sa = (struct sockaddr *)cp;
1029 		if ((cp + sa->sa_len) > cplim) {
1030 			return EINVAL;
1031 		}
1032 		rtinfo->rti_info[i] = sa;
1033 		cp += ROUNDUP(sa->sa_len);
1034 	}
1035 	return 0;
1036 }
1037 
1038 /**
1039 **  stolen from bootp/IPConfiguration.bproj/iputil.c
1040 **
1041 ** inet6_addrlist_*
1042 **/
1043 
1044 #define s6_addr16 __u6_addr.__u6_addr16
1045 
1046 static char *
copy_if_info(unsigned int if_index,int af,int * ret_len_p)1047 copy_if_info(unsigned int if_index, int af, int *ret_len_p)
1048 {
1049 	char *          buf = NULL;
1050 	size_t          buf_len = 0;
1051 	int             mib[6];
1052 
1053 	mib[0] = CTL_NET;
1054 	mib[1] = PF_ROUTE;
1055 	mib[2] = 0;
1056 	mib[3] = af;
1057 	mib[4] = NET_RT_IFLIST;
1058 	mib[5] = (int)if_index;
1059 
1060 	*ret_len_p = 0;
1061 	if (sysctl(mib, 6, NULL, &buf_len, NULL, 0) < 0) {
1062 		T_LOG("sysctl() size failed: %s", strerror(errno));
1063 		goto failed;
1064 	}
1065 	buf_len *= 2; /* just in case something changes */
1066 	buf = malloc(buf_len);
1067 	if (sysctl(mib, 6, buf, &buf_len, NULL, 0) < 0) {
1068 		free(buf);
1069 		buf = NULL;
1070 		T_LOG("sysctl() failed: %s", strerror(errno));
1071 		goto failed;
1072 	}
1073 	*ret_len_p = (int)buf_len;
1074 
1075 failed:
1076 	return buf;
1077 }
1078 
1079 bool
inet6_get_linklocal_address(unsigned int if_index,struct in6_addr * ret_addr)1080 inet6_get_linklocal_address(unsigned int if_index, struct in6_addr *ret_addr)
1081 {
1082 	char *          buf = NULL;
1083 	char *          buf_end;
1084 	int             buf_len;
1085 	bool            found = FALSE;
1086 	char *scan;
1087 	struct rt_msghdr *rtm;
1088 
1089 	bzero(ret_addr, sizeof(*ret_addr));
1090 	buf = copy_if_info(if_index, AF_INET6, &buf_len);
1091 	if (buf == NULL) {
1092 		goto done;
1093 	}
1094 	buf_end = buf + buf_len;
1095 	for (scan = buf; scan < buf_end; scan += rtm->rtm_msglen) {
1096 		struct ifa_msghdr * ifam;
1097 		struct rt_addrinfo  info;
1098 
1099 		/* ALIGN: buf aligned (from calling copy_if_info), scan aligned,
1100 		 * cast ok. */
1101 		rtm = (struct rt_msghdr *)(void *)scan;
1102 		T_LOG("rtm_version %d rtm_type %d", rtm->rtm_version, rtm->rtm_type);
1103 		if (rtm->rtm_version != RTM_VERSION) {
1104 			continue;
1105 		}
1106 		if (rtm->rtm_type == RTM_NEWADDR) {
1107 			errno_t         error;
1108 			struct sockaddr_in6 *sin6_p;
1109 
1110 			ifam = (struct ifa_msghdr *)rtm;
1111 			info.rti_addrs = ifam->ifam_addrs;
1112 			error = rt_xaddrs((char *)(ifam + 1),
1113 			    ((char *)ifam) + ifam->ifam_msglen,
1114 			    &info);
1115 			if (error) {
1116 				T_LOG("couldn't extract rt_addrinfo %s (%d)\n",
1117 				    strerror(error), error);
1118 				goto done;
1119 			}
1120 			/* ALIGN: info.rti_info aligned (sockaddr), cast ok. */
1121 			sin6_p = (struct sockaddr_in6 *)(void *)info.rti_info[RTAX_IFA];
1122 			if (sin6_p == NULL
1123 			    || sin6_p->sin6_len < sizeof(struct sockaddr_in6)) {
1124 				continue;
1125 			}
1126 			if (IN6_IS_ADDR_LINKLOCAL(&sin6_p->sin6_addr)) {
1127 				*ret_addr = sin6_p->sin6_addr;
1128 				ret_addr->s6_addr16[1] = 0; /* mask scope id */
1129 				found = TRUE;
1130 				break;
1131 			}
1132 		}
1133 	}
1134 
1135 done:
1136 	if (buf != NULL) {
1137 		free(buf);
1138 	}
1139 	return found;
1140 }
1141 
1142 void
force_zone_gc(void)1143 force_zone_gc(void)
1144 {
1145 	kern_return_t kr = mach_zone_force_gc(mach_host_self());
1146 
1147 	if (kr != KERN_SUCCESS) {
1148 		T_LOG("mach_zone_force_gc(): failed with error %s\n", mach_error_string(kr));
1149 	} else {
1150 		T_LOG("mach_zone_force_gc(): success\n");
1151 	}
1152 }
1153