]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/ip_plugin.c
configd-1061.80.3.tar.gz
[apple/configd.git] / Plugins / IPMonitor / ip_plugin.c
1 /*
2 * Copyright (c) 2000-2019 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * ip_plugin.c
26 * - decides which interface will be made the "primary" interface,
27 * that is, the one with the default route assigned
28 */
29
30 /*
31 * Modification History
32 *
33 * July 19, 2000 Dieter Siegmund (dieter@apple.com)
34 * - initial revision
35 *
36 * November 15, 2000 Dieter Siegmund (dieter@apple.com)
37 * - changed to use new configuration model
38 *
39 * March 19, 2001 Dieter Siegmund (dieter@apple.com)
40 * - use service state instead of interface state
41 *
42 * July 16, 2001 Allan Nathanson (ajn@apple.com)
43 * - update to public SystemConfiguration.framework APIs
44 *
45 * August 28, 2001 Dieter Siegmund (dieter@apple.com)
46 * - specify the interface name when installing the default route
47 * - this ensures that default traffic goes to the highest priority
48 * service when multiple interfaces are configured to be on the same subnet
49 *
50 * September 16, 2002 Dieter Siegmund (dieter@apple.com)
51 * - don't elect a link-local service to be primary unless it's the only
52 * one that's available
53 *
54 * July 16, 2003 Dieter Siegmund (dieter@apple.com)
55 * - modifications to support IPv6
56 * - don't elect a service to be primary if it doesn't have a default route
57 *
58 * July 29, 2003 Dieter Siegmund (dieter@apple.com)
59 * - support installing a default route to a router that's not on our subnet
60 *
61 * March 22, 2004 Allan Nathanson (ajn@apple.com)
62 * - create expanded DNS configuration
63 *
64 * June 20, 2006 Allan Nathanson (ajn@apple.com)
65 * - add SMB configuration
66 *
67 * December 5, 2007 Dieter Siegmund (dieter@apple.com)
68 * - added support for multiple scoped routes
69 *
70 * November 13, 2013 Dieter Siegmund (dieter@apple.com)
71 * - added generic IPv4 routing support
72 */
73
74 #include <stdlib.h>
75 #include <unistd.h>
76 #include <string.h>
77 #include <stdio.h>
78 #include <sys/fcntl.h>
79 #include <sys/ioctl.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <net/route.h>
83 #include <net/if.h>
84 #include <net/if_dl.h>
85 #include <netinet/in.h>
86 #include <netinet/icmp6.h>
87 #include <netinet6/in6_var.h>
88 #include <netinet6/nd6.h>
89 #include <nw/sa_compare.h>
90 #include <arpa/inet.h>
91 #include <sys/sysctl.h>
92 #include <limits.h>
93 #include <notify.h>
94 #include <mach/mach_time.h>
95 #include <dispatch/dispatch.h>
96 #include <CommonCrypto/CommonDigest.h>
97
98 #include "ip_plugin.h"
99
100 #include <SystemConfiguration/SystemConfiguration.h>
101 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
102 #include <SystemConfiguration/SCValidation.h>
103 #include <SystemConfiguration/scprefs_observer.h>
104 #include <SystemConfiguration/SCPrivate.h>
105 #include "SCNetworkReachabilityInternal.h"
106 #include "SCNetworkSignaturePrivate.h"
107 #include <dnsinfo.h>
108 #include "dnsinfo_server.h"
109
110 #include <ppp/PPPControllerPriv.h>
111
112 #include <dns_sd.h>
113 #include <dns_sd_private.h>
114
115 #include <network_information.h>
116 #include "network_state_information_priv.h"
117 #include "network_state_information_logging.h"
118 #include "network_information_server.h"
119 #include <ppp/ppp_msg.h>
120
121 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
122 #include "set-hostname.h"
123 #include "nat64-configuration.h"
124 #include "agent-monitor.h"
125 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
126
127 #include "dns-configuration.h"
128 #include "proxy-configuration.h"
129
130 #if !TARGET_OS_IPHONE
131 #include "smb-configuration.h"
132 #endif /* !TARGET_OS_IPHONE */
133
134 #define kLoopbackInterface "lo0"
135 #define EROUTENOTAPPLIED 1001
136
137 typedef CF_ENUM(uint8_t, ProtocolFlags) {
138 kProtocolFlagsNone = 0x0,
139 kProtocolFlagsIPv4 = 0x1,
140 kProtocolFlagsIPv6 = 0x2
141 };
142
143 enum {
144 kDebugFlag1 = 0x00000001,
145 kDebugFlag2 = 0x00000002,
146 kDebugFlag4 = 0x00000004,
147 kDebugFlag8 = 0x00000008,
148 kDebugFlagDefault = kDebugFlag1,
149 kDebugFlagAll = 0xffffffff
150 };
151
152 typedef unsigned int IFIndex; /* interface index */
153
154 static dispatch_queue_t __network_change_queue(void);
155
156
157 #pragma mark -
158 #pragma mark Logging
159
160
161 __private_extern__ os_log_t
162 __log_IPMonitor(void)
163 {
164 static os_log_t log = NULL;
165
166 if (log == NULL) {
167 log = os_log_create("com.apple.SystemConfiguration", "IPMonitor");
168 }
169
170 return log;
171 }
172
173 #pragma mark -
174 #pragma mark interface index
175
176
177 #ifndef TEST_ROUTELIST
178
179 #define ROUTELIST_DEBUG(flag, fmt, ...)
180
181 static struct if_nameindex * S_if_nameindex_cache;
182
183 static dispatch_queue_t
184 __my_if_nametoindex_queue()
185 {
186 static dispatch_once_t once;
187 static dispatch_queue_t q;
188
189 dispatch_once(&once, ^{
190 q = dispatch_queue_create("my_if_nametoindex queue", NULL);
191 });
192
193 return q;
194 }
195
196 __private_extern__ IFIndex
197 my_if_nametoindex(const char * ifname)
198 {
199 __block IFIndex idx = 0;
200
201 dispatch_sync(__my_if_nametoindex_queue(), ^{
202 struct if_nameindex * scan;
203
204 if (S_if_nameindex_cache == NULL) {
205 idx = if_nametoindex(ifname);
206 return;
207 }
208 for (scan = S_if_nameindex_cache;
209 scan->if_index != 0 && scan->if_name != NULL;
210 scan++) {
211 if (strcmp(scan->if_name, ifname) == 0) {
212 idx = scan->if_index;
213 break;
214 }
215 }
216 });
217
218 return (idx);
219 }
220
221 __private_extern__ const char *
222 my_if_indextoname(IFIndex idx, char if_name[IFNAMSIZ])
223 {
224 __block const char * name = NULL;
225
226 dispatch_sync(__my_if_nametoindex_queue(), ^{
227 struct if_nameindex * scan;
228
229 if (S_if_nameindex_cache == NULL) {
230 name = if_indextoname(idx, if_name);
231 return;
232 }
233 for (scan = S_if_nameindex_cache;
234 scan->if_index != 0 && scan->if_name != NULL;
235 scan++) {
236 if (scan->if_index == idx) {
237 name = if_name;
238 strlcpy(if_name, scan->if_name, IFNAMSIZ);
239 break;
240 }
241 }
242 });
243
244 return (name);
245 }
246
247 static void
248 my_if_freenameindex(void)
249 {
250 dispatch_sync(__my_if_nametoindex_queue(), ^{
251 if (S_if_nameindex_cache != NULL) {
252 if_freenameindex(S_if_nameindex_cache);
253 S_if_nameindex_cache = NULL;
254 }
255 });
256
257 return;
258 }
259
260 static void
261 my_if_nameindex(void)
262 {
263 my_if_freenameindex();
264 dispatch_sync(__my_if_nametoindex_queue(), ^{
265 S_if_nameindex_cache = if_nameindex();
266 });
267
268 return;
269 }
270
271
272 #else /* TEST_ROUTELIST */
273
274 #define ROUTELIST_DEBUG(flags, format, ...) { if (((S_IPMonitor_debug & (flags)) != 0)) printf((format), ## __VA_ARGS__ ); }
275
276
277 static const char * * list;
278 static int list_count;
279 static int list_size;
280
281 __private_extern__ IFIndex
282 my_if_nametoindex(const char * ifname)
283 {
284 IFIndex ret;
285
286 if (list == NULL) {
287 list_size = 4;
288 list_count = 2;
289 list = (const char * *)malloc(sizeof(*list) * list_size);
290 list[0] = strdup("");
291 list[1] = strdup(kLoopbackInterface);
292 }
293 else {
294 int i;
295
296 for (i = 1; i < list_count; i++) {
297 if (strcmp(list[i], ifname) == 0) {
298 ret = i;
299 goto done;
300 }
301 }
302 }
303 if (list_count == list_size) {
304 list_size += 2;
305 list = (const char * *)realloc(list, sizeof(*list) * list_size);
306 }
307 list[list_count] = strdup(ifname);
308 ret = list_count;
309 list_count++;
310 done:
311 return (ret);
312 }
313
314 __private_extern__ const char *
315 my_if_indextoname(IFIndex idx, char if_name[IFNAMSIZ])
316 {
317 const char * name = NULL;
318
319 if (idx < list_count) {
320 name = if_name;
321 strlcpy(if_name, list[idx], IFNAMSIZ);
322 }
323 return (name);
324 }
325
326 static void
327 my_if_nameindex(void)
328 {
329 }
330
331 static void
332 my_if_freenameindex(void)
333 {
334 }
335
336 #endif /* TEST_ROUTELIST */
337
338 static const char *
339 my_if_indextoname2(IFIndex ifindex, char ifname[IFNAMSIZ])
340 {
341 if (ifindex == 0) {
342 return (NULL);
343 }
344 if (my_if_indextoname(ifindex, ifname) == NULL) {
345 snprintf(ifname, IFNAMSIZ, "[%d]", ifindex);
346 }
347 return (ifname);
348 }
349
350
351 static IFIndex
352 lo0_ifindex(void)
353 {
354 static IFIndex idx;
355
356 if (idx == 0) {
357 idx = my_if_nametoindex(kLoopbackInterface);
358 }
359 return (idx);
360 }
361
362
363 #pragma mark -
364
365
366 /*
367 * Property: kServiceOptionRankAssertion
368 * Purpose:
369 * Key used in the service options dictionary to hold the RankAssertion
370 * derived from the kSCPropNetServicePrimaryRank string.
371 */
372 #define kServiceOptionRankAssertion CFSTR("RankAssertion") /* number */
373
374 /*
375 * Property: kIPIsCoupled
376 * Purpose:
377 * Used to indicate that the IPv4 and IPv6 services are coupled.
378 * Neither the IPv4 part nor the IPv6 part of a coupled service
379 * may become primary if IPv4 or IPv6 is primary for another interface.
380 *
381 * For example, if the service over en3 is "coupled" and has IPv6,
382 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible
383 * to become primary for IPv6.
384 */
385 #define kIPIsCoupled CFSTR("IPIsCoupled")
386
387 #define PPP_PREFIX "ppp"
388
389 #define IP_FORMAT "%d.%d.%d.%d"
390 #define IP_CH(ip) ((u_char *)(ip))
391 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
392
393 static Boolean S_bundle_logging_verbose;
394
395 /*
396 * IPv4 Route management
397 */
398
399 typedef CF_ENUM(uint16_t, RouteFlags) {
400 kRouteFlagsIsScoped = 0x0001,
401 kRouteFlagsHasGateway = 0x0002,
402 kRouteFlagsIsHost = 0x0004,
403 kRouteFlagsIsNULL = 0x0008,
404 kRouteFlagsKernelManaged = 0x0010
405 };
406
407 typedef CF_ENUM(uint16_t, ControlFlags) {
408 kControlFlagsProcessed = 0x0001,
409 kControlFlagsAdded = 0x0002,
410 kControlFlagsForce = 0x0004,
411 };
412
413 #if !TARGET_OS_SIMULATOR
414 static inline ControlFlags
415 ControlFlagsPreserve(ControlFlags flags)
416 {
417 /* only preserve the "processed" and "added" flags */
418 return (flags & (kControlFlagsProcessed | kControlFlagsAdded));
419 }
420 #endif /* !TARGET_OS_SIMULATOR */
421
422 #define ROUTE_COMMON \
423 int prefix_length; \
424 IFIndex ifindex; \
425 IFIndex exclude_ifindex; \
426 Rank rank; \
427 RouteFlags flags; \
428 ControlFlags control_flags;
429
430 typedef struct {
431 ROUTE_COMMON
432 } Route, * RouteRef;
433
434 #define PREFIX_LENGTH_IN_CLASSC 24
435 #define PREFIX_LENGTH_IN_CLASSD 4
436
437 typedef struct {
438 ROUTE_COMMON
439 struct in_addr dest;
440 struct in_addr mask;
441 struct in_addr gateway;
442 struct in_addr ifa;
443 } IPv4Route, * IPv4RouteRef;
444
445 typedef struct {
446 ROUTE_COMMON
447 struct in6_addr dest;
448 struct in6_addr gateway;
449 struct in6_addr ifa;
450 } IPv6Route, * IPv6RouteRef;
451
452 typedef CF_ENUM(uint16_t, RouteListFlags) {
453 kRouteListFlagsExcludeNWI = 0x0001,
454 kRouteListFlagsHasDefault = 0x0002,
455 kRouteListFlagsScopedOnly = 0x0004
456 };
457
458 #define ROUTELIST_COMMON \
459 int count; \
460 int size; \
461 RouteListFlags flags;
462
463 typedef struct {
464 ROUTELIST_COMMON
465 } RouteListCommon, * RouteListRef;
466
467 typedef struct {
468 ROUTELIST_COMMON
469 IPv4Route list[1]; /* variable length */
470 } IPv4RouteList, * IPv4RouteListRef;
471
472 typedef struct {
473 ROUTELIST_COMMON
474 IPv6Route list[1]; /* variable length */
475 } IPv6RouteList, * IPv6RouteListRef;
476
477 typedef union {
478 void * ptr;
479 RouteListRef common;
480 IPv4RouteListRef v4;
481 IPv6RouteListRef v6;
482 } RouteListUnion;
483
484 typedef enum {
485 kRouteCommandAdd,
486 kRouteCommandRemove
487 } RouteCommand;
488
489 /*
490 * Election Information
491 * - information about the current best services
492 */
493 typedef union {
494 struct in_addr v4;
495 struct in6_addr v6;
496 } in_addr;
497
498 typedef union {
499 struct sockaddr_in v4;
500 struct sockaddr_in6 v6;
501 } in_sockaddr;
502
503 typedef struct Candidate {
504 CFStringRef serviceID;
505 CFStringRef if_name;
506 Rank rank;
507 boolean_t ip_is_coupled;
508 boolean_t ineligible;
509 SCNetworkReachabilityFlags reachability_flags;
510 in_addr addr;
511 in_sockaddr vpn_server_addr;
512 CFStringRef signature;
513 } Candidate, * CandidateRef;
514
515 typedef struct ElectionResults {
516 int af;
517 int count;
518 int size;
519 Candidate candidates[1];
520 } ElectionResults, * ElectionResultsRef;
521
522 static __inline__ size_t
523 ElectionResultsComputeSize(unsigned int n)
524 {
525 return (offsetof(ElectionResults, candidates[n]));
526 }
527
528 /*
529 * Rank support
530 */
531
532 static __inline__ Rank
533 RankMake(uint32_t service_index, Rank primary_rank)
534 {
535 return (RANK_INDEX_MASK(service_index) | RANK_ASSERTION_MASK(primary_rank));
536 }
537
538 static Rank
539 InterfaceRankGetRankAssertion(CFNumberRef rank_cf, Boolean * ret_is_set)
540 {
541 SCNetworkServicePrimaryRank if_rank;
542 Boolean is_set = FALSE;
543 Rank rank = kRankAssertionDefault;
544
545 if (rank_cf != NULL
546 && CFNumberGetValue(rank_cf, kCFNumberSInt32Type, &if_rank)
547 && if_rank != kSCNetworkServicePrimaryRankDefault) {
548 if (if_rank == kSCNetworkServicePrimaryRankFirst) {
549 rank = kRankAssertionFirst;
550 }
551 else {
552 rank = RANK_ASSERTION_MAKE(if_rank);
553 }
554 is_set = TRUE;
555 }
556 if (ret_is_set != NULL) {
557 *ret_is_set = is_set;
558 }
559 return (rank);
560 }
561
562 static Rank
563 PrimaryRankGetRankAssertion(CFStringRef rank_str, Boolean * is_set)
564 {
565 struct {
566 const CFStringRef * name;
567 Rank rank_assertion;
568 } values[] = {
569 { &kSCValNetServicePrimaryRankFirst, kRankAssertionFirst },
570 { &kSCValNetServicePrimaryRankLast, kRankAssertionLast },
571 { &kSCValNetServicePrimaryRankNever, kRankAssertionNever },
572 { &kSCValNetServicePrimaryRankScoped, kRankAssertionScoped }
573 };
574
575 if (rank_str != NULL) {
576 for (size_t i = 0; i < countof(values); i++) {
577 if (CFEqual(rank_str, *(values[i].name))) {
578 if (is_set != NULL) {
579 *is_set = TRUE;
580 }
581 return (values[i].rank_assertion);
582 }
583 }
584 }
585 if (is_set != NULL) {
586 *is_set = FALSE;
587 }
588 return (kRankAssertionDefault);
589 }
590
591 /* SCDynamicStore session */
592 static SCDynamicStoreRef S_session = NULL;
593
594 /* debug output flags */
595 static uint32_t S_IPMonitor_debug = 0;
596 static Boolean S_IPMonitor_verbose = FALSE;
597
598 /* are we netbooted? If so, don't touch the default route */
599 static boolean_t S_netboot = FALSE;
600
601 /* dictionary to hold per-service state: key is the serviceID */
602 static CFMutableDictionaryRef S_service_state_dict;
603
604 /* dictionaries to hold per-service rank: key is the serviceID */
605 static CFMutableDictionaryRef S_ipv4_service_rank_dict;
606 static CFMutableDictionaryRef S_ipv6_service_rank_dict;
607
608 /* dictionary to hold per-interface rank information: key is the ifname */
609 static CFDictionaryRef S_if_rank_dict;
610
611 /* if set, a PPP interface overrides the primary */
612 static boolean_t S_ppp_override_primary = FALSE;
613
614 /* the current primary serviceID's */
615 static CFStringRef S_primary_ipv4 = NULL;
616 static CFStringRef S_primary_ipv6 = NULL;
617 static CFStringRef S_primary_dns = NULL;
618 static CFStringRef S_primary_proxies = NULL;
619
620 /* the current election results */
621 static ElectionResultsRef S_ipv4_results;
622 static ElectionResultsRef S_ipv6_results;
623
624 static CFStringRef S_state_global_ipv4 = NULL;
625 static CFStringRef S_state_global_ipv6 = NULL;
626 static CFStringRef S_state_global_dns = NULL;
627 static CFStringRef S_state_global_proxies = NULL;
628 static CFStringRef S_state_service_prefix = NULL;
629 static CFStringRef S_setup_global_ipv4 = NULL;
630 static CFStringRef S_setup_service_prefix = NULL;
631
632 static CFStringRef S_interface_delegation_prefix = NULL;
633
634 static CFStringRef S_multicast_resolvers = NULL;
635 static CFStringRef S_private_resolvers = NULL;
636
637 #if !TARGET_OS_SIMULATOR
638 static IPv4RouteListRef S_ipv4_routelist = NULL;
639 static IPv6RouteListRef S_ipv6_routelist = NULL;
640 #endif /* !TARGET_OS_SIMULATOR */
641
642 static boolean_t S_append_state = FALSE;
643
644 static CFDictionaryRef S_dns_dict = NULL;
645
646 static Boolean S_dnsinfo_synced = TRUE;
647
648 static nwi_state_t S_nwi_state = NULL;
649 static Boolean S_nwi_synced = TRUE;
650
651 static CFDictionaryRef S_proxies_dict = NULL;
652
653 // Note: access should be gated with __network_change_queue()
654 static uint32_t S_network_change_needed = 0;
655 #define NETWORK_CHANGE_NET 1<<0
656 #define NETWORK_CHANGE_DNS 1<<1
657 #define NETWORK_CHANGE_PROXY 1<<2
658 #if !TARGET_OS_IPHONE
659 #define NETWORK_CHANGE_SMB 1<<3
660 #endif /* !TARGET_OS_IPHONE */
661 #define NETWORK_CHANGE_NAT64 1<<4
662 static struct timeval S_network_change_start;
663 static Boolean S_network_change_timeout = FALSE;
664 static dispatch_source_t S_network_change_timer = NULL;
665
666 #if !TARGET_OS_IPHONE
667 static CFStringRef S_primary_smb = NULL;
668 static CFStringRef S_state_global_smb = NULL;
669 static CFDictionaryRef S_smb_dict = NULL;
670 #endif /* !TARGET_OS_IPHONE */
671
672 #if !TARGET_OS_IPHONE
673 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
674 #endif /* !TARGET_OS_IPHONE */
675
676 #ifndef KERN_NETBOOT
677 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
678 #endif /* KERN_NETBOOT */
679
680 /**
681 ** entityType*, GetEntityChanges*
682 ** - definitions for the entity types we handle
683 **/
684 typedef enum {
685 kEntityTypeIPv4 = 0,
686 kEntityTypeIPv6,
687 kEntityTypeDNS,
688 kEntityTypeProxies,
689 #if !TARGET_OS_IPHONE
690 kEntityTypeSMB,
691 #endif /* !TARGET_OS_IPHONE */
692 ENTITY_TYPES_COUNT,
693 kEntityTypeTransientStatus,
694 kEntityTypeServiceOptions = 31
695 } EntityType;
696
697 static const CFStringRef *entityTypeNames[ENTITY_TYPES_COUNT] = {
698 &kSCEntNetIPv4, /* 0 */
699 &kSCEntNetIPv6, /* 1 */
700 &kSCEntNetDNS, /* 2 */
701 &kSCEntNetProxies, /* 3 */
702 #if !TARGET_OS_IPHONE
703 &kSCEntNetSMB, /* 4 */
704 #endif /* !TARGET_OS_IPHONE */
705 };
706
707
708 static Boolean
709 S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value);
710
711 static __inline__ char
712 ipvx_char(int af)
713 {
714 return ((af == AF_INET) ? '4' : '6');
715 }
716
717 static __inline__ char
718 ipvx_other_char(int af)
719 {
720 return ((af == AF_INET) ? '6' : '4');
721 }
722
723 /*
724 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
725 *
726 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
727 * Routes CFData containing IPv4RouteList/IPv6RouteList
728 * Service dictionary containing kSCEntNetIPv[46] service entity
729 */
730 #define kIPDictRoutes CFSTR("Routes") /* data */
731 #define kIPDictService CFSTR("Service") /* dict */
732
733 static CFDictionaryRef
734 ipdict_create(CFDictionaryRef dict, CFDataRef routes_data)
735 {
736 CFStringRef keys[2];
737 CFTypeRef values[2];
738
739 keys[0] = kIPDictService;
740 values[0] = dict;
741 keys[1] = kIPDictRoutes;
742 values[1] = routes_data;
743 return (CFDictionaryCreate(NULL,
744 (const void * *)keys,
745 values,
746 countof(keys),
747 &kCFTypeDictionaryKeyCallBacks,
748 &kCFTypeDictionaryValueCallBacks));
749 }
750
751 static void *
752 ipdict_get_routelist(CFDictionaryRef dict)
753 {
754 void * routes_list = NULL;
755
756 if (dict != NULL) {
757 CFDataRef routes;
758
759 routes = CFDictionaryGetValue(dict, kIPDictRoutes);
760 if (routes != NULL) {
761 routes_list = (void *)CFDataGetBytePtr(routes);
762 }
763 }
764 return (routes_list);
765 }
766
767 static CFDictionaryRef
768 ipdict_get_service(CFDictionaryRef dict)
769 {
770 CFDictionaryRef ip_dict = NULL;
771
772 if (dict != NULL) {
773 ip_dict = CFDictionaryGetValue(dict, kIPDictService);
774 }
775 return (ip_dict);
776 }
777
778 static CFStringRef
779 ipdict_get_ifname(CFDictionaryRef dict)
780 {
781 CFStringRef ifname = NULL;
782 CFDictionaryRef ip_dict;
783
784 ip_dict = ipdict_get_service(dict);
785 if (ip_dict != NULL) {
786 ifname = CFDictionaryGetValue(ip_dict, kSCPropInterfaceName);
787 }
788 return (ifname);
789 }
790
791 typedef boolean_t GetEntityChangesFunc(CFStringRef serviceID,
792 CFDictionaryRef state_dict,
793 CFDictionaryRef setup_dict,
794 CFDictionaryRef info);
795 typedef GetEntityChangesFunc * GetEntityChangesFuncRef;
796
797 static GetEntityChangesFunc get_ipv4_changes;
798 static GetEntityChangesFunc get_ipv6_changes;
799 static GetEntityChangesFunc get_dns_changes;
800 static GetEntityChangesFunc get_proxies_changes;
801 #if !TARGET_OS_IPHONE
802 static GetEntityChangesFunc get_smb_changes;
803 #endif /* !TARGET_OS_IPHONE */
804
805 static __inline__ void
806 my_CFRelease(void * t)
807 {
808 void * * obj = (void * *)t;
809
810 if (obj && *obj) {
811 CFRelease(*obj);
812 *obj = NULL;
813 }
814 return;
815 }
816
817 static void
818 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new);
819
820 static void
821 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key);
822
823 static const GetEntityChangesFuncRef entityChangeFunc[ENTITY_TYPES_COUNT] = {
824 get_ipv4_changes, /* 0 */
825 get_ipv6_changes, /* 1 */
826 get_dns_changes, /* 2 */
827 get_proxies_changes,/* 3 */
828 #if !TARGET_OS_IPHONE
829 get_smb_changes, /* 4 */
830 #endif /* !TARGET_OS_IPHONE */
831 };
832
833 /**
834 ** keyChangeList
835 ** - mechanism to do an atomic update of the SCDynamicStore
836 ** when the content needs to be changed across multiple functions
837 **/
838 typedef struct {
839 CFMutableArrayRef notify;
840 CFMutableArrayRef remove;
841 CFMutableDictionaryRef set;
842 } keyChangeList, * keyChangeListRef;
843
844 static void
845 keyChangeListInit(keyChangeListRef keys)
846 {
847 keys->notify = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
848 keys->remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
849 keys->set = CFDictionaryCreateMutable(NULL, 0,
850 &kCFTypeDictionaryKeyCallBacks,
851 &kCFTypeDictionaryValueCallBacks);
852 return;
853 }
854
855 static void
856 keyChangeListFree(keyChangeListRef keys)
857 {
858 my_CFRelease(&keys->notify);
859 my_CFRelease(&keys->remove);
860 my_CFRelease(&keys->set);
861 return;
862 }
863
864 static Boolean
865 keyChangeListActive(keyChangeListRef keys)
866 {
867 return ((CFDictionaryGetCount(keys->set) > 0) ||
868 (CFArrayGetCount(keys->remove) > 0) ||
869 (CFArrayGetCount(keys->notify) > 0));
870 }
871
872 static void
873 keyChangeListNotifyKey(keyChangeListRef keys, CFStringRef key)
874 {
875 my_CFArrayAppendUniqueValue(keys->notify, key);
876 return;
877 }
878
879 static void
880 keyChangeListRemoveValue(keyChangeListRef keys, CFStringRef key)
881 {
882 my_CFArrayAppendUniqueValue(keys->remove, key);
883 CFDictionaryRemoveValue(keys->set, key);
884 return;
885 }
886
887 static void
888 keyChangeListSetValue(keyChangeListRef keys, CFStringRef key, CFTypeRef value)
889 {
890 my_CFArrayRemoveValue(keys->remove, key);
891 CFDictionarySetValue(keys->set, key, value);
892 return;
893 }
894
895 static void
896 keyChangeListApplyToStore(keyChangeListRef keys, SCDynamicStoreRef session)
897 {
898 CFArrayRef notify = keys->notify;
899 CFArrayRef remove = keys->remove;
900 CFDictionaryRef set = keys->set;
901
902 if (CFArrayGetCount(notify) == 0) {
903 notify = NULL;
904 }
905 if (CFArrayGetCount(remove) == 0) {
906 remove = NULL;
907 }
908 if (CFDictionaryGetCount(set) == 0) {
909 set = NULL;
910 }
911 if (set == NULL && remove == NULL && notify == NULL) {
912 return;
913 }
914 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
915 if (set != NULL) {
916 my_log(LOG_DEBUG, "Setting:\n%@", set);
917 }
918 if (remove != NULL) {
919 my_log(LOG_DEBUG, "Removing:\n%@", remove);
920 }
921 if (notify != NULL) {
922 my_log(LOG_DEBUG, "Notifying:\n%@", notify);
923 }
924 }
925 (void)SCDynamicStoreSetMultiple(session, set, remove, notify);
926
927 return;
928 }
929
930 static boolean_t
931 S_is_network_boot()
932 {
933 int mib[2];
934 size_t len;
935 int netboot = 0;
936
937 mib[0] = CTL_KERN;
938 mib[1] = KERN_NETBOOT;
939 len = sizeof(netboot);
940 sysctl(mib, 2, &netboot, &len, NULL, 0);
941 return (netboot);
942 }
943
944 static int rtm_seq = 0;
945
946 #if !TARGET_OS_SIMULATOR
947 static int
948 open_routing_socket(void)
949 {
950 int sockfd;
951
952 if ((sockfd = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE)) == -1) {
953 my_log(LOG_ERR, "socket() failed: %s", strerror(errno));
954 }
955 return (sockfd);
956 }
957
958 static __inline__ int
959 inet6_dgram_socket(void)
960 {
961 int sockfd;
962
963 sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
964 if (sockfd == -1) {
965 my_log(LOG_ERR, "socket() failed: %s", strerror(errno));
966 }
967
968 return sockfd;
969 }
970
971 static int
972 siocdradd_in6(int s, int if_index, const struct in6_addr * addr, u_char flags)
973 {
974 struct in6_defrouter dr;
975 struct sockaddr_in6 * sin6;
976
977 memset(&dr, 0, sizeof(dr));
978 sin6 = &dr.rtaddr;
979 sin6->sin6_len = sizeof(struct sockaddr_in6);
980 sin6->sin6_family = AF_INET6;
981 sin6->sin6_addr = *addr;
982 dr.flags = flags;
983 dr.if_index = if_index;
984 return (ioctl(s, SIOCDRADD_IN6, &dr));
985 }
986
987 static int
988 siocdrdel_in6(int s, int if_index, const struct in6_addr * addr)
989 {
990 struct in6_defrouter dr;
991 struct sockaddr_in6 * sin6;
992
993 memset(&dr, 0, sizeof(dr));
994 sin6 = &dr.rtaddr;
995 sin6->sin6_len = sizeof(struct sockaddr_in6);
996 sin6->sin6_family = AF_INET6;
997 sin6->sin6_addr = *addr;
998 dr.if_index = if_index;
999 return (ioctl(s, SIOCDRDEL_IN6, &dr));
1000 }
1001
1002 #endif /* !TARGET_OS_SIMULATOR */
1003
1004 static void
1005 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new)
1006 {
1007 CFIndex n = CFArrayGetCount(arr);
1008
1009 if (CFArrayContainsValue(arr, CFRangeMake(0, n), new)) {
1010 return;
1011 }
1012 CFArrayAppendValue(arr, new);
1013 return;
1014 }
1015
1016 static void
1017 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key)
1018 {
1019 CFIndex i;
1020
1021 i = CFArrayGetFirstIndexOfValue(arr,
1022 CFRangeMake(0, CFArrayGetCount(arr)),
1023 key);
1024 if (i != kCFNotFound) {
1025 CFArrayRemoveValueAtIndex(arr, i);
1026 }
1027 return;
1028 }
1029
1030 static CFArrayRef
1031 my_CFArrayCreateCombinedArray(CFArrayRef array1, CFArrayRef array2)
1032 {
1033 CFMutableArrayRef combined;
1034
1035 combined = CFArrayCreateMutableCopy(NULL, 0, array1);
1036 CFArrayAppendArray(combined,
1037 array2,
1038 CFRangeMake(0, CFArrayGetCount(array2)));
1039 return (combined);
1040 }
1041
1042 static CFDictionaryRef
1043 my_CFDictionaryGetDictionary(CFDictionaryRef dict, CFStringRef key)
1044 {
1045 if (isA_CFDictionary(dict) == NULL) {
1046 return (NULL);
1047 }
1048 return (isA_CFDictionary(CFDictionaryGetValue(dict, key)));
1049 }
1050
1051 static CFArrayRef
1052 my_CFDictionaryGetArray(CFDictionaryRef dict, CFStringRef key)
1053 {
1054 if (isA_CFDictionary(dict) == NULL) {
1055 return (NULL);
1056 }
1057 return (isA_CFArray(CFDictionaryGetValue(dict, key)));
1058 }
1059
1060 static void
1061 my_CFSetAddValue(CFMutableSetRef * set_p, CFTypeRef value)
1062 {
1063 if (*set_p == NULL) {
1064 *set_p = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
1065 }
1066 CFSetAddValue(*set_p, value);
1067 }
1068
1069 static Boolean
1070 my_CFSetContainsValue(CFSetRef set, CFTypeRef value)
1071 {
1072 if (set == NULL) {
1073 return (FALSE);
1074 }
1075 return (CFSetContainsValue(set, value));
1076 }
1077
1078 #if !TARGET_OS_SIMULATOR
1079
1080 static void
1081 my_CFSetRemoveValue(CFMutableSetRef * set_p, CFTypeRef value)
1082 {
1083 if (*set_p == NULL) {
1084 return;
1085 }
1086 CFSetRemoveValue(*set_p, value);
1087 if (CFSetGetCount(*set_p) == 0) {
1088 my_CFRelease(set_p);
1089 }
1090 }
1091
1092 typedef CF_ENUM(uint16_t, PLATDiscoveryOption) {
1093 kPLATDiscoveryOptionStart,
1094 kPLATDiscoveryOptionUpdate,
1095 kPLATDiscoveryOptionCancel
1096 };
1097
1098 // Note: must only accessed on __network_change_queue()
1099 static CFMutableSetRef S_nat64_cancel_prefix_requests;
1100 static CFMutableSetRef S_nat64_prefix_updates;
1101 static CFMutableSetRef S_nat64_prefix_requests;
1102
1103 static void
1104 set_plat_discovery_locked(PLATDiscoveryOption option, CFStringRef interface)
1105 {
1106 switch (option) {
1107 case kPLATDiscoveryOptionStart:
1108 my_log(LOG_DEBUG, "NAT64 Start %@", interface);
1109 my_CFSetAddValue(&S_nat64_prefix_requests, interface);
1110 my_CFSetRemoveValue(&S_nat64_prefix_updates, interface);
1111 my_CFSetRemoveValue(&S_nat64_cancel_prefix_requests, interface);
1112 break;
1113 case kPLATDiscoveryOptionUpdate:
1114 my_log(LOG_DEBUG, "NAT64 Update %@", interface);
1115 if (!my_CFSetContainsValue(S_nat64_prefix_requests, interface)) {
1116 my_CFSetAddValue(&S_nat64_prefix_updates, interface);
1117 }
1118 my_CFSetRemoveValue(&S_nat64_cancel_prefix_requests, interface);
1119 break;
1120 case kPLATDiscoveryOptionCancel:
1121 my_log(LOG_DEBUG, "NAT64 Cancel %@", interface);
1122 my_CFSetRemoveValue(&S_nat64_prefix_requests, interface);
1123 my_CFSetRemoveValue(&S_nat64_prefix_updates, interface);
1124 my_CFSetAddValue(&S_nat64_cancel_prefix_requests, interface);
1125 break;
1126 default:
1127 break;
1128 }
1129 }
1130
1131 static void
1132 set_plat_discovery(PLATDiscoveryOption option, CFStringRef interface)
1133 {
1134 CFRetain(interface);
1135 dispatch_async(__network_change_queue(), ^{
1136 set_plat_discovery_locked(option, interface);
1137 CFRelease(interface);
1138 });
1139
1140 return;
1141 }
1142 #endif /* !TARGET_OS_SIMULATOR */
1143
1144 static boolean_t
1145 cfstring_to_ipvx(int family, CFStringRef str, void * addr, size_t addr_size)
1146 {
1147 char buf[128];
1148
1149 if (isA_CFString(str) == NULL) {
1150 goto done;
1151 }
1152
1153 switch (family) {
1154 case AF_INET:
1155 if (addr_size < sizeof(struct in_addr)) {
1156 goto done;
1157 }
1158 break;
1159 case AF_INET6:
1160 if (addr_size < sizeof(struct in6_addr)) {
1161 goto done;
1162 }
1163 break;
1164 default:
1165 goto done;
1166 }
1167 (void)_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII);
1168 if (inet_pton(family, buf, addr) == 1) {
1169 return (TRUE);
1170 }
1171 done:
1172 memset(addr, 0, addr_size);
1173 return (FALSE);
1174 }
1175
1176 __private_extern__
1177 boolean_t
1178 cfstring_to_ip(CFStringRef str, struct in_addr * ip_p)
1179 {
1180 return (cfstring_to_ipvx(AF_INET, str, ip_p, sizeof(*ip_p)));
1181 }
1182
1183 __private_extern__
1184 boolean_t
1185 cfstring_to_ip6(CFStringRef str, struct in6_addr * ip6_p)
1186 {
1187 return (cfstring_to_ipvx(AF_INET6, str, ip6_p, sizeof(*ip6_p)));
1188 }
1189
1190 static boolean_t
1191 cfnumber_to_int(CFNumberRef num, int * int_val)
1192 {
1193 if (isA_CFNumber(num) == NULL) {
1194 return (FALSE);
1195 }
1196 return (CFNumberGetValue(num, kCFNumberIntType, int_val));
1197 }
1198
1199 static CF_RETURNS_RETAINED CFStringRef
1200 setup_service_key(CFStringRef serviceID, CFStringRef entity)
1201 {
1202 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1203 kSCDynamicStoreDomainSetup,
1204 serviceID,
1205 entity));
1206 }
1207
1208 static CF_RETURNS_RETAINED CFStringRef
1209 state_service_key(CFStringRef serviceID, CFStringRef entity)
1210 {
1211 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1212 kSCDynamicStoreDomainState,
1213 serviceID,
1214 entity));
1215 }
1216
1217 static CFStringRef
1218 interface_entity_key_copy(CFStringRef ifname, CFStringRef entity)
1219 {
1220 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
1221 kSCDynamicStoreDomainState,
1222 ifname,
1223 entity));
1224 }
1225
1226 static CFDictionaryRef
1227 get_service_setup_entity(CFDictionaryRef services_info, CFStringRef serviceID,
1228 CFStringRef entity)
1229 {
1230 CFStringRef setup_key;
1231 CFDictionaryRef setup_dict;
1232
1233 setup_key = setup_service_key(serviceID, entity);
1234 setup_dict = my_CFDictionaryGetDictionary(services_info, setup_key);
1235 my_CFRelease(&setup_key);
1236 return (setup_dict);
1237 }
1238
1239 static CFDictionaryRef
1240 get_service_state_entity(CFDictionaryRef services_info, CFStringRef serviceID,
1241 CFStringRef entity)
1242 {
1243 CFStringRef state_key;
1244 CFDictionaryRef state_dict;
1245
1246 state_key = state_service_key(serviceID, entity);
1247 state_dict = my_CFDictionaryGetDictionary(services_info, state_key);
1248 my_CFRelease(&state_key);
1249 return (state_dict);
1250 }
1251
1252 static boolean_t
1253 dict_get_first_ip(CFDictionaryRef dict, CFStringRef prop, struct in_addr * ip_p)
1254 {
1255 CFArrayRef ip_list;
1256
1257 ip_list = CFDictionaryGetValue(dict, prop);
1258 if (isA_CFArray(ip_list) != NULL
1259 && CFArrayGetCount(ip_list) > 0
1260 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list, 0), ip_p)) {
1261 return (TRUE);
1262 }
1263 return (FALSE);
1264 }
1265
1266 static boolean_t
1267 dict_get_first_ipv6(CFDictionaryRef dict, CFStringRef prop,
1268 struct in6_addr * ip_p)
1269 {
1270 CFArrayRef ip_list;
1271
1272 ip_list = CFDictionaryGetValue(dict, prop);
1273 if (isA_CFArray(ip_list) != NULL
1274 && CFArrayGetCount(ip_list) > 0
1275 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list, 0), ip_p)) {
1276 return (TRUE);
1277 }
1278 return (FALSE);
1279 }
1280
1281 static boolean_t
1282 dict_get_first_int(CFDictionaryRef dict, CFStringRef prop,
1283 int * val)
1284 {
1285 CFArrayRef list;
1286
1287 list = CFDictionaryGetValue(dict, prop);
1288 if (isA_CFArray(list) != NULL
1289 && CFArrayGetCount(list) > 0
1290 && cfnumber_to_int(CFArrayGetValueAtIndex(list, 0), val)) {
1291 return (TRUE);
1292 }
1293 return (FALSE);
1294 }
1295
1296 static boolean_t
1297 dict_get_ip(CFDictionaryRef dict, CFStringRef prop, struct in_addr * ip_p)
1298 {
1299 CFStringRef val;
1300
1301 val = CFDictionaryGetValue(dict, prop);
1302 return (cfstring_to_ip(val, ip_p));
1303 }
1304
1305 static boolean_t
1306 dict_get_ipv6(CFDictionaryRef dict, CFStringRef prop, struct in6_addr * ip_p)
1307 {
1308 CFStringRef val;
1309
1310 val = CFDictionaryGetValue(dict, prop);
1311 return (cfstring_to_ip6(val, ip_p));
1312 }
1313
1314 static boolean_t
1315 dict_get_int(CFDictionaryRef dict, CFStringRef prop, int * intval)
1316 {
1317 CFNumberRef val;
1318
1319 val = CFDictionaryGetValue(dict, prop);
1320 return (cfnumber_to_int(val, intval));
1321 }
1322
1323 static boolean_t
1324 get_override_primary(CFDictionaryRef dict)
1325 {
1326 CFTypeRef override;
1327
1328 override = CFDictionaryGetValue(dict, kSCPropNetOverridePrimary);
1329 if (isA_CFNumber(override) != NULL) {
1330 int val = 0;
1331
1332 CFNumberGetValue((CFNumberRef)override, kCFNumberIntType, &val);
1333 if (val != 0) {
1334 return (TRUE);
1335 }
1336 }
1337 else if (isA_CFBoolean(override) != NULL) {
1338 if (CFBooleanGetValue(override)) {
1339 return (TRUE);
1340 }
1341 }
1342 return (FALSE);
1343 }
1344
1345 /**
1346 ** Route*
1347 **/
1348
1349 typedef size_t
1350 (*RouteListComputeSize)(CFIndex n);
1351
1352 typedef boolean_t
1353 (*RouteIsEqual)(RouteRef a, RouteRef b);
1354
1355 typedef int
1356 (*RouteApply)(RouteRef route, int cmd, int sockfd);
1357
1358 typedef const void *
1359 (*RouteGateway)(RouteRef route);
1360
1361 typedef void
1362 (*RouteSetGateway)(RouteRef route, const void * address);
1363
1364 typedef const void *
1365 (*RouteDestination)(RouteRef route);
1366
1367 typedef boolean_t
1368 (*RouteSameSubnet)(RouteRef route, const void * address);
1369
1370 typedef CFStringRef
1371 (*RouteCopyDescription)(RouteRef route);
1372
1373 typedef void
1374 (*RouteLog)(int priority, RouteRef route, const char * msg);
1375
1376 typedef struct {
1377 RouteListComputeSize list_compute_size;
1378
1379 RouteIsEqual route_equal;
1380 RouteApply route_apply;
1381 RouteGateway route_gateway;
1382 RouteSetGateway route_set_gateway;
1383 RouteDestination route_destination;
1384 RouteSameSubnet route_same_subnet;
1385 RouteLog route_log;
1386 RouteCopyDescription route_copy_description;
1387
1388 int element_size;
1389 int address_size;
1390 int all_bits_set;
1391 } RouteListInfo;
1392
1393 typedef const RouteListInfo * RouteListInfoRef;
1394
1395 typedef struct {
1396 RouteListInfoRef info;
1397 RouteListRef old_routes;
1398 RouteListRef new_routes;
1399 int sockfd;
1400 int depth;
1401 } RouteListApplyContext, * RouteListApplyContextRef;
1402
1403
1404 static int
1405 RouteAddressCompare(RouteListInfoRef info,
1406 const void * addr1,
1407 const void * addr2)
1408 {
1409 return (memcmp(addr1, addr2, info->address_size));
1410 }
1411
1412 static int
1413 RouteCompare(RouteListInfoRef info,
1414 RouteRef a, Rank a_rank,
1415 RouteRef b, Rank b_rank, boolean_t * same_dest)
1416 {
1417 int cmp;
1418 RouteDestination route_destination;
1419 RouteCopyDescription route_copy_description;
1420
1421 *same_dest = FALSE;
1422 route_destination = info->route_destination;
1423 route_copy_description = info->route_copy_description;
1424 cmp = RouteAddressCompare(info,
1425 (*route_destination)(a),
1426 (*route_destination)(b));
1427 if (cmp == 0) {
1428 cmp = a->prefix_length - b->prefix_length;
1429 if (cmp == 0) {
1430 int index_cmp = a->ifindex - b->ifindex;
1431
1432 if (index_cmp == 0) {
1433 cmp = 0;
1434 }
1435 else if ((a->ifindex == 0 || b->ifindex == 0)
1436 && (a->flags & kRouteFlagsIsScoped) == 0
1437 && (b->flags & kRouteFlagsIsScoped) == 0) {
1438 /*
1439 * Either of the routes specifies no interface and neither
1440 * route is scoped. Claim they are equal to eliminate the
1441 * duplicate route.
1442 */
1443 cmp = 0;
1444 }
1445 else {
1446 *same_dest = TRUE;
1447 cmp = RankCompare(a_rank, b_rank);
1448 if (cmp == 0) {
1449 cmp = index_cmp;
1450 }
1451 }
1452 }
1453 }
1454 if ((S_IPMonitor_debug & kDebugFlag8) != 0) {
1455 CFStringRef a_str;
1456 CFStringRef b_str;
1457 char ch;
1458
1459 if (cmp < 0) {
1460 ch = '<';
1461 }
1462 else if (cmp == 0) {
1463 ch = '=';
1464 }
1465 else {
1466 ch = '>';
1467 }
1468 a_str = (*route_copy_description)(a);
1469 b_str = (*route_copy_description)(b);
1470 my_log(LOG_DEBUG, "%@ rank 0x%x %c %@ rank 0x%x",
1471 a_str, a_rank, ch, b_str, b_rank);
1472 CFRelease(a_str);
1473 CFRelease(b_str);
1474 }
1475 return (cmp);
1476 }
1477
1478 static RouteRef
1479 RouteListGetRouteAtIndexSimple(RouteListInfoRef info, RouteListRef routes,
1480 CFIndex where)
1481 {
1482 return ((void *)routes + (*info->list_compute_size)(where));
1483 }
1484
1485 static RouteRef
1486 RouteListGetRouteAtIndex(RouteListInfoRef info, RouteListRef routes,
1487 CFIndex where)
1488 {
1489 if (routes->count == 0
1490 || where >= routes->count) {
1491 return (NULL);
1492 }
1493 return (RouteListGetRouteAtIndexSimple(info, routes, where));
1494 }
1495
1496 static RouteRef
1497 RouteListGetFirstRoute(RouteListInfoRef info, RouteListRef routes)
1498 {
1499 return (RouteListGetRouteAtIndexSimple(info, routes, 0));
1500 }
1501
1502 #if !TARGET_OS_SIMULATOR
1503 static CFIndex
1504 RouteListRouteIndex(RouteListInfoRef info, RouteListRef routes,
1505 RouteRef route)
1506 {
1507 return (((void *)route
1508 - (void *)RouteListGetFirstRoute(info, routes))
1509 / info->element_size);
1510 }
1511 #endif /* !TARGET_OS_SIMULATOR */
1512
1513 static RouteRef
1514 RouteGetNextRoute(RouteListInfoRef info, RouteRef route)
1515 {
1516 return ((RouteRef)(((void *)route) + info->element_size));
1517 }
1518
1519 static RouteRef
1520 RouteListAddRouteAtIndex(RouteListInfoRef info, RouteListRef routes,
1521 RouteRef this_route, CFIndex where)
1522 {
1523 RouteRef insert_route;
1524
1525 if (where == kCFNotFound) {
1526 /* add it to the end */
1527 insert_route
1528 = RouteListGetRouteAtIndexSimple(info, routes, routes->count);
1529 }
1530 else {
1531 /* make space at [where] */
1532 insert_route = RouteListGetRouteAtIndexSimple(info, routes, where);
1533 memcpy((void *)insert_route + info->element_size,
1534 insert_route,
1535 info->element_size * (routes->count - where));
1536 }
1537 /* copy the route */
1538 memcpy(insert_route, this_route, info->element_size);
1539 routes->count++;
1540 return (insert_route);
1541 }
1542
1543 static void
1544 RouteListRemoveRouteAtIndex(RouteListInfoRef info, RouteListRef routes,
1545 CFIndex where)
1546 {
1547 if (routes->count == 0
1548 || where >= routes->count) {
1549 return;
1550 }
1551 routes->count--;
1552 if (where == routes->count) {
1553 /* last slot, decrementing gets rid of it */
1554 }
1555 else {
1556 RouteRef remove_route;
1557
1558 remove_route = RouteListGetRouteAtIndexSimple(info, routes, where);
1559 memcpy(remove_route,
1560 (void *)remove_route + info->element_size,
1561 info->element_size * (routes->count - where));
1562 }
1563 return;
1564 }
1565
1566 /*
1567 * Function: RouteListAddRoute
1568 *
1569 * Purpose:
1570 * Add the given route to the list of routes, eliminating lower-ranked
1571 * duplicates on the same interface, and marking any lower ranked duplicates
1572 * on other interfaces with kRouteFlagsIsScoped.
1573 *
1574 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1575 *
1576 * Returns:
1577 * Route list updated with the given route, possibly a different pointer,
1578 * due to using realloc'd memory.
1579 */
1580
1581 typedef enum {
1582 kScopeNone = 0,
1583 kScopeThis = 1,
1584 kScopeNext = 2
1585 } Scope;
1586
1587 static RouteListRef
1588 RouteListAddRoute(RouteListInfoRef info,
1589 RouteListRef routes, int init_size,
1590 RouteRef this_route, Rank this_rank,
1591 boolean_t force)
1592 {
1593 CFIndex i;
1594 RouteRef first_scan = NULL;
1595 RouteFlags flags;
1596 RouteRef scan;
1597 Scope scope_which = kScopeNone;
1598 CFIndex where = kCFNotFound;
1599
1600 if (routes == NULL) {
1601 size_t alloc_size = (*info->list_compute_size)(init_size);
1602
1603 routes = (RouteListRef)malloc(alloc_size);
1604 memset(routes, 0, alloc_size);
1605 routes->size = init_size;
1606 }
1607 for (i = 0, scan = RouteListGetFirstRoute(info, routes);
1608 i < routes->count;
1609 i++, scan = RouteGetNextRoute(info, scan)) {
1610 int cmp;
1611 boolean_t same_dest;
1612
1613 cmp = RouteCompare(info, this_route, this_rank, scan, scan->rank,
1614 &same_dest);
1615 if (same_dest && (first_scan == NULL)) {
1616 first_scan = scan;
1617 }
1618 if (cmp < 0) {
1619 if (where == kCFNotFound) {
1620 if (same_dest
1621 && (first_scan != NULL)
1622 && (first_scan->flags & kRouteFlagsIsScoped) == 0) {
1623 if ((scan->flags & kRouteFlagsIsScoped) != 0) {
1624 ROUTELIST_DEBUG(kDebugFlag8,
1625 "Hit 1: set scope on self\n");
1626 scope_which = kScopeThis;
1627 }
1628 else {
1629 ROUTELIST_DEBUG(kDebugFlag8,
1630 "Hit 2: set scope on next\n");
1631 scope_which = kScopeNext;
1632 }
1633 }
1634 /* remember our insertion point, but keep going to find a dup */
1635 where = i;
1636 }
1637 }
1638 else if (cmp == 0) {
1639 /* exact match */
1640 if (where != kCFNotFound
1641 && scan->ifindex == this_route->ifindex
1642 && scan->exclude_ifindex == 0
1643 && this_route->exclude_ifindex == 0) {
1644 /* this route is a duplicate */
1645 ROUTELIST_DEBUG(kDebugFlag8, "Hit 3: removing [%ld]\n", i);
1646 RouteListRemoveRouteAtIndex(info, routes, i);
1647 break;
1648 }
1649 /*
1650 * this_route is "better" than scan if this_route is not excluded
1651 * and scan is excluded or this_route sorts ahead of scan
1652 */
1653 if (this_route->exclude_ifindex == 0
1654 && (scan->exclude_ifindex != 0 || this_rank < scan->rank)) {
1655 IFIndex ifindex = 0;
1656 boolean_t is_scoped = FALSE;
1657
1658 if (scan->flags & kRouteFlagsIsScoped) {
1659 is_scoped = TRUE;
1660 }
1661 if (this_rank < scan->rank) {
1662 ROUTELIST_DEBUG(kDebugFlag8,
1663 "Hit 4a: replacing [%ld]"
1664 " rank 0x%x < 0x%x\n",
1665 i, this_rank, scan->rank);
1666 }
1667 else {
1668 ROUTELIST_DEBUG(kDebugFlag8,
1669 "Hit 4b: replacing [%ld] excluded route\n",
1670 i);
1671 }
1672 if (scan->ifindex != 0) {
1673 ifindex = scan->ifindex;
1674 }
1675 else if (this_route->ifindex != 0) {
1676 ifindex = this_route->ifindex;
1677 }
1678 memcpy(scan, this_route, info->element_size);
1679 scan->rank = this_rank;
1680 scan->ifindex = ifindex;
1681 scan->exclude_ifindex = 0;
1682 if (is_scoped) {
1683 /* preserve whether route was scoped */
1684 ROUTELIST_DEBUG(kDebugFlag8, "Hit 5: preserved scope\n");
1685 scan->flags |= kRouteFlagsIsScoped;
1686 }
1687 if (force) {
1688 scan->control_flags |= kControlFlagsForce;
1689 }
1690 }
1691 /* we're done */
1692 goto done;
1693 }
1694 else {
1695 if (same_dest) {
1696 if (scope_which == kScopeNone) {
1697 ROUTELIST_DEBUG(kDebugFlag8, "Hit 6: set scope on self\n");
1698 scope_which = kScopeThis;
1699 }
1700 }
1701 #ifdef TEST_ROUTELIST
1702 else if (where != kCFNotFound) {
1703 /* not possible because we maintain a sorted list */
1704 fprintf(stderr,
1705 "Hit 7: moved past routes - can't happen\n");
1706 exit(2);
1707 break;
1708 }
1709 #endif /* TEST_ROUTELIST */
1710 }
1711 }
1712
1713 if (routes->size == routes->count) {
1714 int how_many;
1715 RouteListRef new_routes;
1716 int old_size;
1717
1718 /* double the size */
1719 old_size = routes->size;
1720 how_many = old_size * 2;
1721 new_routes = (RouteListRef)
1722 reallocf(routes, (*info->list_compute_size)(how_many));
1723 if (new_routes == NULL) {
1724 /* no memory */
1725 routes = NULL;
1726 goto done;
1727 }
1728 ROUTELIST_DEBUG(kDebugFlag8, "increasing size from %d to %d\n",
1729 old_size, how_many);
1730 new_routes->size = how_many;
1731 routes = new_routes;
1732 }
1733
1734 /* add/insert the new route */
1735 this_route = RouteListAddRouteAtIndex(info, routes, this_route, where);
1736 this_route->rank = this_rank;
1737 if (force) {
1738 this_route->control_flags |= kControlFlagsForce;
1739 }
1740 flags = 0;
1741 if (RANK_ASSERTION_MASK(this_rank) == kRankAssertionNever) {
1742 flags |= kRouteFlagsIsScoped;
1743 }
1744 switch (scope_which) {
1745 case kScopeThis:
1746 flags |= kRouteFlagsIsScoped;
1747 break;
1748 case kScopeNext:
1749 this_route = RouteListGetRouteAtIndex(info, routes, where + 1);
1750 flags |= kRouteFlagsIsScoped;
1751 break;
1752 default:
1753 case kScopeNone:
1754 break;
1755 }
1756 if (this_route != NULL && flags != 0) {
1757 this_route->flags |= flags;
1758 }
1759
1760 done:
1761 return (routes);
1762 }
1763
1764 /*
1765 * Function: RouteListAddRouteList
1766 * Purpose:
1767 * Invoke RouteListAddRoute for each route in the given list
1768 * 'service_routes' combining them into a combined list 'routes'.
1769 *
1770 * Returns:
1771 * See RouteListAddRoute for more information.
1772 */
1773 static RouteListRef
1774 RouteListAddRouteList(RouteListInfoRef info,
1775 RouteListRef routes, int init_size,
1776 RouteListRef service_routes, Rank rank,
1777 boolean_t force)
1778 {
1779 int i;
1780 RouteRef scan;
1781
1782 for (i = 0, scan = RouteListGetFirstRoute(info, service_routes);
1783 i < service_routes->count;
1784 i++, scan = RouteGetNextRoute(info, scan)) {
1785 Rank this_rank;
1786
1787 if (i == 0
1788 && (service_routes->flags & kRouteListFlagsHasDefault) != 0) {
1789 /* only apply rank to first element of the list (default route) */
1790 this_rank = rank;
1791 }
1792 else {
1793 this_rank = RANK_INDEX_MASK(rank) | RANK_ASSERTION_MASK(scan->rank);
1794 }
1795 routes = RouteListAddRoute(info, routes, init_size, scan, this_rank,
1796 force);
1797 }
1798 return (routes);
1799 }
1800
1801 static void
1802 RouteAddInterfaceToDescription(RouteRef r, CFMutableStringRef str)
1803 {
1804 char if_name[IFNAMSIZ];
1805
1806 if (my_if_indextoname2(r->ifindex, if_name) != NULL) {
1807 CFStringAppendFormat(str, NULL,
1808 CFSTR(" Ifp %s"),
1809 if_name);
1810 }
1811 if (my_if_indextoname2(r->exclude_ifindex, if_name) != NULL) {
1812 CFStringAppendFormat(str, NULL,
1813 CFSTR(" !Ifp %s"),
1814 if_name);
1815 }
1816 return;
1817 }
1818
1819 static void
1820 RouteAddFlagsToDescription(RouteRef r, CFMutableStringRef str)
1821 {
1822 if ((r->flags & kRouteFlagsIsNULL) != 0) {
1823 CFStringAppend(str, CFSTR(" [null]"));
1824 }
1825 else {
1826 Rank rank_assertion = RANK_ASSERTION_MASK(r->rank);
1827
1828 switch (rank_assertion) {
1829 case kRankAssertionFirst:
1830 CFStringAppend(str, CFSTR(" [first]"));
1831 break;
1832 case kRankAssertionLast:
1833 CFStringAppend(str, CFSTR(" [last]"));
1834 break;
1835 case kRankAssertionNever:
1836 CFStringAppend(str, CFSTR(" [never]"));
1837 break;
1838 default:
1839 break;
1840 }
1841 if ((r->flags & kRouteFlagsKernelManaged) != 0) {
1842 CFStringAppend(str, CFSTR(" [kern]"));
1843 }
1844 if ((r->flags & kRouteFlagsIsScoped) != 0) {
1845 CFStringAppend(str, CFSTR(" [SCOPED]"));
1846 }
1847 #ifndef TEST_ROUTELIST
1848 if ((r->control_flags & kControlFlagsForce) != 0) {
1849 CFStringAppend(str, CFSTR(" [force]"));
1850 }
1851 #endif
1852 }
1853 return;
1854 }
1855
1856 #if !TARGET_OS_SIMULATOR
1857 static RouteRef
1858 RouteListFindRoute(RouteListInfoRef info, RouteListRef routes, RouteRef route)
1859 {
1860 int i;
1861 RouteRef match = NULL;
1862 RouteRef scan;
1863
1864 for (i = 0, scan = RouteListGetFirstRoute(info, routes);
1865 i < routes->count;
1866 i++, scan = RouteGetNextRoute(info, scan)) {
1867 if ((*info->route_equal)(scan, route)) {
1868 match = scan;
1869 break;
1870 }
1871
1872 }
1873 return (match);
1874 }
1875
1876 typedef enum {
1877 kRouteLookupFlagsNone = 0x0,
1878 kRouteLookupFlagsExcludeInterface = 0x1
1879 } RouteLookupFlags;
1880
1881 static RouteRef
1882 RouteListLookup(RouteListInfoRef info,
1883 RouteListRef routes,
1884 const void * address,
1885 int n_bits,
1886 IFIndex ifindex,
1887 RouteLookupFlags lookup_flags)
1888 {
1889 RouteRef best_match = NULL;
1890 int i;
1891 RouteRef scan;
1892
1893 for (i = 0, scan = RouteListGetFirstRoute(info, routes);
1894 i < routes->count;
1895 i++, scan = RouteGetNextRoute(info, scan)) {
1896 if (scan->ifindex == 0 || scan->exclude_ifindex != 0) {
1897 /* ignore exclude routes */
1898 continue;
1899 }
1900 if ((lookup_flags & kRouteLookupFlagsExcludeInterface) != 0) {
1901 /* exclude interfaces with the same interface index */
1902 if (ifindex == scan->ifindex) {
1903 continue;
1904 }
1905 }
1906 else if (ifindex != scan->ifindex) {
1907 continue;
1908 }
1909 if ((scan->flags & kRouteFlagsHasGateway) != 0
1910 && RouteAddressCompare(info,
1911 (*info->route_gateway)(scan),
1912 address) == 0) {
1913 /* skip route whose gateway is the address we're looking for */
1914 continue;
1915 }
1916 if ((scan->flags & kRouteFlagsIsHost) != 0) {
1917 /* if host route and we're looking for an exact match */
1918 if (n_bits == info->all_bits_set
1919 && RouteAddressCompare(info,
1920 (*info->route_destination)(scan),
1921 address) == 0) {
1922 /* found exact match */
1923 best_match = scan;
1924 break;
1925 }
1926 /* skip it */
1927 continue;
1928 }
1929 /* verify that address is on the same subnet */
1930 if ((*info->route_same_subnet)(scan, address) == FALSE) {
1931 /* different subnet */
1932 continue;
1933 }
1934
1935 if (scan->prefix_length == n_bits) {
1936 /* exact match */
1937 best_match = scan;
1938 break;
1939 }
1940 if (scan->prefix_length > n_bits) {
1941 /* matched too many bits */
1942 continue;
1943 }
1944 if (best_match == NULL
1945 || scan->prefix_length > best_match->prefix_length) {
1946 best_match = scan;
1947 }
1948 }
1949 return (best_match);
1950 }
1951
1952
1953 /*
1954 * Function: RouteProcess
1955 * Purpose:
1956 * Function to process adding or removing the specified route.
1957 * In the case of adding, that may involve first processing the gateway
1958 * route (recursively).
1959 */
1960 static boolean_t
1961 RouteProcess(RouteRef route,
1962 RouteCommand cmd,
1963 RouteListApplyContextRef context)
1964 {
1965 RouteLog route_log = context->info->route_log;
1966 RouteApply route_apply = context->info->route_apply;
1967 RouteGateway route_gateway = context->info->route_gateway;
1968 int retval;
1969
1970 switch (cmd) {
1971 case kRouteCommandAdd:
1972 if ((route->control_flags & kControlFlagsProcessed) != 0) {
1973 return ((route->control_flags & kControlFlagsAdded) != 0);
1974 }
1975 route->control_flags |= kControlFlagsProcessed;
1976 if ((route->flags & kRouteFlagsHasGateway) != 0) {
1977 boolean_t added;
1978 RouteRef gateway_route;
1979
1980 gateway_route
1981 = RouteListLookup(context->info,
1982 context->new_routes,
1983 (*route_gateway)(route),
1984 context->info->all_bits_set,
1985 route->ifindex,
1986 kRouteLookupFlagsNone);
1987 if (gateway_route == NULL) {
1988 (*route_log)(LOG_NOTICE, route, "no gateway route");
1989 }
1990 else {
1991 #define MAX_RECURSE_DEPTH 10
1992 /* avoid infinite recursion */
1993 if (context->depth == MAX_RECURSE_DEPTH) {
1994 (*route_log)(LOG_NOTICE, route, "routing loop detected, not adding");
1995 return (FALSE);
1996 }
1997 /* recurse to add gateway route */
1998 context->depth++;
1999 added = RouteProcess(gateway_route,
2000 kRouteCommandAdd,
2001 context);
2002 context->depth--;
2003 if (!added) {
2004 (*route_log)(LOG_NOTICE, route, "failed to add");
2005 return (FALSE);
2006 }
2007 }
2008 }
2009 retval = (*route_apply)(route, RTM_ADD, context->sockfd);
2010 if (retval == EEXIST) {
2011 /* delete and add again */
2012 (void)(*route_apply)(route, RTM_DELETE, context->sockfd);
2013 retval = (*route_apply)(route, RTM_ADD, context->sockfd);
2014 }
2015 switch (retval) {
2016 default:
2017 my_log(LOG_NOTICE,
2018 "failed to add route, %s:",
2019 strerror(retval));
2020 (*route_log)(LOG_NOTICE, route, NULL);
2021 break;
2022 case 0:
2023 case EROUTENOTAPPLIED:
2024 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2025 char buf[64];
2026 const char * str;
2027
2028 str = (retval == EROUTENOTAPPLIED) ? "!" : "";
2029 snprintf(buf, sizeof(buf), "%sAdd new[%ld]",
2030 str,
2031 RouteListRouteIndex(context->info,
2032 context->new_routes,
2033 route));
2034 (*route_log)(LOG_DEBUG, route, buf);
2035 }
2036 route->control_flags |= kControlFlagsAdded;
2037 break;
2038 }
2039 break;
2040 case kRouteCommandRemove:
2041 retval = (*route_apply)(route, RTM_DELETE, context->sockfd);
2042 switch (retval) {
2043 case 0:
2044 case ESRCH:
2045 case EROUTENOTAPPLIED:
2046 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2047 char buf[64];
2048 const char * str;
2049
2050 str = (retval == EROUTENOTAPPLIED) ? "!" : "";
2051 snprintf(buf, sizeof(buf), "%sRemove old[%ld]%s",
2052 str,
2053 RouteListRouteIndex(context->info,
2054 context->old_routes,
2055 route),
2056 (retval == ESRCH) ? "(ESRCH)" : "");
2057 (*route_log)(LOG_DEBUG, route, buf);
2058 }
2059 break;
2060 default:
2061 my_log(LOG_NOTICE,
2062 "failed to remove route, %s",
2063 strerror(retval));
2064 (*route_log)(LOG_NOTICE, route, NULL);
2065 break;
2066 }
2067 break;
2068 default:
2069 break;
2070 }
2071 return (TRUE);
2072 }
2073
2074 static void
2075 RouteListApply(RouteListInfoRef info,
2076 RouteListRef old_routes, RouteListRef new_routes,
2077 int sockfd)
2078 {
2079 RouteListApplyContext context;
2080 int i;
2081 RouteRef scan;
2082
2083 if (old_routes == new_routes && old_routes == NULL) {
2084 /* both old and new are NULL, so there's nothing to do */
2085 return;
2086 }
2087 memset(&context, 0, sizeof(context));
2088 context.old_routes = old_routes;
2089 context.new_routes = new_routes;
2090 context.sockfd = sockfd;
2091 context.info = info;
2092 if (old_routes != NULL) {
2093 for (i = 0, scan = RouteListGetFirstRoute(info, old_routes);
2094 i < old_routes->count;
2095 i++, scan = RouteGetNextRoute(info, scan)) {
2096 RouteRef new_route = NULL;
2097
2098 if (new_routes != NULL) {
2099 new_route = RouteListFindRoute(info, new_routes, scan);
2100 }
2101 if (new_route == NULL) {
2102 if ((scan->control_flags & kControlFlagsAdded) != 0) {
2103 RouteProcess(scan, kRouteCommandRemove, &context);
2104 }
2105 }
2106 }
2107 }
2108 if (new_routes != NULL) {
2109 if (old_routes != NULL) {
2110 /* preserve the control flags from any old routes */
2111 for (i = 0, scan = RouteListGetFirstRoute(info, new_routes);
2112 i < new_routes->count;
2113 i++, scan = RouteGetNextRoute(info, scan)) {
2114 RouteRef old_route = NULL;
2115
2116 old_route = RouteListFindRoute(info, old_routes, scan);
2117 if (old_route != NULL) {
2118 if ((scan->control_flags & kControlFlagsForce) == 0) {
2119 /* preserve the control state in the new route */
2120 scan->control_flags
2121 = ControlFlagsPreserve(old_route->control_flags);
2122 }
2123 else {
2124 (*info->route_log)(LOG_NOTICE, (RouteRef)scan,
2125 "Re-applying route");
2126 }
2127 }
2128 }
2129 }
2130 /* add any routes that need to be added */
2131 for (i = 0, scan = RouteListGetFirstRoute(info, new_routes);
2132 i < new_routes->count;
2133 i++, scan = RouteGetNextRoute(info, scan)) {
2134 if ((scan->control_flags & kControlFlagsProcessed) != 0) {
2135 continue;
2136 }
2137 RouteProcess(scan, kRouteCommandAdd, &context);
2138 }
2139 }
2140 return;
2141 }
2142 /*
2143 * Function: RouteListFinalize
2144 * Purpose:
2145 * Look for excluded routes. If the excluded route does not have an assigned
2146 * interface, search for a route that *does not* go over the excluded
2147 * interface.
2148 *
2149 * If the excluded route does have an assigned interface, search for a route
2150 * that *does* go over the assigned interface.
2151 *
2152 * Set the gateway on the excluded route to match the gateway of the found
2153 * route.
2154 */
2155 static void
2156 RouteListFinalize(RouteListInfoRef info, RouteListRef routes)
2157 {
2158 int i;
2159 RouteRef scan;
2160
2161 if (routes == NULL) {
2162 return;
2163 }
2164 for (i = 0, scan = RouteListGetFirstRoute(info, routes);
2165 i < routes->count;
2166 i++, scan = RouteGetNextRoute(info, scan)) {
2167 RouteRef route;
2168 IFIndex ifindex;
2169 RouteLookupFlags flags;
2170
2171 if (scan->exclude_ifindex == 0) {
2172 continue;
2173 }
2174 if (scan->ifindex == 0) {
2175 ifindex = scan->exclude_ifindex;
2176 flags = kRouteLookupFlagsExcludeInterface;
2177 }
2178 else {
2179 ifindex = scan->ifindex;
2180 flags = kRouteLookupFlagsNone;
2181 }
2182 route = RouteListLookup(info, routes,
2183 (*info->route_destination)(scan),
2184 scan->prefix_length, ifindex, flags);
2185 if (route == NULL) {
2186 (*info->route_log)(LOG_NOTICE, (RouteRef)scan,
2187 "can't resolve excluded route");
2188 }
2189 else {
2190 if ((S_IPMonitor_debug & kDebugFlag8) != 0) {
2191 (*info->route_log)(LOG_DEBUG, (RouteRef)scan, "Excluded route");
2192 (*info->route_log)(LOG_DEBUG, (RouteRef)route, "Resolved to");
2193 }
2194 scan->ifindex = route->ifindex;
2195 if ((route->flags & kRouteFlagsHasGateway) != 0) {
2196 (*info->route_set_gateway)(scan, (*info->route_gateway)(route));
2197 scan->flags |= kRouteFlagsHasGateway;
2198 if (scan->prefix_length == info->all_bits_set) {
2199 scan->flags |= kRouteFlagsIsHost;
2200 }
2201 }
2202 else {
2203 /* routes directly to interface */
2204 scan->flags &= ~(kRouteFlagsHasGateway | kRouteFlagsIsHost);
2205 }
2206 }
2207 }
2208 return;
2209 }
2210 #endif /* !TARGET_OS_SIMULATOR */
2211
2212 /**
2213 ** IPv4Route*
2214 **/
2215
2216 #define IPV4_ROUTE_ALL_BITS_SET 32
2217
2218 static __inline__ struct in_addr
2219 subnet_addr(struct in_addr addr, struct in_addr mask)
2220 {
2221 struct in_addr net;
2222
2223 net.s_addr = addr.s_addr & mask.s_addr;
2224 return (net);
2225 }
2226
2227 static void
2228 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r, CFMutableStringRef str)
2229 {
2230 if ((r->flags & kRouteFlagsIsHost) != 0) {
2231 CFStringAppendFormat(str, NULL,
2232 CFSTR("Host " IP_FORMAT),
2233 IP_LIST(&r->dest));
2234 }
2235 else {
2236 CFStringAppendFormat(str, NULL,
2237 CFSTR("Net " IP_FORMAT),
2238 IP_LIST(&r->dest));
2239 CFStringAppendFormat(str, NULL, CFSTR("/%d"),
2240 r->prefix_length);
2241 }
2242 if ((r->flags & kRouteFlagsHasGateway) != 0) {
2243 CFStringAppendFormat(str, NULL,
2244 CFSTR(" Gate " IP_FORMAT),
2245 IP_LIST(&r->gateway));
2246 }
2247 RouteAddInterfaceToDescription((RouteRef)r, str);
2248 if (r->ifa.s_addr != 0) {
2249 CFStringAppendFormat(str, NULL,
2250 CFSTR(" Ifa " IP_FORMAT),
2251 IP_LIST(&r->ifa));
2252 }
2253 RouteAddFlagsToDescription((RouteRef)r, str);
2254 return;
2255 }
2256
2257 static CFStringRef
2258 IPv4RouteCopyDescription(RouteRef r)
2259 {
2260 CFMutableStringRef str;
2261
2262 str = CFStringCreateMutable(NULL, 0);
2263 IPv4RouteCopyDescriptionWithString((IPv4RouteRef)r, str);
2264 return (str);
2265 }
2266
2267 #ifdef TEST_IPV4_ROUTELIST
2268 static CFMutableStringRef
2269 IPv4RouteListCopyDescription(IPv4RouteListRef routes);
2270
2271 static void
2272 IPv4RouteLog(int level, RouteRef route, const char * msg)
2273 {
2274 CFStringRef str = IPv4RouteCopyDescription(route);
2275
2276 if (msg == NULL) {
2277 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
2278 }
2279 else {
2280 SCPrint(TRUE, stdout, CFSTR("%s: %@\n"), msg, str);
2281 }
2282 CFRelease(str);
2283 return;
2284 }
2285
2286 static __inline__ void
2287 IPv4RouteListPrint(IPv4RouteListRef routes)
2288 {
2289 CFStringRef str = IPv4RouteListCopyDescription(routes);
2290
2291 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
2292 CFRelease(str);
2293 return;
2294 }
2295
2296 #else /* TEST_IPV4_ROUTELIST */
2297
2298 static __inline__ void
2299 IPv4RouteLog(int level, RouteRef route, const char * msg)
2300 {
2301 CFStringRef str = IPv4RouteCopyDescription(route);
2302
2303 if (msg == NULL) {
2304 my_log(level, "%@", str);
2305 }
2306 else {
2307 my_log(level, "%s: %@", msg, str);
2308 }
2309 CFRelease(str);
2310 return;
2311 }
2312
2313 #endif /* TEST_IPV4_ROUTELIST */
2314
2315 static boolean_t
2316 IPv4RouteIsEqual(RouteRef r_scan, RouteRef r_route)
2317 {
2318 IPv4RouteRef route = (IPv4RouteRef)r_route;
2319 IPv4RouteRef scan = (IPv4RouteRef)r_scan;
2320
2321 return ((scan->dest.s_addr == route->dest.s_addr)
2322 && (scan->mask.s_addr == route->mask.s_addr)
2323 && (scan->ifindex == route->ifindex)
2324 && (scan->ifa.s_addr == route->ifa.s_addr)
2325 && (scan->gateway.s_addr == route->gateway.s_addr)
2326 && (scan->flags == route->flags));
2327 }
2328
2329 static CFMutableStringRef
2330 IPv4RouteListCopyDescription(IPv4RouteListRef routes)
2331 {
2332 int i;
2333 IPv4RouteRef r;
2334 CFMutableStringRef str;
2335
2336 str = CFStringCreateMutable(NULL, 0);
2337 CFStringAppendFormat(str, NULL, CFSTR("<IPv4RouteList[%d]> = {"),
2338 routes->count);
2339 for (i = 0, r = routes->list; i < routes->count; i++, r++) {
2340 CFStringAppendFormat(str, NULL, CFSTR("\n%2d. "), i);
2341 IPv4RouteCopyDescriptionWithString(r, str);
2342 }
2343 CFStringAppend(str, CFSTR("\n}"));
2344 return (str);
2345 }
2346
2347 static size_t
2348 IPv4RouteListComputeSize(CFIndex n)
2349 {
2350 return (offsetof(IPv4RouteList, list[n]));
2351 }
2352
2353 static int
2354 count_prefix_bits_set(uint32_t n)
2355 {
2356 int count;
2357 const static int8_t bits[16] = {
2358 0, /* 0000 */
2359 -1, /* 0001 */
2360 -1, /* 0010 */
2361 -1, /* 0011 */
2362 -1, /* 0100 */
2363 -1, /* 0101 */
2364 -1, /* 0110 */
2365 -1, /* 0111 */
2366 1, /* 1000 */
2367 -1, /* 1001 */
2368 -1, /* 1010 */
2369 -1, /* 1011 */
2370 2, /* 1100 */
2371 -1, /* 1101 */
2372 3, /* 1110 */
2373 4, /* 1111 */
2374 };
2375
2376 for (count = 0; n != 0; n >>= 4) {
2377 int nbits = bits[n & 0x0f];
2378
2379 if (nbits < 0) {
2380 return (-1);
2381 }
2382 count += nbits;
2383 }
2384 return (count);
2385 }
2386
2387 static uint32_t
2388 prefix_to_mask32(unsigned int prefix_length)
2389 {
2390 if (prefix_length > 32 || prefix_length == 0) {
2391 return (0);
2392 }
2393 return (0xffffffff << (32 - prefix_length));
2394 }
2395
2396 static int
2397 mask_get_prefix_length(struct in_addr mask)
2398 {
2399 int count;
2400
2401 count = count_prefix_bits_set(mask.s_addr);
2402 if (count >= 0) {
2403 uint32_t val;
2404
2405 val = prefix_to_mask32(count);
2406 if (ntohl(mask.s_addr) != val) {
2407 /* expected mask based on prefix length doesn't match */
2408 return (-1);
2409 }
2410 }
2411 return (count);
2412 }
2413
2414 static boolean_t
2415 IPv4RouteSetPrefixLength(IPv4RouteRef route)
2416 {
2417 int length;
2418
2419 length = mask_get_prefix_length(route->mask);
2420 if (length < 0) {
2421 return (FALSE);
2422 }
2423 route->prefix_length = length;
2424 return (TRUE);
2425 }
2426
2427 static const void *
2428 IPv4RouteGateway(RouteRef r_route)
2429 {
2430 IPv4RouteRef route = (IPv4RouteRef)r_route;
2431 return (&route->gateway);
2432 }
2433
2434 static void
2435 IPv4RouteSetGateway(RouteRef r_route, const void * address)
2436 {
2437 IPv4RouteRef route = (IPv4RouteRef)r_route;
2438
2439 route->gateway = *((struct in_addr *)address);
2440 return;
2441 }
2442
2443 static const void *
2444 IPv4RouteDestination(RouteRef r_route)
2445 {
2446 IPv4RouteRef route = (IPv4RouteRef)r_route;
2447 return (&route->dest);
2448 }
2449
2450 static boolean_t
2451 IPv4RouteSameSubnet(RouteRef r_route, const void * addr)
2452 {
2453 const struct in_addr * address;
2454 IPv4RouteRef route = (IPv4RouteRef)r_route;
2455
2456 address = (const struct in_addr *)addr;
2457 return ((address->s_addr & route->mask.s_addr) == route->dest.s_addr);
2458 }
2459
2460 /*
2461 * Define: ROUTE_MSG_ADDRS_SPACE
2462 * Purpose:
2463 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2464 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2465 * someone changes the code and doesn't think to modify this.
2466 */
2467 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2468 + 2 * sizeof(struct sockaddr_dl) \
2469 + 128)
2470 typedef struct {
2471 struct rt_msghdr hdr;
2472 char addrs[ROUTE_MSG_ADDRS_SPACE];
2473 } route_msg;
2474
2475 /*
2476 * Function: IPv4RouteApply
2477 * Purpose:
2478 * Add or remove the specified route to/from the kernel routing table.
2479 */
2480 static int
2481 IPv4RouteApply(RouteRef r_route, int cmd, int sockfd)
2482 {
2483 size_t len;
2484 int ret = 0;
2485 IPv4RouteRef route = (IPv4RouteRef)r_route;
2486 route_msg rtmsg;
2487 union {
2488 struct sockaddr_in * in_p;
2489 struct sockaddr_dl * dl_p;
2490 void * ptr;
2491 } rtaddr;
2492
2493 if (S_netboot && route->dest.s_addr == 0) {
2494 /* don't touch the default route */
2495 return (EROUTENOTAPPLIED);
2496 }
2497 if ((route->flags & kRouteFlagsIsNULL) != 0) {
2498 return (EROUTENOTAPPLIED);
2499 }
2500 if (route->ifindex == 0) {
2501 my_log(LOG_NOTICE,
2502 IP_FORMAT " no interface specified, ignoring",
2503 IP_LIST(&route->dest));
2504 return (ENXIO);
2505 }
2506 if (sockfd == -1) {
2507 #ifdef TEST_IPV4_ROUTELIST
2508 return (0);
2509 #else /* TEST_IPV4_ROUTELIST */
2510 return (EBADF);
2511 #endif /* TEST_IPV4_ROUTELIST */
2512 }
2513 memset(&rtmsg, 0, sizeof(rtmsg));
2514 rtmsg.hdr.rtm_type = cmd;
2515 rtmsg.hdr.rtm_version = RTM_VERSION;
2516 rtmsg.hdr.rtm_seq = ++rtm_seq;
2517 rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_IFP;
2518 if (route->ifa.s_addr != 0) {
2519 rtmsg.hdr.rtm_addrs |= RTA_IFA;
2520 }
2521 rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC;
2522 if ((route->flags & kRouteFlagsIsHost) != 0) {
2523 rtmsg.hdr.rtm_flags |= RTF_HOST;
2524 }
2525 else {
2526 rtmsg.hdr.rtm_addrs |= RTA_NETMASK;
2527 if ((route->flags & kRouteFlagsHasGateway) == 0) {
2528 rtmsg.hdr.rtm_flags |= RTF_CLONING;
2529 }
2530 }
2531 if ((route->flags & kRouteFlagsHasGateway) != 0) {
2532 rtmsg.hdr.rtm_flags |= RTF_GATEWAY;
2533 }
2534 if ((route->flags & kRouteFlagsIsScoped) != 0) {
2535 rtmsg.hdr.rtm_index = route->ifindex;
2536 rtmsg.hdr.rtm_flags |= RTF_IFSCOPE;
2537 }
2538
2539 rtaddr.ptr = rtmsg.addrs;
2540
2541 /* dest */
2542 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2543 rtaddr.in_p->sin_family = AF_INET;
2544 rtaddr.in_p->sin_addr = route->dest;
2545 rtaddr.ptr += sizeof(*rtaddr.in_p);
2546
2547 /* gateway */
2548 if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) {
2549 /* gateway is an IP address */
2550 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2551 rtaddr.in_p->sin_family = AF_INET;
2552 rtaddr.in_p->sin_addr = route->gateway;
2553 rtaddr.ptr += sizeof(*rtaddr.in_p);
2554 }
2555 else {
2556 /* gateway is the interface itself */
2557 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
2558 rtaddr.dl_p->sdl_family = AF_LINK;
2559 rtaddr.dl_p->sdl_index = route->ifindex;
2560 rtaddr.ptr += sizeof(*rtaddr.dl_p);
2561 }
2562
2563 /* mask */
2564 if ((rtmsg.hdr.rtm_addrs & RTA_NETMASK) != 0) {
2565 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2566 rtaddr.in_p->sin_family = AF_INET;
2567 rtaddr.in_p->sin_addr = route->mask;
2568 rtaddr.ptr += sizeof(*rtaddr.in_p);
2569 }
2570
2571 /* interface */
2572 if ((rtmsg.hdr.rtm_addrs & RTA_IFP) != 0) {
2573 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
2574 rtaddr.dl_p->sdl_family = AF_LINK;
2575 rtaddr.dl_p->sdl_index = route->ifindex;
2576 rtaddr.ptr += sizeof(*rtaddr.dl_p);
2577 }
2578 /* interface address */
2579 if ((rtmsg.hdr.rtm_addrs & RTA_IFA) != 0) {
2580 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2581 rtaddr.in_p->sin_family = AF_INET;
2582 rtaddr.in_p->sin_addr = route->ifa;
2583 rtaddr.ptr += sizeof(*rtaddr.in_p);
2584 }
2585
2586 /* apply the route */
2587 len = (int)(sizeof(rtmsg.hdr) + (rtaddr.ptr - (void *)rtmsg.addrs));
2588 rtmsg.hdr.rtm_msglen = len;
2589 if (write(sockfd, &rtmsg, len) == -1) {
2590 ret = errno;
2591 }
2592 return (ret);
2593 }
2594
2595 static const RouteListInfo IPv4RouteListInfo = {
2596 IPv4RouteListComputeSize,
2597
2598 IPv4RouteIsEqual,
2599 IPv4RouteApply,
2600 IPv4RouteGateway,
2601 IPv4RouteSetGateway,
2602 IPv4RouteDestination,
2603 IPv4RouteSameSubnet,
2604 IPv4RouteLog,
2605 IPv4RouteCopyDescription,
2606
2607 sizeof(IPv4Route),
2608 sizeof(struct in_addr),
2609 IPV4_ROUTE_ALL_BITS_SET
2610 };
2611
2612 #if !TARGET_OS_SIMULATOR
2613 static __inline__ void
2614 IPv4RouteListLog(int level, IPv4RouteListRef routes)
2615 {
2616 CFStringRef str = IPv4RouteListCopyDescription(routes);
2617
2618 my_log(level, "%@", str);
2619 CFRelease(str);
2620 return;
2621 }
2622
2623 static void
2624 IPv4RouteListApply(IPv4RouteListRef old_routes, IPv4RouteListRef new_routes,
2625 int sockfd)
2626 {
2627 RouteListApply(&IPv4RouteListInfo,
2628 (RouteListRef)old_routes, (RouteListRef)new_routes,
2629 sockfd);
2630 return;
2631 }
2632
2633 static void
2634 IPv4RouteListFinalize(IPv4RouteListRef routes)
2635 {
2636 RouteListFinalize(&IPv4RouteListInfo, (RouteListRef)routes);
2637 return;
2638 }
2639 #endif /* !TARGET_OS_SIMULATOR */
2640
2641 #if TEST_IPV4_ROUTELIST
2642 static IPv4RouteListRef
2643 IPv4RouteListAddRouteList(IPv4RouteListRef routes, int init_size,
2644 IPv4RouteListRef service_routes, Rank rank)
2645 {
2646 return ((IPv4RouteListRef)
2647 RouteListAddRouteList(&IPv4RouteListInfo,
2648 (RouteListRef)routes, init_size,
2649 (RouteListRef)service_routes, rank,
2650 FALSE));
2651 }
2652 #endif /* TEST_IPV4_ROUTELIST */
2653
2654 static CFStringRef
2655 plist_get_string(CFDictionaryRef dict, CFStringRef prop_name,
2656 char * buf, int buf_size)
2657 {
2658 CFStringRef val;
2659
2660 val = CFDictionaryGetValue(dict, prop_name);
2661 if (isA_CFString(val) == NULL) {
2662 return (NULL);
2663 }
2664 if (!CFStringGetCString(val, buf, buf_size, kCFStringEncodingUTF8)) {
2665 return (NULL);
2666 }
2667 return (val);
2668 }
2669
2670 typedef struct {
2671 struct in_addr addr;
2672 int * count_p;
2673 IFIndex ifindex;
2674 IFIndex exclude_ifindex;
2675 IPv4RouteRef * route_p;
2676 Rank rank;
2677 const char * descr;
2678 } AddIPv4RouteContext, * AddIPv4RouteContextRef;
2679
2680 static void
2681 AddIPv4Route(const void * value, void * context)
2682 {
2683 AddIPv4RouteContextRef ctx = (AddIPv4RouteContextRef)context;
2684 CFDictionaryRef dict = (CFDictionaryRef)value;
2685 IPv4RouteRef r = *ctx->route_p;
2686
2687 dict = isA_CFDictionary(dict);
2688 if (dict == NULL
2689 || !dict_get_ip(dict, kSCPropNetIPv4RouteDestinationAddress, &r->dest)
2690 || !dict_get_ip(dict, kSCPropNetIPv4RouteSubnetMask, &r->mask)) {
2691 /* one less route than we expected */
2692 if (dict == NULL) {
2693 my_log(LOG_NOTICE, "%s route is not a dictionary",
2694 ctx->descr);
2695 }
2696 else {
2697 my_log(LOG_NOTICE, "%s route is invalid, %@",
2698 ctx->descr, dict);
2699 }
2700 goto skip;
2701 }
2702 if (!IPv4RouteSetPrefixLength(r)) {
2703 my_log(LOG_NOTICE, "%s route has invalid subnet mask, %@",
2704 ctx->descr, dict);
2705 goto skip;
2706 }
2707 r->rank = ctx->rank;
2708 r->exclude_ifindex = ctx->exclude_ifindex;
2709 if (ctx->ifindex != 0) {
2710 r->ifindex = ctx->ifindex;
2711 r->ifa = ctx->addr;
2712 if (ctx->exclude_ifindex == 0
2713 && dict_get_ip(dict,
2714 kSCPropNetIPv4RouteGatewayAddress,
2715 &r->gateway)) {
2716 r->flags |= kRouteFlagsHasGateway;
2717 if (r->prefix_length == IPV4_ROUTE_ALL_BITS_SET) {
2718 r->flags |= kRouteFlagsIsHost;
2719 }
2720 }
2721 }
2722 else {
2723 char ifname[IFNAMSIZ];
2724
2725 if (plist_get_string(dict, kSCPropNetIPv4RouteInterfaceName,
2726 ifname, sizeof(ifname)) != NULL) {
2727 IFIndex ifindex;
2728
2729 ifindex = my_if_nametoindex(ifname);
2730 if (ifindex == 0) {
2731 my_log(LOG_NOTICE,
2732 "%s: interface %s does not exist, %@",
2733 ctx->descr, ifname, dict);
2734 goto skip;
2735 }
2736 else if (ifindex == ctx->ifindex) {
2737 my_log(LOG_NOTICE,
2738 "%s: interface %s unexpected, %@",
2739 ctx->descr, ifname, dict);
2740 goto skip;
2741 }
2742 r->ifindex = ifindex;
2743 }
2744 }
2745 (*ctx->route_p)++;
2746 return;
2747
2748 skip:
2749 (*ctx->count_p)--;
2750 return;
2751
2752 }
2753
2754 static boolean_t
2755 confirm_interface_name(CFDictionaryRef dict, CFStringRef ifname)
2756 {
2757 CFStringRef confirmed_ifname;
2758 boolean_t confirmed;
2759
2760 confirmed_ifname
2761 = CFDictionaryGetValue(dict, kSCPropConfirmedInterfaceName);
2762 if (isA_CFString(confirmed_ifname) != NULL) {
2763 confirmed = CFEqual(confirmed_ifname, ifname);
2764 }
2765 else {
2766 confirmed = TRUE;
2767 }
2768 return (confirmed);
2769 }
2770
2771 /*
2772 * Function: IPv4RouteListCreateWithDictionary
2773 *
2774 * Purpose:
2775 * Given the service ipv4 entity dictionary, generate the list of routes.
2776 * Currently, this includes just the default route and subnet route,
2777 * if the service has a subnet mask.
2778 *
2779 * Returns:
2780 * If the passed in route_list is NULL or too small, this routine
2781 * allocates malloc'd memory to hold the routes.
2782 */
2783 static IPv4RouteListRef
2784 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes,
2785 CFDictionaryRef dict,
2786 CFNumberRef rank_assertion)
2787 {
2788 boolean_t add_broadcast_multicast = FALSE;
2789 boolean_t add_default = FALSE;
2790 boolean_t add_router_subnet = FALSE;
2791 boolean_t add_subnet = FALSE;
2792 struct in_addr addr = { 0 };
2793 CFArrayRef additional_routes = NULL;
2794 CFIndex additional_routes_count;
2795 boolean_t allow_additional_routes = FALSE;
2796 boolean_t exclude_from_nwi = FALSE;
2797 CFArrayRef excluded_routes = NULL;
2798 CFIndex excluded_routes_count;
2799 RouteFlags flags = 0;
2800 IFIndex ifindex;
2801 char ifname[IFNAMSIZ];
2802 CFStringRef ifname_cf;
2803 struct in_addr mask = { 0 };
2804 int n = 0;
2805 int prefix_length = 0;
2806 Rank primary_rank = kRankAssertionDefault;
2807 IPv4RouteRef r;
2808 Rank rank = kRankAssertionDefault;
2809 struct in_addr router = { 0 };
2810 boolean_t scoped_only = FALSE;
2811 struct in_addr subnet = { 0 };
2812
2813 if (dict == NULL) {
2814 return (NULL);
2815 }
2816 ifname_cf = plist_get_string(dict, kSCPropInterfaceName,
2817 ifname, sizeof(ifname));
2818 if (ifname_cf == NULL) {
2819 return (NULL);
2820 }
2821 ifindex = my_if_nametoindex(ifname);
2822 if (ifindex == 0) {
2823 /* interface doesn't exist */
2824 return (NULL);
2825 }
2826 allow_additional_routes = confirm_interface_name(dict, ifname_cf);
2827 if (!dict_get_ip(dict, kSCPropNetIPv4Router, &router)) {
2828 (void)dict_get_first_ip(dict, kSCPropNetIPv4DestAddresses, &router);
2829 }
2830 if (dict_get_first_ip(dict, kSCPropNetIPv4Addresses, &addr)
2831 && dict_get_first_ip(dict, kSCPropNetIPv4SubnetMasks, &mask)) {
2832 /* subnet route */
2833 subnet = subnet_addr(addr, mask);
2834 prefix_length = mask_get_prefix_length(mask);
2835 if (prefix_length < 0) {
2836 my_log(LOG_NOTICE,
2837 "ignoring bad subnet mask "
2838 IP_FORMAT " on %s",
2839 IP_LIST(&mask), ifname);
2840 }
2841 else {
2842 add_subnet = TRUE;
2843 n++;
2844 }
2845 }
2846 if (addr.s_addr == 0) {
2847 /* invalid/non-existent address */
2848 return (NULL);
2849 }
2850 if (rank_assertion != NULL) {
2851 (void)CFNumberGetValue(rank_assertion, kCFNumberSInt32Type,
2852 &primary_rank);
2853 }
2854 if (router.s_addr == 0) {
2855 /* if no router is configured, demote the rank if necessary */
2856 switch (primary_rank) {
2857 case kRankAssertionLast:
2858 case kRankAssertionNever:
2859 case kRankAssertionScoped:
2860 /* rank is already demoted */
2861 break;
2862 default:
2863 /* demote to RankLast */
2864 primary_rank = kRankAssertionLast;
2865 break;
2866 }
2867 }
2868 else {
2869 /*
2870 * If the router address is our address and the subnet mask is
2871 * not 255.255.255.255, assume all routes are local to the interface.
2872 */
2873 if (addr.s_addr == router.s_addr
2874 && mask.s_addr != INADDR_BROADCAST) {
2875 ; /* all routes local */
2876 }
2877 else {
2878 flags |= kRouteFlagsHasGateway;
2879 }
2880 if (rank_assertion == NULL && get_override_primary(dict)) {
2881 primary_rank = kRankAssertionFirst;
2882 }
2883 }
2884
2885 if (S_dict_get_boolean(dict, kIsNULL, FALSE)) {
2886 exclude_from_nwi = TRUE;
2887 flags |= kRouteFlagsIsNULL;
2888 }
2889
2890 switch (primary_rank) {
2891 case kRankAssertionScoped:
2892 /* Scoped means all routes for the service get scoped */
2893 primary_rank = rank = kRankAssertionNever;
2894 flags |= kRouteFlagsIsScoped;
2895 scoped_only = TRUE;
2896 break;
2897 case kRankAssertionNever:
2898 /* Never means just the default route gets scoped */
2899 rank = kRankAssertionLast;
2900 flags |= kRouteFlagsIsScoped;
2901 break;
2902 default:
2903 rank = primary_rank;
2904 break;
2905 }
2906
2907 if ((flags & kRouteFlagsHasGateway) != 0) {
2908 add_router_subnet = TRUE;
2909 n++;
2910 }
2911
2912 if (ifindex != lo0_ifindex()) {
2913 if (router.s_addr != 0) {
2914 add_default = TRUE;
2915 n++;
2916 }
2917 add_broadcast_multicast = TRUE;
2918 n += 2;
2919 }
2920 if (allow_additional_routes) {
2921 additional_routes
2922 = CFDictionaryGetValue(dict, kSCPropNetIPv4AdditionalRoutes);
2923 additional_routes = isA_CFArray(additional_routes);
2924 if (additional_routes != NULL) {
2925 additional_routes_count = CFArrayGetCount(additional_routes);
2926 n += additional_routes_count;
2927 }
2928 excluded_routes
2929 = CFDictionaryGetValue(dict, kSCPropNetIPv4ExcludedRoutes);
2930 excluded_routes = isA_CFArray(excluded_routes);
2931 if (excluded_routes != NULL) {
2932 excluded_routes_count = CFArrayGetCount(excluded_routes);
2933 n += excluded_routes_count;
2934 }
2935 }
2936 if (routes == NULL || routes->size < n) {
2937 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(n));
2938 memset(routes, 0, IPv4RouteListComputeSize(n));
2939 routes->size = n;
2940 }
2941 else {
2942 memset(routes->list, 0, sizeof(routes->list[0]) * n);
2943 }
2944 routes->count = n;
2945 if (exclude_from_nwi) {
2946 routes->flags |= kRouteListFlagsExcludeNWI;
2947 }
2948 else if (scoped_only) {
2949 routes->flags |= kRouteListFlagsScopedOnly;
2950 }
2951
2952 /* start at the beginning */
2953 r = routes->list;
2954
2955 if (add_default) {
2956 /* add the default route */
2957 routes->flags |= kRouteListFlagsHasDefault;
2958 r->ifindex = ifindex;
2959 r->ifa = addr;
2960 r->flags = flags;
2961 if ((flags & kRouteFlagsHasGateway) != 0) {
2962 r->gateway = router;
2963 }
2964 else {
2965 r->gateway = addr;
2966 }
2967 r->rank = primary_rank;
2968 r++;
2969 }
2970 if (add_broadcast_multicast) {
2971 /* add the broadcast route (rdar://problem/22149738) */
2972 if ((flags & kRouteFlagsIsNULL) != 0) {
2973 r->flags |= kRouteFlagsIsNULL;
2974 }
2975 r->dest.s_addr = INADDR_BROADCAST;
2976 r->mask.s_addr = INADDR_BROADCAST;
2977 r->prefix_length = IPV4_ROUTE_ALL_BITS_SET;
2978 r->ifindex = ifindex;
2979 r->ifa = addr;
2980 r->rank = rank;
2981 r++;
2982
2983 /* add multicast route (rdar://problem/26457121) */
2984 if ((flags & kRouteFlagsIsNULL) != 0) {
2985 r->flags |= kRouteFlagsIsNULL;
2986 }
2987 r->dest.s_addr = htonl(INADDR_UNSPEC_GROUP);
2988 r->mask.s_addr = htonl(IN_CLASSD_NET);
2989 r->prefix_length = PREFIX_LENGTH_IN_CLASSD;
2990 r->ifindex = ifindex;
2991 r->ifa = addr;
2992 r->rank = rank;
2993 r++;
2994
2995 }
2996
2997 /* add the subnet route */
2998 if (add_subnet) {
2999 if ((flags & kRouteFlagsIsNULL) != 0) {
3000 r->flags |= kRouteFlagsIsNULL;
3001 }
3002 r->ifindex = ifindex;
3003 r->gateway = addr;
3004 r->dest = subnet;
3005 r->mask = mask;
3006 r->prefix_length = prefix_length;
3007 r->ifa = addr;
3008 r->rank = rank;
3009 r++;
3010 }
3011
3012 /* add the router subnet route */
3013 if (add_router_subnet) {
3014 if ((flags & kRouteFlagsIsNULL) != 0) {
3015 r->flags |= kRouteFlagsIsNULL;
3016 }
3017 r->ifindex = ifindex;
3018 r->gateway = addr;
3019 r->dest = router;
3020 r->mask.s_addr = INADDR_BROADCAST;
3021 r->prefix_length = IPV4_ROUTE_ALL_BITS_SET;
3022 r->ifa = addr;
3023 r->rank = rank;
3024 r++;
3025 }
3026
3027 if (additional_routes != NULL || excluded_routes != NULL) {
3028 AddIPv4RouteContext context;
3029
3030 memset(&context, 0, sizeof(context));
3031 context.count_p = &routes->count;
3032 context.route_p = &r;
3033 context.rank = rank;
3034
3035 /* additional routes */
3036 if (additional_routes != NULL) {
3037 context.ifindex = ifindex;
3038 context.addr = addr;
3039 context.descr = "AdditionalRoutes";
3040 CFArrayApplyFunction(additional_routes,
3041 CFRangeMake(0, additional_routes_count),
3042 AddIPv4Route, &context);
3043 }
3044 /* excluded routes */
3045 if (excluded_routes != NULL) {
3046 context.descr = "ExcludedRoutes";
3047 /* exclude this interface */
3048 context.ifindex = 0;
3049 context.exclude_ifindex = ifindex;
3050 CFArrayApplyFunction(excluded_routes,
3051 CFRangeMake(0, excluded_routes_count),
3052 AddIPv4Route, &context);
3053 }
3054 }
3055 return (routes);
3056 }
3057
3058 #if !TARGET_OS_SIMULATOR
3059 static IPv4RouteListRef
3060 IPv4RouteListCopyMulticastLoopback(void)
3061 {
3062 IPv4RouteRef r;
3063 IPv4RouteListRef routes;
3064
3065 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(1));
3066 memset(routes, 0, IPv4RouteListComputeSize(1));
3067 routes->count = routes->size = 1;
3068
3069 r = routes->list;
3070 r->dest.s_addr = htonl(INADDR_UNSPEC_GROUP);
3071 r->mask.s_addr = htonl(IN_CLASSC_NET);
3072 r->prefix_length = PREFIX_LENGTH_IN_CLASSC;
3073 r->ifindex = lo0_ifindex();
3074 return (routes);
3075 }
3076 #endif /* !TARGET_OS_SIMULATOR */
3077
3078 /**
3079 ** IPv6Route*
3080 **/
3081 #define IPV6_ROUTE_ALL_BITS_SET 128
3082
3083 static boolean_t
3084 ipv6_prefix_length_is_valid(int prefix_length)
3085 {
3086 if (prefix_length < 0 || prefix_length > IPV6_ROUTE_ALL_BITS_SET) {
3087 return (FALSE);
3088 }
3089 return (TRUE);
3090 }
3091
3092 /*
3093 * from netinet6/in6.c
3094 */
3095 static void
3096 in6_len2mask(struct in6_addr * mask, int len)
3097 {
3098 int i;
3099
3100 memset(mask, 0, sizeof(*mask));
3101 for (i = 0; i < len / 8; i++)
3102 mask->s6_addr[i] = 0xff;
3103 if (len % 8)
3104 mask->s6_addr[i] = (0xff00 >> (len % 8)) & 0xff;
3105 }
3106
3107 static void
3108 in6_maskaddr(struct in6_addr * addr, const struct in6_addr * mask)
3109 {
3110 for (size_t i = 0; i < sizeof(addr->s6_addr); i++) {
3111 addr->s6_addr[i] &= mask->s6_addr[i];
3112 }
3113 return;
3114 }
3115
3116 static void
3117 in6_netaddr(struct in6_addr * addr, int len)
3118 {
3119 struct in6_addr mask;
3120
3121 in6_len2mask(&mask, len);
3122 in6_maskaddr(addr, &mask);
3123 return;
3124 }
3125
3126 static void
3127 in6_addr_scope_linklocal(struct in6_addr * addr, IFIndex ifindex)
3128 {
3129 if (IN6_IS_ADDR_LINKLOCAL(addr)) {
3130 addr->__u6_addr.__u6_addr16[1] = htons(ifindex);
3131 }
3132 return;
3133 }
3134
3135 static void
3136 string_append_in6_addr(CFMutableStringRef str, const struct in6_addr * addr)
3137 {
3138 char ntopbuf[INET6_ADDRSTRLEN];
3139
3140 CFStringAppendCString(str,
3141 inet_ntop(AF_INET6, addr, ntopbuf, sizeof(ntopbuf)),
3142 kCFStringEncodingASCII);
3143 return;
3144 }
3145
3146 static void
3147 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r, CFMutableStringRef str)
3148 {
3149 if ((r->flags & kRouteFlagsIsHost) != 0) {
3150 CFStringAppend(str, CFSTR("Host "));
3151 string_append_in6_addr(str, &r->dest);
3152 }
3153 else {
3154 CFStringAppend(str, CFSTR("Net "));
3155 string_append_in6_addr(str, &r->dest);
3156 CFStringAppendFormat(str, NULL, CFSTR("/%d"),
3157 r->prefix_length);
3158 }
3159 if ((r->flags & kRouteFlagsHasGateway) != 0) {
3160 CFStringAppend(str, CFSTR(" Gate "));
3161 string_append_in6_addr(str, &r->gateway);
3162 }
3163 RouteAddInterfaceToDescription((RouteRef)r, str);
3164 if (!IN6_ARE_ADDR_EQUAL(&r->ifa, &in6addr_any)) {
3165 CFStringAppend(str, CFSTR(" Ifa "));
3166 string_append_in6_addr(str, &r->ifa);
3167 }
3168 RouteAddFlagsToDescription((RouteRef)r, str);
3169 return;
3170 }
3171
3172 static CFStringRef
3173 IPv6RouteCopyDescription(RouteRef r)
3174 {
3175 CFMutableStringRef str;
3176
3177 str = CFStringCreateMutable(NULL, 0);
3178 IPv6RouteCopyDescriptionWithString((IPv6RouteRef)r, str);
3179 return (str);
3180 }
3181
3182 static CFMutableStringRef
3183 IPv6RouteListCopyDescription(IPv6RouteListRef routes)
3184 {
3185 int i;
3186 IPv6RouteRef r;
3187 CFMutableStringRef str;
3188
3189 str = CFStringCreateMutable(NULL, 0);
3190 CFStringAppendFormat(str, NULL, CFSTR("<IPv6RouteList[%d]> = {"),
3191 routes->count);
3192 for (i = 0, r = routes->list; i < routes->count; i++, r++) {
3193 CFStringAppendFormat(str, NULL, CFSTR("\n%2d. "), i);
3194 IPv6RouteCopyDescriptionWithString(r, str);
3195 }
3196 CFStringAppend(str, CFSTR("\n}"));
3197 return (str);
3198 }
3199
3200 #if TEST_IPV6_ROUTELIST
3201
3202 static void
3203 IPv6RouteLog(int level, RouteRef route, const char * msg)
3204 {
3205 CFStringRef str = IPv6RouteCopyDescription(route);
3206
3207 if (msg == NULL) {
3208 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
3209 }
3210 else {
3211 SCPrint(TRUE, stdout, CFSTR("%s: %@\n"), msg, str);
3212 }
3213 CFRelease(str);
3214 return;
3215 }
3216
3217 static __inline__ void
3218 IPv6RouteListPrint(IPv6RouteListRef routes)
3219 {
3220 CFStringRef str = IPv6RouteListCopyDescription(routes);
3221
3222 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
3223 CFRelease(str);
3224 return;
3225 }
3226
3227 #else /* TEST_IPV6_ROUTELIST */
3228
3229 static __inline__ void
3230 IPv6RouteLog(int level, RouteRef route, const char * msg)
3231 {
3232 CFStringRef str = IPv6RouteCopyDescription(route);
3233
3234 if (msg == NULL) {
3235 my_log(level, "%@", str);
3236 }
3237 else {
3238 my_log(level, "%s: %@", msg, str);
3239 }
3240 CFRelease(str);
3241 return;
3242 }
3243
3244 #endif /* TEST_IPV6_ROUTELIST */
3245
3246 static size_t
3247 IPv6RouteListComputeSize(CFIndex n)
3248 {
3249 return (offsetof(IPv6RouteList, list[n]));
3250 }
3251
3252
3253 typedef struct {
3254 struct in6_addr * addr;
3255 int * count_p;
3256 IFIndex ifindex;
3257 IFIndex exclude_ifindex;
3258 IPv6RouteRef * route_p;
3259 Rank rank;
3260 const char * descr;
3261 } AddIPv6RouteContext, * AddIPv6RouteContextRef;
3262
3263 static void
3264 AddIPv6Route(const void * value, void * context)
3265 {
3266 AddIPv6RouteContextRef ctx = (AddIPv6RouteContextRef)context;
3267 CFDictionaryRef dict = (CFDictionaryRef)value;
3268 IPv6RouteRef r = *ctx->route_p;
3269
3270 dict = isA_CFDictionary(dict);
3271 if (dict == NULL
3272 || !dict_get_ipv6(dict, kSCPropNetIPv6RouteDestinationAddress, &r->dest)
3273 || !dict_get_int(dict, kSCPropNetIPv6RoutePrefixLength,
3274 &r->prefix_length)
3275 || !ipv6_prefix_length_is_valid(r->prefix_length)) {
3276 /* one less route than we expected */
3277 if (dict == NULL) {
3278 my_log(LOG_NOTICE, "%s route is not a dictionary",
3279 ctx->descr);
3280 }
3281 else {
3282 my_log(LOG_NOTICE, "%s route is invalid, %@",
3283 ctx->descr, dict);
3284 }
3285 goto skip;
3286 }
3287 r->rank = ctx->rank;
3288 r->exclude_ifindex = ctx->exclude_ifindex;
3289 if (ctx->ifindex != 0) {
3290 r->ifindex = ctx->ifindex;
3291 r->ifa = *ctx->addr;
3292 if (ctx->exclude_ifindex == 0
3293 && dict_get_ipv6(dict,
3294 kSCPropNetIPv6RouteGatewayAddress,
3295 &r->gateway)) {
3296 r->flags |= kRouteFlagsHasGateway;
3297 if (r->prefix_length == IPV6_ROUTE_ALL_BITS_SET) {
3298 r->flags |= kRouteFlagsIsHost;
3299 }
3300 }
3301 }
3302 else {
3303 char ifname[IFNAMSIZ];
3304
3305 if (plist_get_string(dict, kSCPropNetIPv6RouteInterfaceName,
3306 ifname, sizeof(ifname)) != NULL) {
3307 IFIndex ifindex;
3308
3309 ifindex = my_if_nametoindex(ifname);
3310 if (ifindex == 0) {
3311 my_log(LOG_NOTICE,
3312 "%s: interface %s does not exist, %@",
3313 ctx->descr, ifname, dict);
3314 goto skip;
3315 }
3316 else if (ifindex == ctx->ifindex) {
3317 my_log(LOG_NOTICE,
3318 "%s: interface %s unexpected, %@",
3319 ctx->descr, ifname, dict);
3320 goto skip;
3321 }
3322 r->ifindex = ifindex;
3323 }
3324 }
3325 (*ctx->route_p)++;
3326 return;
3327
3328 skip:
3329 (*ctx->count_p)--;
3330 return;
3331
3332 }
3333
3334 /*
3335 * Function: IPv6RouteListCreateWithDictionary
3336 *
3337 * Purpose:
3338 * Given the service IPv6 entity dictionary, generate the list of routes.
3339 *
3340 * Returns:
3341 * If the passed in route_list is NULL or too small, this routine
3342 * allocates malloc'd memory to hold the routes.
3343 */
3344 static IPv6RouteListRef
3345 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes,
3346 CFDictionaryRef dict,
3347 CFNumberRef rank_assertion)
3348 {
3349 boolean_t add_default = FALSE;
3350 boolean_t add_prefix = FALSE;
3351 struct in6_addr addr;
3352 CFArrayRef additional_routes = NULL;
3353 CFIndex additional_routes_count;
3354 boolean_t allow_additional_routes = FALSE;
3355 boolean_t exclude_from_nwi = FALSE;
3356 CFArrayRef excluded_routes = NULL;
3357 CFIndex excluded_routes_count;
3358 RouteFlags flags = 0;
3359 IFIndex ifindex;
3360 char ifname[IFNAMSIZ];
3361 CFStringRef ifname_cf;
3362 int n = 0;
3363 int prefix_length = 0;
3364 Rank primary_rank = kRankAssertionDefault;
3365 IPv6RouteRef r;
3366 Rank rank = kRankAssertionDefault;
3367 struct in6_addr router = in6addr_any;
3368 boolean_t scoped_only = FALSE;
3369
3370 if (dict == NULL) {
3371 return (NULL);
3372 }
3373 ifname_cf = plist_get_string(dict, kSCPropInterfaceName,
3374 ifname, sizeof(ifname));
3375 if (ifname_cf == NULL) {
3376 return (NULL);
3377 }
3378 ifindex = my_if_nametoindex(ifname);
3379 if (ifindex == 0) {
3380 /* interface doesn't exist */
3381 return (NULL);
3382 }
3383 allow_additional_routes = confirm_interface_name(dict, ifname_cf);
3384 if (!dict_get_ipv6(dict, kSCPropNetIPv6Router, &router)) {
3385 (void)dict_get_first_ipv6(dict, kSCPropNetIPv6DestAddresses, &router);
3386 }
3387 if (dict_get_first_ipv6(dict, kSCPropNetIPv6Addresses, &addr)) {
3388 if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
3389 return (NULL);
3390 }
3391 if (dict_get_first_int(dict, kSCPropNetIPv6PrefixLength,
3392 &prefix_length)
3393 && !IN6_IS_ADDR_LINKLOCAL(&addr)
3394 && ipv6_prefix_length_is_valid(prefix_length)) {
3395 add_prefix = TRUE;
3396 n++;
3397 }
3398 else {
3399 prefix_length = 0;
3400 }
3401 }
3402 else {
3403 /* no addresses */
3404 return (NULL);
3405 }
3406 if (rank_assertion != NULL) {
3407 (void)CFNumberGetValue(rank_assertion, kCFNumberSInt32Type,
3408 &primary_rank);
3409 }
3410 if (!IN6_IS_ADDR_UNSPECIFIED(&router)) {
3411 if (ifindex != lo0_ifindex()) {
3412 add_default = TRUE;
3413 n++;
3414 }
3415 /*
3416 * If the router address is our address and the prefix length is
3417 * not 128, assume all routes are local to the interface.
3418 */
3419 if (IN6_ARE_ADDR_EQUAL(&router, &addr)
3420 && prefix_length != IPV6_ROUTE_ALL_BITS_SET) {
3421 ; /* all routes local */
3422 }
3423 else {
3424 flags |= kRouteFlagsHasGateway;
3425 }
3426 if (rank_assertion == NULL && get_override_primary(dict)) {
3427 primary_rank = kRankAssertionFirst;
3428 }
3429 }
3430 if (S_dict_get_boolean(dict, kIsNULL, FALSE)) {
3431 exclude_from_nwi = TRUE;
3432 flags |= kRouteFlagsIsNULL;
3433 }
3434
3435 switch (primary_rank) {
3436 case kRankAssertionScoped:
3437 /* Scoped means all routes for the service get scoped */
3438 primary_rank = rank = kRankAssertionNever;
3439 flags |= kRouteFlagsIsScoped;
3440 scoped_only = TRUE;
3441 break;
3442 case kRankAssertionNever:
3443 /* Never means just the default route gets scoped */
3444 rank = kRankAssertionLast;
3445 flags |= kRouteFlagsIsScoped;
3446 break;
3447 default:
3448 rank = primary_rank;
3449 break;
3450 }
3451
3452 if (allow_additional_routes) {
3453 additional_routes
3454 = CFDictionaryGetValue(dict, kSCPropNetIPv6AdditionalRoutes);
3455 additional_routes = isA_CFArray(additional_routes);
3456 if (additional_routes != NULL) {
3457 additional_routes_count = CFArrayGetCount(additional_routes);
3458 n += additional_routes_count;
3459 }
3460 excluded_routes = CFDictionaryGetValue(dict,
3461 kSCPropNetIPv6ExcludedRoutes);
3462 excluded_routes = isA_CFArray(excluded_routes);
3463 if (excluded_routes != NULL) {
3464 excluded_routes_count = CFArrayGetCount(excluded_routes);
3465 n += excluded_routes_count;
3466 }
3467 }
3468 if (n == 0) {
3469 return (NULL);
3470 }
3471
3472 /* need IPv6LL subnet route */
3473 n++;
3474
3475 if (routes == NULL || routes->size < n) {
3476 routes = (IPv6RouteListRef)malloc(IPv6RouteListComputeSize(n));
3477 memset(routes, 0, IPv6RouteListComputeSize(n));
3478 routes->size = n;
3479 }
3480 else {
3481 memset(routes->list, 0, sizeof(routes->list[0]) * n);
3482 }
3483 routes->count = n;
3484 if (exclude_from_nwi) {
3485 routes->flags |= kRouteListFlagsExcludeNWI;
3486 }
3487 else if (scoped_only) {
3488 routes->flags |= kRouteListFlagsScopedOnly;
3489 }
3490
3491 /* start at the beginning */
3492 r = routes->list;
3493 if (add_default) {
3494 /* add the default route */
3495 routes->flags |= kRouteListFlagsHasDefault;
3496 r->ifindex = ifindex;
3497 r->ifa = addr;
3498 r->flags = flags;
3499 if ((flags & kRouteFlagsHasGateway) != 0) {
3500 r->gateway = router;
3501 }
3502 else {
3503 r->gateway = addr;
3504 }
3505 r->rank = primary_rank;
3506 r->flags |= kRouteFlagsKernelManaged;
3507 r++;
3508 }
3509
3510
3511 /* add IPv6LL route */
3512 r->ifindex = ifindex;
3513 r->dest.s6_addr[0] = 0xfe;
3514 r->dest.s6_addr[1] = 0x80;
3515 r->prefix_length = 64;
3516 r->rank = rank;
3517 r->flags |= kRouteFlagsKernelManaged;
3518 r++;
3519
3520
3521 /* add the prefix route(s) */
3522 if (add_prefix) {
3523 r->flags |= kRouteFlagsKernelManaged;
3524 if ((flags & kRouteFlagsIsNULL) != 0) {
3525 r->flags |= kRouteFlagsIsNULL;
3526 }
3527 r->ifindex = ifindex;
3528 r->gateway = addr;
3529 r->dest = addr;
3530 in6_netaddr(&r->dest, prefix_length);
3531 r->prefix_length = prefix_length;
3532 r->ifa = addr;
3533 r->rank = rank;
3534 r++;
3535 }
3536
3537 if (additional_routes != NULL || excluded_routes != NULL) {
3538 AddIPv6RouteContext context;
3539
3540 memset(&context, 0, sizeof(context));
3541 context.count_p = &routes->count;
3542 context.route_p = &r;
3543 context.rank = rank;
3544
3545 /* additional routes */
3546 if (additional_routes != NULL) {
3547 context.ifindex = ifindex;
3548 context.addr = &addr;
3549 context.descr = "AdditionalRoutes";
3550 CFArrayApplyFunction(additional_routes,
3551 CFRangeMake(0, additional_routes_count),
3552 AddIPv6Route, &context);
3553 }
3554 /* excluded routes */
3555 if (excluded_routes != NULL) {
3556 context.descr = "ExcludedRoutes";
3557 /* exclude this interface */
3558 context.ifindex = 0;
3559 context.exclude_ifindex = ifindex;
3560 context.addr = NULL;
3561 CFArrayApplyFunction(excluded_routes,
3562 CFRangeMake(0, excluded_routes_count),
3563 AddIPv6Route, &context);
3564 }
3565 }
3566 return (routes);
3567 }
3568
3569 static const void *
3570 IPv6RouteGateway(RouteRef r_route)
3571 {
3572 IPv6RouteRef route = (IPv6RouteRef)r_route;
3573 return (&route->gateway);
3574 }
3575
3576 static void
3577 IPv6RouteSetGateway(RouteRef r_route, const void * address)
3578 {
3579 IPv6RouteRef route = (IPv6RouteRef)r_route;
3580
3581 route->gateway = *((struct in6_addr *)address);
3582 return;
3583 }
3584
3585 static const void *
3586 IPv6RouteDestination(RouteRef r_route)
3587 {
3588 IPv6RouteRef route = (IPv6RouteRef)r_route;
3589 return (&route->dest);
3590 }
3591
3592 static __inline__ int
3593 in6_addr_cmp(const struct in6_addr * a, const struct in6_addr * b)
3594 {
3595 return (memcmp(a->s6_addr, b->s6_addr, sizeof(struct in6_addr)));
3596 }
3597
3598 static boolean_t
3599 IPv6RouteIsEqual(RouteRef r_route1, RouteRef r_route2)
3600 {
3601 IPv6RouteRef route1 = (IPv6RouteRef)r_route1;
3602 IPv6RouteRef route2 = (IPv6RouteRef)r_route2;
3603
3604 return (route1->prefix_length == route2->prefix_length
3605 && route1->ifindex == route2->ifindex
3606 && route1->flags == route2->flags
3607 && in6_addr_cmp(&route1->dest, &route2->dest) == 0
3608 && in6_addr_cmp(&route1->ifa, &route2->ifa) == 0
3609 && in6_addr_cmp(&route1->gateway, &route2->gateway) == 0);
3610 }
3611
3612 static boolean_t
3613 IPv6RouteSameSubnet(RouteRef r_route, const void * addr)
3614 {
3615 const struct in6_addr * address = (const struct in6_addr *)addr;
3616 struct in6_addr netaddr;
3617 IPv6RouteRef route = (IPv6RouteRef)r_route;
3618
3619 netaddr = *address;
3620 in6_netaddr(&netaddr, route->prefix_length);
3621 return (in6_addr_cmp(&netaddr, &route->dest) == 0);
3622 }
3623
3624
3625 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3626
3627 typedef struct {
3628 struct rt_msghdr hdr;
3629 char addrs[V6_ROUTE_MSG_ADDRS_SPACE];
3630 } v6_route_msg;
3631
3632 /*
3633 * Function: IPv6RouteApply
3634 * Purpose:
3635 * Add or remove the specified route to/from the kernel routing table.
3636 */
3637 static int
3638 IPv6RouteApply(RouteRef r_route, int cmd, int sockfd)
3639 {
3640 int len;
3641 int ret = 0;
3642 IPv6RouteRef route = (IPv6RouteRef)r_route;
3643 v6_route_msg rtmsg;
3644 union {
3645 struct sockaddr_in6 * in_p;
3646 struct sockaddr_dl * dl_p;
3647 void * ptr;
3648 } rtaddr;
3649
3650 if ((route->flags & kRouteFlagsKernelManaged) != 0) {
3651 /* the kernel manages this route, don't touch it */
3652 return (EROUTENOTAPPLIED);
3653 }
3654 if ((route->flags & kRouteFlagsIsNULL) != 0) {
3655 return (EROUTENOTAPPLIED);
3656 }
3657 if (route->ifindex == 0) {
3658 IPv6RouteLog(LOG_NOTICE, (RouteRef)route,
3659 "no interface specified");
3660 return (ENXIO);
3661 }
3662 if (sockfd == -1) {
3663 #if TEST_IPV6_ROUTELIST
3664 return (0);
3665 #else /* TEST_IPV6_ROUTELIST */
3666 return (EBADF);
3667 #endif /* TEST_IPV6_ROUTELIST */
3668 }
3669 memset(&rtmsg, 0, sizeof(rtmsg));
3670 rtmsg.hdr.rtm_type = cmd;
3671 rtmsg.hdr.rtm_version = RTM_VERSION;
3672 rtmsg.hdr.rtm_seq = ++rtm_seq;
3673 rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_IFP;
3674 if (!IN6_IS_ADDR_UNSPECIFIED(&route->ifa)) {
3675 rtmsg.hdr.rtm_addrs |= RTA_IFA;
3676 }
3677 rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC;
3678 if ((route->flags & kRouteFlagsIsHost) != 0) {
3679 rtmsg.hdr.rtm_flags |= RTF_HOST;
3680 }
3681 else {
3682 rtmsg.hdr.rtm_addrs |= RTA_NETMASK;
3683 if ((route->flags & kRouteFlagsHasGateway) == 0) {
3684 rtmsg.hdr.rtm_flags |= RTF_CLONING;
3685 }
3686 }
3687 if ((route->flags & kRouteFlagsHasGateway) != 0) {
3688 rtmsg.hdr.rtm_flags |= RTF_GATEWAY;
3689 }
3690 if ((route->flags & kRouteFlagsIsScoped) != 0) {
3691 rtmsg.hdr.rtm_index = route->ifindex;
3692 rtmsg.hdr.rtm_flags |= RTF_IFSCOPE;
3693 }
3694
3695 rtaddr.ptr = rtmsg.addrs;
3696
3697 /* dest */
3698 rtaddr.in_p->sin6_len = sizeof(*rtaddr.in_p);
3699 rtaddr.in_p->sin6_family = AF_INET6;
3700 rtaddr.in_p->sin6_addr = route->dest;
3701 in6_addr_scope_linklocal(&rtaddr.in_p->sin6_addr, route->ifindex);
3702 rtaddr.ptr += sizeof(*rtaddr.in_p);
3703
3704 /* gateway */
3705 if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) {
3706 /* gateway is an IP address */
3707 rtaddr.in_p->sin6_len = sizeof(*rtaddr.in_p);
3708 rtaddr.in_p->sin6_family = AF_INET6;
3709 rtaddr.in_p->sin6_addr = route->gateway;
3710 in6_addr_scope_linklocal(&rtaddr.in_p->sin6_addr, route->ifindex);
3711 rtaddr.ptr += sizeof(*rtaddr.in_p);
3712 }
3713 else {
3714 /* gateway is the interface itself */
3715 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3716 rtaddr.dl_p->sdl_family = AF_LINK;
3717 rtaddr.dl_p->sdl_index = route->ifindex;
3718 rtaddr.ptr += sizeof(*rtaddr.dl_p);
3719 }
3720
3721 /* mask */
3722 if ((rtmsg.hdr.rtm_addrs & RTA_NETMASK) != 0) {
3723 rtaddr.in_p->sin6_len = sizeof(*rtaddr.in_p);
3724 rtaddr.in_p->sin6_family = AF_INET6;
3725 in6_len2mask(&rtaddr.in_p->sin6_addr, route->prefix_length);
3726 rtaddr.ptr += sizeof(*rtaddr.in_p);
3727 }
3728
3729 /* interface */
3730 if ((rtmsg.hdr.rtm_addrs & RTA_IFP) != 0) {
3731 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3732 rtaddr.dl_p->sdl_family = AF_LINK;
3733 rtaddr.dl_p->sdl_index = route->ifindex;
3734 rtaddr.ptr += sizeof(*rtaddr.dl_p);
3735 }
3736 /* interface address */
3737 if ((rtmsg.hdr.rtm_addrs & RTA_IFA) != 0) {
3738 rtaddr.in_p->sin6_len = sizeof(*rtaddr.in_p);
3739 rtaddr.in_p->sin6_family = AF_INET6;
3740 rtaddr.in_p->sin6_addr = route->ifa;
3741 rtaddr.ptr += sizeof(*rtaddr.in_p);
3742 }
3743
3744 /* apply the route */
3745 len = (int)(sizeof(rtmsg.hdr) + (rtaddr.ptr - (void *)rtmsg.addrs));
3746 rtmsg.hdr.rtm_msglen = len;
3747 if (write(sockfd, &rtmsg, len) == -1) {
3748 ret = errno;
3749 }
3750 return (ret);
3751 }
3752
3753 static const RouteListInfo IPv6RouteListInfo = {
3754 IPv6RouteListComputeSize,
3755
3756 IPv6RouteIsEqual,
3757 IPv6RouteApply,
3758 IPv6RouteGateway,
3759 IPv6RouteSetGateway,
3760 IPv6RouteDestination,
3761 IPv6RouteSameSubnet,
3762 IPv6RouteLog,
3763 IPv6RouteCopyDescription,
3764
3765 sizeof(IPv6Route),
3766 sizeof(struct in6_addr),
3767 IPV6_ROUTE_ALL_BITS_SET
3768 };
3769
3770 #if TEST_IPV6_ROUTELIST
3771 static IPv6RouteListRef
3772 IPv6RouteListAddRouteList(IPv6RouteListRef routes, int init_size,
3773 IPv6RouteListRef service_routes, Rank rank)
3774 {
3775 return ((IPv6RouteListRef)
3776 RouteListAddRouteList(&IPv6RouteListInfo,
3777 (RouteListRef)routes, init_size,
3778 (RouteListRef)service_routes, rank,
3779 FALSE));
3780 }
3781 #endif /* TEST_IPV6_ROUTELIST */
3782
3783 #if !TARGET_OS_SIMULATOR
3784 static __inline__ void
3785 IPv6RouteListLog(int level, IPv6RouteListRef routes)
3786 {
3787 CFStringRef str = IPv6RouteListCopyDescription(routes);
3788
3789 my_log(level, "%@", str);
3790 CFRelease(str);
3791 return;
3792 }
3793
3794 static void
3795 IPv6RouteListFinalize(IPv6RouteListRef routes)
3796 {
3797 RouteListFinalize(&IPv6RouteListInfo, (RouteListRef)routes);
3798 return;
3799 }
3800
3801 static void
3802 IPv6RouteListApply(IPv6RouteListRef old_routes, IPv6RouteListRef new_routes,
3803 int sockfd)
3804 {
3805 RouteListApply(&IPv6RouteListInfo,
3806 (RouteListRef)old_routes, (RouteListRef)new_routes,
3807 sockfd);
3808 return;
3809 }
3810 #endif /* !TARGET_OS_SIMULATOR */
3811
3812 /*
3813 * Function: parseNetworkServiceString
3814 * Purpose:
3815 * Parse either of the following two formats:
3816 * <domain>:/Network/Service/<serviceID>
3817 * <domain>:/Network/Service/<serviceID>/<protocol>
3818 * returning <serviceID>, and if available and required, <protocol>.
3819 */
3820 static CF_RETURNS_RETAINED CFStringRef
3821 parseNetworkServiceString(CFStringRef str, CFStringRef *ret_protocol)
3822 {
3823 CFArrayRef components;
3824 CFIndex count;
3825 CFStringRef protocol = NULL;
3826 CFStringRef serviceID = NULL;
3827
3828 /*
3829 * str = "<domain>:/Network/Service/<serviceID>"
3830 * OR
3831 * str = "<domain>:/Network/Service/<serviceID>/<protocol>"
3832 */
3833 components = CFStringCreateArrayBySeparatingStrings(NULL, str, CFSTR("/"));
3834 count = CFArrayGetCount(components);
3835 if (count >= 4) {
3836 /* we have a serviceID */
3837 serviceID = CFArrayGetValueAtIndex(components, 3);
3838 CFRetain(serviceID);
3839 if (count >= 5 && ret_protocol != NULL) {
3840 /* we have and want a protocol */
3841 protocol = CFArrayGetValueAtIndex(components, 4);
3842 CFRetain(protocol);
3843 }
3844 }
3845 if (ret_protocol != NULL) {
3846 *ret_protocol = protocol;
3847 }
3848 my_CFRelease(&components);
3849 return serviceID;
3850 }
3851
3852 static boolean_t
3853 ipdict_is_routable(CFDictionaryRef entity_dict)
3854 {
3855 RouteListRef routes;
3856
3857 routes = ipdict_get_routelist(entity_dict);
3858 if (routes == NULL) {
3859 // if no routes
3860 return FALSE;
3861 }
3862
3863 if ((routes->flags & kRouteListFlagsHasDefault) == 0) {
3864 // if service has no default route
3865 return FALSE;
3866 }
3867
3868 if ((routes->flags & kRouteListFlagsExcludeNWI) != 0) {
3869 // if service should be excluded from NWI
3870 return FALSE;
3871 }
3872
3873 return TRUE;
3874 }
3875
3876
3877 __private_extern__ boolean_t
3878 service_is_routable(CFDictionaryRef service_dict, int af)
3879 {
3880 boolean_t contains_protocol;
3881 CFStringRef entity;
3882 CFDictionaryRef entity_dict;
3883
3884 entity = (af == AF_INET) ? kSCEntNetIPv4 : kSCEntNetIPv6;
3885 entity_dict = CFDictionaryGetValue(service_dict, entity);
3886 if (entity_dict == NULL) {
3887 return FALSE;
3888 }
3889
3890 contains_protocol = ipdict_is_routable(entity_dict);
3891 return contains_protocol;
3892 }
3893
3894
3895 static CFMutableDictionaryRef
3896 service_dict_copy(CFStringRef serviceID)
3897 {
3898 CFDictionaryRef d = NULL;
3899 CFMutableDictionaryRef service_dict;
3900
3901 /* create a modifyable dictionary, a copy or a new one */
3902 d = CFDictionaryGetValue(S_service_state_dict, serviceID);
3903 if (d == NULL) {
3904 service_dict
3905 = CFDictionaryCreateMutable(NULL, 0,
3906 &kCFTypeDictionaryKeyCallBacks,
3907 &kCFTypeDictionaryValueCallBacks);
3908 }
3909 else {
3910 service_dict = CFDictionaryCreateMutableCopy(NULL, 0, d);
3911 }
3912 return (service_dict);
3913 }
3914
3915 __private_extern__ boolean_t
3916 service_is_scoped_only(CFDictionaryRef service_dict)
3917 {
3918 nwi_ifstate_t alias;
3919 CFDictionaryRef dict;
3920 char ifname[IFNAMSIZ];
3921 nwi_ifstate_t ifstate;
3922 CFStringRef interface = NULL;
3923
3924 // get IPv4 (or IPv6) info
3925 dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
3926 if (dict == NULL) {
3927 dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
3928 }
3929 if (dict == NULL) {
3930 // if no connectivity
3931 return FALSE;
3932 }
3933
3934 // get interface
3935 interface = ipdict_get_ifname(dict);
3936 if ((interface == NULL) ||
3937 !CFStringGetCString(interface, ifname, sizeof(ifname), kCFStringEncodingUTF8)) {
3938 // if no interface / interface name
3939 return FALSE;
3940 }
3941
3942 #ifdef TEST_DNS
3943 if (S_nwi_state == NULL) {
3944 S_nwi_state = nwi_state_copy();
3945 }
3946 #endif // TEST_DNS
3947
3948 // get [nwi] interface state
3949 ifstate = nwi_state_get_ifstate(S_nwi_state, ifname);
3950 if (ifstate == NULL) {
3951 // if unknown state
3952 return FALSE;
3953 } else if ((ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0) {
3954 // if scoped (i.e. not in list)
3955 return TRUE;
3956 }
3957
3958 // check both both IPv4 and IPv6
3959 alias = nwi_ifstate_get_alias(ifstate, ifstate->af == AF_INET ? AF_INET6 : AF_INET);
3960 if (alias == NULL) {
3961 // if only one address family
3962 return FALSE;
3963 } else if ((alias->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0) {
3964 // if scoped (i.e. not in list)
3965 return TRUE;
3966 }
3967
3968 return FALSE;
3969 }
3970
3971 static void
3972 log_service_entity(int level, CFStringRef serviceID, CFStringRef entity,
3973 CFStringRef operation, CFTypeRef val)
3974 {
3975 CFMutableStringRef this_val = NULL;
3976
3977 if (val != NULL) {
3978 boolean_t is_ipv4;
3979 boolean_t is_ipv6;
3980
3981 if ((is_ipv4 = CFEqual(entity, kSCEntNetIPv4))
3982 || (is_ipv6 = CFEqual(entity, kSCEntNetIPv6))) {
3983 RouteListUnion routes;
3984
3985 routes.ptr = ipdict_get_routelist(val);
3986 if (routes.ptr != NULL) {
3987 CFDictionaryRef service_dict = NULL;
3988
3989 if (is_ipv4) {
3990 this_val = IPv4RouteListCopyDescription(routes.v4);
3991 }
3992 else {
3993 this_val = IPv6RouteListCopyDescription(routes.v6);
3994 }
3995 service_dict = ipdict_get_service(val);
3996 if (service_dict != NULL) {
3997 CFStringAppendFormat(this_val, NULL,
3998 CFSTR("\n<Service> = %@"),
3999 service_dict);
4000 }
4001 val = this_val;
4002 }
4003 }
4004 }
4005 if (val == NULL) {
4006 val = CFSTR("<none>");
4007 }
4008 my_log(level, "serviceID %@ %@ %@ value = %@",
4009 serviceID, operation, entity, val);
4010
4011 my_CFRelease(&this_val);
4012 return;
4013 }
4014
4015 static boolean_t
4016 service_dict_set(CFStringRef serviceID, CFStringRef entity,
4017 CFTypeRef new_val)
4018 {
4019 boolean_t changed = FALSE;
4020 CFTypeRef old_val;
4021 CFMutableDictionaryRef service_dict;
4022
4023 service_dict = service_dict_copy(serviceID);
4024 old_val = CFDictionaryGetValue(service_dict, entity);
4025 if (new_val == NULL) {
4026 if (old_val != NULL) {
4027 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4028 log_service_entity(LOG_DEBUG, serviceID, entity,
4029 CFSTR("Removed:"), old_val);
4030 }
4031 CFDictionaryRemoveValue(service_dict, entity);
4032 changed = TRUE;
4033 }
4034 }
4035 else {
4036 if (old_val == NULL || !CFEqual(new_val, old_val)) {
4037 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4038 log_service_entity(LOG_DEBUG, serviceID, entity,
4039 CFSTR("Changed: old"), old_val);
4040 log_service_entity(LOG_DEBUG, serviceID, entity,
4041 CFSTR("Changed: new"), new_val);
4042 }
4043 CFDictionarySetValue(service_dict, entity, new_val);
4044 changed = TRUE;
4045 }
4046 }
4047 if (CFDictionaryGetCount(service_dict) == 0) {
4048 CFDictionaryRemoveValue(S_service_state_dict, serviceID);
4049 }
4050 else {
4051 CFDictionarySetValue(S_service_state_dict, serviceID, service_dict);
4052 }
4053 my_CFRelease(&service_dict);
4054 return (changed);
4055 }
4056
4057 static CFDictionaryRef
4058 service_dict_get(CFStringRef serviceID, CFStringRef entity)
4059 {
4060 CFDictionaryRef service_dict;
4061
4062 if (S_service_state_dict == NULL) {
4063 return (NULL);
4064 }
4065 service_dict = CFDictionaryGetValue(S_service_state_dict, serviceID);
4066 if (service_dict == NULL) {
4067 return (NULL);
4068 }
4069 return (CFDictionaryGetValue(service_dict, entity));
4070 }
4071
4072 #if !TARGET_OS_SIMULATOR
4073 static CFStringRef
4074 service_copy_interface(CFStringRef serviceID, CFDictionaryRef new_service)
4075 {
4076 CFDictionaryRef dict;
4077 CFStringRef interface = NULL;
4078
4079 if (new_service != NULL) {
4080 interface = ipdict_get_ifname(new_service);
4081 }
4082 if (interface == NULL) {
4083 dict = service_dict_get(serviceID, kSCEntNetIPv4);
4084 if (dict != NULL) {
4085 interface = ipdict_get_ifname(dict);
4086 }
4087 }
4088 if (interface == NULL) {
4089 dict = service_dict_get(serviceID, kSCEntNetIPv6);
4090 if (dict != NULL) {
4091 interface = ipdict_get_ifname(dict);
4092 }
4093 }
4094 if (interface != NULL) {
4095 CFRetain(interface);
4096 }
4097 return interface;
4098 }
4099 #endif /* !TARGET_OS_SIMULATOR */
4100
4101 static boolean_t
4102 service_has_clat46_address(CFStringRef serviceID)
4103 {
4104 CFDictionaryRef ip_dict;
4105
4106 ip_dict = service_dict_get(serviceID, kSCEntNetIPv4);
4107 if (ip_dict != NULL) {
4108 CFBooleanRef clat46 = NULL;
4109 CFDictionaryRef ipv4;
4110
4111 ipv4 = ipdict_get_service(ip_dict);
4112 if (isA_CFDictionary(ipv4) &&
4113 CFDictionaryGetValueIfPresent(ipv4,
4114 kSCPropNetIPv4CLAT46,
4115 (const void **)&clat46) &&
4116 isA_CFBoolean(clat46)) {
4117 return CFBooleanGetValue(clat46);
4118 }
4119 }
4120
4121 return FALSE;
4122 }
4123
4124 #ifndef kSCPropNetHostname
4125 #define kSCPropNetHostname CFSTR("Hostname")
4126 #endif
4127
4128 __private_extern__
4129 CFStringRef
4130 copy_dhcp_hostname(CFStringRef serviceID)
4131 {
4132 CFDictionaryRef dict = NULL;
4133 CFStringRef hostname = NULL;
4134 CFDictionaryRef service_dict = NULL;
4135
4136 dict = service_dict_get(serviceID, kSCEntNetIPv4);
4137 if (dict == NULL) {
4138 return (NULL);
4139 }
4140 service_dict = ipdict_get_service(dict);
4141 if (service_dict == NULL) {
4142 return (NULL);
4143 }
4144 hostname = CFDictionaryGetValue(service_dict, kSCPropNetHostname);
4145 if (hostname != NULL) {
4146 CFRetain(hostname);
4147 }
4148 return (hostname);
4149 }
4150
4151 #if !TARGET_OS_SIMULATOR
4152
4153 static struct in6_addr *
4154 ipv6_service_get_router(CFDictionaryRef service,
4155 IFIndex * ifindex_p, CFStringRef * ifname_p)
4156 {
4157 IPv6RouteListRef routes;
4158 struct in6_addr * router = NULL;
4159
4160 routes = ipdict_get_routelist(service);
4161 if (routes != NULL
4162 && (routes->flags & kRouteListFlagsExcludeNWI) == 0
4163 && (routes->flags & kRouteListFlagsHasDefault) != 0) {
4164 router = &routes->list[0].gateway;
4165 if (*ifindex_p == 0) {
4166 *ifindex_p = routes->list[0].ifindex;
4167 }
4168 if (*ifname_p == NULL) {
4169 *ifname_p = ipdict_get_ifname(service);
4170 }
4171 }
4172 return (router);
4173 }
4174
4175 static void
4176 ipv6_service_update_router(CFStringRef serviceID, CFDictionaryRef new_service)
4177 {
4178 IFIndex ifindex = 0;
4179 CFStringRef ifname = NULL;
4180 char ntopbuf[INET6_ADDRSTRLEN];
4181 CFDictionaryRef old_service;
4182 struct in6_addr * old_router;
4183 struct in6_addr * new_router;
4184 int s = -1;
4185
4186 old_service = service_dict_get(serviceID, kSCEntNetIPv6);
4187 old_router = ipv6_service_get_router(old_service, &ifindex, &ifname);
4188 new_router = ipv6_service_get_router(new_service, &ifindex, &ifname);
4189 if (ifname == NULL || ifindex == 0) {
4190 return;
4191 }
4192 s = inet6_dgram_socket();
4193 if (s < 0) {
4194 goto done;
4195 }
4196 /* remove the old router if it was defined */
4197 if (old_router != NULL
4198 && (new_router == NULL
4199 || !IN6_ARE_ADDR_EQUAL(old_router, new_router))) {
4200 if (siocdrdel_in6(s, ifindex, old_router) < 0) {
4201 my_log((errno == EINVAL) ? LOG_DEBUG : LOG_ERR,
4202 "siocdrdel_in6(%@, %s) failed: %s",
4203 ifname,
4204 inet_ntop(AF_INET6, old_router,
4205 ntopbuf, sizeof(ntopbuf)),
4206 strerror(errno));
4207 }
4208 else {
4209 my_log(LOG_INFO,
4210 "%@ removed default route %s",
4211 ifname,
4212 inet_ntop(AF_INET6, old_router, ntopbuf, sizeof(ntopbuf)));
4213 }
4214 }
4215 /* add the new router if it is defined */
4216 if (new_router != NULL
4217 && (old_router == NULL
4218 || !IN6_ARE_ADDR_EQUAL(old_router, new_router))) {
4219 if (siocdradd_in6(s, ifindex, new_router, 0) < 0) {
4220 my_log((errno == EINVAL) ? LOG_DEBUG : LOG_ERR,
4221 "siocdradd_in6(%@, %s) failed: %s",
4222 ifname,
4223 inet_ntop(AF_INET6, new_router,
4224 ntopbuf, sizeof(ntopbuf)),
4225 strerror(errno));
4226 }
4227 else {
4228 my_log(LOG_INFO,
4229 "%@ added default route %s",
4230 ifname,
4231 inet_ntop(AF_INET6, new_router, ntopbuf, sizeof(ntopbuf)));
4232 }
4233 }
4234 close(s);
4235
4236 done:
4237 return;
4238 }
4239 #endif /* !TARGET_OS_SIMULATOR */
4240
4241 #define ALLOW_EMPTY_STRING 0x1
4242
4243 static CF_RETURNS_RETAINED CFTypeRef
4244 sanitize_prop(CFTypeRef val, uint32_t flags)
4245 {
4246 if (val != NULL) {
4247 if (isA_CFString(val)) {
4248 CFMutableStringRef str;
4249
4250 str = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)val);
4251 CFStringTrimWhitespace(str);
4252 if (!(flags & ALLOW_EMPTY_STRING) && (CFStringGetLength(str) == 0)) {
4253 CFRelease(str);
4254 str = NULL;
4255 }
4256 val = str;
4257 } else {
4258 CFRetain(val);
4259 }
4260 }
4261
4262 return val;
4263 }
4264
4265 static void
4266 merge_array_prop(CFMutableDictionaryRef dict,
4267 CFStringRef key,
4268 CFDictionaryRef state_dict,
4269 CFDictionaryRef setup_dict,
4270 uint32_t flags,
4271 Boolean append)
4272 {
4273 CFMutableArrayRef merge_prop;
4274 CFArrayRef setup_prop = NULL;
4275 CFArrayRef state_prop = NULL;
4276
4277 if (setup_dict != NULL) {
4278 setup_prop = isA_CFArray(CFDictionaryGetValue(setup_dict, key));
4279 }
4280 if (state_dict != NULL) {
4281 state_prop = isA_CFArray(CFDictionaryGetValue(state_dict, key));
4282 }
4283
4284 if ((setup_prop == NULL) && (state_prop == NULL)) {
4285 return;
4286 }
4287
4288 merge_prop = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
4289 if (setup_prop != NULL) {
4290 CFIndex i;
4291 CFIndex n;
4292
4293 n = CFArrayGetCount(setup_prop);
4294 for (i = 0; i < n; i++) {
4295 CFTypeRef val;
4296
4297 val = CFArrayGetValueAtIndex(setup_prop, i);
4298 val = sanitize_prop(val, flags);
4299 if (val != NULL) {
4300 CFArrayAppendValue(merge_prop, val);
4301 CFRelease(val);
4302 }
4303 }
4304 }
4305 if (state_prop != NULL
4306 && (setup_prop == NULL || S_append_state)) {
4307 CFIndex i;
4308 CFIndex n;
4309 CFRange setup_range = CFRangeMake(0, CFArrayGetCount(merge_prop));
4310
4311 n = CFArrayGetCount(state_prop);
4312 for (i = 0; i < n; i++) {
4313 CFTypeRef val;
4314
4315 val = CFArrayGetValueAtIndex(state_prop, i);
4316 val = sanitize_prop(val, flags);
4317 if (val != NULL) {
4318 if (append || !CFArrayContainsValue(merge_prop, setup_range, val)) {
4319 CFArrayAppendValue(merge_prop, val);
4320 }
4321 CFRelease(val);
4322 }
4323 }
4324 }
4325 if (CFArrayGetCount(merge_prop) > 0) {
4326 CFDictionarySetValue(dict, key, merge_prop);
4327 }
4328 CFRelease(merge_prop);
4329 return;
4330 }
4331
4332 static void
4333 pick_prop(CFMutableDictionaryRef dict,
4334 CFStringRef key,
4335 CFDictionaryRef state_dict,
4336 CFDictionaryRef setup_dict,
4337 uint32_t flags)
4338 {
4339 CFTypeRef val = NULL;
4340
4341 if (setup_dict != NULL) {
4342 val = CFDictionaryGetValue(setup_dict, key);
4343 val = sanitize_prop(val, flags);
4344 }
4345 if (val == NULL && state_dict != NULL) {
4346 val = CFDictionaryGetValue(state_dict, key);
4347 val = sanitize_prop(val, flags);
4348 }
4349 if (val != NULL) {
4350 CFDictionarySetValue(dict, key, val);
4351 CFRelease(val);
4352 }
4353
4354 return;
4355 }
4356
4357 /**
4358 ** GetEntityChangesFunc functions
4359 **/
4360 #define IPV4_ROUTES_N_STATIC 5
4361 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4362 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4363 sizeof(uint32_t)) \
4364 / sizeof(uint32_t))
4365
4366 #define IPV4_ROUTES_BUF_DECL(routes) \
4367 IPv4RouteListRef routes; \
4368 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4369 \
4370 routes = (IPv4RouteListRef)(void *)routes_buf; \
4371 routes->size = IPV4_ROUTES_N_STATIC; \
4372 routes->count = 0; \
4373 routes->flags = 0;
4374
4375 static CFDataRef
4376 IPv4RouteListDataCreate(CFDictionaryRef dict, CFNumberRef rank_assertion)
4377 {
4378 IPv4RouteListRef r;
4379 CFDataRef routes_data;
4380 IPV4_ROUTES_BUF_DECL(routes);
4381
4382 r = IPv4RouteListCreateWithDictionary(routes, dict, rank_assertion);
4383 if (r != NULL) {
4384 routes_data = CFDataCreate(NULL,
4385 (const void *)r,
4386 IPv4RouteListComputeSize(r->count));
4387 if (r != routes) {
4388 free(r);
4389 }
4390 }
4391 else {
4392 routes_data = NULL;
4393 }
4394 return (routes_data);
4395 }
4396 #define IPV6_ROUTES_N_STATIC 3
4397 #define IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4398 (roundup(IPv6RouteListComputeSize(IPV6_ROUTES_N_STATIC), \
4399 sizeof(uint32_t)) \
4400 / sizeof(uint32_t))
4401
4402 #define IPV6_ROUTES_BUF_DECL(routes) \
4403 IPv6RouteListRef routes; \
4404 uint32_t routes_buf[IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4405 \
4406 routes = (IPv6RouteListRef)(void *)routes_buf; \
4407 routes->size = IPV6_ROUTES_N_STATIC; \
4408 routes->count = 0; \
4409 routes->flags = 0;
4410
4411 static CFDataRef
4412 IPv6RouteListDataCreate(CFDictionaryRef dict, CFNumberRef rank_assertion)
4413 {
4414 IPv6RouteListRef r;
4415 CFDataRef routes_data;
4416 IPV6_ROUTES_BUF_DECL(routes);
4417
4418 r = IPv6RouteListCreateWithDictionary(routes, dict, rank_assertion);
4419 if (r != NULL) {
4420 routes_data = CFDataCreate(NULL,
4421 (const void *)r,
4422 IPv6RouteListComputeSize(r->count));
4423 if (r != routes) {
4424 free(r);
4425 }
4426 }
4427 else {
4428 routes_data = NULL;
4429 }
4430 return (routes_data);
4431 }
4432
4433 static CFDictionaryRef
4434 IPDictCreate(int af, _Nonnull CFDictionaryRef state_dict,
4435 CFDictionaryRef setup_dict,
4436 CFNumberRef rank_assertion)
4437 {
4438 CFDictionaryRef aggregated_dict = NULL;
4439 CFDictionaryRef dict;
4440 CFMutableDictionaryRef modified_dict = NULL;
4441 CFDataRef routes_data;
4442
4443 dict = state_dict;
4444 if (setup_dict != NULL) {
4445 /* look for keys in Setup: that override/merge with State: */
4446 CFArrayRef additional_routes;
4447 CFStringRef route_list_prop;
4448 CFStringRef router;
4449 in_addr router_ip;
4450 CFStringRef router_prop;
4451
4452 /* Router */
4453 switch (af) {
4454 case AF_INET:
4455 router_prop = kSCPropNetIPv4Router;
4456 route_list_prop = kSCPropNetIPv4AdditionalRoutes;
4457 break;
4458 default:
4459 case AF_INET6:
4460 router_prop = kSCPropNetIPv6Router;
4461 route_list_prop = kSCPropNetIPv6AdditionalRoutes;
4462 break;
4463 }
4464 router = CFDictionaryGetValue(setup_dict, router_prop);
4465 if (router != NULL
4466 && !cfstring_to_ipvx(af, router, &router_ip, sizeof(router_ip))) {
4467 router = NULL;
4468 }
4469
4470 /* AdditionalRoutes */
4471 additional_routes
4472 = CFDictionaryGetValue(setup_dict, route_list_prop);
4473 additional_routes = isA_CFArray(additional_routes);
4474
4475 if (router != NULL || additional_routes != NULL) {
4476 modified_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
4477 if (router != NULL) {
4478 CFDictionarySetValue(modified_dict,
4479 router_prop,
4480 router);
4481 }
4482 if (additional_routes != NULL) {
4483 CFArrayRef combined_routes = NULL;
4484 CFArrayRef state_routes;
4485
4486 state_routes
4487 = CFDictionaryGetValue(state_dict,
4488 route_list_prop);
4489 if (isA_CFArray(state_routes) != NULL) {
4490 combined_routes
4491 = my_CFArrayCreateCombinedArray(additional_routes,
4492 state_routes);
4493 additional_routes = combined_routes;
4494 }
4495 CFDictionarySetValue(modified_dict,
4496 route_list_prop,
4497 additional_routes);
4498 if (combined_routes != NULL) {
4499 CFRelease(combined_routes);
4500 }
4501 }
4502 dict = modified_dict;
4503 }
4504 }
4505 switch (af) {
4506 case AF_INET:
4507 routes_data = IPv4RouteListDataCreate(dict, rank_assertion);
4508 break;
4509 default:
4510 case AF_INET6:
4511 routes_data = IPv6RouteListDataCreate(dict, rank_assertion);
4512 break;
4513 }
4514 if (routes_data != NULL) {
4515 aggregated_dict = ipdict_create(dict, routes_data);
4516 CFRelease(routes_data);
4517 }
4518 if (modified_dict != NULL) {
4519 CFRelease(modified_dict);
4520 }
4521 return (aggregated_dict);
4522 }
4523
4524 static boolean_t
4525 get_ipv4_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
4526 CFDictionaryRef setup_dict, CFDictionaryRef info)
4527 {
4528 #pragma unused(info)
4529 CFDictionaryRef dict = NULL;
4530 boolean_t changed = FALSE;
4531 CFNumberRef rank_assertion = NULL;
4532 CFDictionaryRef service_options;
4533
4534 if (state_dict == NULL) {
4535 goto done;
4536 }
4537 service_options = service_dict_get(serviceID, kSCEntNetService);
4538 if (service_options != NULL) {
4539 rank_assertion
4540 = CFDictionaryGetValue(service_options,
4541 kServiceOptionRankAssertion);
4542 }
4543 dict = IPDictCreate(AF_INET, state_dict, setup_dict, rank_assertion);
4544
4545 done:
4546 changed = service_dict_set(serviceID, kSCEntNetIPv4, dict);
4547 if (dict == NULL) {
4548 /* clean up the rank too */
4549 CFDictionaryRemoveValue(S_ipv4_service_rank_dict, serviceID);
4550 }
4551 my_CFRelease(&dict);
4552 return (changed);
4553 }
4554
4555
4556 static boolean_t
4557 get_ipv6_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
4558 CFDictionaryRef setup_dict, CFDictionaryRef info)
4559 {
4560 #pragma unused(info)
4561 CFDictionaryRef dict = NULL;
4562 boolean_t changed = FALSE;
4563 #if !TARGET_OS_SIMULATOR
4564 CFStringRef interface;
4565 #endif /* !TARGET_OS_SIMULATOR */
4566 CFNumberRef rank_assertion = NULL;
4567 CFDictionaryRef service_options;
4568
4569 if (state_dict == NULL) {
4570 goto done;
4571 }
4572 service_options = service_dict_get(serviceID, kSCEntNetService);
4573 if (service_options != NULL) {
4574 rank_assertion
4575 = CFDictionaryGetValue(service_options,
4576 kServiceOptionRankAssertion);
4577 }
4578
4579 dict = IPDictCreate(AF_INET6, state_dict, setup_dict, rank_assertion);
4580
4581 done:
4582
4583 #if !TARGET_OS_SIMULATOR
4584 interface = service_copy_interface(serviceID, dict);
4585 ipv6_service_update_router(serviceID, dict);
4586 #endif /* !TARGET_OS_SIMULATOR */
4587
4588 changed = service_dict_set(serviceID, kSCEntNetIPv6, dict);
4589
4590 #if !TARGET_OS_SIMULATOR
4591 if (interface != NULL) {
4592 if (changed) {
4593 CFBooleanRef needs_plat = NULL;
4594
4595 if (dict == NULL) {
4596 // if service is unpublished, cancel the request
4597 set_plat_discovery(kPLATDiscoveryOptionCancel, interface);
4598 } else if ((state_dict != NULL) &&
4599 CFDictionaryGetValueIfPresent(state_dict,
4600 kSCPropNetIPv6PerformPLATDiscovery,
4601 (const void **)&needs_plat) &&
4602 isA_CFBoolean(needs_plat) &&
4603 CFBooleanGetValue(needs_plat)) {
4604 // perform PLAT discovery
4605 set_plat_discovery(kPLATDiscoveryOptionStart, interface);
4606 } else {
4607 // IPv6 configuration changed for this interface, poke NAT64
4608 set_plat_discovery(kPLATDiscoveryOptionUpdate, interface);
4609 }
4610 }
4611 CFRelease(interface);
4612 }
4613 #endif /* !TARGET_OS_SIMULATOR */
4614
4615 if (dict == NULL) {
4616 /* service removed, clean up the rank too */
4617 CFDictionaryRemoveValue(S_ipv6_service_rank_dict, serviceID);
4618 }
4619 my_CFRelease(&dict);
4620 return (changed);
4621 }
4622
4623
4624 #ifdef TEST_DNS
4625 __private_extern__ CFDictionaryRef
4626 ipv4_dict_create(CFDictionaryRef state_dict)
4627 {
4628 return (IPDictCreate(AF_INET, state_dict, NULL, NULL, NULL));
4629 }
4630
4631 __private_extern__ CFDictionaryRef
4632 ipv6_dict_create(CFDictionaryRef state_dict)
4633 {
4634 return (IPDictCreate(AF_INET6, state_dict, NULL, NULL, NULL));
4635 }
4636
4637 #endif /* TEST_DNS */
4638
4639 static void
4640 accumulate_dns_servers(CFArrayRef in_servers, ProtocolFlags active_protos,
4641 CFMutableArrayRef out_servers, CFStringRef interface)
4642 {
4643 CFIndex count;
4644 CFIndex i;
4645
4646 count = CFArrayGetCount(in_servers);
4647 for (i = 0; i < count; i++) {
4648 CFStringRef addr;
4649 struct in6_addr ipv6_addr;
4650 struct in_addr ip_addr;
4651
4652 addr = CFArrayGetValueAtIndex(in_servers, i);
4653 assert(addr != NULL);
4654
4655 if (cfstring_to_ip(addr, &ip_addr)) {
4656 /* IPv4 address */
4657 if ((active_protos & kProtocolFlagsIPv4) == 0
4658 && ntohl(ip_addr.s_addr) != INADDR_LOOPBACK) {
4659 my_log(LOG_INFO,
4660 "no IPv4 connectivity, "
4661 "ignoring DNS server address " IP_FORMAT,
4662 IP_LIST(&ip_addr));
4663 continue;
4664 }
4665
4666 CFRetain(addr);
4667 }
4668 else if (cfstring_to_ip6(addr, &ipv6_addr)) {
4669 /* IPv6 address */
4670 if ((active_protos & kProtocolFlagsIPv6) == 0
4671 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr)) {
4672 char ntopbuf[INET6_ADDRSTRLEN];
4673
4674 my_log(LOG_INFO,
4675 "no IPv6 connectivity, "
4676 "ignoring DNS server address %s",
4677 inet_ntop(AF_INET6, &ipv6_addr,
4678 ntopbuf, sizeof(ntopbuf)));
4679 continue;
4680 }
4681
4682 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr) ||
4683 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr))
4684 && (interface != NULL)
4685 && (CFStringFind(addr, CFSTR("%"), 0).location == kCFNotFound)) {
4686 // append interface name to IPv6 link local address
4687 addr = CFStringCreateWithFormat(NULL, NULL,
4688 CFSTR("%@%%%@"),
4689 addr,
4690 interface);
4691 } else {
4692 CFRetain(addr);
4693 }
4694 }
4695 else {
4696 /* bad IP address */
4697 my_log(LOG_NOTICE, "ignoring bad DNS server address '%@'", addr);
4698 continue;
4699 }
4700
4701 /* DNS server is valid and one we want */
4702 CFArrayAppendValue(out_servers, addr);
4703 CFRelease(addr);
4704 }
4705 return;
4706 }
4707
4708 static CF_RETURNS_RETAINED CFArrayRef
4709 order_dns_servers(CFArrayRef servers, ProtocolFlags active_protos)
4710 {
4711 Boolean favor_v4 = FALSE;
4712 CFMutableArrayRef ordered_servers;
4713 ProtocolFlags proto_last = kProtocolFlagsIPv4;
4714 struct sockaddr_in v4_dns1 = { .sin_family = AF_INET,
4715 .sin_len = sizeof(struct sockaddr_in) };
4716 CFIndex v4_n = 0;
4717 struct sockaddr_in6 v6_dns1 = { .sin6_family = AF_INET6,
4718 .sin6_len = sizeof(struct sockaddr_in6),
4719 .sin6_scope_id = 0 };
4720 CFIndex v6_n = 0;
4721
4722 if (((active_protos & kProtocolFlagsIPv4) == 0) ||
4723 ((active_protos & kProtocolFlagsIPv6) == 0)) {
4724 /* only one protocol */
4725 #ifdef TEST_DNS_ORDER
4726 printf("only one protocol\n");
4727 #endif // TEST_DNS_ORDER
4728 return CFRetain(servers);
4729 }
4730
4731 ordered_servers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
4732 for (CFIndex i = 0, n = CFArrayGetCount(servers); i < n; i++) {
4733 struct in_addr ia;
4734 struct in6_addr ia6;
4735 ProtocolFlags proto;
4736 CFStringRef server;
4737
4738 server = CFArrayGetValueAtIndex(servers, i);
4739 if (cfstring_to_ip(server, &ia)) {
4740 proto = kProtocolFlagsIPv4;
4741 if (v4_n++ == 0) {
4742 v4_dns1.sin_addr = ia;
4743 }
4744 } else if (cfstring_to_ip6(server, &ia6)) {
4745 proto = kProtocolFlagsIPv6;
4746 if (v6_n++ == 0) {
4747 memcpy(&v6_dns1.sin6_addr, &ia6, sizeof(ia6));
4748 }
4749 } else {
4750 CFRelease(ordered_servers);
4751 return CFRetain(servers);
4752 }
4753
4754 if ((i > 0) && (proto != proto_last)) {
4755 /* if the protocol of the server addresses changed */
4756 if (((proto == kProtocolFlagsIPv4) && (v4_n == 1)) ||
4757 ((proto == kProtocolFlagsIPv6) && (v6_n == 1))) {
4758 /* if we now have the 1st server address of another protocol */
4759 favor_v4 = (sa_dst_compare_no_dependencies((struct sockaddr *)&v4_dns1,
4760 (struct sockaddr *)&v6_dns1) >= 0);
4761 #ifdef TEST_DNS_ORDER
4762 char v4_buf[INET_ADDRSTRLEN];
4763 char v6_buf[INET6_ADDRSTRLEN];
4764 printf("comparing %s vs %s, favoring %s\n",
4765 inet_ntop(v4_dns1.sin_family, &v4_dns1.sin_addr, v4_buf, sizeof(v4_buf)),
4766 inet_ntop(v6_dns1.sin6_family, &v6_dns1.sin6_addr, v6_buf, sizeof(v6_buf)),
4767 favor_v4 ? "v4" : "v6");
4768 #endif // TEST_DNS_ORDER
4769 } else {
4770 /* if the server addresses array is randomly mixed */
4771 #ifdef TEST_DNS_ORDER
4772 printf("v4/v6 not ordered\n");
4773 #endif // TEST_DNS_ORDER
4774 CFRelease(ordered_servers);
4775 return CFRetain(servers);
4776 }
4777 }
4778 proto_last = proto;
4779
4780 if ((proto == kProtocolFlagsIPv4) && favor_v4) {
4781 CFArrayInsertValueAtIndex(ordered_servers, v4_n - 1, server);
4782 } else if ((proto == kProtocolFlagsIPv6) && !favor_v4) {
4783 CFArrayInsertValueAtIndex(ordered_servers, v6_n - 1, server);
4784 } else {
4785 CFArrayAppendValue(ordered_servers, server);
4786 }
4787 }
4788
4789 return ordered_servers;
4790 }
4791
4792 static void
4793 merge_dns_servers(CFMutableDictionaryRef new_dict,
4794 CFArrayRef state_servers,
4795 CFArrayRef setup_servers,
4796 Boolean have_setup,
4797 Boolean trust_state,
4798 ProtocolFlags active_protos,
4799 CFStringRef interface)
4800 {
4801 CFMutableArrayRef dns_servers;
4802 Boolean have_dns_setup = FALSE;
4803
4804 if (state_servers == NULL && setup_servers == NULL) {
4805 /* no DNS servers */
4806 return;
4807 }
4808 dns_servers = CFArrayCreateMutable(NULL, 0,
4809 &kCFTypeArrayCallBacks);
4810 if (setup_servers != NULL) {
4811 accumulate_dns_servers(setup_servers, active_protos,
4812 dns_servers, interface);
4813 if (CFArrayGetCount(dns_servers) > 0) {
4814 have_dns_setup = TRUE;
4815 }
4816 }
4817 if ((CFArrayGetCount(dns_servers) == 0 || S_append_state)
4818 && state_servers != NULL) {
4819 CFArrayRef ordered_servers;
4820
4821 ordered_servers = order_dns_servers(state_servers, active_protos);
4822 accumulate_dns_servers(ordered_servers, active_protos,
4823 dns_servers, NULL);
4824 CFRelease(ordered_servers);
4825 }
4826
4827 /*
4828 * Here, we determine whether or not we want all queries for this DNS
4829 * configuration to be bound to the associated network interface.
4830 *
4831 * For dynamically derived network configurations (i.e. from State:)
4832 * this would be the preferred option using the argument "Hey, the
4833 * server told us to use these servers on this network so let's not
4834 * argue".
4835 *
4836 * But, when a DNS configuration has been provided by the user/admin
4837 * via the Network pref pane (i.e. from Setup:) we opt to not force
4838 * binding of the outbound queries. The simplest example why we take
4839 * this stance is with a multi-homing configuration. Consider a system
4840 * with one network service associated with "en0" and a second service
4841 * associated with "en1". The "en0" service has been set higher in
4842 * the network service order so it would be primary but the user/admin
4843 * wants the DNS queries to go to a server only accessible via "en1".
4844 * Without this exception we would take the DNS server addresses from
4845 * the Network pref pane (for "en0") and have the queries bound to
4846 * "en0" where they'd never reach their intended destination (via
4847 * "en1"). So, our exception to the rule is that we will not bind
4848 * user/admin configurations to any specific network interface.
4849 *
4850 * We also add an exception to the "follow the dynamically derived
4851 * network configuration" path for on-the-fly (no Setup: content)
4852 * network services.
4853 *
4854 * But, we add an exception to the exception to support our own
4855 * VPN code. Here, we look for a "ServiceID" property in the DNS
4856 * entity. If present, and if it matches, then we extend our
4857 * trust even when there is no Setup: content.
4858 */
4859 if (CFArrayGetCount(dns_servers) != 0) {
4860 CFDictionarySetValue(new_dict,
4861 kSCPropNetDNSServerAddresses, dns_servers);
4862 if ((have_setup && !have_dns_setup) || (!have_setup && trust_state)) {
4863 // if this is a "setup"+"state" service with only "state" DNS content (i.e. no
4864 // setup override) or this is a TRUSTED "state"-only service
4865 CFDictionarySetValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue);
4866 }
4867 }
4868
4869 my_CFRelease(&dns_servers);
4870 return;
4871 }
4872
4873
4874 static boolean_t
4875 get_dns_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
4876 CFDictionaryRef setup_dict, CFDictionaryRef info)
4877 {
4878 ProtocolFlags active_protos = kProtocolFlagsNone;
4879 boolean_t changed = FALSE;
4880 CFStringRef domain;
4881 Boolean have_setup = FALSE;
4882 CFStringRef interface = NULL;
4883 CFDictionaryRef ipv4;
4884 CFDictionaryRef ipv6;
4885 const struct {
4886 CFStringRef key;
4887 uint32_t flags;
4888 Boolean append;
4889 } merge_list[] = {
4890 { kSCPropNetDNSSearchDomains, 0, FALSE },
4891 { kSCPropNetDNSSortList, 0, FALSE },
4892 { kSCPropNetDNSSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE },
4893 { kSCPropNetDNSSupplementalMatchOrders, 0, TRUE },
4894 };
4895 CFMutableDictionaryRef new_dict = NULL;
4896 const CFStringRef pick_list[] = {
4897 kSCPropNetDNSDomainName,
4898 kSCPropNetDNSOptions,
4899 kSCPropNetDNSSearchOrder,
4900 kSCPropNetDNSServerPort,
4901 kSCPropNetDNSServerTimeout,
4902 kSCPropNetDNSServiceIdentifier,
4903 kSCPropNetDNSSupplementalMatchDomainsNoSearch,
4904 };
4905 Boolean trust_state = FALSE;
4906
4907 if ((state_dict == NULL) && (setup_dict == NULL)) {
4908 /* there is no DNS content */
4909 goto done;
4910 }
4911
4912 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
4913 if (ipdict_is_routable(ipv4)) {
4914 if (get_service_setup_entity(info, serviceID, kSCEntNetIPv4) != NULL) {
4915 have_setup = TRUE;
4916 }
4917 active_protos |= kProtocolFlagsIPv4;
4918 interface = ipdict_get_ifname(ipv4);
4919 }
4920
4921 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
4922 if (ipdict_is_routable(ipv6)) {
4923 if (!have_setup &&
4924 (get_service_setup_entity(info, serviceID, kSCEntNetIPv6) != NULL)) {
4925 have_setup = TRUE;
4926 }
4927 active_protos |= kProtocolFlagsIPv6;
4928 if (interface == NULL) {
4929 interface = ipdict_get_ifname(ipv6);
4930 }
4931 }
4932
4933
4934 if (active_protos == kProtocolFlagsNone) {
4935 /* there is no IPv4 nor IPv6 */
4936 if (state_dict == NULL) {
4937 /* ... and no DNS content that we care about */
4938 goto done;
4939 }
4940 setup_dict = NULL;
4941 }
4942
4943 if (state_dict != NULL) {
4944 CFStringRef state_serviceID = NULL;
4945
4946 if (CFDictionaryGetValueIfPresent(state_dict,
4947 kSCPropNetDNSConfirmedServiceID,
4948 (const void **)&state_serviceID) &&
4949 isA_CFString(state_serviceID) &&
4950 CFEqual(serviceID, state_serviceID)) {
4951 trust_state = TRUE;
4952 }
4953 }
4954
4955 /* merge DNS configuration */
4956 new_dict = CFDictionaryCreateMutable(NULL, 0,
4957 &kCFTypeDictionaryKeyCallBacks,
4958 &kCFTypeDictionaryValueCallBacks);
4959
4960 if (active_protos == kProtocolFlagsNone) {
4961 merge_dns_servers(new_dict,
4962 my_CFDictionaryGetArray(state_dict,
4963 kSCPropNetDNSServerAddresses),
4964 NULL,
4965 FALSE,
4966 trust_state,
4967 kProtocolFlagsIPv4 | kProtocolFlagsIPv6,
4968 NULL);
4969 }
4970 else {
4971 merge_dns_servers(new_dict,
4972 my_CFDictionaryGetArray(state_dict,
4973 kSCPropNetDNSServerAddresses),
4974 my_CFDictionaryGetArray(setup_dict,
4975 kSCPropNetDNSServerAddresses),
4976 have_setup,
4977 trust_state,
4978 active_protos,
4979 interface);
4980 }
4981
4982 for (size_t i = 0; i < countof(merge_list); i++) {
4983 merge_array_prop(new_dict,
4984 merge_list[i].key,
4985 state_dict,
4986 setup_dict,
4987 merge_list[i].flags,
4988 merge_list[i].append);
4989 }
4990
4991 for (size_t i = 0; i < countof(pick_list); i++) {
4992 pick_prop(new_dict,
4993 pick_list[i],
4994 state_dict,
4995 setup_dict,
4996 0);
4997 }
4998
4999 if (active_protos == kProtocolFlagsNone) {
5000 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
5001 if (CFDictionaryContainsKey(new_dict,
5002 kSCPropNetDNSSupplementalMatchDomains)) {
5003 /* only keep State: supplemental */
5004 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSDomainName);
5005 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchDomains);
5006 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchOrder);
5007 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSortList);
5008
5009 if ((interface == NULL) && (setup_dict == NULL) && (state_dict != NULL)) {
5010 /*
5011 * for supplemental-only configurations, add any scoped (or
5012 * wild-card "*") interface
5013 */
5014 interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName);
5015 }
5016 } else if (CFDictionaryContainsKey(new_dict, kSCPropNetDNSServiceIdentifier) &&
5017 (interface == NULL) &&
5018 (state_dict != NULL)) {
5019 interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName);
5020 } else {
5021 goto done;
5022 }
5023 }
5024
5025 if (CFDictionaryGetCount(new_dict) == 0) {
5026 my_CFRelease(&new_dict);
5027 goto done;
5028 }
5029
5030 if (interface != NULL) {
5031 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
5032 }
5033
5034 if (S_append_state) {
5035 /*
5036 * ensure any specified domain name (e.g. the domain returned by
5037 * a DHCP server) is in the search list.
5038 */
5039 domain = CFDictionaryGetValue(new_dict, kSCPropNetDNSDomainName);
5040 if (isA_CFString(domain)) {
5041 CFArrayRef search;
5042
5043 search = CFDictionaryGetValue(new_dict, kSCPropNetDNSSearchDomains);
5044 if (isA_CFArray(search) &&
5045 !CFArrayContainsValue(search, CFRangeMake(0, CFArrayGetCount(search)), domain)) {
5046 CFMutableArrayRef new_search;
5047
5048 new_search = CFArrayCreateMutableCopy(NULL, 0, search);
5049 CFArrayAppendValue(new_search, domain);
5050 CFDictionarySetValue(new_dict, kSCPropNetDNSSearchDomains, new_search);
5051 my_CFRelease(&new_search);
5052 }
5053 }
5054 }
5055
5056 done:
5057
5058 #if !TARGET_OS_SIMULATOR
5059 if (interface != NULL) {
5060 CFRetain(interface);
5061 }
5062 #endif /* !TARGET_OS_SIMULATOR */
5063
5064 changed = service_dict_set(serviceID, kSCEntNetDNS, new_dict);
5065
5066 #if !TARGET_OS_SIMULATOR
5067 if (interface != NULL) {
5068 if (changed) {
5069 // DNS configuration changed for this interface, poke NAT64
5070 if ((active_protos & kProtocolFlagsIPv6) != 0) {
5071 set_plat_discovery(kPLATDiscoveryOptionUpdate, interface);
5072 }
5073 }
5074 CFRelease(interface);
5075 }
5076 #endif /* !TARGET_OS_SIMULATOR */
5077
5078 my_CFRelease(&new_dict);
5079 return (changed);
5080 }
5081
5082 static void
5083 merge_dict(const void *key, const void *value, void *context)
5084 {
5085 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context;
5086
5087 CFDictionarySetValue(dict, key, value);
5088 return;
5089 }
5090
5091 #define PROXY_AUTO_DISCOVERY_URL 252
5092
5093 static CF_RETURNS_RETAINED CFStringRef
5094 wpadURL_dhcp(CFDictionaryRef dhcp_options)
5095 {
5096 CFStringRef urlString = NULL;
5097
5098 if (dhcp_options != NULL) {
5099 CFDataRef data;
5100
5101 data = DHCPInfoGetOptionData(dhcp_options, PROXY_AUTO_DISCOVERY_URL);
5102 if (data != NULL) {
5103 CFURLRef url;
5104 const UInt8 *urlBytes;
5105 CFIndex urlLen;
5106
5107 urlBytes = CFDataGetBytePtr(data);
5108 urlLen = CFDataGetLength(data);
5109 while ((urlLen > 0) && (urlBytes[urlLen - 1] == 0)) {
5110 // remove trailing NUL
5111 urlLen--;
5112 }
5113
5114 if (urlLen <= 0) {
5115 return NULL;
5116 }
5117
5118 url = CFURLCreateWithBytes(NULL, urlBytes, urlLen, kCFStringEncodingUTF8, NULL);
5119 if (url != NULL) {
5120 urlString = CFURLGetString(url);
5121 if (urlString != NULL) {
5122 CFRetain(urlString);
5123 }
5124 CFRelease(url);
5125 }
5126 }
5127 }
5128
5129 return urlString;
5130 }
5131
5132 static CF_RETURNS_RETAINED CFStringRef
5133 wpadURL_dns(void)
5134 {
5135 CFURLRef url;
5136 CFStringRef urlString = NULL;
5137
5138 url = CFURLCreateWithString(NULL, CFSTR("http://wpad/wpad.dat"), NULL);
5139 if (url != NULL) {
5140 urlString = CFURLGetString(url);
5141 if (urlString != NULL) {
5142 CFRetain(urlString);
5143 }
5144 CFRelease(url);
5145 }
5146
5147 return urlString;
5148 }
5149
5150 static boolean_t
5151 get_proxies_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
5152 CFDictionaryRef setup_dict, CFDictionaryRef info)
5153 {
5154 ProtocolFlags active_protos = kProtocolFlagsNone;
5155 boolean_t changed = FALSE;
5156 CFStringRef interface = NULL;
5157 CFDictionaryRef ipv4;
5158 CFDictionaryRef ipv6;
5159 CFMutableDictionaryRef new_dict = NULL;
5160 const struct {
5161 CFStringRef key;
5162 uint32_t flags;
5163 Boolean append;
5164 } merge_list[] = {
5165 { kSCPropNetProxiesSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE },
5166 { kSCPropNetProxiesSupplementalMatchOrders, 0, TRUE },
5167 };
5168 const struct {
5169 CFStringRef key1; /* an "enable" key */
5170 CFStringRef key2;
5171 CFStringRef key3;
5172 } pick_list[] = {
5173 { kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort },
5174 { kSCPropNetProxiesGopherEnable, kSCPropNetProxiesGopherProxy, kSCPropNetProxiesGopherPort },
5175 { kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort },
5176 { kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort },
5177 { kSCPropNetProxiesRTSPEnable, kSCPropNetProxiesRTSPProxy, kSCPropNetProxiesRTSPPort },
5178 { kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort },
5179 { kSCPropNetProxiesProxyAutoConfigEnable,
5180 kSCPropNetProxiesProxyAutoConfigURLString,
5181 kSCPropNetProxiesProxyAutoConfigJavaScript, },
5182 { kSCPropNetProxiesProxyAutoDiscoveryEnable,
5183 NULL,
5184 NULL, }
5185 };
5186
5187 if ((state_dict == NULL) && (setup_dict == NULL)) {
5188 /* there is no proxy content */
5189 goto done;
5190 }
5191 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
5192 if (ipdict_is_routable(ipv4)) {
5193 active_protos |= kProtocolFlagsIPv4;
5194 interface = ipdict_get_ifname(ipv4);
5195 }
5196 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
5197 if (ipdict_is_routable(ipv6)) {
5198 active_protos |= kProtocolFlagsIPv6;
5199 if (interface == NULL) {
5200 interface = ipdict_get_ifname(ipv6);
5201 }
5202 }
5203 if (active_protos == kProtocolFlagsNone) {
5204 /* there is no IPv4 nor IPv6 */
5205 if (state_dict == NULL) {
5206 /* ... and no proxy content that we care about */
5207 goto done;
5208 }
5209 setup_dict = NULL;
5210 }
5211
5212 if ((setup_dict != NULL) && (state_dict != NULL)) {
5213 CFMutableDictionaryRef setup_copy;
5214
5215 /*
5216 * Merge the per-service "Setup:" and "State:" proxy information with
5217 * the "Setup:" information always taking precedence. Additionally,
5218 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
5219 * Port) is defined than all of the values for that group will be
5220 * used. That is, we don't allow mixing some of the values from
5221 * the "Setup:" keys and others from the "State:" keys.
5222 */
5223 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
5224 for (size_t i = 0; i < countof(merge_list); i++) {
5225 merge_array_prop(new_dict,
5226 merge_list[i].key,
5227 state_dict,
5228 setup_dict,
5229 merge_list[i].flags,
5230 merge_list[i].append);
5231 }
5232
5233 setup_copy = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
5234 for (size_t i = 0; i < countof(pick_list); i++) {
5235 if (CFDictionaryContainsKey(setup_copy, pick_list[i].key1)) {
5236 /*
5237 * if a "Setup:" enabled key has been provided than we want to
5238 * ignore all of the "State:" keys
5239 */
5240 CFDictionaryRemoveValue(new_dict, pick_list[i].key1);
5241 if (pick_list[i].key2 != NULL) {
5242 CFDictionaryRemoveValue(new_dict, pick_list[i].key2);
5243 }
5244 if (pick_list[i].key3 != NULL) {
5245 CFDictionaryRemoveValue(new_dict, pick_list[i].key3);
5246 }
5247 } else if (CFDictionaryContainsKey(state_dict, pick_list[i].key1) ||
5248 ((pick_list[i].key2 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key2)) ||
5249 ((pick_list[i].key3 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key3))) {
5250 /*
5251 * if a "Setup:" enabled key has not been provided and we have
5252 * some" "State:" keys than we remove all of of "Setup:" keys
5253 */
5254 CFDictionaryRemoveValue(setup_copy, pick_list[i].key1);
5255 if (pick_list[i].key2 != NULL) {
5256 CFDictionaryRemoveValue(setup_copy, pick_list[i].key2);
5257 }
5258 if (pick_list[i].key3 != NULL) {
5259 CFDictionaryRemoveValue(setup_copy, pick_list[i].key3);
5260 }
5261 }
5262 }
5263
5264 /* merge the "Setup:" keys */
5265 CFDictionaryApplyFunction(setup_copy, merge_dict, new_dict);
5266 CFRelease(setup_copy);
5267 }
5268 else if (setup_dict != NULL) {
5269 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
5270 }
5271 else if (state_dict != NULL) {
5272 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
5273 }
5274
5275 if ((new_dict != NULL) && (CFDictionaryGetCount(new_dict) == 0)) {
5276 CFRelease(new_dict);
5277 new_dict = NULL;
5278 }
5279
5280 if ((new_dict != NULL) && (interface != NULL)) {
5281 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
5282 }
5283
5284 /* process WPAD */
5285 if (new_dict != NULL) {
5286 CFDictionaryRef dhcp_options;
5287 CFNumberRef num;
5288 CFNumberRef wpad = NULL;
5289 int wpadEnabled = 0;
5290 CFStringRef wpadURL = NULL;
5291
5292 if (CFDictionaryGetValueIfPresent(new_dict,
5293 kSCPropNetProxiesProxyAutoDiscoveryEnable,
5294 (const void **)&num) &&
5295 isA_CFNumber(num)) {
5296 /* if we have a WPAD key */
5297 wpad = num;
5298 if (!CFNumberGetValue(num, kCFNumberIntType, &wpadEnabled)) {
5299 /* if we don't like the enabled key/value */
5300 wpadEnabled = 0;
5301 }
5302 }
5303
5304 if (wpadEnabled) {
5305 int pacEnabled = 0;
5306
5307 num = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigEnable);
5308 if (!isA_CFNumber(num) ||
5309 !CFNumberGetValue(num, kCFNumberIntType, &pacEnabled)) {
5310 /* if we don't like the enabled key/value */
5311 pacEnabled = 0;
5312 }
5313
5314 if (pacEnabled) {
5315 CFStringRef pacURL;
5316
5317 pacURL = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigURLString);
5318 if (pacURL != NULL) {
5319 if (!isA_CFString(pacURL) || (CFStringGetLength(pacURL) == 0)) {
5320 /* if we don't like the PAC URL */
5321 pacEnabled = 0;
5322 }
5323 } else {
5324 CFStringRef pacJS;
5325
5326 pacJS = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigJavaScript);
5327 if (!isA_CFString(pacJS) || (CFStringGetLength(pacJS) == 0)) {
5328 /* if we don't have (or like) the PAC JavaScript */
5329 pacEnabled = 0;
5330 }
5331 }
5332 }
5333
5334 if (pacEnabled) {
5335 /*
5336 * we already have a PAC URL so disable WPAD.
5337 */
5338 wpadEnabled = 0;
5339 goto setWPAD;
5340 }
5341
5342 /*
5343 * if WPAD is enabled and we don't already have a PAC URL then
5344 * we check for a DHCP provided URL. If not available, we use
5345 * a PAC URL pointing to a well-known file (wpad.dat) on a
5346 * well-known host (wpad.<domain>).
5347 */
5348 dhcp_options = get_service_state_entity(info, serviceID, kSCEntNetDHCP);
5349 wpadURL = wpadURL_dhcp(dhcp_options);
5350 if (wpadURL == NULL) {
5351 wpadURL = wpadURL_dns();
5352 }
5353 if (wpadURL == NULL) {
5354 wpadEnabled = 0; /* if we don't have a WPAD URL */
5355 goto setWPAD;
5356 }
5357
5358 pacEnabled = 1;
5359 num = CFNumberCreate(NULL, kCFNumberIntType, &pacEnabled);
5360 CFDictionarySetValue(new_dict,
5361 kSCPropNetProxiesProxyAutoConfigEnable,
5362 num);
5363 CFRelease(num);
5364 CFDictionarySetValue(new_dict,
5365 kSCPropNetProxiesProxyAutoConfigURLString,
5366 wpadURL);
5367 CFRelease(wpadURL);
5368 }
5369
5370 setWPAD:
5371 if (wpad != NULL) {
5372 num = CFNumberCreate(NULL, kCFNumberIntType, &wpadEnabled);
5373 CFDictionarySetValue(new_dict,
5374 kSCPropNetProxiesProxyAutoDiscoveryEnable,
5375 num);
5376 CFRelease(num);
5377 }
5378 }
5379
5380 done:
5381 changed = service_dict_set(serviceID, kSCEntNetProxies, new_dict);
5382 my_CFRelease(&new_dict);
5383 return (changed);
5384 }
5385
5386 #if !TARGET_OS_IPHONE
5387 static boolean_t
5388 get_smb_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
5389 CFDictionaryRef setup_dict, CFDictionaryRef info)
5390 {
5391 #pragma unused(info)
5392 boolean_t changed = FALSE;
5393 CFMutableDictionaryRef new_dict = NULL;
5394 const CFStringRef pick_list[] = {
5395 kSCPropNetSMBNetBIOSName,
5396 kSCPropNetSMBNetBIOSNodeType,
5397 #ifdef ADD_NETBIOS_SCOPE
5398 kSCPropNetSMBNetBIOSScope,
5399 #endif // ADD_NETBIOS_SCOPE
5400 kSCPropNetSMBWorkgroup,
5401 };
5402
5403 if (state_dict == NULL && setup_dict == NULL) {
5404 /* there is no SMB */
5405 goto done;
5406 }
5407 if (service_dict_get(serviceID, kSCEntNetIPv4) == NULL
5408 && service_dict_get(serviceID, kSCEntNetIPv6) == NULL) {
5409 /* there is no IPv4 or IPv6 */
5410 goto done;
5411 }
5412
5413 /* merge SMB configuration */
5414 new_dict = CFDictionaryCreateMutable(NULL, 0,
5415 &kCFTypeDictionaryKeyCallBacks,
5416 &kCFTypeDictionaryValueCallBacks);
5417 merge_array_prop(new_dict,
5418 kSCPropNetSMBWINSAddresses,
5419 state_dict,
5420 setup_dict,
5421 0,
5422 FALSE);
5423 for (size_t i = 0; i < countof(pick_list); i++) {
5424 pick_prop(new_dict,
5425 pick_list[i],
5426 state_dict,
5427 setup_dict,
5428 0);
5429 }
5430
5431 if (CFDictionaryGetCount(new_dict) == 0) {
5432 my_CFRelease(&new_dict);
5433 goto done;
5434 }
5435
5436 done:
5437 changed = service_dict_set(serviceID, kSCEntNetSMB, new_dict);
5438 my_CFRelease(&new_dict);
5439 return (changed);
5440 }
5441 #endif /* !TARGET_OS_IPHONE */
5442
5443 static CFStringRef
5444 services_info_get_interface(CFDictionaryRef services_info,
5445 CFStringRef serviceID)
5446 {
5447 CFStringRef interface = NULL;
5448 CFDictionaryRef ipv4_dict;
5449
5450 ipv4_dict = get_service_state_entity(services_info, serviceID,
5451 kSCEntNetIPv4);
5452 if (ipv4_dict != NULL) {
5453 interface = CFDictionaryGetValue(ipv4_dict, kSCPropInterfaceName);
5454 }
5455 else {
5456 CFDictionaryRef ipv6_dict;
5457
5458 ipv6_dict = get_service_state_entity(services_info, serviceID,
5459 kSCEntNetIPv6);
5460 if (ipv6_dict != NULL) {
5461 interface = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
5462 }
5463 }
5464 return (interface);
5465 }
5466
5467
5468 static const struct {
5469 const CFStringRef * entityName;
5470 const CFStringRef * statusKey;
5471 } transientServiceInfo[] = {
5472 { &kSCEntNetIPSec, &kSCPropNetIPSecStatus },
5473 { &kSCEntNetPPP, &kSCPropNetPPPStatus },
5474 { &kSCEntNetVPN, &kSCPropNetVPNStatus },
5475 };
5476
5477 static Boolean
5478 get_transient_status_changes(CFStringRef serviceID,
5479 CFDictionaryRef services_info)
5480 {
5481 boolean_t changed = FALSE;
5482
5483 for (size_t i = 0; i < countof(transientServiceInfo); i++) {
5484 CFDictionaryRef dict;
5485 CFNumberRef status = NULL;
5486 CFMutableDictionaryRef ts_dict = NULL;
5487
5488 dict = get_service_state_entity(services_info, serviceID,
5489 *transientServiceInfo[i].entityName);
5490
5491 if (dict != NULL) {
5492 status = CFDictionaryGetValue(dict,
5493 *transientServiceInfo[i].statusKey);
5494 }
5495
5496 if (isA_CFNumber(status) != NULL) {
5497 ts_dict = CFDictionaryCreateMutable(NULL,
5498 0,
5499 &kCFTypeDictionaryKeyCallBacks,
5500 &kCFTypeDictionaryValueCallBacks);
5501 CFDictionaryAddValue(ts_dict,
5502 *transientServiceInfo[i].statusKey,
5503 status);
5504 }
5505
5506 if (service_dict_set(serviceID, *transientServiceInfo[i].entityName,
5507 ts_dict)) {
5508 changed = TRUE;
5509 }
5510
5511 if (ts_dict != NULL) {
5512 CFRelease(ts_dict);
5513 }
5514 }
5515 return (changed);
5516 }
5517
5518 static boolean_t
5519 if_dict_is_expensive(CFDictionaryRef if_dict)
5520 {
5521 boolean_t is_expensive = FALSE;
5522
5523 if (isA_CFDictionary(if_dict) != NULL) {
5524 CFBooleanRef expensive;
5525 expensive = CFDictionaryGetValue(if_dict, kSCPropNetLinkExpensive);
5526 if (isA_CFBoolean(expensive) != NULL
5527 && CFBooleanGetValue(expensive)) {
5528 is_expensive = TRUE;
5529 }
5530 }
5531 return is_expensive;
5532 }
5533
5534 static boolean_t
5535 service_is_expensive(CFStringRef serviceID, CFDictionaryRef services_info)
5536 {
5537 CFStringRef ifname;
5538 boolean_t is_expensive = FALSE;
5539
5540 ifname = services_info_get_interface(services_info, serviceID);
5541 if (ifname != NULL) {
5542 CFDictionaryRef if_dict;
5543 CFStringRef key;
5544
5545 key = interface_entity_key_copy(ifname, kSCEntNetLink);
5546 if_dict = CFDictionaryGetValue(services_info, key);
5547 CFRelease(key);
5548 is_expensive = if_dict_is_expensive(if_dict);
5549 }
5550 return (is_expensive);
5551 }
5552
5553 static boolean_t
5554 interface_is_expensive(CFStringRef ifname)
5555 {
5556 boolean_t is_expensive = FALSE;
5557
5558 if (ifname != NULL) {
5559 CFDictionaryRef if_dict;
5560 CFStringRef key;
5561
5562 key = interface_entity_key_copy(ifname, kSCEntNetLink);
5563 if_dict = SCDynamicStoreCopyValue(S_session, key);
5564 CFRelease(key);
5565 if (if_dict != NULL) {
5566 is_expensive = if_dict_is_expensive(if_dict);
5567 CFRelease(if_dict);
5568 }
5569 }
5570 return (is_expensive);
5571 }
5572
5573 static CFNumberRef
5574 service_rank_entity_get_index(CFDictionaryRef dict, CFStringRef serviceID,
5575 CFStringRef which, uint32_t * ret_val)
5576 {
5577 CFNumberRef service_index = NULL;
5578
5579 if (dict != NULL) {
5580 service_index = CFDictionaryGetValue(dict,
5581 kSCPropNetServiceServiceIndex);
5582 service_index = isA_CFNumber(service_index);
5583 }
5584 if (service_index != NULL) {
5585 SInt32 index_val;
5586
5587 if (!CFNumberGetValue(service_index, kCFNumberSInt32Type,
5588 &index_val)
5589 || index_val <= 0) {
5590 /* ServiceIndex must be >= 1 */
5591 my_log(LOG_NOTICE,
5592 "%@%@ ServiceIndex %@ is invalid, ignoring",
5593 which, serviceID, service_index);
5594 service_index = NULL;
5595 }
5596 else if (ret_val != NULL) {
5597 *ret_val = (uint32_t)index_val;
5598 }
5599 }
5600 return (service_index);
5601 }
5602
5603 static boolean_t
5604 get_rank_changes(CFStringRef serviceID, CFDictionaryRef state_options,
5605 CFDictionaryRef setup_options, CFDictionaryRef services_info)
5606 {
5607 boolean_t changed = FALSE;
5608 CFStringRef interface;
5609 boolean_t ip_is_coupled = FALSE;
5610 CFMutableDictionaryRef new_dict = NULL;
5611 Rank rank_assertion = kRankAssertionDefault;
5612 Boolean rank_assertion_is_set = FALSE;
5613 CFStringRef setup_rank = NULL;
5614 CFStringRef state_rank = NULL;
5615 CFNumberRef service_index = NULL;
5616 boolean_t use_setup_rank = TRUE;
5617
5618
5619 if (setup_options != NULL) {
5620 CFBooleanRef coupled;
5621
5622 setup_rank
5623 = CFDictionaryGetValue(setup_options, kSCPropNetServicePrimaryRank);
5624 setup_rank = isA_CFString(setup_rank);
5625 if (setup_rank != NULL && !use_setup_rank) {
5626 my_log(LOG_DEBUG, "%@ ignoring Setup PrimaryRank = %@",
5627 serviceID, setup_rank);
5628 setup_rank = NULL;
5629 }
5630 coupled = CFDictionaryGetValue(setup_options, kIPIsCoupled);
5631 if (isA_CFBoolean(coupled) != NULL && CFBooleanGetValue(coupled)) {
5632 ip_is_coupled = TRUE;
5633 }
5634 service_index
5635 = service_rank_entity_get_index(setup_options,
5636 serviceID,
5637 kSCDynamicStoreDomainSetup,
5638 NULL);
5639 }
5640 if (state_options != NULL) {
5641 CFBooleanRef coupled;
5642
5643 state_rank
5644 = CFDictionaryGetValue(state_options, kSCPropNetServicePrimaryRank);
5645 state_rank = isA_CFString(state_rank);
5646 coupled = CFDictionaryGetValue(state_options, kIPIsCoupled);
5647 if (isA_CFBoolean(coupled) != NULL && CFBooleanGetValue(coupled)) {
5648 ip_is_coupled = TRUE;
5649 }
5650 if (service_index == NULL) {
5651 service_index
5652 = service_rank_entity_get_index(state_options,
5653 serviceID,
5654 kSCDynamicStoreDomainState,
5655 NULL);
5656 }
5657 }
5658
5659 if (!ip_is_coupled) {
5660 ip_is_coupled = service_is_expensive(serviceID, services_info);
5661 }
5662 if (setup_rank != NULL || state_rank != NULL) {
5663 /* rank assertion is set on the service */
5664 Rank setup_assertion;
5665 Boolean setup_assertion_is_set = FALSE;
5666 Rank state_assertion;
5667 Boolean state_assertion_is_set = FALSE;
5668
5669 setup_assertion = PrimaryRankGetRankAssertion(setup_rank,
5670 &setup_assertion_is_set);
5671 state_assertion = PrimaryRankGetRankAssertion(state_rank,
5672 &state_assertion_is_set);
5673 if (setup_assertion_is_set && state_assertion_is_set) {
5674 if (setup_assertion > state_assertion) {
5675 rank_assertion = setup_assertion;
5676 }
5677 else {
5678 rank_assertion = state_assertion;
5679 }
5680 rank_assertion_is_set = TRUE;
5681 }
5682 else if (setup_assertion_is_set) {
5683 rank_assertion = setup_assertion;
5684 rank_assertion_is_set = TRUE;
5685 }
5686 else if (state_assertion_is_set) {
5687 rank_assertion = state_assertion;
5688 rank_assertion_is_set = TRUE;
5689 }
5690 }
5691
5692 interface = services_info_get_interface(services_info, serviceID);
5693 if (interface != NULL) {
5694 if (!rank_assertion_is_set) {
5695 /* check for a rank assertion on the interface */
5696 CFNumberRef if_rank = NULL;
5697
5698 if (S_if_rank_dict != NULL) {
5699 if_rank = CFDictionaryGetValue(S_if_rank_dict, interface);
5700 }
5701 rank_assertion
5702 = InterfaceRankGetRankAssertion(if_rank,
5703 &rank_assertion_is_set);
5704 #define kNotSetString ((CFTypeRef)CFSTR("not set"))
5705 my_log(LOG_INFO,
5706 "serviceID %@ interface %@ rank = 0x%x (%@)%s",
5707 serviceID,
5708 interface,
5709 rank_assertion,
5710 (if_rank != NULL) ? (CFTypeRef)if_rank : kNotSetString,
5711 ip_is_coupled ? " [coupled]" : "");
5712 }
5713 else {
5714 my_log(LOG_INFO,
5715 "serviceID %@ interface %@ rank = 0x%x%s",
5716 serviceID, interface, rank_assertion,
5717 ip_is_coupled ? " [coupled]" : "");
5718 }
5719 }
5720
5721
5722 if (service_index != NULL || rank_assertion_is_set || ip_is_coupled) {
5723 new_dict = CFDictionaryCreateMutable(NULL, 0,
5724 &kCFTypeDictionaryKeyCallBacks,
5725 &kCFTypeDictionaryValueCallBacks);
5726 if (rank_assertion_is_set) {
5727 CFNumberRef new_rank;
5728
5729 new_rank = CFNumberCreate(NULL, kCFNumberSInt32Type,
5730 (const void *)&rank_assertion);
5731 CFDictionarySetValue(new_dict, kServiceOptionRankAssertion,
5732 new_rank);
5733 CFRelease(new_rank);
5734 }
5735 if (ip_is_coupled) {
5736 CFDictionarySetValue(new_dict, kIPIsCoupled, kCFBooleanTrue);
5737 }
5738 if (service_index != NULL) {
5739 CFDictionarySetValue(new_dict, kSCPropNetServiceServiceIndex,
5740 service_index);
5741 }
5742 }
5743 changed = service_dict_set(serviceID, kSCEntNetService, new_dict);
5744 my_CFRelease(&new_dict);
5745 return (changed);
5746 }
5747
5748 static void
5749 add_service_keys(CFStringRef serviceID,
5750 CFMutableArrayRef keys,
5751 CFMutableArrayRef patterns)
5752 {
5753 int i;
5754 CFStringRef key;
5755
5756 if (CFEqual(serviceID, kSCCompAnyRegex)) {
5757 keys = patterns;
5758 }
5759
5760 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
5761 CFStringRef name = *entityTypeNames[i];
5762
5763 key = setup_service_key(serviceID, name);
5764 my_CFArrayAppendUniqueValue(keys, key);
5765 CFRelease(key);
5766 key = state_service_key(serviceID, name);
5767 my_CFArrayAppendUniqueValue(keys, key);
5768 CFRelease(key);
5769 }
5770
5771 key = state_service_key(serviceID, kSCEntNetDHCP);
5772 my_CFArrayAppendUniqueValue(keys, key);
5773 CFRelease(key);
5774
5775 key = setup_service_key(serviceID, NULL);
5776 my_CFArrayAppendUniqueValue(keys, key);
5777 CFRelease(key);
5778 key = state_service_key(serviceID, NULL);
5779 my_CFArrayAppendUniqueValue(keys, key);
5780 CFRelease(key);
5781
5782 return;
5783 }
5784
5785 static void
5786 add_transient_status_keys(CFStringRef serviceID,
5787 CFMutableArrayRef keys,
5788 CFMutableArrayRef patterns)
5789 {
5790 if (CFEqual(serviceID, kSCCompAnyRegex)) {
5791 keys = patterns;
5792 }
5793
5794 for (size_t i = 0; i < countof(transientServiceInfo); i++) {
5795 CFStringRef key;
5796
5797 key = state_service_key(serviceID,
5798 *transientServiceInfo[i].entityName);
5799 my_CFArrayAppendUniqueValue(keys, key);
5800 CFRelease(key);
5801 }
5802
5803 return;
5804 }
5805
5806 static const CFStringRef *reachabilitySetupKeys[] = {
5807 &kSCEntNetPPP,
5808 &kSCEntNetInterface,
5809 &kSCEntNetIPv4,
5810 &kSCEntNetIPv6,
5811 };
5812
5813
5814 static void
5815 add_reachability_patterns(CFMutableArrayRef patterns)
5816 {
5817 for (size_t i = 0; i < countof(reachabilitySetupKeys); i++) {
5818 CFStringRef pattern;
5819 pattern = setup_service_key(kSCCompAnyRegex, *reachabilitySetupKeys[i]);
5820 my_CFArrayAppendUniqueValue(patterns, pattern);
5821 CFRelease(pattern);
5822 }
5823 }
5824
5825
5826 static void
5827 add_vpn_pattern(CFMutableArrayRef patterns)
5828 {
5829 CFStringRef pattern;
5830
5831 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
5832 my_CFArrayAppendUniqueValue(patterns, pattern);
5833 CFRelease(pattern);
5834 }
5835
5836 static void
5837 add_interface_link_pattern(CFMutableArrayRef patterns)
5838 {
5839 CFStringRef pattern;
5840
5841 pattern = interface_entity_key_copy(kSCCompAnyRegex, kSCEntNetLink);
5842 my_CFArrayAppendUniqueValue(patterns, pattern);
5843 CFRelease(pattern);
5844 }
5845
5846 static CFDictionaryRef
5847 services_info_copy(SCDynamicStoreRef session, CFArrayRef service_list)
5848 {
5849 CFIndex count;
5850 CFMutableArrayRef keys;
5851 CFDictionaryRef info;
5852 CFMutableArrayRef patterns;
5853
5854 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5855 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5856
5857 CFArrayAppendValue(keys, S_setup_global_ipv4);
5858 CFArrayAppendValue(keys, S_multicast_resolvers);
5859 CFArrayAppendValue(keys, S_private_resolvers);
5860
5861 count = CFArrayGetCount(service_list);
5862 for (CFIndex s = 0; s < count; s++) {
5863 CFStringRef serviceID = CFArrayGetValueAtIndex(service_list, s);
5864
5865 add_service_keys(serviceID, keys, patterns);
5866 add_transient_status_keys(serviceID, keys, patterns);
5867 }
5868
5869 add_reachability_patterns(patterns);
5870
5871 add_vpn_pattern(patterns);
5872
5873 add_interface_link_pattern(patterns);
5874
5875 info = SCDynamicStoreCopyMultiple(session, keys, patterns);
5876 my_CFRelease(&keys);
5877 my_CFRelease(&patterns);
5878 return (info);
5879 }
5880
5881 #if !TARGET_OS_SIMULATOR
5882
5883 static boolean_t
5884 set_ipv6_default_interface(IFIndex ifindex)
5885 {
5886 struct in6_ndifreq ndifreq;
5887 int sock;
5888 boolean_t success = FALSE;
5889
5890 memset((char *)&ndifreq, 0, sizeof(ndifreq));
5891 strlcpy(ndifreq.ifname, kLoopbackInterface, sizeof(ndifreq.ifname));
5892 if (ifindex != 0) {
5893 ndifreq.ifindex = ifindex;
5894 }
5895 else {
5896 ndifreq.ifindex = lo0_ifindex();
5897 }
5898 sock = inet6_dgram_socket();
5899 if (sock < 0) {
5900 goto done;
5901 }
5902 if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) == -1) {
5903 my_log(LOG_ERR,
5904 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5905 strerror(errno));
5906 }
5907 else {
5908 success = TRUE;
5909 }
5910 close(sock);
5911 done:
5912 return (success);
5913 }
5914
5915 #endif /* !TARGET_OS_SIMULATOR */
5916
5917 #if !TARGET_OS_IPHONE
5918 static __inline__ void
5919 empty_dns()
5920 {
5921 (void)unlink(VAR_RUN_RESOLV_CONF);
5922 }
5923
5924 static void
5925 set_dns(CFArrayRef val_search_domains,
5926 CFStringRef val_domain_name,
5927 CFArrayRef val_servers,
5928 CFArrayRef val_sortlist)
5929 {
5930 FILE * f = fopen(VAR_RUN_RESOLV_CONF "-", "w");
5931
5932 /* publish new resolv.conf */
5933 if (f) {
5934 CFIndex i;
5935 CFIndex n;
5936
5937 SCPrint(TRUE, f, CFSTR("#\n"));
5938 SCPrint(TRUE, f, CFSTR("# macOS Notice\n"));
5939 SCPrint(TRUE, f, CFSTR("#\n"));
5940 SCPrint(TRUE, f, CFSTR("# This file is not consulted for DNS hostname resolution, address\n"));
5941 SCPrint(TRUE, f, CFSTR("# resolution, or the DNS query routing mechanism used by most\n"));
5942 SCPrint(TRUE, f, CFSTR("# processes on this system.\n"));
5943 SCPrint(TRUE, f, CFSTR("#\n"));
5944 SCPrint(TRUE, f, CFSTR("# To view the DNS configuration used by this system, use:\n"));
5945 SCPrint(TRUE, f, CFSTR("# scutil --dns\n"));
5946 SCPrint(TRUE, f, CFSTR("#\n"));
5947 SCPrint(TRUE, f, CFSTR("# SEE ALSO\n"));
5948 SCPrint(TRUE, f, CFSTR("# dns-sd(1), scutil(8)\n"));
5949 SCPrint(TRUE, f, CFSTR("#\n"));
5950 SCPrint(TRUE, f, CFSTR("# This file is automatically generated.\n"));
5951 SCPrint(TRUE, f, CFSTR("#\n"));
5952
5953 if (isA_CFArray(val_search_domains)) {
5954 SCPrint(TRUE, f, CFSTR("search"));
5955 n = CFArrayGetCount(val_search_domains);
5956 for (i = 0; i < n; i++) {
5957 CFStringRef domain;
5958
5959 domain = CFArrayGetValueAtIndex(val_search_domains, i);
5960 if (isA_CFString(domain)) {
5961 SCPrint(TRUE, f, CFSTR(" %@"), domain);
5962 }
5963 }
5964 SCPrint(TRUE, f, CFSTR("\n"));
5965 }
5966 else if (isA_CFString(val_domain_name)) {
5967 SCPrint(TRUE, f, CFSTR("domain %@\n"), val_domain_name);
5968 }
5969
5970 if (isA_CFArray(val_servers)) {
5971 n = CFArrayGetCount(val_servers);
5972 for (i = 0; i < n; i++) {
5973 CFStringRef nameserver;
5974
5975 nameserver = CFArrayGetValueAtIndex(val_servers, i);
5976 if (isA_CFString(nameserver)) {
5977 SCPrint(TRUE, f, CFSTR("nameserver %@\n"), nameserver);
5978 }
5979 }
5980 }
5981
5982 if (isA_CFArray(val_sortlist)) {
5983 SCPrint(TRUE, f, CFSTR("sortlist"));
5984 n = CFArrayGetCount(val_sortlist);
5985 for (i = 0; i < n; i++) {
5986 CFStringRef address;
5987
5988 address = CFArrayGetValueAtIndex(val_sortlist, i);
5989 if (isA_CFString(address)) {
5990 SCPrint(TRUE, f, CFSTR(" %@"), address);
5991 }
5992 }
5993 SCPrint(TRUE, f, CFSTR("\n"));
5994 }
5995
5996 fclose(f);
5997 (void)rename(VAR_RUN_RESOLV_CONF "-", VAR_RUN_RESOLV_CONF);
5998 }
5999 return;
6000 }
6001 #endif /* !TARGET_OS_IPHONE */
6002
6003 static boolean_t
6004 service_get_ip_is_coupled(CFStringRef serviceID)
6005 {
6006 CFDictionaryRef dict;
6007 boolean_t ip_is_coupled = FALSE;
6008
6009 dict = service_dict_get(serviceID, kSCEntNetService);
6010 if (dict != NULL) {
6011 if (CFDictionaryContainsKey(dict, kIPIsCoupled)) {
6012 ip_is_coupled = TRUE;
6013 }
6014 }
6015 return (ip_is_coupled);
6016 }
6017
6018 static CFStringRef
6019 my_CFStringCreateWithInAddr(struct in_addr ip)
6020 {
6021 CFStringRef str;
6022
6023 str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT), IP_LIST(&ip));
6024 return (str);
6025 }
6026
6027 static CFStringRef
6028 my_CFStringCreateWithIn6Addr(const struct in6_addr * ip)
6029 {
6030 char ntopbuf[INET6_ADDRSTRLEN];
6031
6032 (void)inet_ntop(AF_INET6, ip, ntopbuf, sizeof(ntopbuf));
6033 return (CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), ntopbuf));
6034 }
6035
6036 /*
6037 * Function: update_ipv4
6038 * Purpose:
6039 * Update the IPv4 configuration based on the latest information.
6040 * Publish the State:/Network/Global/IPv4 information, and update the
6041 * IPv4 routing table.
6042 */
6043 static void
6044 update_ipv4(CFStringRef primary,
6045 IPv4RouteListRef new_routelist,
6046 keyChangeListRef keys)
6047 {
6048 #if !TARGET_OS_SIMULATOR
6049 int sockfd;
6050 #endif /* !TARGET_OS_SIMULATOR */
6051
6052 if (keys != NULL) {
6053 if (new_routelist != NULL && primary != NULL) {
6054 const char * ifn_p = NULL;
6055 char ifname[IFNAMSIZ];
6056 IPv4RouteRef r;
6057 CFMutableDictionaryRef dict = NULL;
6058
6059 dict = CFDictionaryCreateMutable(NULL, 0,
6060 &kCFTypeDictionaryKeyCallBacks,
6061 &kCFTypeDictionaryValueCallBacks);
6062 /* the first entry is the default route */
6063 r = new_routelist->list;
6064 if (r->gateway.s_addr != 0) {
6065 CFStringRef str;
6066
6067 str = my_CFStringCreateWithInAddr(r->gateway);
6068 CFDictionarySetValue(dict, kSCPropNetIPv4Router, str);
6069 CFRelease(str);
6070 }
6071 ifn_p = my_if_indextoname(r->ifindex, ifname);
6072 if (ifn_p != NULL) {
6073 CFStringRef ifname_cf;
6074
6075 ifname_cf = CFStringCreateWithCString(NULL,
6076 ifn_p,
6077 kCFStringEncodingASCII);
6078 if (ifname_cf != NULL) {
6079 CFDictionarySetValue(dict,
6080 kSCDynamicStorePropNetPrimaryInterface,
6081 ifname_cf);
6082 CFRelease(ifname_cf);
6083 }
6084 }
6085 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
6086 primary);
6087 keyChangeListSetValue(keys, S_state_global_ipv4, dict);
6088 CFRelease(dict);
6089 }
6090 else {
6091 keyChangeListRemoveValue(keys, S_state_global_ipv4);
6092 }
6093 }
6094
6095 #if !TARGET_OS_SIMULATOR
6096 sockfd = open_routing_socket();
6097 if (sockfd != -1) {
6098 /* go through routelist and bind any unbound routes */
6099 if (new_routelist != NULL) {
6100 IPv4RouteListFinalize(new_routelist);
6101 }
6102 else {
6103 /* provide a routelist with just loopback multicast */
6104 new_routelist = IPv4RouteListCopyMulticastLoopback();
6105 }
6106 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
6107 if (S_ipv4_routelist == NULL) {
6108 my_log(LOG_DEBUG, "Old Routes = <none>");
6109 }
6110 else {
6111 my_log(LOG_DEBUG, "Old Routes = ");
6112 IPv4RouteListLog(LOG_DEBUG, S_ipv4_routelist);
6113 }
6114 if (new_routelist == NULL) {
6115 my_log(LOG_DEBUG, "New Routes = <none>");
6116 }
6117 else {
6118 my_log(LOG_DEBUG, "New Routes = ");
6119 IPv4RouteListLog(LOG_DEBUG, new_routelist);
6120 }
6121 }
6122 IPv4RouteListApply(S_ipv4_routelist, new_routelist, sockfd);
6123 close(sockfd);
6124 }
6125 if (S_ipv4_routelist != NULL) {
6126 free(S_ipv4_routelist);
6127 }
6128 S_ipv4_routelist = new_routelist;
6129 #else /* !TARGET_OS_SIMULATOR */
6130 if (new_routelist != NULL) {
6131 free(new_routelist);
6132 }
6133 #endif /* !TARGET_OS_SIMULATOR */
6134
6135 return;
6136 }
6137
6138 /*
6139 * Function: update_ipv6
6140 * Purpose:
6141 * Update the IPv6 configuration based on the latest information.
6142 * Publish the State:/Network/Global/IPv6 information, and update the
6143 * IPv6 routing table.
6144 */
6145 static void
6146 update_ipv6(CFStringRef primary,
6147 IPv6RouteListRef new_routelist,
6148 keyChangeListRef keys)
6149 {
6150 #if !TARGET_OS_SIMULATOR
6151 int sockfd;
6152 #endif /* !TARGET_OS_SIMULATOR */
6153
6154 if (keys != NULL) {
6155 if (new_routelist != NULL && primary != NULL) {
6156 const char * ifn_p = NULL;
6157 char ifname[IFNAMSIZ];
6158 IPv6RouteRef r;
6159 CFMutableDictionaryRef dict = NULL;
6160
6161 dict = CFDictionaryCreateMutable(NULL, 0,
6162 &kCFTypeDictionaryKeyCallBacks,
6163 &kCFTypeDictionaryValueCallBacks);
6164 /* the first entry is the default route */
6165 r = new_routelist->list;
6166 if ((r->flags & kRouteFlagsHasGateway) != 0) {
6167 CFStringRef router;
6168
6169 router = my_CFStringCreateWithIn6Addr(&r->gateway);
6170 CFDictionarySetValue(dict, kSCPropNetIPv6Router, router);
6171 CFRelease(router);
6172 }
6173 ifn_p = my_if_indextoname(r->ifindex, ifname);
6174 if (ifn_p != NULL) {
6175 CFStringRef ifname_cf;
6176
6177 ifname_cf = CFStringCreateWithCString(NULL,
6178 ifn_p,
6179 kCFStringEncodingASCII);
6180 if (ifname_cf != NULL) {
6181 CFDictionarySetValue(dict,
6182 kSCDynamicStorePropNetPrimaryInterface,
6183 ifname_cf);
6184 CFRelease(ifname_cf);
6185 }
6186 }
6187 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
6188 primary);
6189 keyChangeListSetValue(keys, S_state_global_ipv6, dict);
6190 CFRelease(dict);
6191 #if !TARGET_OS_SIMULATOR
6192 set_ipv6_default_interface(r->ifindex);
6193 #endif /* !TARGET_OS_SIMULATOR */
6194 }
6195 else {
6196 #if !TARGET_OS_SIMULATOR
6197 set_ipv6_default_interface(0);
6198 #endif /* !TARGET_OS_SIMULATOR */
6199 keyChangeListRemoveValue(keys, S_state_global_ipv6);
6200 }
6201 }
6202
6203 #if !TARGET_OS_SIMULATOR
6204 sockfd = open_routing_socket();
6205 if (sockfd != -1) {
6206 /* go through routelist and bind any unbound routes */
6207 IPv6RouteListFinalize(new_routelist);
6208 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
6209 if (S_ipv6_routelist == NULL) {
6210 my_log(LOG_DEBUG, "Old Routes = <none>");
6211 }
6212 else {
6213 my_log(LOG_DEBUG, "Old Routes = ");
6214 IPv6RouteListLog(LOG_DEBUG, S_ipv6_routelist);
6215 }
6216 if (new_routelist == NULL) {
6217 my_log(LOG_DEBUG, "New Routes = <none>");
6218 }
6219 else {
6220 my_log(LOG_DEBUG, "New Routes = ");
6221 IPv6RouteListLog(LOG_DEBUG, new_routelist);
6222 }
6223 }
6224 IPv6RouteListApply(S_ipv6_routelist, new_routelist, sockfd);
6225 close(sockfd);
6226 }
6227 if (S_ipv6_routelist != NULL) {
6228 free(S_ipv6_routelist);
6229 }
6230 S_ipv6_routelist = new_routelist;
6231 #else /* !TARGET_OS_SIMULATOR */
6232 if (new_routelist != NULL) {
6233 free(new_routelist);
6234 }
6235 #endif /* !TARGET_OS_SIMULATOR */
6236
6237 return;
6238 }
6239
6240 static Boolean
6241 update_dns(CFDictionaryRef services_info,
6242 CFStringRef primary,
6243 keyChangeListRef keys)
6244 {
6245 #pragma unused(services_info)
6246 Boolean changed = FALSE;
6247 CFDictionaryRef dict = NULL;
6248
6249 if (primary != NULL) {
6250 CFDictionaryRef service_dict;
6251
6252 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
6253 if (service_dict != NULL) {
6254 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
6255 }
6256 }
6257
6258 if (!_SC_CFEqual(S_dns_dict, dict)) {
6259 if (dict == NULL) {
6260 #if !TARGET_OS_IPHONE
6261 empty_dns();
6262 #endif /* !TARGET_OS_IPHONE */
6263 keyChangeListRemoveValue(keys, S_state_global_dns);
6264 } else {
6265 CFMutableDictionaryRef new_dict;
6266
6267 #if !TARGET_OS_IPHONE
6268 set_dns(CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains),
6269 CFDictionaryGetValue(dict, kSCPropNetDNSDomainName),
6270 CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses),
6271 CFDictionaryGetValue(dict, kSCPropNetDNSSortList));
6272 #endif /* !TARGET_OS_IPHONE */
6273 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
6274 CFDictionaryRemoveValue(new_dict, kSCPropInterfaceName);
6275 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchDomains);
6276 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchOrders);
6277 CFDictionaryRemoveValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY);
6278 keyChangeListSetValue(keys, S_state_global_dns, new_dict);
6279 CFRelease(new_dict);
6280 }
6281 changed = TRUE;
6282 }
6283
6284 if (dict != NULL) CFRetain(dict);
6285 if (S_dns_dict != NULL) CFRelease(S_dns_dict);
6286 S_dns_dict = dict;
6287
6288 return changed;
6289 }
6290
6291 static Boolean
6292 update_dnsinfo(CFDictionaryRef services_info,
6293 CFStringRef primary,
6294 keyChangeListRef keys,
6295 CFArrayRef service_order)
6296 {
6297 Boolean changed;
6298 CFDictionaryRef dict = NULL;
6299 CFArrayRef multicastResolvers;
6300 CFArrayRef privateResolvers;
6301
6302 multicastResolvers = CFDictionaryGetValue(services_info, S_multicast_resolvers);
6303 privateResolvers = CFDictionaryGetValue(services_info, S_private_resolvers);
6304
6305 if (primary != NULL) {
6306 CFDictionaryRef service_dict;
6307
6308 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
6309 if (service_dict != NULL) {
6310 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
6311 }
6312 }
6313
6314 changed = dns_configuration_set(dict,
6315 S_service_state_dict,
6316 service_order,
6317 multicastResolvers,
6318 privateResolvers);
6319 if (changed) {
6320 keyChangeListNotifyKey(keys, S_state_global_dns);
6321 }
6322 return changed;
6323 }
6324
6325 static Boolean
6326 update_nwi(nwi_state_t state)
6327 {
6328 unsigned char signature[CC_SHA256_DIGEST_LENGTH];
6329 static unsigned char signature_last[CC_SHA256_DIGEST_LENGTH];
6330
6331 _nwi_state_compute_sha256_hash(state, signature);
6332 if (bcmp(signature, signature_last, sizeof(signature)) == 0) {
6333 my_log(LOG_DEBUG, "Not updating network information");
6334 return FALSE;
6335 }
6336
6337 // save [new] signature
6338 memcpy(signature_last, signature, sizeof(signature));
6339
6340 // save [new] configuration
6341 my_log(LOG_INFO, "Updating network information");
6342 _nwi_state_log(state, TRUE, NULL);
6343
6344 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
6345 if (!_nwi_state_store(state)) {
6346 my_log(LOG_ERR, "Notifying nwi_state_store failed");
6347 }
6348 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
6349
6350 return TRUE;
6351 }
6352
6353 static Boolean
6354 update_proxies(CFDictionaryRef services_info,
6355 CFStringRef primary,
6356 keyChangeListRef keys,
6357 CFArrayRef service_order)
6358 {
6359 Boolean changed = FALSE;
6360 CFDictionaryRef dict = NULL;
6361 CFDictionaryRef new_dict;
6362
6363 if (primary != NULL) {
6364 CFDictionaryRef service_dict;
6365
6366 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
6367 if (service_dict != NULL) {
6368 dict = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
6369 }
6370 }
6371
6372 new_dict = proxy_configuration_update(dict,
6373 S_service_state_dict,
6374 service_order,
6375 services_info);
6376 if (!_SC_CFEqual(S_proxies_dict, new_dict)) {
6377 if (new_dict == NULL) {
6378 keyChangeListRemoveValue(keys, S_state_global_proxies);
6379 } else {
6380 keyChangeListSetValue(keys, S_state_global_proxies, new_dict);
6381 }
6382 changed = TRUE;
6383 }
6384
6385 if (S_proxies_dict != NULL) CFRelease(S_proxies_dict);
6386 S_proxies_dict = new_dict;
6387
6388 return changed;
6389 }
6390
6391 #if !TARGET_OS_IPHONE
6392 static Boolean
6393 update_smb(CFDictionaryRef services_info,
6394 CFStringRef primary,
6395 keyChangeListRef keys)
6396 {
6397 #pragma unused(services_info)
6398 Boolean changed = FALSE;
6399 CFDictionaryRef dict = NULL;
6400
6401 if (primary != NULL) {
6402 CFDictionaryRef service_dict;
6403
6404 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
6405 if (service_dict != NULL) {
6406 dict = CFDictionaryGetValue(service_dict, kSCEntNetSMB);
6407 }
6408 }
6409
6410 if (!_SC_CFEqual(S_smb_dict, dict)) {
6411 if (dict == NULL) {
6412 keyChangeListRemoveValue(keys, S_state_global_smb);
6413 } else {
6414 keyChangeListSetValue(keys, S_state_global_smb, dict);
6415 }
6416 changed = TRUE;
6417 }
6418
6419 if (dict != NULL) CFRetain(dict);
6420 if (S_smb_dict != NULL) CFRelease(S_smb_dict);
6421 S_smb_dict = dict;
6422
6423 return changed;
6424 }
6425 #endif /* !TARGET_OS_IPHONE */
6426
6427 static Rank
6428 get_service_index(CFDictionaryRef rank_entity,
6429 CFArrayRef order, CFIndex n_order, CFStringRef serviceID)
6430 {
6431 CFIndex i;
6432 Rank rank = kRankIndexMask;
6433 CFNumberRef service_index;
6434
6435 service_index
6436 = service_rank_entity_get_index(rank_entity,
6437 serviceID,
6438 CFSTR(""),
6439 &rank);
6440 if (service_index != NULL) {
6441 /* ServiceIndex specified in service entity */
6442 rank += n_order;
6443 my_log(LOG_INFO,
6444 "%@ specifies ServiceIndex %@, effective index is %d",
6445 serviceID, service_index, rank);
6446 }
6447 else if (serviceID != NULL && order != NULL && n_order > 0) {
6448 for (i = 0; i < n_order; i++) {
6449 CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(order, i));
6450
6451 if (s == NULL) {
6452 continue;
6453 }
6454 if (CFEqual(serviceID, s)) {
6455 rank = (Rank)i + 1;
6456 break;
6457 }
6458 }
6459 }
6460 return (rank);
6461 }
6462
6463 /**
6464 ** Service election:
6465 **/
6466 /*
6467 * Function: rank_dict_get_service_rank
6468 * Purpose:
6469 * Retrieve the service rank in the given dictionary.
6470 */
6471 static Rank
6472 rank_dict_get_service_rank(CFDictionaryRef rank_dict, CFStringRef serviceID)
6473 {
6474 CFNumberRef rank;
6475 Rank rank_val = kRankAssertionDefault;
6476
6477 rank_val = RankMake(kRankIndexMask, kRankAssertionDefault);
6478 rank = CFDictionaryGetValue(rank_dict, serviceID);
6479 if (rank != NULL) {
6480 if (!CFNumberGetValue(rank, kCFNumberSInt32Type, &rank_val)) {
6481 /* if we don't like the rank value */
6482 rank_val = kRankAssertionDefault;
6483 }
6484
6485 }
6486 return (rank_val);
6487 }
6488
6489 /*
6490 * Function: rank_dict_set_service_rank
6491 * Purpose:
6492 * Save the results of ranking the service so we can look it up later without
6493 * repeating all of the ranking code.
6494 */
6495 static void
6496 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict,
6497 CFStringRef serviceID, Rank rank_val)
6498 {
6499 CFNumberRef rank;
6500
6501 rank = CFNumberCreate(NULL, kCFNumberSInt32Type, (const void *)&rank_val);
6502 if (rank != NULL) {
6503 CFDictionarySetValue(rank_dict, serviceID, rank);
6504 CFRelease(rank);
6505 }
6506 return;
6507 }
6508
6509 static const CFStringRef *transientInterfaceEntityNames[] = {
6510 &kSCEntNetPPP,
6511 };
6512
6513
6514 static void
6515 CollectTransientServices(const void * key,
6516 const void * value,
6517 void * context)
6518 {
6519 #pragma unused(value)
6520 CFStringRef service = key;
6521 CFMutableArrayRef vif_setup_keys = context;
6522
6523 /* This service is either a vpn type service or a comm center service */
6524 if (!CFStringHasPrefix(service, kSCDynamicStoreDomainSetup)) {
6525 return;
6526 }
6527
6528 for (size_t i = 0; i < countof(transientInterfaceEntityNames); i++) {
6529 if (CFStringHasSuffix(service, *transientInterfaceEntityNames[i])) {
6530 my_CFArrayAppendUniqueValue(vif_setup_keys, service);
6531 break;
6532 }
6533 }
6534
6535 return;
6536 }
6537
6538
6539 static SCNetworkReachabilityFlags
6540 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info,
6541 CFStringRef service_id,
6542 CFStringRef entity,
6543 CFStringRef vpn_setup_key)
6544 {
6545 CFStringRef key;
6546 CFDictionaryRef dict;
6547 SCNetworkReachabilityFlags flags = 0;
6548
6549
6550 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6551 kSCDynamicStoreDomainSetup,
6552 service_id,
6553 kSCEntNetInterface);
6554 dict = CFDictionaryGetValue(services_info, key);
6555 CFRelease(key);
6556
6557 if (isA_CFDictionary(dict)
6558 && CFDictionaryContainsKey(dict, kSCPropNetInterfaceDeviceName)) {
6559
6560 flags = (kSCNetworkReachabilityFlagsReachable
6561 | kSCNetworkReachabilityFlagsTransientConnection
6562 | kSCNetworkReachabilityFlagsConnectionRequired);
6563
6564 if (CFEqual(entity, kSCEntNetPPP)) {
6565 CFNumberRef num;
6566 CFDictionaryRef p_dict = CFDictionaryGetValue(services_info, vpn_setup_key);
6567
6568 if (!isA_CFDictionary(p_dict)) {
6569 return (flags);
6570 }
6571
6572 // get PPP dial-on-traffic status
6573 num = CFDictionaryGetValue(p_dict, kSCPropNetPPPDialOnDemand);
6574 if (isA_CFNumber(num)) {
6575 int32_t ppp_demand;
6576
6577 if (CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand)) {
6578 if (ppp_demand) {
6579 flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
6580 }
6581 }
6582 }
6583 }
6584 }
6585 return (flags);
6586 }
6587
6588 static Boolean
6589 S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value)
6590 {
6591 Boolean ret = def_value;
6592
6593 if (dict != NULL) {
6594 CFBooleanRef val;
6595
6596 val = CFDictionaryGetValue(dict, key);
6597 if (isA_CFBoolean(val) != NULL) {
6598 ret = CFBooleanGetValue(val);
6599 }
6600 }
6601 return (ret);
6602 }
6603
6604
6605 static void
6606 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info,
6607 SCNetworkReachabilityFlags *reach_flags_v4,
6608 SCNetworkReachabilityFlags *reach_flags_v6)
6609 {
6610 CFIndex i;
6611 CFIndex count;
6612 CFMutableArrayRef vif_setup_keys;
6613
6614 vif_setup_keys = CFArrayCreateMutable(NULL,
6615 0,
6616 &kCFTypeArrayCallBacks);
6617 CFDictionaryApplyFunction(services_info, CollectTransientServices,
6618 vif_setup_keys);
6619 count = CFArrayGetCount(vif_setup_keys);
6620 for (i = 0; i < count; i++) {
6621 CFArrayRef components = NULL;
6622 CFStringRef entity;
6623 CFStringRef service_id;
6624 CFStringRef vif_setup_key;
6625
6626 vif_setup_key = CFArrayGetValueAtIndex(vif_setup_keys, i);
6627
6628 /*
6629 * setup key in the following format:
6630 * Setup:/Network/Service/<Service ID>/<Entity>
6631 */
6632 components = CFStringCreateArrayBySeparatingStrings(NULL, vif_setup_key, CFSTR("/"));
6633
6634 if (CFArrayGetCount(components) != 5) {
6635 // invalid Setup key encountered
6636 goto skip;
6637 }
6638
6639 /* service id is the 3rd element */
6640 service_id = CFArrayGetValueAtIndex(components, 3);
6641
6642 /* entity id is the 4th element */
6643 entity = CFArrayGetValueAtIndex(components, 4);
6644
6645
6646 if (CFEqual(entity, kSCEntNetPPP)) {
6647 SCNetworkReachabilityFlags flags;
6648 CFStringRef key;
6649
6650 flags = GetReachabilityFlagsFromVPN(services_info,
6651 service_id,
6652 entity,
6653 vif_setup_key);
6654
6655 /* Check for the v4 reachability flags */
6656 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6657 kSCDynamicStoreDomainSetup,
6658 service_id,
6659 kSCEntNetIPv4);
6660
6661 if (CFDictionaryContainsKey(services_info, key)) {
6662 *reach_flags_v4 |= flags;
6663 my_log(LOG_DEBUG, "Service %@ setting ipv4 reach flags: %d", service_id, *reach_flags_v4);
6664 }
6665
6666 CFRelease(key);
6667
6668 /* Check for the v6 reachability flags */
6669 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6670 kSCDynamicStoreDomainSetup,
6671 service_id,
6672 kSCEntNetIPv6);
6673
6674 if (CFDictionaryContainsKey(services_info, key)) {
6675 *reach_flags_v6 |= flags;
6676 my_log(LOG_DEBUG, "Service %@ setting ipv6 reach flags: %d", service_id, *reach_flags_v6);
6677 }
6678 CFRelease(key);
6679
6680 if (flags != 0) {
6681 if (components != NULL) {
6682 CFRelease(components);
6683 }
6684 goto done;
6685 }
6686 }
6687 skip:
6688 if (components != NULL) {
6689 CFRelease(components);
6690 }
6691 }
6692 done:
6693 CFRelease(vif_setup_keys);
6694 return;
6695 }
6696
6697 static SCNetworkReachabilityFlags
6698 GetReachFlagsFromStatus(CFStringRef entity, int status)
6699 {
6700 SCNetworkReachabilityFlags flags = 0;
6701
6702 if (CFEqual(entity, kSCEntNetPPP)) {
6703 switch (status) {
6704 case PPP_RUNNING :
6705 /* if we're really UP and RUNNING */
6706 break;
6707 case PPP_ONHOLD :
6708 /* if we're effectively UP and RUNNING */
6709 break;
6710 case PPP_IDLE :
6711 /* if we're not connected at all */
6712 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6713 break;
6714 case PPP_STATERESERVED :
6715 // if we're not connected at all
6716 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6717 break;
6718 default :
6719 /* if we're in the process of [dis]connecting */
6720 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6721 break;
6722 }
6723 }
6724 else if (CFEqual(entity, kSCEntNetIPSec)) {
6725 switch (status) {
6726 case IPSEC_RUNNING :
6727 /* if we're really UP and RUNNING */
6728 break;
6729 case IPSEC_IDLE :
6730 /* if we're not connected at all */
6731 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6732 break;
6733 default :
6734 /* if we're in the process of [dis]connecting */
6735 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6736 break;
6737 }
6738 }
6739 else if (CFEqual(entity, kSCEntNetVPN)) {
6740 switch (status) {
6741 case VPN_RUNNING :
6742 /* if we're really UP and RUNNING */
6743 break;
6744 case VPN_IDLE :
6745 case VPN_LOADING :
6746 case VPN_LOADED :
6747 case VPN_UNLOADING :
6748 /* if we're not connected at all */
6749 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6750 break;
6751 default :
6752 /* if we're in the process of [dis]connecting */
6753 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6754 break;
6755 }
6756 }
6757 return (flags);
6758 }
6759
6760 static void
6761 VPNAttributesGet(CFStringRef service_id,
6762 CFDictionaryRef services_info,
6763 SCNetworkReachabilityFlags *flags,
6764 CFStringRef *server_address,
6765 int af)
6766 {
6767 CFDictionaryRef entity_dict;
6768 CFNumberRef num;
6769 CFDictionaryRef p_state = NULL;
6770 int status = 0;
6771 CFStringRef transient_entity = NULL;
6772
6773 if (af == AF_INET) {
6774 entity_dict = service_dict_get(service_id, kSCEntNetIPv4);
6775 } else {
6776 entity_dict = service_dict_get(service_id, kSCEntNetIPv6);
6777 }
6778 entity_dict = ipdict_get_service(entity_dict);
6779 if (entity_dict == NULL) {
6780 return;
6781 }
6782
6783 for (size_t i = 0; i < countof(transientServiceInfo); i++) {
6784 CFStringRef entity = *transientServiceInfo[i].entityName;
6785
6786 p_state = service_dict_get(service_id, entity);
6787
6788 /* ensure that this is a VPN Type service */
6789 if (isA_CFDictionary(p_state)) {
6790 transient_entity = entity;
6791 break;
6792 }
6793 }
6794
6795 /* Did we find a vpn type service? If not, we are done.*/
6796 if (transient_entity == NULL) {
6797 return;
6798 }
6799
6800 *flags |= (kSCNetworkReachabilityFlagsReachable
6801 | kSCNetworkReachabilityFlagsTransientConnection);
6802
6803 /* Get the Server Address */
6804 if (server_address != NULL) {
6805 *server_address = CFDictionaryGetValue(entity_dict,
6806 CFSTR("ServerAddress"));
6807 *server_address = isA_CFString(*server_address);
6808 if (*server_address != NULL) {
6809 CFRetain(*server_address);
6810 }
6811 }
6812
6813 /* get status */
6814 if (!CFDictionaryGetValueIfPresent(p_state,
6815 kSCPropNetVPNStatus, // IPSecStatus, PPPStatus, VPNStatus
6816 (const void **)&num) ||
6817 !isA_CFNumber(num) ||
6818 !CFNumberGetValue(num, kCFNumberIntType, &status)) {
6819 return;
6820 }
6821
6822 *flags |= GetReachFlagsFromStatus(transient_entity, status);
6823 if (CFEqual(transient_entity, kSCEntNetPPP)) {
6824 CFStringRef key;
6825 CFDictionaryRef p_setup;
6826 int ppp_demand;
6827
6828 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6829 kSCDynamicStoreDomainSetup,
6830 service_id,
6831 kSCEntNetPPP);
6832 p_setup = CFDictionaryGetValue(services_info, key);
6833 CFRelease(key);
6834
6835 /* get dial-on-traffic status */
6836 if (isA_CFDictionary(p_setup) &&
6837 CFDictionaryGetValueIfPresent(p_setup,
6838 kSCPropNetPPPDialOnDemand,
6839 (const void **)&num) &&
6840 isA_CFNumber(num) &&
6841 CFNumberGetValue(num, kCFNumberIntType, &ppp_demand) &&
6842 (ppp_demand != 0)) {
6843 *flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
6844 if (status == PPP_IDLE) {
6845 *flags |= kSCNetworkReachabilityFlagsInterventionRequired;
6846 }
6847 }
6848 }
6849 return;
6850 }
6851
6852
6853 typedef struct ElectionInfo {
6854 int af;
6855 CFStringRef entity;
6856 int n_services;
6857 CFArrayRef order;
6858 CFIndex n_order;
6859 ElectionResultsRef results;
6860 CFMutableDictionaryRef rank_dict;
6861 } ElectionInfo, * ElectionInfoRef;
6862
6863 typedef CFDictionaryApplierFunction ElectionFuncRef;
6864
6865 static void
6866 CandidateRelease(CandidateRef candidate)
6867 {
6868 my_CFRelease(&candidate->serviceID);
6869 my_CFRelease(&candidate->if_name);
6870 my_CFRelease(&candidate->signature);
6871 return;
6872 }
6873
6874 static void
6875 CandidateCopy(CandidateRef dest, CandidateRef src)
6876 {
6877 *dest = *src;
6878 if (dest->serviceID) {
6879 CFRetain(dest->serviceID);
6880 }
6881 if (dest->if_name) {
6882 CFRetain(dest->if_name);
6883 }
6884 if(dest->signature) {
6885 CFRetain(dest->signature);
6886 }
6887 return;
6888 }
6889
6890 static ElectionResultsRef
6891 ElectionResultsAlloc(int af, int size)
6892 {
6893 ElectionResultsRef results;
6894
6895 results = (ElectionResultsRef)malloc(ElectionResultsComputeSize(size));
6896 results->af = af;
6897 results->count = 0;
6898 results->size = size;
6899 return (results);
6900 }
6901
6902 static void
6903 ElectionResultsRelease(ElectionResultsRef results)
6904 {
6905 int i;
6906 CandidateRef scan;
6907
6908 for (i = 0, scan = results->candidates;
6909 i < results->count;
6910 i++, scan++) {
6911 CandidateRelease(scan);
6912 }
6913 free(results);
6914 return;
6915 }
6916
6917 static void
6918 ElectionResultsLog(int level, ElectionResultsRef results, const char * prefix)
6919 {
6920 int i;
6921 CandidateRef scan;
6922
6923 if (results == NULL) {
6924 my_log(level, "%s: no candidates", prefix);
6925 return;
6926 }
6927 my_log(level, "%s: %d candidates", prefix, results->count);
6928 for (i = 0, scan = results->candidates;
6929 i < results->count;
6930 i++, scan++) {
6931 char ntopbuf[INET6_ADDRSTRLEN];
6932
6933 (void)inet_ntop(results->af, &scan->addr, ntopbuf, sizeof(ntopbuf));
6934 my_log(level, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6935 i, scan->if_name, scan->serviceID, ntopbuf, scan->rank,
6936 scan->ineligible ? " [ineligible]" : "");
6937 }
6938 return;
6939 }
6940
6941 /*
6942 * Function: ElectionResultsAddCandidate
6943 * Purpose:
6944 * Add the candidate into the election results. Find the insertion point
6945 * by comparing the rank of the candidate with existing entries.
6946 */
6947 static void
6948 ElectionResultsAddCandidate(ElectionResultsRef results, CandidateRef candidate)
6949 {
6950 CFIndex i;
6951 CFIndex where;
6952
6953 if (results->count == results->size) {
6954 /* this should not happen */
6955 my_log(LOG_NOTICE, "can't fit another candidate");
6956 return;
6957 }
6958
6959 /* find the insertion point */
6960 where = kCFNotFound;
6961 for (i = 0; i < results->count; i++) {
6962 CandidateRef this_candidate = results->candidates + i;
6963
6964 if (candidate->rank < this_candidate->rank) {
6965 where = i;
6966 break;
6967 }
6968 }
6969 /* add it to the end */
6970 if (where == kCFNotFound) {
6971 CandidateCopy(results->candidates + results->count, candidate);
6972 results->count++;
6973 return;
6974 }
6975 /* slide existing entries over */
6976 for (i = results->count; i > where; i--) {
6977 results->candidates[i] = results->candidates[i - 1];
6978 }
6979 /* insert element */
6980 CandidateCopy(results->candidates + where, candidate);
6981 results->count++;
6982 return;
6983 }
6984
6985 static void
6986 elect_ip(const void * key, const void * value, void * context);
6987
6988 /*
6989 * Function: ElectionResultsCopy
6990 * Purpose:
6991 * Visit all of the services and invoke the protocol-specific election
6992 * function. Return the results of the election.
6993 */
6994 static ElectionResultsRef
6995 ElectionResultsCopy(int af, CFArrayRef order)
6996 {
6997 int count;
6998 ElectionInfo info;
6999
7000 count = (int)CFDictionaryGetCount(S_service_state_dict);
7001 if (count == 0) {
7002 return (NULL);
7003 }
7004 info.af = af;
7005 if (af == AF_INET) {
7006 info.entity = kSCEntNetIPv4;
7007 info.rank_dict = S_ipv4_service_rank_dict;
7008 }
7009 else {
7010 info.entity = kSCEntNetIPv6;
7011 info.rank_dict = S_ipv6_service_rank_dict;
7012 }
7013 info.results = ElectionResultsAlloc(af, count);
7014 info.n_services = count;
7015 info.order = order;
7016 if (order != NULL) {
7017 info.n_order = CFArrayGetCount(order);
7018 }
7019 else {
7020 info.order = 0;
7021 }
7022 CFDictionaryApplyFunction(S_service_state_dict, elect_ip, (void *)&info);
7023 if (info.results->count == 0) {
7024 ElectionResultsRelease(info.results);
7025 info.results = NULL;
7026 }
7027 return (info.results);
7028 }
7029
7030 /*
7031 * Function: ElectionResultsCandidateNeedsDemotion
7032 * Purpose:
7033 * Check whether the given candidate requires demotion. A candidate
7034 * might need to be demoted if its IPv4 and IPv6 services must be coupled
7035 * but a higher ranked service has IPv4 or IPv6.
7036 *
7037 * The converse is also true: if the given candidate has lower rank than
7038 * the other candidate and the other candidate is coupled, this candidate
7039 * needs to be demoted.
7040 */
7041 static Boolean
7042 ElectionResultsCandidateNeedsDemotion(CandidateRef other_candidate,
7043 CandidateRef candidate)
7044 {
7045 Boolean ret = FALSE;
7046
7047 if (other_candidate == NULL) {
7048 /* no other candidate */
7049 goto done;
7050 }
7051 if (other_candidate->ineligible) {
7052 /* other candidate can't become primary */
7053 goto done;
7054 }
7055 if (RANK_ASSERTION_MASK(other_candidate->rank) == kRankAssertionNever) {
7056 /* the other candidate can't become primary */
7057 goto done;
7058 }
7059 if (!candidate->ip_is_coupled && !other_candidate->ip_is_coupled) {
7060 /* neither candidate is coupled */
7061 goto done;
7062 }
7063 if (CFEqual(other_candidate->if_name, candidate->if_name)) {
7064 /* they are over the same interface, no need to demote */
7065 goto done;
7066 }
7067 if (CFStringHasPrefix(other_candidate->if_name, CFSTR("stf"))) {
7068 /* avoid creating a feedback loop */
7069 goto done;
7070 }
7071 if (candidate->rank < other_candidate->rank) {
7072 /* we're higher ranked than the other candidate, ignore */
7073 goto done;
7074 }
7075 if (candidate->ip_is_coupled) {
7076 if (other_candidate->ip_is_coupled
7077 && candidate->rank == other_candidate->rank) {
7078 /* same rank as another service that is coupled, ignore */
7079 goto done;
7080 }
7081 }
7082 else if (other_candidate->ip_is_coupled) { /* must be true */
7083 if (candidate->rank == other_candidate->rank) {
7084 /* other candidate will be demoted, so we don't need to */
7085 goto done;
7086 }
7087 /* we're lower rank and need to be demoted */
7088 }
7089 else { /* can't happen, we already tested for this above */
7090 /* neither candidate is coupled */
7091 goto done;
7092 }
7093 ret = TRUE;
7094
7095 done:
7096 return (ret);
7097
7098 }
7099
7100
7101 static void
7102 get_signature_sha256(CFStringRef signature,
7103 unsigned char * sha256)
7104 {
7105 CC_SHA256_CTX ctx;
7106 CFDataRef signature_data;
7107
7108 signature_data = CFStringCreateExternalRepresentation(NULL,
7109 signature,
7110 kCFStringEncodingUTF8,
7111 0);
7112
7113 CC_SHA256_Init(&ctx);
7114 CC_SHA256_Update(&ctx,
7115 CFDataGetBytePtr(signature_data),
7116 (CC_LONG)CFDataGetLength(signature_data));
7117 CC_SHA256_Final(sha256, &ctx);
7118
7119 CFRelease(signature_data);
7120
7121 return;
7122 }
7123
7124
7125 static void
7126 add_candidate_to_nwi_state(nwi_state_t nwi_state, int af,
7127 CandidateRef candidate, Boolean not_in_list,
7128 Boolean not_in_iflist)
7129 {
7130 uint64_t flags = 0;
7131 char ifname[IFNAMSIZ];
7132 nwi_ifstate_t ifstate;
7133
7134 if (nwi_state == NULL) {
7135 /* can't happen */
7136 return;
7137 }
7138 if (not_in_list
7139 || RANK_ASSERTION_MASK(candidate->rank) == kRankAssertionNever) {
7140 flags |= NWI_IFSTATE_FLAGS_NOT_IN_LIST;
7141 }
7142 if (not_in_iflist) {
7143 flags |= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST;
7144 }
7145 if (service_dict_get(candidate->serviceID, kSCEntNetDNS) != NULL) {
7146 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
7147 }
7148 if ((af == AF_INET) && service_has_clat46_address(candidate->serviceID)) {
7149 flags |= NWI_IFSTATE_FLAGS_HAS_CLAT46;
7150 }
7151 CFStringGetCString(candidate->if_name, ifname, sizeof(ifname),
7152 kCFStringEncodingASCII);
7153 if ((S_IPMonitor_debug & kDebugFlag2) != 0) {
7154 char ntopbuf[INET6_ADDRSTRLEN];
7155
7156 (void)inet_ntop(af, &candidate->addr, ntopbuf, sizeof(ntopbuf));
7157 my_log(LOG_DEBUG,
7158 "Adding IPv%c [%s] %s "
7159 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
7160 ipvx_char(af), ifname, ntopbuf,
7161 flags, candidate->rank, candidate->reachability_flags);
7162 }
7163 ifstate = nwi_state_add_ifstate(nwi_state, ifname, af, flags,
7164 candidate->rank,
7165 (void *)&candidate->addr,
7166 (void *)&candidate->vpn_server_addr,
7167 candidate->reachability_flags);
7168 if (ifstate != NULL && candidate->signature) {
7169 uint8_t hash[CC_SHA256_DIGEST_LENGTH];
7170
7171 get_signature_sha256(candidate->signature, hash);
7172 nwi_ifstate_set_signature(ifstate, hash);
7173 }
7174 return;
7175 }
7176
7177
7178 static void
7179 add_reachability_flags_to_candidate(CandidateRef candidate, CFDictionaryRef services_info, int af)
7180 {
7181 SCNetworkReachabilityFlags flags = kSCNetworkReachabilityFlagsReachable;
7182 CFStringRef vpn_server_address = NULL;
7183
7184 assert(candidate != NULL);
7185 assert(services_info != NULL);
7186
7187 VPNAttributesGet(candidate->serviceID,
7188 services_info,
7189 &flags,
7190 &vpn_server_address,
7191 af);
7192
7193 candidate->reachability_flags = flags;
7194
7195 if (vpn_server_address == NULL) {
7196 memset(&candidate->vpn_server_addr, 0, sizeof(candidate->vpn_server_addr));
7197 } else {
7198 char buf[128];
7199
7200 CFStringGetCString(vpn_server_address, buf, sizeof(buf),
7201 kCFStringEncodingASCII);
7202 _SC_string_to_sockaddr(buf,
7203 AF_UNSPEC,
7204 (void *)&candidate->vpn_server_addr,
7205 sizeof(candidate->vpn_server_addr));
7206
7207 CFRelease(vpn_server_address);
7208 }
7209 return;
7210 }
7211 /*
7212 * Function: ElectionResultsGetPrimary
7213 * Purpose:
7214 * Use the results of the current protocol and the other protocol to
7215 * determine which service should become primary.
7216 *
7217 * At the same time, generate the IPv4/IPv6 routing table and
7218 * the nwi_state for the protocol.
7219 */
7220 static CandidateRef
7221 ElectionResultsGetPrimary(ElectionResultsRef results,
7222 CandidateRef other_candidate,
7223 nwi_state_t nwi_state, int af,
7224 RouteListRef * ret_routes,
7225 CFDictionaryRef services_info,
7226 CFSetRef ip_service_changes)
7227 {
7228 CandidateRef primary = NULL;
7229 Boolean primary_is_null = FALSE;
7230 RouteListRef routes = NULL;
7231
7232 assert(services_info != NULL);
7233
7234 if (results != NULL) {
7235 CandidateRef deferred[results->count];
7236 int deferred_count;
7237 CFStringRef entity_name;
7238 int i;
7239 int initial_size;
7240 RouteListInfoRef info;
7241 CandidateRef scan;
7242
7243 switch (af) {
7244 case AF_INET:
7245 entity_name = kSCEntNetIPv4;
7246 info = &IPv4RouteListInfo;
7247 initial_size = results->count * IPV4_ROUTES_N_STATIC;
7248 break;
7249 default:
7250 case AF_INET6:
7251 entity_name = kSCEntNetIPv6;
7252 info = &IPv6RouteListInfo;
7253 initial_size = results->count * IPV6_ROUTES_N_STATIC;
7254 break;
7255 }
7256 deferred_count = 0;
7257 for (i = 0, scan = results->candidates;
7258 i < results->count;
7259 i++, scan++) {
7260 Boolean is_primary = FALSE;
7261 CFDictionaryRef service_dict;
7262 RouteListRef service_routes;
7263 Boolean skip = FALSE;
7264
7265 if (!scan->ineligible
7266 && primary == NULL
7267 && RANK_ASSERTION_MASK(scan->rank) != kRankAssertionNever) {
7268 if (ElectionResultsCandidateNeedsDemotion(other_candidate,
7269 scan)) {
7270 /* demote the service */
7271 my_log(LOG_NOTICE,
7272 "IPv%c over %@ (rank 0x%x) demoted: "
7273 "primary IPv%c %@ (rank 0x%x)",
7274 ipvx_char(af), scan->if_name, scan->rank,
7275 ipvx_other_char(af), other_candidate->if_name,
7276 other_candidate->rank);
7277 deferred[deferred_count++] = scan;
7278 skip = TRUE;
7279 }
7280 else {
7281 primary = scan;
7282 is_primary = TRUE;
7283 }
7284 }
7285 /* contribute to the routing table */
7286 service_dict = service_dict_get(scan->serviceID, entity_name);
7287 service_routes = ipdict_get_routelist(service_dict);
7288 if (service_routes != NULL) {
7289 boolean_t force;
7290 Rank rank = scan->rank;
7291
7292 if (skip) {
7293 /* routes are RankNever to prevent becoming primary */
7294 rank = RankMake(rank, kRankAssertionNever);
7295 }
7296 force = my_CFSetContainsValue(ip_service_changes,
7297 scan->serviceID);
7298 routes = RouteListAddRouteList(info, routes, initial_size,
7299 service_routes, rank, force);
7300 if ((service_routes->flags & kRouteListFlagsExcludeNWI) != 0) {
7301 skip = TRUE;
7302 }
7303 }
7304 else {
7305 skip = TRUE;
7306 }
7307 if (skip) {
7308 /* if we're skipping the primary, it's NULL */
7309 if (is_primary) {
7310 primary_is_null = TRUE;
7311 }
7312 }
7313 else if (!scan->ineligible) {
7314 Boolean not_in_iflist;
7315
7316 add_reachability_flags_to_candidate(scan, services_info, af);
7317 not_in_iflist
7318 = (service_routes->flags & kRouteListFlagsScopedOnly) != 0;
7319 add_candidate_to_nwi_state(nwi_state, af, scan,
7320 primary_is_null,
7321 not_in_iflist);
7322 }
7323 }
7324 for (i = 0; i < deferred_count; i++) {
7325 CandidateRef candidate = deferred[i];
7326
7327 add_reachability_flags_to_candidate(candidate, services_info, af);
7328 add_candidate_to_nwi_state(nwi_state, af, candidate, TRUE, FALSE);
7329 }
7330 }
7331 if (ret_routes != NULL) {
7332 *ret_routes = routes;
7333 }
7334 else if (routes != NULL) {
7335 free(routes);
7336 }
7337 if (primary_is_null) {
7338 primary = NULL;
7339 }
7340 return (primary);
7341 }
7342
7343
7344 static inline
7345 CFStringRef
7346 service_dict_get_signature(CFDictionaryRef service_dict)
7347 {
7348 CFStringRef ifname;
7349
7350 ifname = CFDictionaryGetValue(service_dict, kSCPropInterfaceName);
7351 if (isA_CFString(ifname) == NULL
7352 || !confirm_interface_name(service_dict, ifname)) {
7353 return (NULL);
7354 }
7355 return (CFDictionaryGetValue(service_dict, kStoreKeyNetworkSignature));
7356 }
7357
7358 /*
7359 * Function: elect_ip
7360 * Purpose:
7361 * Evaluate the service and determine what rank the service should have.
7362 * If it's a suitable candidate, add it to the election results.
7363 */
7364 static void
7365 elect_ip(const void * key, const void * value, void * context)
7366 {
7367 CFDictionaryRef all_entities_dict = (CFDictionaryRef)value;
7368 Candidate candidate;
7369 Rank default_rank;
7370 ElectionInfoRef elect_info;
7371 CFStringRef if_name;
7372 CFDictionaryRef ipdict;
7373 Rank primary_rank;
7374 CFDictionaryRef rank_entity;
7375 RouteListUnion routelist;
7376 CFDictionaryRef service_dict;
7377
7378 elect_info = (ElectionInfoRef)context;
7379 ipdict = CFDictionaryGetValue(all_entities_dict, elect_info->entity);
7380 if (ipdict != NULL) {
7381 routelist.ptr = ipdict_get_routelist(ipdict);
7382 service_dict = ipdict_get_service(ipdict);
7383 }
7384 else {
7385 routelist.ptr = NULL;
7386 }
7387 if (routelist.ptr == NULL || service_dict == NULL) {
7388 /* no connectivity */
7389 return;
7390 }
7391 if_name = CFDictionaryGetValue(service_dict, kSCPropInterfaceName);
7392 if (if_name == NULL) {
7393 /* need an interface name */
7394 return;
7395 }
7396 if (CFEqual(if_name, CFSTR(kLoopbackInterface))) {
7397 /* don't process loopback */
7398 return;
7399 }
7400 memset(&candidate, 0, sizeof(candidate));
7401 candidate.serviceID = (CFStringRef)key;
7402 if ((routelist.common->flags & kRouteListFlagsHasDefault) == 0) {
7403 /* no default route means it's ineligible to become primary */
7404 candidate.ineligible = TRUE;
7405 }
7406 rank_entity = CFDictionaryGetValue(all_entities_dict, kSCEntNetService);
7407 candidate.rank = get_service_index(rank_entity,
7408 elect_info->order, elect_info->n_order,
7409 candidate.serviceID);
7410 if (elect_info->af == AF_INET) {
7411 default_rank = routelist.v4->list->rank;
7412 candidate.addr.v4 = routelist.v4->list->ifa;
7413 }
7414 else {
7415 default_rank = routelist.v6->list->rank;
7416 candidate.addr.v6 = routelist.v6->list->ifa;
7417 }
7418 primary_rank = RANK_ASSERTION_MASK(default_rank);
7419 if (S_ppp_override_primary) {
7420 char ifn[IFNAMSIZ];
7421
7422 if (CFStringGetCString(if_name, ifn, sizeof(ifn),
7423 kCFStringEncodingASCII)
7424 && (strncmp(PPP_PREFIX, ifn, sizeof(PPP_PREFIX) - 1) == 0)) {
7425 /* PPP override: make ppp* look the best */
7426 primary_rank = kRankAssertionFirst;
7427 }
7428 }
7429 candidate.rank = RankMake(candidate.rank, primary_rank);
7430 candidate.ip_is_coupled = service_get_ip_is_coupled(candidate.serviceID);
7431 candidate.if_name = if_name;
7432 rank_dict_set_service_rank(elect_info->rank_dict,
7433 candidate.serviceID, candidate.rank);
7434 candidate.signature = service_dict_get_signature(service_dict);
7435 ElectionResultsAddCandidate(elect_info->results, &candidate);
7436 return;
7437 }
7438
7439
7440 static uint32_t
7441 service_changed(CFDictionaryRef services_info, CFStringRef serviceID)
7442 {
7443 uint32_t changed = 0;
7444 int i;
7445
7446 /* update service options first (e.g. rank) */
7447 if (get_rank_changes(serviceID,
7448 get_service_state_entity(services_info, serviceID,
7449 NULL),
7450 get_service_setup_entity(services_info, serviceID,
7451 NULL),
7452 services_info)) {
7453 changed |= (1 << kEntityTypeServiceOptions);
7454 }
7455
7456 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7457 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
7458 GetEntityChangesFuncRef func;
7459 CFStringRef name;
7460
7461 func = entityChangeFunc[i];
7462 name = *entityTypeNames[i];
7463 if ((*func)(serviceID,
7464 get_service_state_entity(services_info, serviceID, name),
7465 get_service_setup_entity(services_info, serviceID, name),
7466 services_info)) {
7467 changed |= (1 << i);
7468 }
7469 }
7470
7471 /* update transient service status */
7472 if (get_transient_status_changes(serviceID, services_info)) {
7473 changed |= (1 << kEntityTypeTransientStatus);
7474 }
7475
7476 return (changed);
7477 }
7478
7479 static CFStringRef
7480 serviceID_get_ifname(CFStringRef serviceID)
7481 {
7482 CFDictionaryRef entity_dict;
7483 CFStringRef ifname = NULL;
7484
7485 entity_dict = service_dict_get(serviceID, kSCEntNetIPv4);
7486 if (entity_dict == NULL) {
7487 entity_dict = service_dict_get(serviceID, kSCEntNetIPv6);
7488 }
7489 if (entity_dict != NULL) {
7490 ifname = ipdict_get_ifname(entity_dict);
7491 }
7492 return (ifname);
7493 }
7494
7495 __private_extern__ boolean_t
7496 check_if_service_expensive(CFStringRef serviceID)
7497 {
7498 CFStringRef ifname;
7499 ifname = serviceID_get_ifname(serviceID);
7500
7501 return interface_is_expensive(ifname);
7502 }
7503
7504 static CFArrayRef
7505 service_order_get(CFDictionaryRef services_info)
7506 {
7507 CFArrayRef order = NULL;
7508 CFDictionaryRef ipv4_dict;
7509
7510 ipv4_dict = my_CFDictionaryGetDictionary(services_info,
7511 S_setup_global_ipv4);
7512 if (ipv4_dict != NULL) {
7513 CFNumberRef ppp_override;
7514 int ppp_val = 0;
7515
7516 order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
7517 order = isA_CFArray(order);
7518
7519 /* get ppp override primary */
7520 ppp_override = CFDictionaryGetValue(ipv4_dict,
7521 kSCPropNetPPPOverridePrimary);
7522 ppp_override = isA_CFNumber(ppp_override);
7523 if (ppp_override != NULL) {
7524 CFNumberGetValue(ppp_override, kCFNumberIntType, &ppp_val);
7525 }
7526 S_ppp_override_primary = (ppp_val != 0) ? TRUE : FALSE;
7527 }
7528 else {
7529 S_ppp_override_primary = FALSE;
7530 }
7531 return (order);
7532 }
7533
7534 static boolean_t
7535 set_new_primary(CFStringRef * primary_p, CFStringRef new_primary,
7536 const char * entity)
7537 {
7538 boolean_t changed = FALSE;
7539 CFStringRef primary = *primary_p;
7540
7541 if (new_primary != NULL) {
7542 if (primary != NULL && CFEqual(new_primary, primary)) {
7543 my_log(LOG_INFO, "%@ is still primary %s", new_primary, entity);
7544 }
7545 else {
7546 my_CFRelease(primary_p);
7547 *primary_p = CFRetain(new_primary);
7548 my_log(LOG_INFO, "%@ is the new primary %s", new_primary, entity);
7549 changed = TRUE;
7550 }
7551 }
7552 else if (primary != NULL) {
7553 my_log(LOG_INFO, "%@ is no longer primary %s", primary, entity);
7554 my_CFRelease(primary_p);
7555 changed = TRUE;
7556 }
7557 return (changed);
7558 }
7559
7560 static Rank
7561 rank_service_entity(CFDictionaryRef rank_dict, CFStringRef serviceID,
7562 CFStringRef entity)
7563 {
7564 if (service_dict_get(serviceID, entity) == NULL) {
7565 return (RankMake(kRankIndexMask, kRankAssertionDefault));
7566 }
7567 return (rank_dict_get_service_rank(rank_dict, serviceID));
7568 }
7569
7570 static void
7571 append_serviceIDs_for_interface(CFMutableArrayRef services_changed,
7572 CFStringRef ifname)
7573 {
7574 CFIndex count;
7575 CFIndex i;
7576 void * * keys;
7577 #define N_KEYS_VALUES_STATIC 10
7578 void * keys_values_buf[N_KEYS_VALUES_STATIC * 2];
7579 void * * values;
7580
7581 count = CFDictionaryGetCount(S_service_state_dict);
7582 if (count <= N_KEYS_VALUES_STATIC) {
7583 keys = keys_values_buf;
7584 } else {
7585 keys = (void * *)malloc(sizeof(*keys) * count * 2);
7586 }
7587 values = keys + count;
7588 CFDictionaryGetKeysAndValues(S_service_state_dict,
7589 (const void * *)keys,
7590 (const void * *)values);
7591
7592 for (i = 0; i < count; i++) {
7593 CFDictionaryRef ipdict = NULL;
7594 CFStringRef interface = NULL;
7595 CFStringRef serviceID;
7596 CFDictionaryRef service_dict;
7597
7598 serviceID = (CFStringRef)keys[i];
7599 service_dict = (CFDictionaryRef)values[i];
7600
7601 /* check whether service has IPv4 or IPv6 */
7602 ipdict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
7603 if (ipdict == NULL) {
7604 ipdict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
7605 if (ipdict == NULL) {
7606 continue;
7607 }
7608 }
7609 interface = ipdict_get_ifname(ipdict);
7610 if (interface != NULL && CFEqual(interface, ifname)) {
7611 my_log(LOG_DEBUG,
7612 "Found IP service %@ on interface %@",
7613 serviceID, ifname);
7614 my_CFArrayAppendUniqueValue(services_changed, serviceID);
7615 }
7616 }
7617 if (keys != keys_values_buf) {
7618 free(keys);
7619 }
7620 return;
7621 }
7622
7623 static __inline__ const char *
7624 get_changed_str(CFStringRef serviceID, CFStringRef entity,
7625 CFDictionaryRef old_dict)
7626 {
7627 CFDictionaryRef new_dict = NULL;
7628
7629 if (serviceID != NULL) {
7630 new_dict = service_dict_get(serviceID, entity);
7631 }
7632
7633 if (old_dict == NULL) {
7634 if (new_dict != NULL) {
7635 return "+";
7636 }
7637 } else {
7638 if (new_dict == NULL) {
7639 return "-";
7640 } else if (!CFEqual(old_dict, new_dict)) {
7641 return "!";
7642 }
7643 }
7644 return "";
7645 }
7646
7647 #if !TARGET_OS_SIMULATOR
7648
7649 #ifdef SIOCSIFORDER
7650 #define MANAGE_IF_ORDER
7651 #define MANAGE_IF_IOCTL
7652 #endif /* SIOCSIFORDER */
7653
7654 #ifdef SIOCSIFNETSIGNATURE
7655 #define MANAGE_IF_SIGNATURE
7656 #define MANAGE_IF_IOCTL
7657 #endif /* SIOCSIFNETSIGNATURE */
7658
7659 #ifdef MANAGE_IF_IOCTL
7660 static int
7661 inet_dgram_socket(void)
7662 {
7663 int sockfd;
7664
7665 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
7666 if (sockfd == -1) {
7667 my_log(LOG_ERR, "socket() failed: %s", strerror(errno));
7668 }
7669
7670 return sockfd;
7671 }
7672 #endif /* MANAGE_IF_IOCTL */
7673
7674 #ifdef MANAGE_IF_ORDER
7675 static Boolean
7676 interface_order_changed(nwi_state_t old_state, nwi_state_t new_state)
7677 {
7678 if (old_state == NULL && new_state == NULL) {
7679 // Both are NULL, nothing changed
7680 return FALSE;
7681 }
7682
7683 if (old_state == NULL || new_state == NULL) {
7684 // One is NULL, something changed
7685 return TRUE;
7686 }
7687
7688 if (old_state->if_list_count != new_state->if_list_count) {
7689 // Count is different, something changed
7690 return TRUE;
7691 }
7692
7693 if (new_state->if_list_count == 0) {
7694 // Count is same and 0, nothing changed
7695 return FALSE;
7696 }
7697
7698 int i;
7699 nwi_ifindex_t *old_scan;
7700 nwi_ifindex_t *new_scan;
7701 for (i = 0, old_scan = nwi_state_if_list(old_state), new_scan = nwi_state_if_list(new_state);
7702 i < new_state->if_list_count; i++, old_scan++, new_scan++) {
7703 if (strcmp(old_state->ifstate_list[*old_scan].ifname, new_state->ifstate_list[*new_scan].ifname) != 0) {
7704 // Some interface in the list is different, something changed
7705 return TRUE;
7706 }
7707 }
7708
7709 // Count and contents are the same, nothing changed
7710 return FALSE;
7711 }
7712
7713 static Boolean
7714 update_interface_order(nwi_state_t state, int sockfd)
7715 {
7716 Boolean success = FALSE;
7717
7718 // Set interface order into the kernel
7719 struct if_order interface_order;
7720 interface_order.ifo_count = (uint32_t)state->if_list_count;
7721 interface_order.ifo_ordered_indices = (mach_vm_address_t)calloc((size_t)interface_order.ifo_count, sizeof(uint32_t));
7722 if (((uint32_t *)interface_order.ifo_ordered_indices) != NULL) {
7723 int i;
7724 nwi_ifindex_t *scan;
7725 for (i = 0, scan = nwi_state_if_list(state);
7726 i < state->if_list_count; i++, scan++) {
7727 const char *ifname = state->ifstate_list[*scan].ifname;
7728 ((uint32_t *)interface_order.ifo_ordered_indices)[i] = my_if_nametoindex(ifname);
7729 }
7730 }
7731 if (ioctl(sockfd, SIOCSIFORDER, &interface_order) != 0) {
7732 my_log(LOG_ERR, "SIOCSIFORDER for %u(%p) failed on %d: %s", interface_order.ifo_count, (void *)interface_order.ifo_ordered_indices, sockfd, strerror(errno));
7733 } else {
7734 my_log(LOG_INFO, "Set kernel interface order for %u interfaces", interface_order.ifo_count);
7735 success = TRUE;
7736 }
7737 if (((uint32_t *)interface_order.ifo_ordered_indices) != NULL) {
7738 free((void *)interface_order.ifo_ordered_indices);
7739 interface_order.ifo_ordered_indices = (mach_vm_address_t)NULL;
7740 }
7741
7742 return success;
7743 }
7744 #endif /* MANAGE_IF_ORDER */
7745
7746 #ifdef MANAGE_IF_SIGNATURE
7747 static int
7748 siocsifnetsignature(int s, const char * ifname, int af,
7749 const uint8_t * signature, size_t signature_length)
7750 {
7751 struct if_nsreq nsreq;
7752
7753 memset(&nsreq, 0, sizeof(nsreq));
7754 strlcpy(nsreq.ifnsr_name, ifname, sizeof(nsreq.ifnsr_name));
7755 nsreq.ifnsr_family = af;
7756 if (signature_length > 0) {
7757 if (signature_length > sizeof(nsreq.ifnsr_data)) {
7758 signature_length = sizeof(nsreq.ifnsr_data);
7759 }
7760 nsreq.ifnsr_len = signature_length;
7761 memcpy(nsreq.ifnsr_data, signature, signature_length);
7762 }
7763 return (ioctl(s, SIOCSIFNETSIGNATURE, &nsreq));
7764 }
7765
7766 static void
7767 process_ifstate_difference(nwi_ifstate_t ifstate, int af, int sockfd)
7768 {
7769 nwi_ifstate_difference_t diff;
7770 boolean_t set_signature = FALSE;
7771 int signature_length = 0;
7772
7773 diff = nwi_ifstate_get_difference(ifstate);
7774 switch (diff) {
7775 case knwi_ifstate_difference_changed:
7776 /* set signature for this interface */
7777 set_signature = TRUE;
7778 if ((ifstate->flags & NWI_IFSTATE_FLAGS_HAS_SIGNATURE) != 0) {
7779 signature_length = sizeof(ifstate->signature);
7780 }
7781 break;
7782 case knwi_ifstate_difference_removed:
7783 /* remove signature for this interface */
7784 set_signature = TRUE;
7785 break;
7786 default:
7787 break;
7788 }
7789 if (set_signature) {
7790 if (siocsifnetsignature(sockfd, ifstate->ifname, af,
7791 ifstate->signature,
7792 signature_length) < 0) {
7793 my_log(LOG_ERR,
7794 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7795 ifstate->ifname, ipvx_char(af),
7796 signature_length,
7797 strerror(errno));
7798 }
7799 else {
7800 my_log(LOG_DEBUG, "IPv%c Network Signature %s %s",
7801 ipvx_char(af),
7802 (signature_length > 0) ? "Set" : "Cleared",
7803 ifstate->ifname);
7804 if (signature_length > 0
7805 && (S_IPMonitor_debug & kDebugFlag1) != 0) {
7806 int i;
7807 char sig_buf[signature_length * 3 + 1];
7808
7809 sig_buf[0] = '\0';
7810 for (i = 0; i < signature_length; i++) {
7811 char byte_buf[4];
7812
7813 snprintf(byte_buf, sizeof(byte_buf),
7814 "%02x ", ifstate->signature[i]);
7815 strlcat(sig_buf, byte_buf, sizeof(sig_buf));
7816 }
7817 my_log(LOG_DEBUG, "Signature Bytes: %s", sig_buf);
7818 }
7819 }
7820 }
7821 return;
7822 }
7823
7824 static void
7825 process_state_differences(nwi_state_t state, int af, int sockfd)
7826 {
7827 int count;
7828 int i;
7829 nwi_ifstate_t scan;
7830
7831 if (af == AF_INET) {
7832 count = state->ipv4_count;
7833 }
7834 else {
7835 count = state->ipv6_count;
7836 }
7837 for (i = 0, scan = nwi_state_ifstate_list(state, af);
7838 i < count; i++, scan++) {
7839 process_ifstate_difference(scan, af, sockfd);
7840 }
7841 return;
7842 }
7843 #endif /* MANAGE_IF_SIGNATURE */
7844
7845 #endif /* !TARGET_OS_SIMULATOR */
7846
7847 static void
7848 process_nwi_changes(CFMutableStringRef log_output,
7849 nwi_state_t changes_state,
7850 nwi_state_t new_state,
7851 nwi_state_t old_state,
7852 boolean_t dns_changed,
7853 boolean_t dnsinfo_changed,
7854 CFDictionaryRef old_primary_dns,
7855 boolean_t proxy_changed,
7856 CFDictionaryRef old_primary_proxy,
7857 boolean_t smb_changed,
7858 CFDictionaryRef old_primary_smb)
7859 {
7860 #ifndef MANAGE_IF_ORDER
7861 #pragma unused(new_state)
7862 #pragma unused(old_state)
7863 #endif // !MANAGE_IF_ORDER
7864 #if TARGET_OS_IPHONE
7865 #pragma unused(smb_changed)
7866 #pragma unused(old_primary_smb)
7867 #endif // TARGET_OS_IPHONE
7868
7869 if (changes_state != NULL) {
7870 const sa_family_t af_list[] = {AF_INET, AF_INET6};
7871 nwi_ifstate_t scan;
7872 #ifdef MANAGE_IF_IOCTL
7873 int sockfd = inet_dgram_socket();
7874 #endif /* MANAGE_IF_IOCTL */
7875
7876 #ifdef MANAGE_IF_ORDER
7877 if (interface_order_changed(new_state, old_state)) {
7878 update_interface_order(new_state, sockfd);
7879 }
7880 #endif /* MANAGE_IF_ORDER */
7881
7882 for (size_t idx = 0; idx < countof(af_list); idx++) {
7883 int af = af_list[idx];
7884 CFMutableStringRef changes = NULL;
7885 CFMutableStringRef primary_str = NULL;
7886
7887 #ifdef MANAGE_IF_SIGNATURE
7888 process_state_differences(changes_state, af, sockfd);
7889 #endif /* MANAGE_IF_SIGNATURE */
7890 scan = nwi_state_get_first_ifstate(changes_state, af);
7891 while (scan != NULL) {
7892 const char * changed_str;
7893
7894 changed_str = nwi_ifstate_get_diff_str(scan);
7895 if (changed_str != NULL) {
7896 void * address;
7897 const char * addr_str;
7898 char ntopbuf[INET6_ADDRSTRLEN];
7899
7900 address = (void *)nwi_ifstate_get_address(scan);
7901 addr_str = inet_ntop(scan->af, address, ntopbuf,
7902 sizeof(ntopbuf));
7903 if (primary_str == NULL) {
7904 primary_str = CFStringCreateMutable(NULL, 0);
7905 CFStringAppendFormat(primary_str, NULL,
7906 CFSTR("%s%s:%s"),
7907 nwi_ifstate_get_ifname(scan),
7908 changed_str, addr_str);
7909 } else {
7910 if (changes == NULL) {
7911 changes = CFStringCreateMutable(NULL, 0);
7912 }
7913 CFStringAppendFormat(changes, NULL, CFSTR(", %s"),
7914 nwi_ifstate_get_ifname(scan));
7915 if (strcmp(changed_str, "") != 0) {
7916 CFStringAppendFormat(changes, NULL, CFSTR("%s:%s"),
7917 changed_str, addr_str);
7918 }
7919 }
7920 }
7921 scan = nwi_ifstate_get_next(scan, scan->af);
7922 }
7923
7924 if (primary_str != NULL) {
7925 CFStringAppendFormat(log_output, NULL, CFSTR(" %s(%@"),
7926 af == AF_INET ? "v4" : "v6",
7927 primary_str);
7928
7929 if (changes != NULL && CFStringGetLength(changes) != 0) {
7930 CFStringAppendFormat(log_output, NULL, CFSTR("%@"),
7931 changes);
7932 }
7933 CFStringAppend(log_output, CFSTR(")"));
7934
7935 my_CFRelease(&primary_str);
7936 my_CFRelease(&changes);
7937 }
7938 }
7939 #ifdef MANAGE_IF_IOCTL
7940 if (sockfd >= 0) {
7941 close(sockfd);
7942 }
7943 #endif /* MANAGE_IF_IOCTL */
7944 }
7945
7946 if (dns_changed || dnsinfo_changed) {
7947 const char *str;
7948
7949 str = get_changed_str(S_primary_dns, kSCEntNetDNS, old_primary_dns);
7950 if ((strcmp(str, "") == 0) && dnsinfo_changed) {
7951 str = "*"; // dnsinfo change w/no change to primary
7952 }
7953 CFStringAppendFormat(log_output, NULL, CFSTR(" DNS%s"), str);
7954 } else if (S_primary_dns != NULL) {
7955 CFStringAppend(log_output, CFSTR(" DNS"));
7956 }
7957
7958 if (proxy_changed) {
7959 const char *str;
7960
7961 str = get_changed_str(S_primary_proxies, kSCEntNetProxies, old_primary_proxy);
7962 CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy%s"), str);
7963 } else if (S_primary_proxies != NULL) {
7964 CFStringAppend(log_output, CFSTR(" Proxy"));
7965 }
7966
7967 #if !TARGET_OS_IPHONE
7968 if (smb_changed) {
7969 const char *str;
7970
7971 str = get_changed_str(S_primary_smb, kSCEntNetSMB, old_primary_smb);
7972 CFStringAppendFormat(log_output, NULL, CFSTR(" SMB%s"), str);
7973 } else if (S_primary_smb != NULL) {
7974 CFStringAppend(log_output, CFSTR(" SMB"));
7975 }
7976 #endif // !TARGET_OS_IPHONE
7977
7978 return;
7979 }
7980
7981 #pragma mark -
7982 #pragma mark Network changed notification
7983
7984 static dispatch_queue_t
7985 __network_change_queue()
7986 {
7987 static dispatch_once_t once;
7988 static dispatch_queue_t q;
7989
7990 dispatch_once(&once, ^{
7991 q = dispatch_queue_create("network change queue", NULL);
7992 });
7993
7994 return q;
7995 }
7996
7997 // Note: must run on __network_change_queue()
7998 static void
7999 post_network_change_when_ready()
8000 {
8001 int status;
8002
8003 dispatch_assert_queue(__network_change_queue());
8004
8005 if (S_network_change_needed == 0) {
8006 return;
8007 }
8008
8009 if (!S_network_change_timeout &&
8010 (!S_dnsinfo_synced || !S_nwi_synced)) {
8011 // if we [still] need to wait for the DNS configuration
8012 // or network information changes to be ack'd
8013 my_log(LOG_DEBUG,
8014 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s, %s)",
8015 S_dnsinfo_synced ? "DNS" : "!DNS",
8016 S_nwi_synced ? "nwi" : "!nwi");
8017 return;
8018 }
8019
8020 // cancel any running timer
8021 if (S_network_change_timer != NULL) {
8022 dispatch_source_cancel(S_network_change_timer);
8023 dispatch_release(S_network_change_timer);
8024 S_network_change_timer = NULL;
8025 S_network_change_timeout = FALSE;
8026 }
8027
8028 // set (and log?) the post time
8029 {
8030 struct timeval elapsed;
8031 struct timeval end;
8032
8033 (void) gettimeofday(&end, NULL);
8034 timersub(&end, &S_network_change_start, &elapsed);
8035
8036 #define QUERY_TIME__FMT "%ld.%6.6d"
8037 #define QUERY_TIME__DIV 1
8038
8039 my_log(LOG_INFO,
8040 "Post \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s: " QUERY_TIME__FMT ": 0x%x)",
8041 S_network_change_timeout ? "timeout" : "delayed",
8042 elapsed.tv_sec,
8043 elapsed.tv_usec / QUERY_TIME__DIV,
8044 S_network_change_needed);
8045 }
8046
8047
8048 /* We are about to post a network change to everyone, get the agents up to date */
8049 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8050 if ((S_network_change_needed & NETWORK_CHANGE_DNS) != 0) {
8051 /* Setup or Update config agents */
8052 process_AgentMonitor_DNS();
8053 }
8054 #endif //!TARGET_OS_SIMULATOR
8055
8056 if ((S_network_change_needed & NETWORK_CHANGE_NET) != 0) {
8057 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI);
8058 if (status != NOTIFY_STATUS_OK) {
8059 my_log(LOG_ERR,
8060 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI ") failed: error=%d", status);
8061 }
8062 }
8063
8064 if ((S_network_change_needed & NETWORK_CHANGE_DNS) != 0) {
8065 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS);
8066 if (status != NOTIFY_STATUS_OK) {
8067 my_log(LOG_ERR,
8068 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS ") failed: error=%d", status);
8069 }
8070 }
8071
8072 if ((S_network_change_needed & NETWORK_CHANGE_PROXY) != 0) {
8073 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8074 /* Setup or Update config agents */
8075 process_AgentMonitor_Proxy();
8076 #endif //!TARGET_OS_SIMULATOR
8077 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
8078 if (status != NOTIFY_STATUS_OK) {
8079 my_log(LOG_ERR,
8080 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY ") failed: error=%d", status);
8081 }
8082 }
8083
8084 if ((S_network_change_needed & NETWORK_CHANGE_NAT64) != 0) {
8085 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8086 // process any NAT64 prefix update requests (and refresh existing prefixes on change)
8087 if (S_nat64_prefix_requests != NULL || S_nat64_prefix_updates != NULL
8088 || S_nat64_cancel_prefix_requests != NULL) {
8089 nat64_configuration_update(S_nat64_prefix_requests,
8090 S_nat64_prefix_updates,
8091 S_nat64_cancel_prefix_requests);
8092 my_CFRelease(&S_nat64_prefix_requests);
8093 my_CFRelease(&S_nat64_prefix_updates);
8094 my_CFRelease(&S_nat64_cancel_prefix_requests);
8095 }
8096 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8097
8098 S_network_change_needed &= ~(NETWORK_CHANGE_NAT64);
8099 }
8100
8101 if (S_network_change_needed != 0) {
8102 // if more than just a NAT64 prefix change
8103 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE);
8104 if (status != NOTIFY_STATUS_OK) {
8105 my_log(LOG_ERR,
8106 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE ") failed: error=%d", status);
8107 }
8108 }
8109
8110 S_network_change_needed = 0;
8111 return;
8112 }
8113
8114 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
8115
8116 // Note: must run on __network_change_queue()
8117 static void
8118 post_network_change(uint32_t change)
8119 {
8120 dispatch_assert_queue(__network_change_queue());
8121
8122 if (S_network_change_needed == 0) {
8123 // set the start time
8124 (void) gettimeofday(&S_network_change_start, NULL);
8125 }
8126
8127 // indicate that we need to post a change for ...
8128 S_network_change_needed |= change;
8129
8130 // cancel any running timer
8131 if (S_network_change_timer != NULL) {
8132 dispatch_source_cancel(S_network_change_timer);
8133 dispatch_release(S_network_change_timer);
8134 S_network_change_timer = NULL;
8135 S_network_change_timeout = FALSE;
8136 }
8137
8138 // if needed, start new timer
8139 if (!S_dnsinfo_synced || !S_nwi_synced) {
8140 S_network_change_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
8141 0,
8142 0,
8143 __network_change_queue());
8144 dispatch_source_set_event_handler(S_network_change_timer, ^{
8145 S_network_change_timeout = TRUE;
8146 post_network_change_when_ready();
8147 });
8148 dispatch_source_set_timer(S_network_change_timer,
8149 dispatch_time(DISPATCH_TIME_NOW,
8150 TRAILING_EDGE_TIMEOUT_NSEC), // start
8151 DISPATCH_TIME_FOREVER, // interval
8152 10 * NSEC_PER_MSEC); // leeway
8153 dispatch_resume(S_network_change_timer);
8154 }
8155
8156 post_network_change_when_ready();
8157
8158 return;
8159 }
8160
8161 #pragma mark -
8162 #pragma mark Process network (SCDynamicStore) changes
8163
8164 static void
8165 IPMonitorProcessChanges(SCDynamicStoreRef session, CFArrayRef changed_keys,
8166 CFArrayRef if_rank_changes)
8167 {
8168 CFIndex count = 0;
8169 uint32_t changes = 0;
8170 nwi_state_t changes_state = NULL;
8171 boolean_t dns_changed = FALSE;
8172 boolean_t dnsinfo_changed = FALSE;
8173 boolean_t global_ipv4_changed = FALSE;
8174 boolean_t global_ipv6_changed = FALSE;
8175 CFMutableSetRef ipv4_service_changes = NULL;
8176 CFMutableSetRef ipv6_service_changes = NULL;
8177 keyChangeList keys;
8178 CFIndex n;
8179 boolean_t nat64_changed = FALSE;
8180 CFMutableStringRef network_change_msg = NULL;
8181 int n_services;
8182 nwi_state_t old_nwi_state = NULL;
8183 CFDictionaryRef old_primary_dns = NULL;
8184 CFDictionaryRef old_primary_proxy = NULL;
8185 #if !TARGET_OS_IPHONE
8186 CFDictionaryRef old_primary_smb = NULL;
8187 #endif // !TARGET_OS_IPHONE
8188 boolean_t proxies_changed = FALSE;
8189 boolean_t reachability_changed = FALSE;
8190 CFArrayRef service_order;
8191 CFMutableArrayRef service_changes = NULL;
8192 CFDictionaryRef services_info = NULL;
8193 #if !TARGET_OS_IPHONE
8194 boolean_t smb_changed = FALSE;
8195 #endif // !TARGET_OS_IPHONE
8196
8197 /* populate name/index cache */
8198 my_if_nameindex();
8199
8200 if (changed_keys != NULL) {
8201 count = CFArrayGetCount(changed_keys);
8202 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
8203 my_log(LOG_DEBUG,
8204 "changed keys %@ (%ld)", changed_keys, count);
8205 }
8206 }
8207 if (if_rank_changes == NULL && count == 0) {
8208 return;
8209 }
8210
8211 if (S_primary_dns != NULL) {
8212 old_primary_dns = service_dict_get(S_primary_dns, kSCEntNetDNS);
8213 if (old_primary_dns != NULL) {
8214 old_primary_dns = CFDictionaryCreateCopy(NULL, old_primary_dns);
8215 }
8216 }
8217
8218 if (S_primary_proxies != NULL) {
8219 old_primary_proxy
8220 = service_dict_get(S_primary_proxies, kSCEntNetProxies);
8221 if (old_primary_proxy != NULL) {
8222 old_primary_proxy = CFDictionaryCreateCopy(NULL, old_primary_proxy);
8223 }
8224 }
8225
8226 #if !TARGET_OS_IPHONE
8227 if (S_primary_smb != NULL) {
8228 old_primary_smb = service_dict_get(S_primary_smb, kSCEntNetSMB);
8229 if (old_primary_smb != NULL) {
8230 old_primary_smb = CFDictionaryCreateCopy(NULL, old_primary_smb);
8231 }
8232 }
8233 #endif // !TARGET_OS_IPHONE
8234
8235 keyChangeListInit(&keys);
8236 service_changes = CFArrayCreateMutable(NULL, 0,
8237 &kCFTypeArrayCallBacks);
8238 for (CFIndex i = 0; i < count; i++) {
8239 CFStringRef change;
8240 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8241 CFStringRef interface = NULL;
8242 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8243
8244 change = CFArrayGetValueAtIndex(changed_keys, i);
8245 if (CFEqual(change, S_setup_global_ipv4)) {
8246 global_ipv4_changed = TRUE;
8247 global_ipv6_changed = TRUE;
8248 }
8249 else if (CFEqual(change, S_multicast_resolvers)) {
8250 dnsinfo_changed = TRUE;
8251 }
8252 else if (CFEqual(change, S_private_resolvers)) {
8253 dnsinfo_changed = TRUE;
8254 }
8255 else if (CFEqual(change, CFSTR(_PATH_RESOLVER_DIR))) {
8256 dnsinfo_changed = TRUE;
8257 }
8258 else if (CFStringHasPrefix(change, S_interface_delegation_prefix) &&
8259 CFStringHasSuffix(change, kSCEntNetInterfaceDelegation)) {
8260 reachability_changed = TRUE;
8261 }
8262 else if (CFStringHasPrefix(change, S_state_service_prefix)) {
8263 CFStringRef protocol = NULL;
8264 CFStringRef serviceID;
8265
8266 serviceID = parseNetworkServiceString(change, &protocol);
8267 if (serviceID != NULL) {
8268 my_CFArrayAppendUniqueValue(service_changes, serviceID);
8269 if (protocol != NULL) {
8270 if (CFEqual(protocol, kSCEntNetIPv4)) {
8271 /* IPv4 service changed, remember that */
8272 my_CFSetAddValue(&ipv4_service_changes, serviceID);
8273 }
8274 else if (CFEqual(protocol, kSCEntNetIPv6)) {
8275 /* IPv6 service changed, remember that */
8276 my_CFSetAddValue(&ipv6_service_changes, serviceID);
8277 }
8278 }
8279 CFRelease(serviceID);
8280 }
8281 my_CFRelease(&protocol);
8282 }
8283 else if (CFStringHasPrefix(change, S_setup_service_prefix)) {
8284 CFStringRef serviceID;
8285
8286 serviceID = parseNetworkServiceString(change, NULL);
8287 if (serviceID != NULL) {
8288 my_CFArrayAppendUniqueValue(service_changes, serviceID);
8289 CFRelease(serviceID);
8290 }
8291
8292 for (size_t j = 0; j < countof(transientInterfaceEntityNames); j++) {
8293 if (CFStringHasSuffix(change,
8294 *transientInterfaceEntityNames[j])) {
8295 reachability_changed = TRUE;
8296 break;
8297 }
8298 }
8299
8300 if (CFStringHasSuffix(change, kSCEntNetInterface)) {
8301 reachability_changed = TRUE;
8302 }
8303 }
8304 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8305 else if (is_nat64_prefix_request(change, &interface)) {
8306 set_plat_discovery(kPLATDiscoveryOptionStart, interface);
8307 nat64_changed = TRUE;
8308 }
8309 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8310 }
8311
8312 /* determine which serviceIDs are impacted by the interface rank changes */
8313 if (if_rank_changes != NULL) {
8314 n = CFArrayGetCount(if_rank_changes);
8315 for (CFIndex i = 0; i < n; i++) {
8316 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i);
8317
8318 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
8319 my_log(LOG_DEBUG, "Interface rank changed %@", ifname);
8320 }
8321 append_serviceIDs_for_interface(service_changes, ifname);
8322 }
8323 }
8324
8325 /* grab a snapshot of everything we need */
8326 services_info = services_info_copy(session, service_changes);
8327 assert(services_info != NULL);
8328
8329 /* grab the service order */
8330 service_order = service_order_get(services_info);
8331 if (service_order != NULL) {
8332 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
8333 my_log(LOG_DEBUG, "service_order %@ ", service_order);
8334 }
8335 }
8336
8337 /*
8338 * process protocol (v4, v6, rank, ...) and
8339 * configuration (dns, proxies, smb, ...) changes
8340 */
8341 n = CFArrayGetCount(service_changes);
8342 for (CFIndex i = 0; i < n; i++) {
8343 uint32_t changes;
8344 CFStringRef serviceID;
8345
8346 serviceID = CFArrayGetValueAtIndex(service_changes, i);
8347 changes = service_changed(services_info, serviceID);
8348 if (my_CFSetContainsValue(ipv4_service_changes, serviceID)) {
8349 changes |= (1 << kEntityTypeIPv4);
8350 }
8351 if (my_CFSetContainsValue(ipv6_service_changes, serviceID)) {
8352 changes |= (1 << kEntityTypeIPv6);
8353 }
8354 if ((changes & (1 << kEntityTypeServiceOptions)) != 0) {
8355 /* if __Service__ (e.g. PrimaryRank) changed */
8356 global_ipv4_changed = TRUE;
8357 global_ipv6_changed = TRUE;
8358 }
8359 else {
8360 if ((changes & (1 << kEntityTypeIPv4)) != 0) {
8361 global_ipv4_changed = TRUE;
8362 dnsinfo_changed = TRUE;
8363 proxies_changed = TRUE;
8364 }
8365 if ((changes & (1 << kEntityTypeIPv6)) != 0) {
8366 global_ipv6_changed = TRUE;
8367 dnsinfo_changed = TRUE;
8368 proxies_changed = TRUE;
8369 nat64_changed = TRUE;
8370 }
8371 }
8372 if ((changes & (1 << kEntityTypeDNS)) != 0) {
8373 if (S_primary_dns != NULL && CFEqual(S_primary_dns, serviceID)) {
8374 dns_changed = TRUE;
8375 }
8376 dnsinfo_changed = TRUE;
8377 nat64_changed = TRUE;
8378 }
8379 if ((changes & (1 << kEntityTypeProxies)) != 0) {
8380 proxies_changed = TRUE;
8381 }
8382 #if !TARGET_OS_IPHONE
8383 if ((changes & (1 << kEntityTypeSMB)) != 0) {
8384 if (S_primary_smb != NULL && CFEqual(S_primary_smb, serviceID)) {
8385 smb_changed = TRUE;
8386 }
8387 }
8388 #endif
8389 if ((changes & (1 << kEntityTypeTransientStatus)) != 0
8390 && (service_dict_get(serviceID, kSCEntNetIPv4) != NULL
8391 || service_dict_get(serviceID, kSCEntNetIPv6) != NULL)) {
8392 dnsinfo_changed = TRUE;
8393 }
8394 }
8395
8396 /* ensure S_nwi_state can hold as many services as we have currently */
8397 n_services = (int)CFDictionaryGetCount(S_service_state_dict);
8398 old_nwi_state = nwi_state_make_copy(S_nwi_state);
8399 S_nwi_state = nwi_state_new(S_nwi_state, n_services);
8400
8401 if (global_ipv4_changed) {
8402 if (S_ipv4_results != NULL) {
8403 ElectionResultsRelease(S_ipv4_results);
8404 }
8405 S_ipv4_results
8406 = ElectionResultsCopy(AF_INET, service_order);
8407 ElectionResultsLog(LOG_INFO, S_ipv4_results, "IPv4");
8408 }
8409 if (global_ipv6_changed) {
8410 if (S_ipv6_results != NULL) {
8411 ElectionResultsRelease(S_ipv6_results);
8412 }
8413 S_ipv6_results
8414 = ElectionResultsCopy(AF_INET6, service_order);
8415 ElectionResultsLog(LOG_INFO, S_ipv6_results, "IPv6");
8416 }
8417 if (global_ipv4_changed || global_ipv6_changed || dnsinfo_changed) {
8418 CFStringRef new_primary;
8419 CFStringRef new_primary_dns = NULL;
8420 CFStringRef new_primary_proxies = NULL;
8421 #if !TARGET_OS_IPHONE
8422 CFStringRef new_primary_smb = NULL;
8423 #endif /* !TARGET_OS_IPHONE */
8424 RouteListUnion new_routelist;
8425 CandidateRef other_candidate;
8426 CandidateRef primary_candidate;
8427
8428 if (S_nwi_state != NULL) {
8429 nwi_state_clear(S_nwi_state, AF_INET);
8430 nwi_state_clear(S_nwi_state, AF_INET6);
8431 }
8432
8433 /* IPv4 */
8434 my_log(LOG_DEBUG, "electing IPv4 primary");
8435 new_routelist.ptr = NULL;
8436 other_candidate = (S_ipv6_results != NULL) /* get IPv6 primary */
8437 ? S_ipv6_results->candidates : NULL;
8438 primary_candidate = ElectionResultsGetPrimary(S_ipv4_results,
8439 other_candidate,
8440 S_nwi_state, AF_INET,
8441 &new_routelist.common,
8442 services_info,
8443 ipv4_service_changes);
8444 new_primary = (primary_candidate != NULL)
8445 ? primary_candidate->serviceID : NULL;
8446 (void)set_new_primary(&S_primary_ipv4, new_primary, "IPv4");
8447 update_ipv4(S_primary_ipv4, new_routelist.v4, &keys);
8448
8449 /* IPv6 */
8450 my_log(LOG_DEBUG, "electing IPv6 primary");
8451 new_routelist.ptr = NULL;
8452 other_candidate = primary_candidate; /* get IPv4 primary */
8453 primary_candidate = ElectionResultsGetPrimary(S_ipv6_results,
8454 other_candidate,
8455 S_nwi_state, AF_INET6,
8456 &new_routelist.common,
8457 services_info,
8458 ipv6_service_changes);
8459 new_primary = (primary_candidate != NULL)
8460 ? primary_candidate->serviceID : NULL;
8461 (void)set_new_primary(&S_primary_ipv6, new_primary, "IPv6");
8462 update_ipv6(S_primary_ipv6, new_routelist.v6, &keys);
8463
8464 nwi_state_finalize(S_nwi_state);
8465
8466 if (S_primary_ipv4 != NULL && S_primary_ipv6 != NULL) {
8467 /* decide between IPv4 and IPv6 */
8468 if (rank_service_entity(S_ipv4_service_rank_dict,
8469 S_primary_ipv4, kSCEntNetDNS)
8470 <= rank_service_entity(S_ipv6_service_rank_dict,
8471 S_primary_ipv6, kSCEntNetDNS)) {
8472 new_primary_dns = S_primary_ipv4;
8473 }
8474 else {
8475 new_primary_dns = S_primary_ipv6;
8476 }
8477 if (rank_service_entity(S_ipv4_service_rank_dict,
8478 S_primary_ipv4, kSCEntNetProxies)
8479 <= rank_service_entity(S_ipv6_service_rank_dict,
8480 S_primary_ipv6, kSCEntNetProxies)) {
8481 new_primary_proxies = S_primary_ipv4;
8482 }
8483 else {
8484 new_primary_proxies = S_primary_ipv6;
8485 }
8486 #if !TARGET_OS_IPHONE
8487 if (rank_service_entity(S_ipv4_service_rank_dict,
8488 S_primary_ipv4, kSCEntNetSMB)
8489 <= rank_service_entity(S_ipv6_service_rank_dict,
8490 S_primary_ipv6, kSCEntNetSMB)) {
8491 new_primary_smb = S_primary_ipv4;
8492 }
8493 else {
8494 new_primary_smb = S_primary_ipv6;
8495 }
8496 #endif /* !TARGET_OS_IPHONE */
8497
8498 }
8499 else if (S_primary_ipv6 != NULL) {
8500 new_primary_dns = S_primary_ipv6;
8501 new_primary_proxies = S_primary_ipv6;
8502 #if !TARGET_OS_IPHONE
8503 new_primary_smb = S_primary_ipv6;
8504 #endif /* !TARGET_OS_IPHONE */
8505 }
8506 else if (S_primary_ipv4 != NULL) {
8507 new_primary_dns = S_primary_ipv4;
8508 new_primary_proxies = S_primary_ipv4;
8509 #if !TARGET_OS_IPHONE
8510 new_primary_smb = S_primary_ipv4;
8511 #endif /* !TARGET_OS_IPHONE */
8512 }
8513
8514 if (set_new_primary(&S_primary_dns, new_primary_dns, "DNS")) {
8515 dns_changed = TRUE;
8516 dnsinfo_changed = TRUE;
8517 }
8518 if (set_new_primary(&S_primary_proxies, new_primary_proxies,
8519 "Proxies")) {
8520 proxies_changed = TRUE;
8521 }
8522 #if !TARGET_OS_IPHONE
8523 if (set_new_primary(&S_primary_smb, new_primary_smb, "SMB")) {
8524 smb_changed = TRUE;
8525 }
8526 #endif /* !TARGET_OS_IPHONE */
8527 }
8528
8529 if (!proxies_changed && dnsinfo_changed
8530 && ((G_supplemental_proxies_follow_dns != NULL)
8531 && CFBooleanGetValue(G_supplemental_proxies_follow_dns))) {
8532 proxies_changed = TRUE;
8533 }
8534
8535 changes_state = nwi_state_diff(old_nwi_state, S_nwi_state);
8536
8537 if (global_ipv4_changed || global_ipv6_changed
8538 || dnsinfo_changed || reachability_changed) {
8539 if (S_nwi_state != NULL) {
8540 S_nwi_state->generation_count = mach_absolute_time();
8541 if (global_ipv4_changed || global_ipv6_changed
8542 || reachability_changed) {
8543 SCNetworkReachabilityFlags reach_flags_v4 = 0;
8544 SCNetworkReachabilityFlags reach_flags_v6 = 0;
8545
8546 GetReachabilityFlagsFromTransientServices(services_info,
8547 &reach_flags_v4,
8548 &reach_flags_v6);
8549
8550 _nwi_state_set_reachability_flags(S_nwi_state, reach_flags_v4,
8551 reach_flags_v6);
8552 }
8553
8554 /* Update the per-interface generation count */
8555 _nwi_state_update_interface_generations(old_nwi_state, S_nwi_state,
8556 changes_state);
8557 }
8558
8559 if (update_nwi(S_nwi_state)) {
8560 changes |= NETWORK_CHANGE_NET;
8561
8562 /*
8563 * the DNS configuration includes per-resolver configuration
8564 * reachability flags that are based on the nwi state. Let's
8565 * make sure that we check for changes
8566 */
8567 dnsinfo_changed = TRUE;
8568 }
8569 }
8570 if (dns_changed) {
8571 if (update_dns(services_info, S_primary_dns, &keys)) {
8572 changes |= NETWORK_CHANGE_DNS;
8573 dnsinfo_changed = TRUE;
8574 } else {
8575 dns_changed = FALSE;
8576 }
8577 }
8578 if (dnsinfo_changed) {
8579 if (update_dnsinfo(services_info, S_primary_dns,
8580 &keys, service_order)) {
8581 changes |= NETWORK_CHANGE_DNS;
8582 } else {
8583 dnsinfo_changed = FALSE;
8584 }
8585 }
8586 if (proxies_changed) {
8587 // if proxy change OR supplemental Proxies follow supplemental DNS
8588 if (update_proxies(services_info, S_primary_proxies,
8589 &keys, service_order)) {
8590 changes |= NETWORK_CHANGE_PROXY;
8591 } else {
8592 proxies_changed = FALSE;
8593 }
8594 }
8595 #if !TARGET_OS_IPHONE
8596 if (smb_changed) {
8597 if (update_smb(services_info, S_primary_smb, &keys)) {
8598 changes |= NETWORK_CHANGE_SMB;
8599 } else {
8600 smb_changed = FALSE;
8601 }
8602 }
8603 #endif /* !TARGET_OS_IPHONE */
8604 if (nat64_changed) {
8605 changes |= NETWORK_CHANGE_NAT64;
8606 }
8607 my_CFRelease(&service_changes);
8608 my_CFRelease(&services_info);
8609 my_CFRelease(&ipv4_service_changes);
8610 my_CFRelease(&ipv6_service_changes);
8611
8612 if (changes != 0) {
8613 network_change_msg = CFStringCreateMutable(NULL, 0);
8614 process_nwi_changes(network_change_msg,
8615 changes_state,
8616 S_nwi_state,
8617 old_nwi_state,
8618 dns_changed,
8619 dnsinfo_changed,
8620 old_primary_dns,
8621 proxies_changed,
8622 old_primary_proxy,
8623 #if !TARGET_OS_IPHONE
8624 smb_changed,
8625 old_primary_smb
8626 #else // !TARGET_OS_IPHONE
8627 FALSE, // smb_changed
8628 NULL // old_primary_smb
8629 #endif // !TARGET_OS_IPHONE
8630 );
8631 }
8632
8633 keyChangeListApplyToStore(&keys, session);
8634 my_CFRelease(&old_primary_dns);
8635 my_CFRelease(&old_primary_proxy);
8636 #if !TARGET_OS_IPHONE
8637 my_CFRelease(&old_primary_smb);
8638 #endif // !TARGET_OS_IPHONE
8639
8640 if (changes != 0) {
8641 dispatch_async(__network_change_queue(), ^{
8642 post_network_change(changes);
8643 });
8644 }
8645
8646 if ((network_change_msg != NULL)
8647 && (CFStringGetLength(network_change_msg) != 0)) {
8648 my_log(LOG_NOTICE, "network changed:%@", network_change_msg);
8649 } else if (keyChangeListActive(&keys)) {
8650 my_log(LOG_NOTICE, "network changed");
8651 } else if (nat64_changed) {
8652 my_log(LOG_NOTICE, "nat64 update");
8653 } else {
8654 my_log(LOG_INFO, "network event w/no changes");
8655 }
8656
8657 my_CFRelease(&network_change_msg);
8658
8659 if (changes_state != NULL) {
8660 nwi_state_free(changes_state);
8661 }
8662 if (old_nwi_state != NULL) {
8663 nwi_state_free(old_nwi_state);
8664 }
8665 keyChangeListFree(&keys);
8666
8667 /* release the name/index cache */
8668 my_if_freenameindex();
8669
8670 return;
8671 }
8672
8673 static void
8674 IPMonitorNotify(SCDynamicStoreRef session, CFArrayRef changed_keys,
8675 void * info)
8676 {
8677 #pragma unused(info)
8678 IPMonitorProcessChanges(session, changed_keys, NULL);
8679 return;
8680 }
8681
8682 #if !TARGET_OS_IPHONE
8683 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_mcx
8684 #else
8685 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_global
8686 #endif
8687
8688 static void
8689 watch_proxies()
8690 {
8691 static dispatch_queue_t proxy_cb_queue;
8692
8693 proxy_cb_queue = dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL);
8694 _scprefs_observer_watch(PROXY_GLOBAL_OBSERVER_TYPE,
8695 "com.apple.SystemConfiguration.plist",
8696 proxy_cb_queue,
8697 ^{
8698 SCDynamicStoreNotifyValue(NULL, S_state_global_proxies);
8699 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8700 /* Setup or Update config agents */
8701 process_AgentMonitor_Proxy();
8702 #endif //!TARGET_OS_SIMULATOR
8703 (void)notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
8704 my_log(LOG_INFO, "Notifying:\n%@",
8705 S_state_global_proxies);
8706 });
8707 return;
8708 }
8709
8710 #if TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST
8711
8712 static void
8713 prefs_changed_callback_init(void)
8714 {
8715 }
8716
8717 #else /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8718
8719 #include "IPMonitorControlPrefs.h"
8720
8721 static void
8722 prefs_changed(SCPreferencesRef prefs)
8723 {
8724 #pragma unused(prefs)
8725 if (S_bundle_logging_verbose || IPMonitorControlPrefsIsVerbose()) {
8726 S_IPMonitor_debug = kDebugFlagDefault;
8727 S_IPMonitor_verbose = TRUE;
8728 my_log(LOG_DEBUG, "Setting logging verbose mode on");
8729 } else {
8730 my_log(LOG_DEBUG, "Setting logging verbose mode off");
8731 S_IPMonitor_debug = 0;
8732 S_IPMonitor_verbose = FALSE;
8733 }
8734 return;
8735 }
8736
8737 static void
8738 prefs_changed_callback_init(void)
8739 {
8740 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed);
8741 prefs_changed(NULL);
8742 return;
8743 }
8744
8745
8746 #endif /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8747
8748 #if !TARGET_OS_SIMULATOR
8749 static int
8750 flush_routes(int s)
8751 {
8752 char * buf = NULL;
8753 int i;
8754 char * lim;
8755 #define N_MIB 6
8756 int mib[N_MIB];
8757 size_t needed;
8758 char * next;
8759 struct rt_msghdr * rtm;
8760 struct sockaddr_in *sin;
8761
8762 mib[0] = CTL_NET;
8763 mib[1] = PF_ROUTE;
8764 mib[2] = 0;
8765 mib[3] = AF_INET;
8766 mib[4] = NET_RT_FLAGS;
8767 mib[5] = RTF_STATIC | RTF_DYNAMIC;
8768 for (i = 0; i < 3; i++) {
8769 if (sysctl(mib, N_MIB, NULL, &needed, NULL, 0) < 0) {
8770 break;
8771 }
8772 if ((buf = malloc(needed)) == NULL) {
8773 break;
8774 }
8775 if (sysctl(mib, N_MIB, buf, &needed, NULL, 0) >= 0) {
8776 break;
8777 }
8778 free(buf);
8779 buf = NULL;
8780 }
8781 if (buf == NULL) {
8782 return (-1);
8783 }
8784 lim = buf + needed;
8785 for (next = buf; next < lim; next += rtm->rtm_msglen) {
8786 uint32_t addr;
8787
8788 /* ALIGN: assume kernel provides necessary alignment */
8789 rtm = (struct rt_msghdr *)(void *)next;
8790 sin = (struct sockaddr_in *)(rtm + 1);
8791
8792 addr = ntohl(sin->sin_addr.s_addr);
8793 if (IN_LOOPBACK(addr)) {
8794 my_log(LOG_DEBUG,
8795 "flush_routes: ignoring loopback route");
8796 continue;
8797 }
8798 if (IN_LOCAL_GROUP(addr)) {
8799 my_log(LOG_DEBUG,
8800 "flush_routes: ignoring multicast route");
8801 continue;
8802 }
8803 rtm->rtm_type = RTM_DELETE;
8804 rtm->rtm_seq = ++rtm_seq;
8805 if (write(s, rtm, rtm->rtm_msglen) < 0) {
8806 my_log(LOG_NOTICE,
8807 "flush_routes: removing route for "
8808 IP_FORMAT " failed: %s",
8809 IP_LIST(&sin->sin_addr),
8810 strerror(errno));
8811 }
8812 else {
8813 my_log(LOG_DEBUG,
8814 "flush_routes: removed route for " IP_FORMAT,
8815 IP_LIST(&sin->sin_addr));
8816 }
8817 }
8818 free(buf);
8819 return (0);
8820 }
8821
8822 static void
8823 flush_inet_routes(void)
8824 {
8825 int s;
8826
8827 s = open_routing_socket();
8828 if (s != -1) {
8829 flush_routes(s);
8830 close(s);
8831 }
8832 }
8833
8834 #else /* !TARGET_OS_SIMULATOR */
8835
8836 static void
8837 flush_inet_routes(void)
8838 {
8839 }
8840
8841 #endif /* !TARGET_OS_SIMULATOR */
8842
8843
8844
8845 static void
8846 ip_plugin_init()
8847 {
8848 CFMutableArrayRef keys = NULL;
8849 CFStringRef pattern;
8850 CFMutableArrayRef patterns = NULL;
8851 CFRunLoopSourceRef rls = NULL;
8852
8853 if (S_is_network_boot() != 0) {
8854 S_netboot = TRUE;
8855 }
8856 else {
8857 /* flush routes */
8858 flush_inet_routes();
8859 }
8860
8861 S_session = SCDynamicStoreCreate(NULL, CFSTR("IPMonitor"),
8862 IPMonitorNotify, NULL);
8863 if (S_session == NULL) {
8864 my_log(LOG_ERR,
8865 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8866 SCErrorString(SCError()));
8867 return;
8868 }
8869 S_state_global_ipv4
8870 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8871 kSCDynamicStoreDomainState,
8872 kSCEntNetIPv4);
8873 S_state_global_ipv6
8874 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8875 kSCDynamicStoreDomainState,
8876 kSCEntNetIPv6);
8877 S_state_global_dns
8878 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8879 kSCDynamicStoreDomainState,
8880 kSCEntNetDNS);
8881 S_state_global_proxies
8882 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8883 kSCDynamicStoreDomainState,
8884 kSCEntNetProxies);
8885 #if !TARGET_OS_IPHONE
8886 S_state_global_smb
8887 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8888 kSCDynamicStoreDomainState,
8889 kSCEntNetSMB);
8890 #endif /* !TARGET_OS_IPHONE */
8891 S_setup_global_ipv4
8892 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8893 kSCDynamicStoreDomainSetup,
8894 kSCEntNetIPv4);
8895 S_state_service_prefix
8896 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
8897 kSCDynamicStoreDomainState,
8898 CFSTR(""),
8899 NULL);
8900 S_setup_service_prefix
8901 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
8902 kSCDynamicStoreDomainSetup,
8903 CFSTR(""),
8904 NULL);
8905 S_interface_delegation_prefix
8906 = SCDynamicStoreKeyCreateNetworkInterface(NULL,
8907 kSCDynamicStoreDomainState);
8908
8909 S_service_state_dict
8910 = CFDictionaryCreateMutable(NULL, 0,
8911 &kCFTypeDictionaryKeyCallBacks,
8912 &kCFTypeDictionaryValueCallBacks);
8913
8914 S_ipv4_service_rank_dict
8915 = CFDictionaryCreateMutable(NULL, 0,
8916 &kCFTypeDictionaryKeyCallBacks,
8917 &kCFTypeDictionaryValueCallBacks);
8918
8919 S_ipv6_service_rank_dict
8920 = CFDictionaryCreateMutable(NULL, 0,
8921 &kCFTypeDictionaryKeyCallBacks,
8922 &kCFTypeDictionaryValueCallBacks);
8923
8924 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
8925 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
8926
8927 /* register for State: and Setup: per-service notifications */
8928 add_service_keys(kSCCompAnyRegex, keys, patterns);
8929
8930 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetPPP);
8931 CFArrayAppendValue(patterns, pattern);
8932 CFRelease(pattern);
8933
8934 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
8935 CFArrayAppendValue(patterns, pattern);
8936 CFRelease(pattern);
8937
8938 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetInterface);
8939 CFArrayAppendValue(patterns, pattern);
8940 CFRelease(pattern);
8941
8942 /* register for State: per-service PPP/VPN/IPSec status notifications */
8943 add_transient_status_keys(kSCCompAnyRegex, NULL, patterns);
8944
8945 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
8946 CFArrayAppendValue(keys, S_setup_global_ipv4);
8947
8948 /* add notifier for multicast DNS configuration (Bonjour/.local) */
8949 S_multicast_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
8950 kSCDynamicStoreDomainState,
8951 kSCCompNetwork,
8952 CFSTR(kDNSServiceCompMulticastDNS));
8953 CFArrayAppendValue(keys, S_multicast_resolvers);
8954
8955 /* add notifier for private DNS configuration (Back to My Mac) */
8956 S_private_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
8957 kSCDynamicStoreDomainState,
8958 kSCCompNetwork,
8959 CFSTR(kDNSServiceCompPrivateDNS));
8960 CFArrayAppendValue(keys, S_private_resolvers);
8961
8962 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8963 /* add NAT64 prefix request pattern */
8964 nat64_prefix_request_add_pattern(patterns);
8965 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8966
8967 /* add interface delegation pattern */
8968 pattern = interface_entity_key_copy(kSCCompAnyRegex, kSCEntNetInterfaceDelegation);
8969 CFArrayAppendValue(patterns, pattern);
8970 CFRelease(pattern);
8971
8972 if (!SCDynamicStoreSetNotificationKeys(S_session, keys, patterns)) {
8973 my_log(LOG_ERR,
8974 "SCDynamicStoreSetNotificationKeys() failed: %s",
8975 SCErrorString(SCError()));
8976 goto done;
8977 }
8978
8979 rls = SCDynamicStoreCreateRunLoopSource(NULL, S_session, 0);
8980 if (rls == NULL) {
8981 my_log(LOG_ERR,
8982 "SCDynamicStoreCreateRunLoopSource() failed: %s",
8983 SCErrorString(SCError()));
8984 goto done;
8985 }
8986
8987 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
8988 CFRelease(rls);
8989
8990 /* initialize dns configuration */
8991 (void)dns_configuration_set(NULL, NULL, NULL, NULL, NULL);
8992 #if !TARGET_OS_IPHONE
8993 empty_dns();
8994 #endif /* !TARGET_OS_IPHONE */
8995 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_dns);
8996
8997 #if !TARGET_OS_IPHONE
8998 /* initialize SMB configuration */
8999 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_smb);
9000 #endif /* !TARGET_OS_IPHONE */
9001
9002 watch_proxies();
9003
9004 done:
9005 my_CFRelease(&keys);
9006 my_CFRelease(&patterns);
9007 return;
9008 }
9009
9010 __private_extern__
9011 void
9012 prime_IPMonitor()
9013 {
9014 /* initialize multicast route */
9015 update_ipv4(NULL, NULL, NULL);
9016
9017 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9018 process_AgentMonitor();
9019 #endif // !TARGET_OS_SIMULATOR
9020
9021 return;
9022 }
9023
9024 static boolean_t
9025 S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
9026 boolean_t def)
9027 {
9028 CFBooleanRef b;
9029 boolean_t ret = def;
9030
9031 b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
9032 if (b != NULL) {
9033 ret = CFBooleanGetValue(b);
9034 }
9035 return (ret);
9036 }
9037
9038 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9039 #include "IPMonitorControlServer.h"
9040
9041 static void
9042 InterfaceRankChanged(void * info)
9043 {
9044 #pragma unused(info)
9045 CFDictionaryRef assertions = NULL;
9046 CFArrayRef changes;
9047
9048 changes = IPMonitorControlServerCopyInterfaceRankInformation(&assertions);
9049 if (S_if_rank_dict != NULL) {
9050 CFRelease(S_if_rank_dict);
9051 }
9052 S_if_rank_dict = assertions;
9053 if (changes != NULL) {
9054 IPMonitorProcessChanges(S_session, NULL, changes);
9055 CFRelease(changes);
9056 }
9057
9058 return;
9059 }
9060
9061 static void
9062 StartIPMonitorControlServer(void)
9063 {
9064 CFRunLoopSourceContext context;
9065 CFRunLoopSourceRef rls;
9066
9067 memset(&context, 0, sizeof(context));
9068 context.perform = InterfaceRankChanged;
9069 rls = CFRunLoopSourceCreate(NULL, 0, &context);
9070 if (!IPMonitorControlServerStart(CFRunLoopGetCurrent(),
9071 rls,
9072 &S_bundle_logging_verbose)) {
9073 my_log(LOG_ERR, "IPMonitorControlServerStart failed");
9074 }
9075 else {
9076 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls,
9077 kCFRunLoopDefaultMode);
9078 }
9079 CFRelease(rls);
9080 return;
9081 }
9082
9083 #endif /* !TARGET_OS_SIMULATOR */
9084
9085 __private_extern__
9086 void
9087 load_IPMonitor(CFBundleRef bundle, Boolean bundleVerbose)
9088 {
9089 CFDictionaryRef info_dict;
9090
9091 info_dict = CFBundleGetInfoDictionary(bundle);
9092
9093 if (info_dict != NULL) {
9094 S_append_state
9095 = S_get_plist_boolean(info_dict,
9096 CFSTR("AppendStateArrayToSetupArray"),
9097 FALSE);
9098 }
9099 if (bundleVerbose) {
9100 S_IPMonitor_debug = kDebugFlagDefault;
9101 S_bundle_logging_verbose = TRUE;
9102 S_IPMonitor_verbose = TRUE;
9103 }
9104
9105 /* register to receive changes to the "verbose" flag and read the initial setting */
9106 prefs_changed_callback_init();
9107
9108 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9109 /* start DNS configuration (dnsinfo) server */
9110 load_DNSConfiguration(bundle, // bundle
9111 ^(Boolean inSync) { // syncHandler
9112 dispatch_async(__network_change_queue(), ^{
9113 S_dnsinfo_synced = inSync;
9114
9115 if (inSync &&
9116 ((S_network_change_needed & NETWORK_CHANGE_DNS) == 0)) {
9117 // all of the DNS service ack's should result
9118 // in a [new] network change being posted
9119 post_network_change(NETWORK_CHANGE_DNS);
9120 } else {
9121 post_network_change_when_ready();
9122 }
9123 });
9124 });
9125
9126 /* start Network Information (nwi) server */
9127 load_NetworkInformation(bundle, // bundle
9128 ^(Boolean inSync) { // syncHandler
9129 dispatch_async(__network_change_queue(), ^{
9130 S_nwi_synced = inSync;
9131 post_network_change_when_ready();
9132 });
9133 });
9134 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
9135
9136 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9137 /* start IPMonitor Control (InterfaceRank) server */
9138 StartIPMonitorControlServer();
9139 #endif /* !TARGET_OS_IPHONE */
9140
9141 /* initialize DNS configuration */
9142 dns_configuration_init(bundle);
9143
9144 /* initialize proxy configuration */
9145 proxy_configuration_init(bundle);
9146
9147 ip_plugin_init();
9148
9149 if (S_session != NULL) {
9150 dns_configuration_monitor(S_session, IPMonitorNotify);
9151 }
9152
9153 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9154 load_hostname(TRUE);
9155 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
9156
9157 #if !TARGET_OS_IPHONE
9158 load_smb_configuration(TRUE);
9159 #endif /* !TARGET_OS_IPHONE */
9160
9161 return;
9162 }
9163
9164
9165 #pragma mark -
9166 #pragma mark Standalone test code
9167
9168
9169 #ifdef TEST_IPMONITOR
9170
9171 int
9172 main(int argc, char **argv)
9173 {
9174 _sc_log = FALSE;
9175
9176 S_IPMonitor_debug = kDebugFlag1;
9177 if (argc > 1) {
9178 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
9179 }
9180
9181 load_IPMonitor(CFBundleGetMainBundle(), FALSE);
9182 prime_IPMonitor();
9183 S_IPMonitor_debug = kDebugFlag1;
9184 CFRunLoopRun();
9185 /* not reached */
9186 exit(0);
9187 return 0;
9188 }
9189 #endif /* TEST_IPMONITOR */
9190
9191 #ifdef TEST_ROUTELIST
9192
9193 struct route {
9194 const char * dest;
9195 int prefix_length;
9196 const char * gateway;
9197 const char * ifname;
9198 };
9199
9200 #endif
9201
9202 #if TEST_IPV4_ROUTELIST
9203
9204 typedef struct {
9205 const char * addr;
9206 int prefix_length;
9207 const char * dest;
9208 const char * router;
9209 const char * ifname;
9210 Rank rank;
9211 const CFStringRef * primary_rank;
9212 struct route * additional_routes;
9213 int additional_routes_count;
9214 struct route * excluded_routes;
9215 int excluded_routes_count;
9216 } IPv4ServiceContents;
9217
9218 typedef const IPv4ServiceContents * IPv4ServiceContentsRef;
9219
9220 struct route loop_routelist[] = {
9221 { "1.1.1.1", 32, "1.1.1.2", NULL },
9222 { "1.1.1.2", 32, "1.1.1.3", NULL },
9223 { "1.1.1.3", 32, "1.1.1.4", NULL },
9224 { "1.1.1.4", 32, "1.1.1.5", NULL },
9225 { "1.1.1.5", 32, "1.1.1.6", NULL },
9226 { "1.1.1.6", 32, "1.1.1.7", NULL },
9227 { "1.1.1.7", 32, "1.1.1.8", NULL },
9228 { "1.1.1.8", 32, "1.1.1.9", NULL },
9229 { "1.1.1.9", 32, "1.1.1.10", NULL },
9230 { "1.1.1.10", 32, "1.1.1.11", NULL },
9231 { "1.1.1.11", 32, "1.1.1.1", NULL },
9232 };
9233
9234 struct route vpn_routelist[] = {
9235 { "10.1.3.0", 24, "17.153.46.24", NULL },
9236 { "10.1.4.0", 24, "17.153.46.24", NULL },
9237 { "10.1.5.0", 24, "17.153.46.24", NULL },
9238 { "10.1.6.0", 24, "17.153.46.24", NULL },
9239 { "10.1.7.0", 24, "17.153.46.24", NULL },
9240 { "10.16.0.0", 12, "17.153.46.24", NULL },
9241 { "10.45.0.0", 16, "17.153.46.24", NULL },
9242 { "10.53.0.0", 16, "17.153.46.24", NULL },
9243 { "10.70.0.0", 15, "17.153.46.24", NULL },
9244 { "10.74.0.0", 15, "17.153.46.24", NULL },
9245 { "10.90.0.0", 15, "17.153.46.24", NULL },
9246 { "10.91.0.0", 16, "17.153.46.24", NULL },
9247 { "10.100.0.0", 16, "17.153.46.24", NULL },
9248 { "10.113.0.0", 16, "17.153.46.24", NULL },
9249 { "10.128.0.0", 9, "17.153.46.24", NULL },
9250 { "17.0.0.0", 9, "17.153.46.24", NULL },
9251 { "17.34.0.0", 16, "17.153.46.24", NULL },
9252 { "17.112.156.53", 32, "17.153.46.24", NULL },
9253 { "17.128.0.0", 10, "17.153.46.24", NULL },
9254 { "17.149.0.121", 32, "17.153.46.24", NULL },
9255 { "17.149.7.200", 32, "17.153.46.24", NULL },
9256 { "17.153.46.24", 32, "17.153.46.24", NULL },
9257 { "17.192.0.0", 12, "17.153.46.24", NULL },
9258 { "17.208.0.0", 15, "17.153.46.24", NULL },
9259 { "17.211.0.0", 16, "17.153.46.24", NULL },
9260 { "17.212.0.0", 14, "17.153.46.24", NULL },
9261 { "17.216.0.0", 13, "17.153.46.24", NULL },
9262 { "17.224.0.0", 12, "17.153.46.24", NULL },
9263 { "17.240.0.0", 16, "17.153.46.24", NULL },
9264 { "17.241.0.0", 16, "17.153.46.24", NULL },
9265 { "17.248.0.0", 14, "17.153.46.24", NULL },
9266 { "17.251.104.200", 32, "17.153.46.24", NULL },
9267 { "17.252.0.0", 16, "17.153.46.24", NULL },
9268 { "17.253.0.0", 16, "17.153.46.24", NULL },
9269 { "17.254.0.0", 16, "17.153.46.24", NULL },
9270 { "17.255.0.0", 16, "17.153.46.24", NULL },
9271 { "151.193.141.0", 27, "17.153.46.24", NULL },
9272 { "172.16.2.0", 24, "17.153.46.24", NULL },
9273 { "192.35.50.0", 24, "17.153.46.24", NULL },
9274 { "204.179.20.0", 24, "17.153.46.24", NULL },
9275 { "206.112.116.0", 24, "17.153.46.24", NULL },
9276 };
9277
9278 struct route vpn_routelist_ext[] = {
9279 { "17.151.63.82", 32, "10.0.0.1", "en0" },
9280 { "17.151.63.81", 32, "17.151.63.81", "en0" },
9281 { "17.151.63.80", 32, NULL, NULL },
9282 { "17.1.0.0", 16, NULL, NULL },
9283 { "17.2.0.0", 24, NULL, NULL },
9284 { "10.0.0.0", 24, NULL, NULL },
9285 };
9286
9287 /*
9288 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
9289 */
9290 const IPv4ServiceContents en0_10 = {
9291 "10.0.0.10", 24, NULL, "10.0.0.1", "en0", 10, NULL, NULL, 0, NULL, 0
9292 };
9293
9294 const IPv4ServiceContents en0_15 = {
9295 "10.0.0.19", 24, NULL, "10.0.0.1", "en0", 15, NULL, NULL, 0, NULL, 0
9296 };
9297
9298 const IPv4ServiceContents en0_30 = {
9299 "10.0.0.11", 24, NULL, "10.0.0.1", "en0", 30, NULL, NULL, 0, NULL, 0
9300 };
9301
9302 const IPv4ServiceContents en0_40 = {
9303 "10.0.0.12", 24, NULL, "10.0.0.1", "en0", 40, NULL, NULL, 0, NULL, 0
9304 };
9305
9306 const IPv4ServiceContents en0_50 = {
9307 "10.0.0.13", 24, NULL, "10.0.0.1", "en0", 50, NULL, NULL, 0, NULL, 0
9308 };
9309
9310 const IPv4ServiceContents en0_110 = {
9311 "192.168.2.10", 24, NULL, "192.168.2.1", "en0", 110, NULL, NULL, 0, NULL, 0
9312 };
9313
9314 const IPv4ServiceContents en0_1 = {
9315 "17.202.40.191", 22, NULL, "17.202.20.1", "en0", 1, NULL, NULL, 0, NULL, 0
9316 };
9317
9318 const IPv4ServiceContents en1_20 = {
9319 "10.0.0.20", 24, NULL, "10.0.0.1", "en1", 20, NULL, NULL, 0, NULL, 0
9320 };
9321
9322 const IPv4ServiceContents en1_2 = {
9323 "17.202.42.24", 22, NULL, "17.202.20.1", "en1", 2, NULL, NULL, 0, NULL, 0
9324 };
9325
9326 const IPv4ServiceContents en1_125 = {
9327 "192.168.2.20", 24, NULL, "192.168.2.1", "en1", 125, NULL, NULL, 0, NULL, 0
9328 };
9329
9330 const IPv4ServiceContents fw0_25 = {
9331 "192.168.2.30", 24, NULL, "192.168.2.1", "fw0", 25, NULL, NULL, 0, NULL, 0
9332 };
9333
9334 const IPv4ServiceContents fw0_21 = {
9335 "192.168.3.30", 24, NULL, "192.168.3.1", "fw0", 21, NULL, NULL, 0, NULL, 0
9336 };
9337
9338 const IPv4ServiceContents ppp0_0_1 = {
9339 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL, NULL, 0, NULL, 0
9340 };
9341
9342 const IPv4ServiceContents utun0 = {
9343 "17.153.46.24", -1, "17.153.46.24", "17.153.46.24", "utun0", 20, NULL, vpn_routelist, countof(vpn_routelist), vpn_routelist_ext, countof(vpn_routelist_ext)
9344 };
9345
9346 const IPv4ServiceContents en0_test6 = {
9347 "17.202.42.113", 22, NULL, "17.202.40.1", "en0", 2, NULL, NULL, 0, NULL, 0
9348 };
9349
9350 const IPv4ServiceContents en1_test6 = {
9351 "17.202.42.111", 22, NULL, "17.202.40.1", "en1", 3, NULL, NULL, 0, NULL, 0
9352 };
9353
9354 const IPv4ServiceContents en2_test6 = {
9355 "17.255.98.164", 20, NULL, "17.255.96.1", "en2", 1, NULL, NULL, 0, NULL, 0
9356 };
9357
9358 const IPv4ServiceContents en0_test7 = {
9359 "17.202.42.113", 22, NULL, "17.202.40.1", "en0", 3, NULL, NULL, 0, NULL, 0
9360 };
9361
9362 const IPv4ServiceContents en1_test7 = {
9363 "17.202.42.111", 22, NULL, "17.202.40.1", "en1", 2, NULL, NULL, 0, NULL, 0
9364 };
9365
9366 const IPv4ServiceContents en2_test7 = {
9367 "17.255.98.164", 20, NULL, "17.255.96.1", "en2", 1, NULL, NULL, 0, NULL, 0
9368 };
9369
9370 const IPv4ServiceContents fw0_test6_and_7 = {
9371 "169.254.11.33", 16, NULL, NULL, "fw0", 0x0ffffff, NULL, NULL, 0, NULL, 0
9372 };
9373
9374 const IPv4ServiceContents en0_10_last = {
9375 "10.0.0.10", 24, NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast, NULL, 0, NULL, 0
9376 };
9377
9378 const IPv4ServiceContents en0_10_never = {
9379 "10.0.0.10", 24, NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever, NULL, 0, NULL, 0
9380 };
9381
9382 const IPv4ServiceContents en1_20_first = {
9383 "10.0.0.20", 24, NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst, NULL, 0, NULL, 0
9384 };
9385
9386 const IPv4ServiceContents en1_20_never = {
9387 "10.0.0.20", 24, NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever, NULL, 0, NULL, 0
9388 };
9389
9390 const IPv4ServiceContents en1_20_other_never = {
9391 "192.168.2.50", 24, NULL, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever, NULL, 0, NULL, 0
9392 };
9393
9394 const IPv4ServiceContents en0_linklocal = {
9395 "169.254.22.44", 16, NULL, NULL, "en0", 0xfffff, NULL, NULL, 0, NULL, 0
9396 };
9397
9398 const IPv4ServiceContents en0_route_loop = {
9399 "192.168.130.16", 24, NULL, "192.168.130.1", "en0", 2, NULL, loop_routelist, countof(loop_routelist), NULL, 0
9400 };
9401
9402 typedef struct {
9403 const char * name;
9404 IPv4ServiceContentsRef test[];
9405 } IPv4RouteTest, * IPv4RouteTestRef;
9406
9407 static IPv4RouteTest test1 = {
9408 "test1",
9409 {
9410 &en0_40,
9411 &en0_15,
9412 &fw0_25,
9413 &en0_30,
9414 &en1_20,
9415 &en0_50,
9416 &en0_10,
9417 NULL
9418 }
9419 };
9420
9421 static IPv4RouteTest test2 = {
9422 "test2",
9423 {
9424 &en0_40,
9425 &fw0_25,
9426 &en0_30,
9427 &en1_20,
9428 &en0_50,
9429 &en0_10,
9430 NULL
9431 }
9432 };
9433
9434 static IPv4RouteTest test3 = {
9435 "test3",
9436 {
9437 &en0_40,
9438 &en1_20,
9439 &en0_50,
9440 &en0_10,
9441 &en0_110,
9442 &en1_125,
9443 &fw0_25,
9444 &fw0_21,
9445 &en0_40,
9446 &en0_30,
9447 NULL
9448 }
9449 };
9450
9451 static IPv4RouteTest test4 = {
9452 "test4",
9453 {
9454 &en0_1,
9455 &en0_40,
9456 &en0_30,
9457 &en1_20,
9458 &en1_2,
9459 NULL
9460 }
9461 };
9462
9463 static IPv4RouteTest test5 = {
9464 "test5",
9465 {
9466 &ppp0_0_1,
9467 &en0_1,
9468 &en0_40,
9469 &en0_30,
9470 &en1_20,
9471 &en1_2,
9472 NULL
9473 }
9474 };
9475
9476 static IPv4RouteTest test6 = {
9477 "test6",
9478 {
9479 &en0_test6,
9480 &en1_test6,
9481 &en2_test6,
9482 &fw0_test6_and_7,
9483 NULL
9484 }
9485 };
9486
9487 static IPv4RouteTest test7 = {
9488 "test7",
9489 {
9490 &en0_test7,
9491 &en1_test7,
9492 &en2_test7,
9493 &fw0_test6_and_7,
9494 NULL
9495 }
9496 };
9497
9498 static IPv4RouteTest test8 = {
9499 "test8",
9500 {
9501 &en0_10,
9502 &en1_20,
9503 NULL
9504 }
9505 };
9506
9507 static IPv4RouteTest test9 = {
9508 "test9",
9509 {
9510 &en0_10,
9511 &en1_20_first,
9512 &fw0_25,
9513 NULL
9514 }
9515 };
9516
9517 static IPv4RouteTest test10 = {
9518 "test10",
9519 {
9520 &en0_10_last,
9521 &en1_20,
9522 &fw0_25,
9523 NULL
9524 }
9525 };
9526
9527 static IPv4RouteTest test11 = {
9528 "test11",
9529 {
9530 &en0_10_never,
9531 &en1_20,
9532 &fw0_25,
9533 NULL
9534 }
9535 };
9536
9537 static IPv4RouteTest test12 = {
9538 "test12",
9539 {
9540 &en0_10,
9541 &en1_20,
9542 NULL
9543 }
9544 };
9545
9546 static IPv4RouteTest test13 = {
9547 "test13",
9548 {
9549 &en0_10,
9550 &en1_20_never,
9551 NULL
9552 }
9553 };
9554
9555 static IPv4RouteTest test14 = {
9556 "test14",
9557 {
9558 &en1_20_never,
9559 NULL
9560 }
9561 };
9562
9563 static IPv4RouteTest test15 = {
9564 "test15",
9565 {
9566 &en0_linklocal,
9567 NULL
9568 }
9569 };
9570
9571 static IPv4RouteTest test16 = {
9572 "test16",
9573 {
9574 &en0_10,
9575 &utun0,
9576 NULL
9577 }
9578 };
9579
9580 static IPv4RouteTest test17 = {
9581 "test17",
9582 {
9583 &en0_10,
9584 &en1_20_other_never,
9585 NULL
9586 }
9587 };
9588
9589 static IPv4RouteTest test18 = {
9590 "test18",
9591 {
9592 &en0_route_loop,
9593 NULL
9594 }
9595 };
9596
9597 static IPv4RouteTestRef ipv4_tests[] = {
9598 &test1,
9599 &test2,
9600 &test3,
9601 &test4,
9602 &test5,
9603 &test6,
9604 &test7,
9605 &test8,
9606 &test9,
9607 &test10,
9608 &test11,
9609 &test12,
9610 &test13,
9611 &test14,
9612 &test15,
9613 &test16,
9614 &test17,
9615 &test18,
9616 NULL
9617 };
9618
9619 static boolean_t
9620 ipv4_prefix_length_is_valid(int prefix_length)
9621 {
9622 if (prefix_length < 0 || prefix_length > IPV4_ROUTE_ALL_BITS_SET) {
9623 return (FALSE);
9624 }
9625 return (TRUE);
9626 }
9627
9628 static void
9629 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
9630 const char * str)
9631 {
9632 CFStringRef prop_val;
9633
9634 if (str == NULL) {
9635 return;
9636 }
9637 prop_val = CFStringCreateWithCString(NULL,
9638 str,
9639 kCFStringEncodingASCII);
9640 CFDictionarySetValue(dict, prop_name, prop_val);
9641 CFRelease(prop_val);
9642 return;
9643 }
9644
9645 static void
9646 dict_add_string_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
9647 const char * str)
9648 {
9649 CFArrayRef array;
9650 CFStringRef prop_val;
9651
9652 if (str == NULL) {
9653 return;
9654 }
9655 prop_val = CFStringCreateWithCString(NULL,
9656 str,
9657 kCFStringEncodingASCII);
9658 array = CFArrayCreate(NULL,
9659 (const void **)&prop_val, 1,
9660 &kCFTypeArrayCallBacks);
9661 CFRelease(prop_val);
9662 CFDictionarySetValue(dict, prop_name, array);
9663 CFRelease(array);
9664 return;
9665 }
9666
9667 static void
9668 dict_add_ip(CFMutableDictionaryRef dict, CFStringRef prop_name,
9669 struct in_addr ip)
9670 {
9671 CFStringRef str;
9672
9673 str = my_CFStringCreateWithInAddr(ip);
9674 CFDictionarySetValue(dict, prop_name, str);
9675 CFRelease(str);
9676 return;
9677 }
9678
9679 static void
9680 dict_add_ip_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
9681 struct in_addr ip)
9682 {
9683 CFArrayRef array;
9684 CFStringRef str;
9685
9686 str = my_CFStringCreateWithInAddr(ip);
9687 array = CFArrayCreate(NULL,
9688 (const void **)&str, 1,
9689 &kCFTypeArrayCallBacks);
9690 CFRelease(str);
9691 CFDictionarySetValue(dict, prop_name, array);
9692 CFRelease(array);
9693 return;
9694 }
9695
9696 static void
9697 dict_insert_routes(CFMutableDictionaryRef dict, CFStringRef prop_name,
9698 struct route * routes, int routes_count)
9699 {
9700 int i;
9701 CFMutableArrayRef route_list;
9702 struct route * scan;
9703
9704 if (routes == NULL || routes_count == 0) {
9705 return;
9706 }
9707 route_list = CFArrayCreateMutable(NULL, routes_count,
9708 &kCFTypeArrayCallBacks);
9709 for (i = 0, scan = routes; i < routes_count; i++, scan++) {
9710 struct in_addr mask;
9711 CFMutableDictionaryRef route_dict;
9712
9713 route_dict
9714 = CFDictionaryCreateMutable(NULL, 0,
9715 &kCFTypeDictionaryKeyCallBacks,
9716 &kCFTypeDictionaryValueCallBacks);
9717 dict_add_string(route_dict, kSCPropNetIPv4RouteDestinationAddress,
9718 scan->dest);
9719 if (ipv4_prefix_length_is_valid(scan->prefix_length)) {
9720 mask.s_addr = htonl(prefix_to_mask32(scan->prefix_length));
9721 dict_add_ip(route_dict, kSCPropNetIPv4RouteSubnetMask, mask);
9722 }
9723 dict_add_string(route_dict, kSCPropNetIPv4RouteGatewayAddress,
9724 scan->gateway);
9725 dict_add_string(route_dict, kSCPropNetIPv4RouteInterfaceName,
9726 scan->ifname);
9727 CFArrayAppendValue(route_list, route_dict);
9728 CFRelease(route_dict);
9729 }
9730 CFDictionarySetValue(dict, prop_name, route_list);
9731 CFRelease(route_list);
9732 return;
9733 }
9734
9735 static CFDictionaryRef
9736 make_IPv4_dict(IPv4ServiceContentsRef t)
9737 {
9738 CFMutableDictionaryRef dict;
9739
9740 dict = CFDictionaryCreateMutable(NULL, 0,
9741 &kCFTypeDictionaryKeyCallBacks,
9742 &kCFTypeDictionaryValueCallBacks);
9743 dict_add_string_as_array(dict, kSCPropNetIPv4Addresses, t->addr);
9744 if (ipv4_prefix_length_is_valid(t->prefix_length)) {
9745 struct in_addr mask;
9746
9747 mask.s_addr = htonl(prefix_to_mask32(t->prefix_length));
9748 dict_add_ip_as_array(dict, kSCPropNetIPv4SubnetMasks, mask);
9749 }
9750 dict_add_string_as_array(dict, kSCPropNetIPv4DestAddresses, t->dest);
9751 dict_add_string(dict, kSCPropNetIPv4Router, t->router);
9752 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
9753 dict_add_string(dict, kSCPropConfirmedInterfaceName, t->ifname);
9754 dict_insert_routes(dict, kSCPropNetIPv4AdditionalRoutes,
9755 t->additional_routes, t->additional_routes_count);
9756 dict_insert_routes(dict, kSCPropNetIPv4ExcludedRoutes,
9757 t->excluded_routes, t->excluded_routes_count);
9758 return (dict);
9759 }
9760
9761 typedef enum {
9762 kDirectionForwards = 0,
9763 kDirectionBackwards = 1
9764 } Direction;
9765
9766 typedef enum {
9767 kLogRouteDisabled = 0,
9768 kLogRouteEnabled = 1
9769 } LogRoute;
9770
9771 static IPv4RouteListRef
9772 make_IPv4RouteList_for_test(IPv4RouteListRef list,
9773 IPv4ServiceContentsRef test,
9774 LogRoute log_it)
9775 {
9776 CFDictionaryRef dict;
9777 IPv4RouteListRef r;
9778 Rank rank;
9779 Rank rank_assertion = kRankAssertionDefault;
9780 CFNumberRef rank_assertion_cf = NULL;
9781 Boolean rank_assertion_is_set = FALSE;
9782 IPv4RouteListRef ret = NULL;
9783 IPV4_ROUTES_BUF_DECL(routes);
9784
9785 dict = make_IPv4_dict(test);
9786 if (dict == NULL) {
9787 fprintf(stderr, "make_IPv4_dict failed\n");
9788 exit(1);
9789 }
9790 if (test->primary_rank != NULL) {
9791 rank_assertion
9792 = PrimaryRankGetRankAssertion(*test->primary_rank,
9793 &rank_assertion_is_set);
9794 if (rank_assertion_is_set) {
9795 rank_assertion_cf
9796 = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank_assertion);
9797 }
9798 }
9799 r = IPv4RouteListCreateWithDictionary(routes, dict, rank_assertion_cf);
9800 my_CFRelease(&rank_assertion_cf);
9801 if (r == NULL) {
9802 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
9803 exit(1);
9804 }
9805
9806 if (rank_assertion == kRankAssertionScoped) {
9807 rank_assertion = kRankAssertionNever;
9808 }
9809 rank = RankMake(test->rank, rank_assertion);
9810 if (log_it == kLogRouteEnabled
9811 && (S_IPMonitor_debug & kDebugFlag4) != 0) {
9812 CFStringRef descr;
9813
9814 descr = IPv4RouteListCopyDescription(r);
9815 SCPrint(TRUE, stdout, CFSTR("Adding %@\n"), descr);
9816 CFRelease(descr);
9817 }
9818 ret = IPv4RouteListAddRouteList(list, 1, r, rank);
9819 if (r != routes) {
9820 free(r);
9821 }
9822 CFRelease(dict);
9823 return (ret);
9824 }
9825
9826 static IPv4RouteListRef
9827 make_IPv4RouteList(IPv4ServiceContentsRef * test, Direction direction,
9828 LogRoute log_it)
9829 {
9830 IPv4RouteListRef ret = NULL;
9831 IPv4ServiceContentsRef * scan;
9832
9833 switch (direction) {
9834 case kDirectionBackwards:
9835 for (scan = test; *scan != NULL; scan++) {
9836 /* find the end of the list */
9837 }
9838 for (scan--; scan >= test; scan--) {
9839 ret = make_IPv4RouteList_for_test(ret, *scan, log_it);
9840 }
9841 break;
9842 default:
9843 case kDirectionForwards:
9844 for (scan = test; *scan != NULL; scan++) {
9845 ret = make_IPv4RouteList_for_test(ret, *scan, log_it);
9846 }
9847 break;
9848 }
9849 IPv4RouteListFinalize(ret);
9850 return (ret);
9851 }
9852
9853 #define EMPHASIS_CHARS "================="
9854
9855 /*
9856 * Function: routelist_build_test
9857 * Purpose:
9858 * Runs through the given set of routes first in the forward direction,
9859 * then again backwards. We should end up with exactly the same set of
9860 * routes at the end.
9861 */
9862 static boolean_t
9863 routelist_build_test(IPv4RouteTestRef test)
9864 {
9865 CFStringRef descr;
9866 boolean_t ret = FALSE;
9867 IPv4RouteListRef routes1;
9868 IPv4RouteListRef routes2;
9869
9870 printf("\n" EMPHASIS_CHARS "> RouteList Build '%s' <"
9871 EMPHASIS_CHARS "\n",
9872 test->name);
9873
9874 routes1 = make_IPv4RouteList(test->test, kDirectionForwards,
9875 kLogRouteEnabled);
9876 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
9877 if (routes1 != NULL) {
9878 descr = IPv4RouteListCopyDescription(routes1);
9879 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
9880 CFRelease(descr);
9881 }
9882 }
9883 routes2 = make_IPv4RouteList(test->test, kDirectionBackwards,
9884 kLogRouteEnabled);
9885 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
9886 if (routes2 != NULL) {
9887 descr = IPv4RouteListCopyDescription(routes2);
9888 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
9889 CFRelease(descr);
9890 }
9891 }
9892 if ((routes1 != NULL && routes2 == NULL)
9893 || (routes1 == NULL && routes2 != NULL)) {
9894 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
9895 (routes1 != NULL) ? "not " : "",
9896 (routes2 != NULL) ? "not " : "");
9897 }
9898 else if (routes1 != NULL && routes2 != NULL) {
9899 /* check if they are different */
9900 if (routes1->count != routes2->count) {
9901 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
9902 routes1->count, routes2->count);
9903 }
9904 else if (bcmp(routes1, routes2,
9905 IPv4RouteListComputeSize(routes1->count)) != 0) {
9906 fprintf(stderr, "routes1 and routes2 are different\n");
9907 }
9908 else {
9909 printf("routes1 and routes2 are the same\n");
9910 ret = TRUE;
9911 }
9912 }
9913 if (routes1 != NULL) {
9914 free(routes1);
9915 }
9916 if (routes2 != NULL) {
9917 free(routes2);
9918 }
9919 printf(EMPHASIS_CHARS "> RouteList Build '%s': %s <"
9920 EMPHASIS_CHARS "\n",
9921 test->name, ret ? "PASSED" : "FAILED");
9922 return (ret);
9923 }
9924
9925 static void
9926 apply_test(IPv4RouteTestRef old_test, IPv4RouteTestRef new_test)
9927 {
9928 IPv4RouteListRef new_routes;
9929 IPv4RouteListRef old_routes;
9930
9931 printf("\n" EMPHASIS_CHARS "> Apply '%s', '%s' Begin <"
9932 EMPHASIS_CHARS "\n",
9933 old_test->name, new_test->name);
9934
9935 old_routes = make_IPv4RouteList(old_test->test, kDirectionForwards,
9936 kLogRouteDisabled);
9937 new_routes = make_IPv4RouteList(new_test->test, kDirectionForwards,
9938 kLogRouteDisabled);
9939 if (old_routes == NULL) {
9940 printf("No Old Routes\n");
9941 }
9942 else {
9943 printf("Old routes ('%s') = ", old_test->name);
9944 IPv4RouteListPrint(old_routes);
9945 }
9946
9947 /* apply the old routes */
9948 IPv4RouteListApply(NULL, old_routes, -1);
9949
9950 if (new_routes == NULL) {
9951 printf("No New Routes\n");
9952 }
9953 else {
9954 printf("New Routes ('%s') = ", new_test->name);
9955 IPv4RouteListPrint(new_routes);
9956 }
9957
9958 /* apply the new routes */
9959 IPv4RouteListApply(old_routes, new_routes, -1);
9960
9961 if (old_routes != NULL) {
9962 free(old_routes);
9963 }
9964 if (new_routes != NULL) {
9965 free(new_routes);
9966 }
9967 printf(EMPHASIS_CHARS "> Apply '%s', '%s' End <"
9968 EMPHASIS_CHARS "\n",
9969 old_test->name, new_test->name);
9970 return;
9971 }
9972
9973 int
9974 main(int argc, char **argv)
9975 {
9976 IPv4RouteTestRef * test;
9977
9978 _sc_log = FALSE;
9979 _sc_verbose = (argc > 1) ? TRUE : FALSE;
9980 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
9981 if (argc > 1) {
9982 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
9983 }
9984 for (test = ipv4_tests; *test != NULL; test++) {
9985 if (!routelist_build_test(*test)) {
9986 fprintf(stderr, "%s failed\n", (*test)->name);
9987 exit(1);
9988 }
9989 }
9990 for (test = ipv4_tests; *test != NULL; test++) {
9991 IPv4RouteTestRef * test2;
9992
9993 for (test2 = test + 1; *test2 != NULL; test2++) {
9994 apply_test(*test, *test2);
9995 apply_test(*test2, *test);
9996 }
9997 }
9998
9999 {
10000 char cmd[128];
10001
10002 printf("\nChecking for leaks\n");
10003 sprintf(cmd, "leaks %d 2>&1", getpid());
10004 fflush(stdout);
10005 (void)system(cmd);
10006 }
10007 exit(0);
10008 return (0);
10009 }
10010
10011 #endif /* TEST_IPV4_ROUTELIST */
10012
10013 #if TEST_IPV6_ROUTELIST
10014
10015 typedef struct {
10016 const char * addr;
10017 int prefix_length;
10018 const char * dest;
10019 } IPv6Address;
10020
10021 typedef const IPv6Address * IPv6AddressRef;
10022
10023 typedef struct {
10024 IPv6AddressRef addr;
10025 int addr_count;
10026 const char * router;
10027 const char * ifname;
10028 Rank rank;
10029 const CFStringRef * primary_rank;
10030 struct route * additional_routes;
10031 int additional_routes_count;
10032 struct route * excluded_routes;
10033 int excluded_routes_count;
10034 } IPv6ServiceContents;
10035
10036 typedef const IPv6ServiceContents * IPv6ServiceContentsRef;
10037
10038 struct route loop_routelist[] = {
10039 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
10040 "2620:149:4:f01:225:ff:fecc:89a2", NULL },
10041 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
10042 "2620:149:4:f01:225:ff:fecc:89a3", NULL },
10043 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
10044 "2620:149:4:f01:225:ff:fecc:89a4", NULL },
10045 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
10046 "2620:149:4:f01:225:ff:fecc:89a5", NULL },
10047 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
10048 "2620:149:4:f01:225:ff:fecc:89a6", NULL },
10049 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
10050 "2620:149:4:f01:225:ff:fecc:89a7", NULL },
10051 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
10052 "2620:149:4:f01:225:ff:fecc:89a8", NULL },
10053 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
10054 "2620:149:4:f01:225:ff:fecc:89a9", NULL },
10055 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
10056 "2620:149:4:f01:225:ff:fecc:89aa", NULL },
10057 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
10058 "2620:149:4:f01:225:ff:fecc:89ab", NULL },
10059 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
10060 "2620:149:4:f01:225:ff:fecc:89a1", NULL },
10061 };
10062
10063 struct route vpn_routelist[] = {
10064 { "2010:470:1f05:3cb::", 64,
10065 "fe80::2d0:bcff:fe3d:8c00", NULL },
10066 { "2010:222:3fa5:acb::", 48,
10067 "fe80::2d0:bcff:fe3d:8c00", NULL },
10068 { "2010:222:3fa5:1234::", 40,
10069 "fe80::2d0:bcff:fe3d:8c00", NULL },
10070 { "2010:222:3fa5:5678::", 40,
10071 NULL, NULL },
10072 };
10073
10074 struct route vpn_routelist_ext[] = {
10075 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL, NULL },
10076 };
10077
10078 struct route en1_routelist_ext[] = {
10079 { "2020:299:abcd:ef12::", 64, NULL, NULL },
10080 };
10081
10082
10083 static const IPv6Address en0_addr1[] = {
10084 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL },
10085 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL }
10086 };
10087
10088 static const IPv6Address en0_addr2[] = {
10089 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL },
10090 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL }
10091 };
10092
10093 static const IPv6Address en0_addr3[] = {
10094 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL },
10095 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL }
10096 };
10097
10098 static const IPv6Address en0_addr4[] = {
10099 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL },
10100 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL }
10101 };
10102
10103 static const IPv6Address en0_addr5[] = {
10104 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL },
10105 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL }
10106 };
10107
10108 static const IPv6Address en0_addr6[] = {
10109 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL },
10110 };
10111
10112 static const IPv6Address en0_lladdr[] = {
10113 { "fe80::cabc:c8ff:fe96:96af", 64, NULL }
10114 };
10115
10116 static const IPv6Address en1_addr[] = {
10117 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL },
10118 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL }
10119 };
10120
10121 static const IPv6Address utun0_addr[] = {
10122 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL },
10123 };
10124
10125 static const IPv6Address fw0_addr1[] = {
10126 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL },
10127 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL }
10128 };
10129
10130 /*
10131 * address+address-count
10132 * router ifname pri rank additional-routes+count excluded-routes+count
10133 */
10134
10135 static const IPv6ServiceContents en0_10 = {
10136 en0_addr1, countof(en0_addr1),
10137 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL, NULL, 0, NULL, 0
10138 };
10139
10140 static const IPv6ServiceContents en0_15 = {
10141 en0_addr2, countof(en0_addr2),
10142 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL, NULL, 0, NULL, 0
10143 };
10144
10145 static const IPv6ServiceContents en0_30 = {
10146 en0_addr3, countof(en0_addr3),
10147 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL, NULL, 0, NULL, 0
10148 };
10149
10150 static const IPv6ServiceContents en0_40 = {
10151 en0_addr4, countof(en0_addr4),
10152 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL, NULL, 0, NULL, 0
10153 };
10154
10155 static const IPv6ServiceContents en0_50 = {
10156 en0_addr5, countof(en0_addr5),
10157 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL, NULL, 0, NULL, 0
10158 };
10159
10160 static const IPv6ServiceContents en0_10_a = {
10161 en0_addr6, countof(en0_addr6),
10162 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL, NULL, 0, NULL, 0
10163 };
10164
10165 static const IPv6ServiceContents fw0_25 = {
10166 fw0_addr1, countof(fw0_addr1),
10167 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL, NULL, 0, NULL, 0
10168 };
10169
10170 static const IPv6ServiceContents en1_20 = {
10171 en1_addr, countof(en1_addr),
10172 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL, NULL, 0, NULL, 0
10173 };
10174
10175 static const IPv6ServiceContents en1_10_ext = {
10176 en1_addr, countof(en1_addr),
10177 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL, NULL, 0,
10178 en1_routelist_ext, countof(en1_routelist_ext)
10179 };
10180
10181 static const IPv6ServiceContents en0_0_lladdr = {
10182 en0_lladdr, countof(en0_lladdr),
10183 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL, NULL, 0, NULL, 0
10184 };
10185
10186 static const IPv6ServiceContents en0_loop = {
10187 en0_addr1, countof(en0_addr1),
10188 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL,
10189 loop_routelist, countof(loop_routelist), NULL, 0
10190 };
10191
10192 static const IPv6ServiceContents utun0 = {
10193 utun0_addr, countof(utun0_addr),
10194 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL,
10195 vpn_routelist, countof(vpn_routelist),
10196 vpn_routelist_ext, countof(vpn_routelist_ext),
10197 };
10198
10199 typedef struct {
10200 const char * name;
10201 IPv6ServiceContentsRef test[];
10202 } IPv6RouteTest, * IPv6RouteTestRef;
10203
10204 static IPv6RouteTest test1 = {
10205 "test1",
10206 {
10207 &en0_40,
10208 &en0_15,
10209 &fw0_25,
10210 &en0_30,
10211 &en1_20,
10212 &en0_50,
10213 &en0_10,
10214 NULL
10215 }
10216 };
10217
10218 static IPv6RouteTest test2 = {
10219 "test2",
10220 {
10221 &en0_40,
10222 &fw0_25,
10223 &en0_30,
10224 &en1_20,
10225 &en0_50,
10226 &en0_10,
10227 NULL
10228 }
10229 };
10230
10231 static IPv6RouteTest test3 = {
10232 "test3",
10233 {
10234 &en0_10_a,
10235 &en1_10_ext,
10236 NULL
10237 }
10238 };
10239
10240 static IPv6RouteTest test4 = {
10241 "test4",
10242 {
10243 &en0_loop,
10244 &en1_20,
10245 NULL
10246 }
10247 };
10248
10249 static IPv6RouteTest test5 = {
10250 "test5",
10251 {
10252 &en0_10,
10253 &utun0,
10254 &en0_0_lladdr,
10255 &en1_20,
10256 NULL
10257 }
10258 };
10259
10260
10261 static IPv6RouteTestRef ipv6_tests[] = {
10262 &test1,
10263 &test2,
10264 &test3,
10265 &test4,
10266 &test5,
10267 NULL
10268 };
10269
10270
10271 static void
10272 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
10273 const char * str)
10274 {
10275 CFStringRef prop_val;
10276
10277 if (str == NULL) {
10278 return;
10279 }
10280 prop_val = CFStringCreateWithCString(NULL,
10281 str,
10282 kCFStringEncodingASCII);
10283 CFDictionarySetValue(dict, prop_name, prop_val);
10284 CFRelease(prop_val);
10285 return;
10286 }
10287
10288 static void
10289 dict_add_int(CFMutableDictionaryRef dict, CFStringRef prop_name,
10290 int int_val)
10291 {
10292 CFNumberRef num;
10293
10294 num = CFNumberCreate(NULL, kCFNumberIntType, &int_val);
10295 CFDictionarySetValue(dict, prop_name, num);
10296 CFRelease(num);
10297 return;
10298 }
10299
10300 static void
10301 dict_insert_v6_routes(CFMutableDictionaryRef dict, CFStringRef prop_name,
10302 struct route * routes, int routes_count)
10303 {
10304 int i;
10305 CFMutableArrayRef route_list;
10306 struct route * scan;
10307
10308 if (routes == NULL || routes_count == 0) {
10309 return;
10310 }
10311 route_list = CFArrayCreateMutable(NULL, routes_count,
10312 &kCFTypeArrayCallBacks);
10313 for (i = 0, scan = routes; i < routes_count; i++, scan++) {
10314 CFMutableDictionaryRef route_dict;
10315
10316 route_dict = CFDictionaryCreateMutable(NULL, 0,
10317 &kCFTypeDictionaryKeyCallBacks,
10318 &kCFTypeDictionaryValueCallBacks);
10319 dict_add_string(route_dict, kSCPropNetIPv6RouteDestinationAddress,
10320 scan->dest);
10321 dict_add_int(route_dict, kSCPropNetIPv6PrefixLength,
10322 scan->prefix_length);
10323 dict_add_string(route_dict, kSCPropNetIPv6RouteGatewayAddress,
10324 scan->gateway);
10325 dict_add_string(route_dict, kSCPropNetIPv6RouteInterfaceName,
10326 scan->ifname);
10327 CFArrayAppendValue(route_list, route_dict);
10328 CFRelease(route_dict);
10329 }
10330 CFDictionarySetValue(dict, prop_name, route_list);
10331 CFRelease(route_list);
10332 return;
10333 }
10334
10335 static void
10336 array_add_string(CFMutableArrayRef array, const char * c_str)
10337 {
10338 CFStringRef str;
10339
10340 str = CFStringCreateWithCString(NULL,
10341 c_str,
10342 kCFStringEncodingUTF8);
10343 CFArrayAppendValue(array, str);
10344 CFRelease(str);
10345 return;
10346 }
10347
10348 static void
10349 array_add_int(CFMutableArrayRef array, int int_val)
10350 {
10351 CFNumberRef num;
10352
10353 num = CFNumberCreate(NULL, kCFNumberIntType, &int_val);
10354 CFArrayAppendValue(array, num);
10355 CFRelease(num);
10356 return;
10357 }
10358
10359 static void
10360 dict_add_ipv6_addressing(CFMutableDictionaryRef dict,
10361 IPv6AddressRef list, int list_count)
10362 {
10363 CFMutableArrayRef addr = NULL;
10364 CFMutableArrayRef dest = NULL;
10365 int i;
10366 CFMutableArrayRef prefix = NULL;
10367 IPv6AddressRef scan;
10368
10369 if (list == NULL || list_count == 0) {
10370 return;
10371 }
10372 for (i = 0, scan = list; i < list_count; i++, scan++) {
10373 if (scan->addr != NULL) {
10374 if (addr == NULL) {
10375 addr = CFArrayCreateMutable(NULL, list_count,
10376 &kCFTypeArrayCallBacks);
10377 }
10378 array_add_string(addr, scan->addr);
10379 }
10380 if (scan->prefix_length >= 0) {
10381 if (prefix == NULL) {
10382 prefix = CFArrayCreateMutable(NULL, list_count,
10383 &kCFTypeArrayCallBacks);
10384 }
10385 array_add_int(prefix, scan->prefix_length);
10386 }
10387 if (scan->dest != NULL) {
10388 if (dest == NULL) {
10389 dest = CFArrayCreateMutable(NULL, list_count,
10390 &kCFTypeArrayCallBacks);
10391 }
10392 array_add_string(dest, scan->dest);
10393 }
10394 }
10395 if (addr != NULL) {
10396 CFDictionarySetValue(dict, kSCPropNetIPv6Addresses, addr);
10397 CFRelease(addr);
10398 }
10399 if (dest != NULL) {
10400 CFDictionarySetValue(dict, kSCPropNetIPv6DestAddresses, dest);
10401 CFRelease(dest);
10402 }
10403 if (prefix != NULL) {
10404 CFDictionarySetValue(dict, kSCPropNetIPv6PrefixLength, prefix);
10405 CFRelease(prefix);
10406 }
10407 return;
10408 }
10409
10410 static CFDictionaryRef
10411 make_IPv6_dict(IPv6ServiceContentsRef t)
10412 {
10413 CFMutableDictionaryRef dict;
10414
10415 dict = CFDictionaryCreateMutable(NULL, 0,
10416 &kCFTypeDictionaryKeyCallBacks,
10417 &kCFTypeDictionaryValueCallBacks);
10418 dict_add_ipv6_addressing(dict, t->addr, t->addr_count);
10419 dict_add_string(dict, kSCPropNetIPv6Router, t->router);
10420 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
10421 dict_insert_v6_routes(dict, kSCPropNetIPv6AdditionalRoutes,
10422 t->additional_routes, t->additional_routes_count);
10423 dict_insert_v6_routes(dict, kSCPropNetIPv6ExcludedRoutes,
10424 t->excluded_routes, t->excluded_routes_count);
10425 return (dict);
10426 }
10427
10428 typedef enum {
10429 kDirectionForwards = 0,
10430 kDirectionBackwards = 1
10431 } Direction;
10432
10433 typedef enum {
10434 kLogRouteDisabled = 0,
10435 kLogRouteEnabled = 1
10436 } LogRoute;
10437
10438 static IPv6RouteListRef
10439 make_IPv6RouteList_for_test(IPv6RouteListRef list,
10440 IPv6ServiceContentsRef test,
10441 LogRoute log_it)
10442 {
10443 CFDictionaryRef dict;
10444 IPv6RouteListRef r;
10445 Rank rank;
10446 Rank rank_assertion = kRankAssertionDefault;
10447 CFNumberRef rank_assertion_cf = NULL;
10448 Boolean rank_assertion_is_set = FALSE;
10449 IPv6RouteListRef ret = NULL;
10450 IPV6_ROUTES_BUF_DECL(routes);
10451
10452 dict = make_IPv6_dict(test);
10453 if (dict == NULL) {
10454 fprintf(stderr, "make_IPv6_dict failed\n");
10455 exit(1);
10456 }
10457 if (test->primary_rank != NULL) {
10458 rank_assertion
10459 = PrimaryRankGetRankAssertion(*test->primary_rank,
10460 &rank_assertion_is_set);
10461 if (rank_assertion_is_set) {
10462 rank_assertion_cf
10463 = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank_assertion);
10464 }
10465 }
10466 r = IPv6RouteListCreateWithDictionary(routes, dict, rank_assertion_cf);
10467 my_CFRelease(&rank_assertion_cf);
10468 if (r == NULL) {
10469 fprintf(stderr, "IPv6RouteListCreateWithDictionary failed\n");
10470 exit(1);
10471 }
10472
10473 if (rank_assertion == kRankAssertionScoped) {
10474 rank_assertion = kRankAssertionNever;
10475 }
10476 rank = RankMake(test->rank, rank_assertion);
10477 if (log_it == kLogRouteEnabled
10478 && (S_IPMonitor_debug & kDebugFlag4) != 0) {
10479 CFStringRef descr;
10480
10481 descr = IPv6RouteListCopyDescription(r);
10482 SCPrint(TRUE, stdout, CFSTR("Adding %@\n"), descr);
10483 CFRelease(descr);
10484 }
10485 ret = IPv6RouteListAddRouteList(list, 1, r, rank);
10486 if (r != routes) {
10487 free(r);
10488 }
10489 CFRelease(dict);
10490 return (ret);
10491 }
10492
10493 static IPv6RouteListRef
10494 make_IPv6RouteList(IPv6ServiceContentsRef * test, Direction direction,
10495 LogRoute log_it)
10496 {
10497 IPv6RouteListRef ret = NULL;
10498 IPv6ServiceContentsRef * scan;
10499
10500 switch (direction) {
10501 case kDirectionBackwards:
10502 for (scan = test; *scan != NULL; scan++) {
10503 /* find the end of the list */
10504 }
10505 for (scan--; scan >= test; scan--) {
10506 ret = make_IPv6RouteList_for_test(ret, *scan, log_it);
10507 }
10508 break;
10509 default:
10510 case kDirectionForwards:
10511 for (scan = test; *scan != NULL; scan++) {
10512 ret = make_IPv6RouteList_for_test(ret, *scan, log_it);
10513 }
10514 break;
10515 }
10516 IPv6RouteListFinalize(ret);
10517 return (ret);
10518 }
10519
10520 #define EMPHASIS_CHARS "================="
10521
10522 /*
10523 * Function: routelist_build_test
10524 * Purpose:
10525 * Runs through the given set of routes first in the forward direction,
10526 * then again backwards. We should end up with exactly the same set of
10527 * routes at the end.
10528 */
10529 static boolean_t
10530 routelist_build_test(IPv6RouteTestRef test)
10531 {
10532 CFStringRef descr;
10533 boolean_t ret = FALSE;
10534 IPv6RouteListRef routes1;
10535 IPv6RouteListRef routes2;
10536
10537 printf("\n" EMPHASIS_CHARS "> RouteList Build '%s' <"
10538 EMPHASIS_CHARS "\n",
10539 test->name);
10540
10541 routes1 = make_IPv6RouteList(test->test, kDirectionForwards,
10542 kLogRouteEnabled);
10543 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
10544 if (routes1 != NULL) {
10545 descr = IPv6RouteListCopyDescription(routes1);
10546 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
10547 CFRelease(descr);
10548 }
10549 }
10550 routes2 = make_IPv6RouteList(test->test, kDirectionBackwards,
10551 kLogRouteEnabled);
10552 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
10553 if (routes2 != NULL) {
10554 descr = IPv6RouteListCopyDescription(routes2);
10555 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
10556 CFRelease(descr);
10557 }
10558 }
10559 if ((routes1 != NULL && routes2 == NULL)
10560 || (routes1 == NULL && routes2 != NULL)) {
10561 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
10562 (routes1 != NULL) ? "not " : "",
10563 (routes2 != NULL) ? "not " : "");
10564 }
10565 else if (routes1 != NULL && routes2 != NULL) {
10566 /* check if they are different */
10567 if (routes1->count != routes2->count) {
10568 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
10569 routes1->count, routes2->count);
10570 }
10571 else if (bcmp(routes1, routes2,
10572 IPv6RouteListComputeSize(routes1->count)) != 0) {
10573 fprintf(stderr, "routes1 and routes2 are different\n");
10574 }
10575 else {
10576 printf("routes1 and routes2 are the same\n");
10577 ret = TRUE;
10578 }
10579 }
10580 if (routes1 != NULL) {
10581 free(routes1);
10582 }
10583 if (routes2 != NULL) {
10584 free(routes2);
10585 }
10586 printf(EMPHASIS_CHARS "> RouteList Build '%s': %s <"
10587 EMPHASIS_CHARS "\n",
10588 test->name, ret ? "PASSED" : "FAILED");
10589 return (ret);
10590 }
10591
10592 static void
10593 apply_test(IPv6RouteTestRef old_test, IPv6RouteTestRef new_test)
10594 {
10595 IPv6RouteListRef new_routes;
10596 IPv6RouteListRef old_routes;
10597
10598 printf("\n" EMPHASIS_CHARS "> Apply '%s', '%s' Begin <"
10599 EMPHASIS_CHARS "\n",
10600 old_test->name, new_test->name);
10601
10602 old_routes = make_IPv6RouteList(old_test->test, kDirectionForwards,
10603 kLogRouteDisabled);
10604 new_routes = make_IPv6RouteList(new_test->test, kDirectionForwards,
10605 kLogRouteDisabled);
10606 if (old_routes == NULL) {
10607 printf("No Old Routes\n");
10608 }
10609 else {
10610 printf("Old routes ('%s') = ", old_test->name);
10611 IPv6RouteListPrint(old_routes);
10612 }
10613
10614 /* apply the old routes */
10615 IPv6RouteListApply(NULL, old_routes, -1);
10616 if (new_routes == NULL) {
10617 printf("No New Routes\n");
10618 }
10619 else {
10620 printf("New Routes ('%s') = ", new_test->name);
10621 IPv6RouteListPrint(new_routes);
10622 }
10623
10624 /* apply the new routes */
10625 IPv6RouteListApply(old_routes, new_routes, -1);
10626 if (old_routes != NULL) {
10627 free(old_routes);
10628 }
10629 if (new_routes != NULL) {
10630 free(new_routes);
10631 }
10632 printf(EMPHASIS_CHARS "> Apply '%s', '%s' End <"
10633 EMPHASIS_CHARS "\n",
10634 old_test->name, new_test->name);
10635 return;
10636 }
10637
10638 int
10639 main(int argc, char **argv)
10640 {
10641 IPv6RouteTestRef * test;
10642
10643 _sc_log = FALSE;
10644 _sc_verbose = (argc > 1) ? TRUE : FALSE;
10645 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
10646 if (argc > 1) {
10647 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
10648 }
10649 for (test = ipv6_tests; *test != NULL; test++) {
10650 if (!routelist_build_test(*test)) {
10651 fprintf(stderr, "%s failed\n", (*test)->name);
10652 exit(1);
10653 }
10654 }
10655 for (test = ipv6_tests; *test != NULL; test++) {
10656 IPv6RouteTestRef * test2;
10657
10658 for (test2 = test + 1; *test2 != NULL; test2++) {
10659 apply_test(*test, *test2);
10660 apply_test(*test2, *test);
10661 }
10662 }
10663
10664 {
10665 char cmd[128];
10666
10667 printf("\nChecking for leaks\n");
10668 sprintf(cmd, "leaks %d 2>&1", getpid());
10669 fflush(stdout);
10670 (void)system(cmd);
10671 }
10672 exit(0);
10673 return (0);
10674 }
10675
10676 #endif /* TEST_IPV6_ROUTELIST */
10677
10678 #ifdef TEST_DNS_ORDER
10679
10680 #define kProtocolFlagsIPv4v6 (kProtocolFlagsIPv4 | kProtocolFlagsIPv6)
10681
10682 #define V4_ADDR_LOOP CFSTR("127.0.0.1")
10683 #define V4_ADDR_1 CFSTR("192.168.1.1")
10684 #define V4_ADDR_2 CFSTR("192.168.1.2")
10685 #define V4_ADDR_3 CFSTR("8.8.8.8")
10686 #define V4_ADDR_4 CFSTR("8.8.4.4")
10687
10688 #define V6_ADDR_LOOP CFSTR("::1")
10689 #define V6_ADDR_1 CFSTR("fe80::0123:4567:89ab:cdef%en0")
10690 #define V6_ADDR_2 CFSTR("fd00::2acf:e9ff:fe14:8c59")
10691 #define V6_ADDR_3 CFSTR("2001:4860:4860::8888")
10692
10693 typedef struct {
10694 const char * name;
10695 const ProtocolFlags flags;
10696 const CFStringRef server_addrs[];
10697 } DNSOrderTest, * DNSOrderTestRef;
10698
10699 static DNSOrderTest test0a = {
10700 "test0a",
10701 kProtocolFlagsIPv4,
10702 {
10703 V4_ADDR_1, V4_ADDR_2, V4_ADDR_3, V4_ADDR_4, NULL
10704 }
10705 };
10706
10707 static DNSOrderTest test0b = {
10708 "test0b",
10709 kProtocolFlagsIPv6,
10710 {
10711 V6_ADDR_1, V6_ADDR_2, V6_ADDR_3, NULL
10712 }
10713 };
10714
10715 static DNSOrderTest test1a = {
10716 "test1a",
10717 kProtocolFlagsIPv4v6,
10718 {
10719 V4_ADDR_1, V6_ADDR_1, NULL
10720 }
10721 };
10722
10723 static DNSOrderTest test2a = {
10724 "test2a",
10725 kProtocolFlagsIPv4v6,
10726 {
10727 V4_ADDR_1, V6_ADDR_2, NULL
10728 }
10729 };
10730
10731 static DNSOrderTest test3a = {
10732 "test3a",
10733 kProtocolFlagsIPv4v6,
10734 {
10735 V4_ADDR_1, V6_ADDR_3, NULL
10736 }
10737 };
10738
10739 static DNSOrderTest test1b = {
10740 "test1b",
10741 kProtocolFlagsIPv4v6,
10742 {
10743 V4_ADDR_3, V6_ADDR_1, NULL
10744 }
10745 };
10746
10747 static DNSOrderTest test2b = {
10748 "test2b",
10749 kProtocolFlagsIPv4v6,
10750 {
10751 V4_ADDR_3, V6_ADDR_2, NULL
10752 }
10753 };
10754
10755 static DNSOrderTest test3b = {
10756 "test3b",
10757 kProtocolFlagsIPv4v6,
10758 {
10759 V4_ADDR_3, V6_ADDR_3, NULL
10760 }
10761 };
10762
10763 static DNSOrderTest test1c = {
10764 "test1c",
10765 kProtocolFlagsIPv4v6,
10766 {
10767 V6_ADDR_1, V4_ADDR_1, NULL
10768 }
10769 };
10770
10771 static DNSOrderTest test2c = {
10772 "test2c",
10773 kProtocolFlagsIPv4v6,
10774 {
10775 V6_ADDR_2, V4_ADDR_1, NULL
10776 }
10777 };
10778
10779 static DNSOrderTest test3c = {
10780 "test3c",
10781 kProtocolFlagsIPv4v6,
10782 {
10783 V6_ADDR_3, V4_ADDR_1, NULL
10784 }
10785 };
10786
10787 static DNSOrderTest test1d = {
10788 "test1d",
10789 kProtocolFlagsIPv4v6,
10790 {
10791 V6_ADDR_1, V4_ADDR_3, NULL
10792 }
10793 };
10794
10795 static DNSOrderTest test2d = {
10796 "test2d",
10797 kProtocolFlagsIPv4v6,
10798 {
10799 V6_ADDR_2, V4_ADDR_3, NULL
10800 }
10801 };
10802
10803 static DNSOrderTest test3d = {
10804 "test3d",
10805 kProtocolFlagsIPv4v6,
10806 {
10807 V6_ADDR_3, V4_ADDR_3, NULL
10808 }
10809 };
10810
10811 static DNSOrderTest test4 = {
10812 "test4",
10813 kProtocolFlagsIPv4v6,
10814 {
10815 V4_ADDR_LOOP, V4_ADDR_3, V6_ADDR_2, NULL
10816 }
10817 };
10818
10819 static DNSOrderTest test5 = {
10820 "test5",
10821 kProtocolFlagsIPv4v6,
10822 {
10823 V4_ADDR_3, V6_ADDR_LOOP, V6_ADDR_2, NULL
10824 }
10825 };
10826
10827 static DNSOrderTest test6 = {
10828 "test6",
10829 kProtocolFlagsIPv4v6,
10830 {
10831 V4_ADDR_1, V4_ADDR_2, V4_ADDR_3, V4_ADDR_4, V6_ADDR_1, V6_ADDR_2, V6_ADDR_3, NULL
10832 }
10833 };
10834
10835 static DNSOrderTest test7 = {
10836 "test7",
10837 kProtocolFlagsIPv4v6,
10838 {
10839 V4_ADDR_1, V6_ADDR_1, V4_ADDR_3, V6_ADDR_2, NULL
10840 }
10841 };
10842
10843 static DNSOrderTestRef dns_order_tests[] = {
10844 &test0a, &test0b,
10845 &test1a, &test2a, &test3a,
10846 &test1b, &test2b, &test3b,
10847 &test1c, &test2c, &test3c,
10848 &test1d, &test2d, &test3d,
10849 &test4,
10850 &test5,
10851 &test6,
10852 &test7,
10853 NULL
10854 };
10855
10856 #define EMPHASIS_CHARS "================="
10857
10858 static void
10859 apply_order(CFArrayRef servers, ProtocolFlags flags)
10860 {
10861 CFArrayRef ordered_servers;
10862
10863 ordered_servers = order_dns_servers(servers, flags);
10864 if (ordered_servers != NULL) {
10865 SCPrint(TRUE, stdout, CFSTR("After :\n%@\n"), ordered_servers);
10866 CFRelease(ordered_servers);
10867 } else {
10868 printf("FAIL: No ordered servers\n");
10869 }
10870
10871 return;
10872 }
10873
10874 static void
10875 apply_test(DNSOrderTestRef test)
10876 {
10877 CFMutableArrayRef servers;
10878
10879 printf("\n" EMPHASIS_CHARS "> '%s' Begin <" EMPHASIS_CHARS "\n\n", test->name);
10880
10881 servers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
10882 for (int i = 0; test->server_addrs[i] != NULL; i++) {
10883 CFStringRef server_addr = test->server_addrs[i];
10884
10885 CFArrayAppendValue(servers, server_addr);
10886 }
10887
10888 SCPrint(TRUE, stdout, CFSTR("Before :\n%@\n"), servers);
10889
10890 apply_order(servers, test->flags);
10891
10892 CFRelease(servers);
10893
10894 return;
10895 }
10896
10897 int
10898 main(int argc, char **argv)
10899 {
10900 _sc_log = FALSE;
10901 _sc_verbose = (argc > 1) ? TRUE : FALSE;
10902 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
10903 if (argc > 1) {
10904 S_IPMonitor_debug = (uint32)strtoul(argv[1], NULL, 0);
10905 }
10906
10907 for (DNSOrderTestRef * test = dns_order_tests; *test != NULL; test++) {
10908 apply_test(*test);
10909 }
10910
10911 {
10912 char cmd[128];
10913
10914 printf("\nChecking for leaks\n");
10915 sprintf(cmd, "leaks %d 2>&1", getpid());
10916 fflush(stdout);
10917 (void)system(cmd);
10918 }
10919
10920 exit(0);
10921 return (0);
10922 }
10923
10924 #endif /* TEST_DNS_ORDER */