2 * Copyright (c) 2000-2020 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 * - decides which interface will be made the "primary" interface,
27 * that is, the one with the default route assigned
31 * Modification History
33 * July 19, 2000 Dieter Siegmund (dieter@apple.com)
36 * November 15, 2000 Dieter Siegmund (dieter@apple.com)
37 * - changed to use new configuration model
39 * March 19, 2001 Dieter Siegmund (dieter@apple.com)
40 * - use service state instead of interface state
42 * July 16, 2001 Allan Nathanson (ajn@apple.com)
43 * - update to public SystemConfiguration.framework APIs
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
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
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
58 * July 29, 2003 Dieter Siegmund (dieter@apple.com)
59 * - support installing a default route to a router that's not on our subnet
61 * March 22, 2004 Allan Nathanson (ajn@apple.com)
62 * - create expanded DNS configuration
64 * June 20, 2006 Allan Nathanson (ajn@apple.com)
65 * - add SMB configuration
67 * December 5, 2007 Dieter Siegmund (dieter@apple.com)
68 * - added support for multiple scoped routes
70 * November 13, 2013 Dieter Siegmund (dieter@apple.com)
71 * - added generic IPv4 routing support
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>
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/private.h>
90 #include <arpa/inet.h>
91 #include <sys/sysctl.h>
94 #include <mach/mach_time.h>
95 #include <dispatch/dispatch.h>
96 #include <CommonCrypto/CommonDigest.h>
98 #include "ip_plugin.h"
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"
108 #include "dnsinfo_server.h"
110 #include <ppp/PPPControllerPriv.h>
113 #include <dns_sd_private.h>
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>
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 */
127 #include "dns-configuration.h"
128 #include "proxy-configuration.h"
130 #if !TARGET_OS_IPHONE
131 #include "smb-configuration.h"
132 #endif /* !TARGET_OS_IPHONE */
134 #define kLoopbackInterface "lo0"
135 #define EROUTENOTAPPLIED 1001
137 typedef CF_ENUM(uint8_t, ProtocolFlags
) {
138 kProtocolFlagsNone
= 0x0,
139 kProtocolFlagsIPv4
= 0x1,
140 kProtocolFlagsIPv6
= 0x2
144 kDebugFlag1
= 0x00000001,
145 kDebugFlag2
= 0x00000002,
146 kDebugFlag4
= 0x00000004,
147 kDebugFlag8
= 0x00000008,
148 kDebugFlagDefault
= kDebugFlag1
,
149 kDebugFlagAll
= 0xffffffff
152 typedef unsigned int IFIndex
; /* interface index */
154 static dispatch_queue_t
__network_change_queue(void);
161 __private_extern__ os_log_t
162 __log_IPMonitor(void)
164 static os_log_t log
= NULL
;
167 log
= os_log_create("com.apple.SystemConfiguration", "IPMonitor");
174 #pragma mark interface index
177 #ifndef TEST_ROUTELIST
179 #define ROUTELIST_DEBUG(flag, fmt, ...)
181 static struct if_nameindex
* S_if_nameindex_cache
;
183 static dispatch_queue_t
184 __my_if_nametoindex_queue()
186 static dispatch_once_t once
;
187 static dispatch_queue_t q
;
189 dispatch_once(&once
, ^{
190 q
= dispatch_queue_create("my_if_nametoindex queue", NULL
);
196 __private_extern__ IFIndex
197 my_if_nametoindex(const char * ifname
)
199 __block IFIndex idx
= 0;
201 dispatch_sync(__my_if_nametoindex_queue(), ^{
202 struct if_nameindex
* scan
;
204 if (S_if_nameindex_cache
== NULL
) {
205 idx
= if_nametoindex(ifname
);
208 for (scan
= S_if_nameindex_cache
;
209 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
211 if (strcmp(scan
->if_name
, ifname
) == 0) {
212 idx
= scan
->if_index
;
221 __private_extern__
const char *
222 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
224 __block
const char * name
= NULL
;
226 dispatch_sync(__my_if_nametoindex_queue(), ^{
227 struct if_nameindex
* scan
;
229 if (S_if_nameindex_cache
== NULL
) {
230 name
= if_indextoname(idx
, if_name
);
233 for (scan
= S_if_nameindex_cache
;
234 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
236 if (scan
->if_index
== idx
) {
238 strlcpy(if_name
, scan
->if_name
, IFNAMSIZ
);
248 my_if_freenameindex(void)
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
;
261 my_if_nameindex(void)
263 my_if_freenameindex();
264 dispatch_sync(__my_if_nametoindex_queue(), ^{
265 S_if_nameindex_cache
= if_nameindex();
272 #else /* TEST_ROUTELIST */
274 #define ROUTELIST_DEBUG(flags, format, ...) { if (((S_IPMonitor_debug & (flags)) != 0)) printf((format), ## __VA_ARGS__ ); }
277 static const char * * list
;
278 static int list_count
;
279 static int list_size
;
281 __private_extern__ IFIndex
282 my_if_nametoindex(const char * ifname
)
289 list
= (const char * *)malloc(sizeof(*list
) * list_size
);
290 list
[0] = strdup("");
291 list
[1] = strdup(kLoopbackInterface
);
296 for (i
= 1; i
< list_count
; i
++) {
297 if (strcmp(list
[i
], ifname
) == 0) {
303 if (list_count
== list_size
) {
305 list
= (const char * *)realloc(list
, sizeof(*list
) * list_size
);
307 list
[list_count
] = strdup(ifname
);
314 __private_extern__
const char *
315 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
317 const char * name
= NULL
;
319 if (idx
< list_count
) {
321 strlcpy(if_name
, list
[idx
], IFNAMSIZ
);
327 my_if_nameindex(void)
332 my_if_freenameindex(void)
336 #endif /* TEST_ROUTELIST */
339 my_if_indextoname2(IFIndex ifindex
, char ifname
[IFNAMSIZ
])
344 if (my_if_indextoname(ifindex
, ifname
) == NULL
) {
345 snprintf(ifname
, IFNAMSIZ
, "[%d]", ifindex
);
357 idx
= my_if_nametoindex(kLoopbackInterface
);
367 * Property: kServiceOptionRankAssertion
369 * Key used in the service options dictionary to hold the RankAssertion
370 * derived from the kSCPropNetServicePrimaryRank string.
372 #define kServiceOptionRankAssertion CFSTR("RankAssertion") /* number */
375 * Property: kIPIsCoupled
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.
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.
385 #define kIPIsCoupled CFSTR("IPIsCoupled")
387 #define PPP_PREFIX "ppp"
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]
393 static Boolean S_bundle_logging_verbose
;
396 * IPv4 Route management
399 typedef CF_ENUM(uint16_t, RouteFlags
) {
400 kRouteFlagsIsScoped
= 0x0001,
401 kRouteFlagsHasGateway
= 0x0002,
402 kRouteFlagsIsHost
= 0x0004,
403 kRouteFlagsIsNULL
= 0x0008,
404 kRouteFlagsKernelManaged
= 0x0010
407 typedef CF_ENUM(uint16_t, ControlFlags
) {
408 kControlFlagsProcessed
= 0x0001,
409 kControlFlagsAdded
= 0x0002,
410 kControlFlagsForce
= 0x0004,
413 #if !TARGET_OS_SIMULATOR
414 static inline ControlFlags
415 ControlFlagsPreserve(ControlFlags flags
)
417 /* only preserve the "processed" and "added" flags */
418 return (flags
& (kControlFlagsProcessed
| kControlFlagsAdded
));
420 #endif /* !TARGET_OS_SIMULATOR */
422 #define ROUTE_COMMON \
425 IFIndex exclude_ifindex; \
428 ControlFlags control_flags;
434 #define PREFIX_LENGTH_IN_CLASSC 24
435 #define PREFIX_LENGTH_IN_CLASSD 4
441 struct in_addr gateway
;
443 } IPv4Route
, * IPv4RouteRef
;
447 struct in6_addr dest
;
448 struct in6_addr gateway
;
450 } IPv6Route
, * IPv6RouteRef
;
452 typedef CF_ENUM(uint16_t, RouteListFlags
) {
453 kRouteListFlagsExcludeNWI
= 0x0001,
454 kRouteListFlagsHasDefault
= 0x0002,
455 kRouteListFlagsScopedOnly
= 0x0004
458 #define ROUTELIST_COMMON \
461 RouteListFlags flags;
465 } RouteListCommon
, * RouteListRef
;
469 IPv4Route list
[1]; /* variable length */
470 } IPv4RouteList
, * IPv4RouteListRef
;
474 IPv6Route list
[1]; /* variable length */
475 } IPv6RouteList
, * IPv6RouteListRef
;
490 * Election Information
491 * - information about the current best services
499 struct sockaddr_in v4
;
500 struct sockaddr_in6 v6
;
503 typedef struct Candidate
{
504 CFStringRef serviceID
;
507 boolean_t ip_is_coupled
;
508 boolean_t ineligible
;
509 SCNetworkReachabilityFlags reachability_flags
;
511 in_sockaddr vpn_server_addr
;
512 CFStringRef signature
;
513 } Candidate
, * CandidateRef
;
515 typedef struct ElectionResults
{
519 Candidate candidates
[1];
520 } ElectionResults
, * ElectionResultsRef
;
522 static __inline__
size_t
523 ElectionResultsComputeSize(unsigned int n
)
525 return (offsetof(ElectionResults
, candidates
[n
]));
532 static __inline__ Rank
533 RankMake(uint32_t service_index
, Rank primary_rank
)
535 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
539 InterfaceRankGetRankAssertion(CFNumberRef rank_cf
, Boolean
* ret_is_set
)
541 SCNetworkServicePrimaryRank if_rank
;
542 Boolean is_set
= FALSE
;
543 Rank rank
= kRankAssertionDefault
;
546 && CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &if_rank
)
547 && if_rank
!= kSCNetworkServicePrimaryRankDefault
) {
548 if (if_rank
== kSCNetworkServicePrimaryRankFirst
) {
549 rank
= kRankAssertionFirst
;
552 rank
= RANK_ASSERTION_MAKE(if_rank
);
556 if (ret_is_set
!= NULL
) {
557 *ret_is_set
= is_set
;
563 PrimaryRankGetRankAssertion(CFStringRef rank_str
, Boolean
* is_set
)
566 const CFStringRef
* name
;
569 { &kSCValNetServicePrimaryRankFirst
, kRankAssertionFirst
},
570 { &kSCValNetServicePrimaryRankLast
, kRankAssertionLast
},
571 { &kSCValNetServicePrimaryRankNever
, kRankAssertionNever
},
572 { &kSCValNetServicePrimaryRankScoped
, kRankAssertionScoped
}
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
) {
581 return (values
[i
].rank_assertion
);
585 if (is_set
!= NULL
) {
588 return (kRankAssertionDefault
);
591 /* SCDynamicStore session */
592 static SCDynamicStoreRef S_session
= NULL
;
594 /* debug output flags */
595 static uint32_t S_IPMonitor_debug
= 0;
596 static Boolean S_IPMonitor_verbose
= FALSE
;
598 /* are we netbooted? If so, don't touch the default route */
599 static boolean_t S_netboot
= FALSE
;
601 /* dictionary to hold per-service state: key is the serviceID */
602 static CFMutableDictionaryRef S_service_state_dict
;
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
;
608 /* dictionary to hold per-interface rank information: key is the ifname */
609 static CFDictionaryRef S_if_rank_dict
;
611 /* if set, a PPP interface overrides the primary */
612 static boolean_t S_ppp_override_primary
= FALSE
;
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
;
620 /* the current election results */
621 static ElectionResultsRef S_ipv4_results
;
622 static ElectionResultsRef S_ipv6_results
;
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
;
632 static CFStringRef S_interface_delegation_prefix
= NULL
;
634 static CFStringRef S_multicast_resolvers
= NULL
;
635 static CFStringRef S_private_resolvers
= NULL
;
637 #if !TARGET_OS_SIMULATOR
638 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
639 static IPv6RouteListRef S_ipv6_routelist
= NULL
;
640 #endif /* !TARGET_OS_SIMULATOR */
642 static boolean_t S_append_state
= FALSE
;
644 static CFDictionaryRef S_dns_global_dict
= NULL
;
646 static CFDictionaryRef S_dns_primary_dict
= NULL
;
648 static Boolean S_dnsinfo_synced
= TRUE
;
650 static nwi_state_t S_nwi_state
= NULL
;
651 static Boolean S_nwi_synced
= TRUE
;
653 static CFDictionaryRef S_proxies_dict
= NULL
;
655 // Note: access should be gated with __network_change_queue()
656 static uint32_t S_network_change_needed
= 0;
657 #define NETWORK_CHANGE_NET 1<<0
658 #define NETWORK_CHANGE_DNS 1<<1
659 #define NETWORK_CHANGE_PROXY 1<<2
660 #if !TARGET_OS_IPHONE
661 #define NETWORK_CHANGE_SMB 1<<3
662 #endif /* !TARGET_OS_IPHONE */
663 #define NETWORK_CHANGE_NAT64 1<<4
664 static struct timeval S_network_change_start
;
665 static Boolean S_network_change_timeout
= FALSE
;
666 static dispatch_source_t S_network_change_timer
= NULL
;
668 #if !TARGET_OS_IPHONE
669 static CFStringRef S_primary_smb
= NULL
;
670 static CFStringRef S_state_global_smb
= NULL
;
671 static CFDictionaryRef S_smb_dict
= NULL
;
672 #endif /* !TARGET_OS_IPHONE */
674 #if !TARGET_OS_IPHONE
675 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
676 #endif /* !TARGET_OS_IPHONE */
679 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
680 #endif /* KERN_NETBOOT */
683 ** entityType*, GetEntityChanges*
684 ** - definitions for the entity types we handle
691 #if !TARGET_OS_IPHONE
693 #endif /* !TARGET_OS_IPHONE */
695 kEntityTypeTransientStatus
,
696 kEntityTypeServiceOptions
= 31
699 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
700 &kSCEntNetIPv4
, /* 0 */
701 &kSCEntNetIPv6
, /* 1 */
702 &kSCEntNetDNS
, /* 2 */
703 &kSCEntNetProxies
, /* 3 */
704 #if !TARGET_OS_IPHONE
705 &kSCEntNetSMB
, /* 4 */
706 #endif /* !TARGET_OS_IPHONE */
711 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
);
713 static __inline__
char
716 return ((af
== AF_INET
) ? '4' : '6');
719 static __inline__
char
720 ipvx_other_char(int af
)
722 return ((af
== AF_INET
) ? '6' : '4');
726 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
728 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
729 * Routes CFData containing IPv4RouteList/IPv6RouteList
730 * Service dictionary containing kSCEntNetIPv[46] service entity
732 #define kIPDictRoutes CFSTR("Routes") /* data */
733 #define kIPDictService CFSTR("Service") /* dict */
735 static CFDictionaryRef
736 ipdict_create(CFDictionaryRef dict
, CFDataRef routes_data
)
741 keys
[0] = kIPDictService
;
743 keys
[1] = kIPDictRoutes
;
744 values
[1] = routes_data
;
745 return (CFDictionaryCreate(NULL
,
746 (const void * *)keys
,
749 &kCFTypeDictionaryKeyCallBacks
,
750 &kCFTypeDictionaryValueCallBacks
));
754 ipdict_get_routelist(CFDictionaryRef dict
)
756 void * routes_list
= NULL
;
761 routes
= CFDictionaryGetValue(dict
, kIPDictRoutes
);
762 if (routes
!= NULL
) {
763 routes_list
= (void *)CFDataGetBytePtr(routes
);
766 return (routes_list
);
769 static CFDictionaryRef
770 ipdict_get_service(CFDictionaryRef dict
)
772 CFDictionaryRef ip_dict
= NULL
;
775 ip_dict
= CFDictionaryGetValue(dict
, kIPDictService
);
781 ipdict_get_ifname(CFDictionaryRef dict
)
783 CFStringRef ifname
= NULL
;
784 CFDictionaryRef ip_dict
;
786 ip_dict
= ipdict_get_service(dict
);
787 if (ip_dict
!= NULL
) {
788 ifname
= CFDictionaryGetValue(ip_dict
, kSCPropInterfaceName
);
793 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
794 CFDictionaryRef state_dict
,
795 CFDictionaryRef setup_dict
,
796 CFDictionaryRef info
);
797 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
799 static GetEntityChangesFunc get_ipv4_changes
;
800 static GetEntityChangesFunc get_ipv6_changes
;
801 static GetEntityChangesFunc get_dns_changes
;
802 static GetEntityChangesFunc get_proxies_changes
;
803 #if !TARGET_OS_IPHONE
804 static GetEntityChangesFunc get_smb_changes
;
805 #endif /* !TARGET_OS_IPHONE */
807 static __inline__
void
808 my_CFRelease(void * t
)
810 void * * obj
= (void * *)t
;
820 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
823 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
825 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
826 get_ipv4_changes
, /* 0 */
827 get_ipv6_changes
, /* 1 */
828 get_dns_changes
, /* 2 */
829 get_proxies_changes
,/* 3 */
830 #if !TARGET_OS_IPHONE
831 get_smb_changes
, /* 4 */
832 #endif /* !TARGET_OS_IPHONE */
837 ** - mechanism to do an atomic update of the SCDynamicStore
838 ** when the content needs to be changed across multiple functions
841 CFMutableArrayRef notify
;
842 CFMutableArrayRef remove
;
843 CFMutableDictionaryRef set
;
844 } keyChangeList
, * keyChangeListRef
;
847 keyChangeListInit(keyChangeListRef keys
)
849 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
850 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
851 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
852 &kCFTypeDictionaryKeyCallBacks
,
853 &kCFTypeDictionaryValueCallBacks
);
858 keyChangeListFree(keyChangeListRef keys
)
860 my_CFRelease(&keys
->notify
);
861 my_CFRelease(&keys
->remove
);
862 my_CFRelease(&keys
->set
);
867 keyChangeListActive(keyChangeListRef keys
)
869 return ((CFDictionaryGetCount(keys
->set
) > 0) ||
870 (CFArrayGetCount(keys
->remove
) > 0) ||
871 (CFArrayGetCount(keys
->notify
) > 0));
875 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
877 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
882 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
884 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
885 CFDictionaryRemoveValue(keys
->set
, key
);
890 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
892 my_CFArrayRemoveValue(keys
->remove
, key
);
893 CFDictionarySetValue(keys
->set
, key
, value
);
898 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
900 CFArrayRef notify
= keys
->notify
;
901 CFArrayRef remove
= keys
->remove
;
902 CFDictionaryRef set
= keys
->set
;
904 if (CFArrayGetCount(notify
) == 0) {
907 if (CFArrayGetCount(remove
) == 0) {
910 if (CFDictionaryGetCount(set
) == 0) {
913 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
916 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
918 my_log(LOG_DEBUG
, "Setting:\n%@", set
);
920 if (remove
!= NULL
) {
921 my_log(LOG_DEBUG
, "Removing:\n%@", remove
);
923 if (notify
!= NULL
) {
924 my_log(LOG_DEBUG
, "Notifying:\n%@", notify
);
927 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
940 mib
[1] = KERN_NETBOOT
;
941 len
= sizeof(netboot
);
942 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
946 static int rtm_seq
= 0;
948 #if !TARGET_OS_SIMULATOR
950 open_routing_socket(void)
954 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
955 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
960 static __inline__
int
961 inet6_dgram_socket(void)
965 sockfd
= socket(AF_INET6
, SOCK_DGRAM
, 0);
967 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
974 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
976 struct in6_defrouter dr
;
977 struct sockaddr_in6
* sin6
;
979 memset(&dr
, 0, sizeof(dr
));
981 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
982 sin6
->sin6_family
= AF_INET6
;
983 sin6
->sin6_addr
= *addr
;
985 dr
.if_index
= if_index
;
986 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
990 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
992 struct in6_defrouter dr
;
993 struct sockaddr_in6
* sin6
;
995 memset(&dr
, 0, sizeof(dr
));
997 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
998 sin6
->sin6_family
= AF_INET6
;
999 sin6
->sin6_addr
= *addr
;
1000 dr
.if_index
= if_index
;
1001 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
1004 #endif /* !TARGET_OS_SIMULATOR */
1007 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
1009 CFIndex n
= CFArrayGetCount(arr
);
1011 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
1014 CFArrayAppendValue(arr
, new);
1019 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
1023 i
= CFArrayGetFirstIndexOfValue(arr
,
1024 CFRangeMake(0, CFArrayGetCount(arr
)),
1026 if (i
!= kCFNotFound
) {
1027 CFArrayRemoveValueAtIndex(arr
, i
);
1033 my_CFArrayCreateCombinedArray(CFArrayRef array1
, CFArrayRef array2
)
1035 CFMutableArrayRef combined
;
1037 combined
= CFArrayCreateMutableCopy(NULL
, 0, array1
);
1038 CFArrayAppendArray(combined
,
1040 CFRangeMake(0, CFArrayGetCount(array2
)));
1044 static CFDictionaryRef
1045 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
1047 if (isA_CFDictionary(dict
) == NULL
) {
1050 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
1054 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
1056 if (isA_CFDictionary(dict
) == NULL
) {
1059 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
1063 my_CFSetAddValue(CFMutableSetRef
* set_p
, CFTypeRef value
)
1065 if (*set_p
== NULL
) {
1066 *set_p
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1068 CFSetAddValue(*set_p
, value
);
1072 my_CFSetContainsValue(CFSetRef set
, CFTypeRef value
)
1077 return (CFSetContainsValue(set
, value
));
1080 #if !TARGET_OS_SIMULATOR
1083 my_CFSetRemoveValue(CFMutableSetRef
* set_p
, CFTypeRef value
)
1085 if (*set_p
== NULL
) {
1088 CFSetRemoveValue(*set_p
, value
);
1089 if (CFSetGetCount(*set_p
) == 0) {
1090 my_CFRelease(set_p
);
1094 typedef CF_ENUM(uint16_t, PLATDiscoveryOption
) {
1095 kPLATDiscoveryOptionStart
,
1096 kPLATDiscoveryOptionUpdate
,
1097 kPLATDiscoveryOptionCancel
1100 // Note: must only accessed on __network_change_queue()
1101 static CFMutableSetRef S_nat64_cancel_prefix_requests
;
1102 static CFMutableSetRef S_nat64_prefix_updates
;
1103 static CFMutableSetRef S_nat64_prefix_requests
;
1106 set_plat_discovery_locked(PLATDiscoveryOption option
, CFStringRef interface
)
1109 case kPLATDiscoveryOptionStart
:
1110 my_log(LOG_DEBUG
, "NAT64 Start %@", interface
);
1111 my_CFSetAddValue(&S_nat64_prefix_requests
, interface
);
1112 my_CFSetRemoveValue(&S_nat64_prefix_updates
, interface
);
1113 my_CFSetRemoveValue(&S_nat64_cancel_prefix_requests
, interface
);
1115 case kPLATDiscoveryOptionUpdate
:
1116 my_log(LOG_DEBUG
, "NAT64 Update %@", interface
);
1117 if (!my_CFSetContainsValue(S_nat64_prefix_requests
, interface
)) {
1118 my_CFSetAddValue(&S_nat64_prefix_updates
, interface
);
1120 my_CFSetRemoveValue(&S_nat64_cancel_prefix_requests
, interface
);
1122 case kPLATDiscoveryOptionCancel
:
1123 my_log(LOG_DEBUG
, "NAT64 Cancel %@", interface
);
1124 my_CFSetRemoveValue(&S_nat64_prefix_requests
, interface
);
1125 my_CFSetRemoveValue(&S_nat64_prefix_updates
, interface
);
1126 my_CFSetAddValue(&S_nat64_cancel_prefix_requests
, interface
);
1134 set_plat_discovery(PLATDiscoveryOption option
, CFStringRef interface
)
1136 CFRetain(interface
);
1137 dispatch_async(__network_change_queue(), ^{
1138 set_plat_discovery_locked(option
, interface
);
1139 CFRelease(interface
);
1144 #endif /* !TARGET_OS_SIMULATOR */
1147 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, size_t addr_size
)
1151 if (isA_CFString(str
) == NULL
) {
1157 if (addr_size
< sizeof(struct in_addr
)) {
1162 if (addr_size
< sizeof(struct in6_addr
)) {
1169 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
1170 if (inet_pton(family
, buf
, addr
) == 1) {
1174 memset(addr
, 0, addr_size
);
1180 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
1182 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
1187 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
1189 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
1193 cfnumber_to_int(CFNumberRef num
, int * int_val
)
1195 if (isA_CFNumber(num
) == NULL
) {
1198 return (CFNumberGetValue(num
, kCFNumberIntType
, int_val
));
1201 static CF_RETURNS_RETAINED CFStringRef
1202 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
1204 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1205 kSCDynamicStoreDomainSetup
,
1210 static CF_RETURNS_RETAINED CFStringRef
1211 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
1213 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1214 kSCDynamicStoreDomainState
,
1220 interface_entity_key_copy(CFStringRef ifname
, CFStringRef entity
)
1222 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1223 kSCDynamicStoreDomainState
,
1228 static CFDictionaryRef
1229 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1232 CFStringRef setup_key
;
1233 CFDictionaryRef setup_dict
;
1235 setup_key
= setup_service_key(serviceID
, entity
);
1236 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
1237 my_CFRelease(&setup_key
);
1238 return (setup_dict
);
1241 static CFDictionaryRef
1242 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1245 CFStringRef state_key
;
1246 CFDictionaryRef state_dict
;
1248 state_key
= state_service_key(serviceID
, entity
);
1249 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
1250 my_CFRelease(&state_key
);
1251 return (state_dict
);
1255 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1259 ip_list
= CFDictionaryGetValue(dict
, prop
);
1260 if (isA_CFArray(ip_list
) != NULL
1261 && CFArrayGetCount(ip_list
) > 0
1262 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1269 dict_get_first_ipv6(CFDictionaryRef dict
, CFStringRef prop
,
1270 struct in6_addr
* ip_p
)
1274 ip_list
= CFDictionaryGetValue(dict
, prop
);
1275 if (isA_CFArray(ip_list
) != NULL
1276 && CFArrayGetCount(ip_list
) > 0
1277 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1284 dict_get_first_int(CFDictionaryRef dict
, CFStringRef prop
,
1289 list
= CFDictionaryGetValue(dict
, prop
);
1290 if (isA_CFArray(list
) != NULL
1291 && CFArrayGetCount(list
) > 0
1292 && cfnumber_to_int(CFArrayGetValueAtIndex(list
, 0), val
)) {
1299 dict_get_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1303 val
= CFDictionaryGetValue(dict
, prop
);
1304 return (cfstring_to_ip(val
, ip_p
));
1308 dict_get_ipv6(CFDictionaryRef dict
, CFStringRef prop
, struct in6_addr
* ip_p
)
1312 val
= CFDictionaryGetValue(dict
, prop
);
1313 return (cfstring_to_ip6(val
, ip_p
));
1317 dict_get_int(CFDictionaryRef dict
, CFStringRef prop
, int * intval
)
1321 val
= CFDictionaryGetValue(dict
, prop
);
1322 return (cfnumber_to_int(val
, intval
));
1326 get_override_primary(CFDictionaryRef dict
)
1330 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
1331 if (isA_CFNumber(override
) != NULL
) {
1334 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
1339 else if (isA_CFBoolean(override
) != NULL
) {
1340 if (CFBooleanGetValue(override
)) {
1352 (*RouteListComputeSize
)(CFIndex n
);
1355 (*RouteIsEqual
)(RouteRef a
, RouteRef b
);
1358 (*RouteApply
)(RouteRef route
, int cmd
, int sockfd
);
1360 typedef const void *
1361 (*RouteGateway
)(RouteRef route
);
1364 (*RouteSetGateway
)(RouteRef route
, const void * address
);
1366 typedef const void *
1367 (*RouteDestination
)(RouteRef route
);
1370 (*RouteSameSubnet
)(RouteRef route
, const void * address
);
1373 (*RouteCopyDescription
)(RouteRef route
);
1376 (*RouteLog
)(int priority
, RouteRef route
, const char * msg
);
1379 RouteListComputeSize list_compute_size
;
1381 RouteIsEqual route_equal
;
1382 RouteApply route_apply
;
1383 RouteGateway route_gateway
;
1384 RouteSetGateway route_set_gateway
;
1385 RouteDestination route_destination
;
1386 RouteSameSubnet route_same_subnet
;
1388 RouteCopyDescription route_copy_description
;
1395 typedef const RouteListInfo
* RouteListInfoRef
;
1398 RouteListInfoRef info
;
1399 RouteListRef old_routes
;
1400 RouteListRef new_routes
;
1403 } RouteListApplyContext
, * RouteListApplyContextRef
;
1407 RouteAddressCompare(RouteListInfoRef info
,
1411 return (memcmp(addr1
, addr2
, info
->address_size
));
1415 RouteCompare(RouteListInfoRef info
,
1416 RouteRef a
, Rank a_rank
,
1417 RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
1420 RouteDestination route_destination
;
1421 RouteCopyDescription route_copy_description
;
1424 route_destination
= info
->route_destination
;
1425 route_copy_description
= info
->route_copy_description
;
1426 cmp
= RouteAddressCompare(info
,
1427 (*route_destination
)(a
),
1428 (*route_destination
)(b
));
1430 cmp
= a
->prefix_length
- b
->prefix_length
;
1432 int index_cmp
= a
->ifindex
- b
->ifindex
;
1434 if (index_cmp
== 0) {
1437 else if ((a
->ifindex
== 0 || b
->ifindex
== 0)
1438 && (a
->flags
& kRouteFlagsIsScoped
) == 0
1439 && (b
->flags
& kRouteFlagsIsScoped
) == 0) {
1441 * Either of the routes specifies no interface and neither
1442 * route is scoped. Claim they are equal to eliminate the
1449 cmp
= RankCompare(a_rank
, b_rank
);
1456 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
1464 else if (cmp
== 0) {
1470 a_str
= (*route_copy_description
)(a
);
1471 b_str
= (*route_copy_description
)(b
);
1472 my_log(LOG_DEBUG
, "%@ rank 0x%x %c %@ rank 0x%x",
1473 a_str
, a_rank
, ch
, b_str
, b_rank
);
1481 RouteListGetRouteAtIndexSimple(RouteListInfoRef info
, RouteListRef routes
,
1484 return ((void *)routes
+ (*info
->list_compute_size
)(where
));
1488 RouteListGetRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1491 if (routes
->count
== 0
1492 || where
>= routes
->count
) {
1495 return (RouteListGetRouteAtIndexSimple(info
, routes
, where
));
1499 RouteListGetFirstRoute(RouteListInfoRef info
, RouteListRef routes
)
1501 return (RouteListGetRouteAtIndexSimple(info
, routes
, 0));
1504 #if !TARGET_OS_SIMULATOR
1506 RouteListRouteIndex(RouteListInfoRef info
, RouteListRef routes
,
1509 return (((void *)route
1510 - (void *)RouteListGetFirstRoute(info
, routes
))
1511 / info
->element_size
);
1513 #endif /* !TARGET_OS_SIMULATOR */
1516 RouteGetNextRoute(RouteListInfoRef info
, RouteRef route
)
1518 return ((RouteRef
)(((void *)route
) + info
->element_size
));
1522 RouteListAddRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1523 RouteRef this_route
, CFIndex where
)
1525 RouteRef insert_route
;
1527 if (where
== kCFNotFound
) {
1528 /* add it to the end */
1530 = RouteListGetRouteAtIndexSimple(info
, routes
, routes
->count
);
1533 /* make space at [where] */
1534 insert_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1535 /* overlapping copy requires memmove */
1536 memmove((void *)insert_route
+ info
->element_size
,
1538 info
->element_size
* (routes
->count
- where
));
1540 /* copy the route */
1541 memcpy(insert_route
, this_route
, info
->element_size
);
1543 return (insert_route
);
1547 RouteListRemoveRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1550 if (routes
->count
== 0
1551 || where
>= routes
->count
) {
1555 if (where
== routes
->count
) {
1556 /* last slot, decrementing gets rid of it */
1559 RouteRef remove_route
;
1561 remove_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1562 /* overlapping copy requires memmove */
1563 memmove(remove_route
,
1564 (void *)remove_route
+ info
->element_size
,
1565 info
->element_size
* (routes
->count
- where
));
1571 * Function: RouteListAddRoute
1574 * Add the given route to the list of routes, eliminating lower-ranked
1575 * duplicates on the same interface, and marking any lower ranked duplicates
1576 * on other interfaces with kRouteFlagsIsScoped.
1578 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1581 * Route list updated with the given route, possibly a different pointer,
1582 * due to using realloc'd memory.
1592 RouteListAddRoute(RouteListInfoRef info
,
1593 RouteListRef routes
, int init_size
,
1594 RouteRef this_route
, Rank this_rank
,
1598 RouteRef first_scan
= NULL
;
1601 Scope scope_which
= kScopeNone
;
1602 CFIndex where
= kCFNotFound
;
1604 if (routes
== NULL
) {
1605 size_t alloc_size
= (*info
->list_compute_size
)(init_size
);
1607 routes
= (RouteListRef
)malloc(alloc_size
);
1608 memset(routes
, 0, alloc_size
);
1609 routes
->size
= init_size
;
1611 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1613 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1615 boolean_t same_dest
;
1617 cmp
= RouteCompare(info
, this_route
, this_rank
, scan
, scan
->rank
,
1619 if (same_dest
&& (first_scan
== NULL
)) {
1623 if (where
== kCFNotFound
) {
1625 && (first_scan
!= NULL
)
1626 && (first_scan
->flags
& kRouteFlagsIsScoped
) == 0) {
1627 if ((scan
->flags
& kRouteFlagsIsScoped
) != 0) {
1628 ROUTELIST_DEBUG(kDebugFlag8
,
1629 "Hit 1: set scope on self\n");
1630 scope_which
= kScopeThis
;
1633 ROUTELIST_DEBUG(kDebugFlag8
,
1634 "Hit 2: set scope on next\n");
1635 scope_which
= kScopeNext
;
1638 /* remember our insertion point, but keep going to find a dup */
1642 else if (cmp
== 0) {
1644 if (where
!= kCFNotFound
1645 && scan
->ifindex
== this_route
->ifindex
1646 && scan
->exclude_ifindex
== 0
1647 && this_route
->exclude_ifindex
== 0) {
1648 /* this route is a duplicate */
1649 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 3: removing [%ld]\n", i
);
1650 RouteListRemoveRouteAtIndex(info
, routes
, i
);
1654 * this_route is "better" than scan if this_route is not excluded
1655 * and scan is excluded or this_route sorts ahead of scan
1657 if (this_route
->exclude_ifindex
== 0
1658 && (scan
->exclude_ifindex
!= 0 || this_rank
< scan
->rank
)) {
1659 IFIndex ifindex
= 0;
1660 boolean_t is_scoped
= FALSE
;
1662 if (scan
->flags
& kRouteFlagsIsScoped
) {
1665 if (this_rank
< scan
->rank
) {
1666 ROUTELIST_DEBUG(kDebugFlag8
,
1667 "Hit 4a: replacing [%ld]"
1668 " rank 0x%x < 0x%x\n",
1669 i
, this_rank
, scan
->rank
);
1672 ROUTELIST_DEBUG(kDebugFlag8
,
1673 "Hit 4b: replacing [%ld] excluded route\n",
1676 if (scan
->ifindex
!= 0) {
1677 ifindex
= scan
->ifindex
;
1679 else if (this_route
->ifindex
!= 0) {
1680 ifindex
= this_route
->ifindex
;
1682 memcpy(scan
, this_route
, info
->element_size
);
1683 scan
->rank
= this_rank
;
1684 scan
->ifindex
= ifindex
;
1685 scan
->exclude_ifindex
= 0;
1687 /* preserve whether route was scoped */
1688 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 5: preserved scope\n");
1689 scan
->flags
|= kRouteFlagsIsScoped
;
1692 scan
->control_flags
|= kControlFlagsForce
;
1700 if (scope_which
== kScopeNone
) {
1701 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 6: set scope on self\n");
1702 scope_which
= kScopeThis
;
1705 #ifdef TEST_ROUTELIST
1706 else if (where
!= kCFNotFound
) {
1707 /* not possible because we maintain a sorted list */
1709 "Hit 7: moved past routes - can't happen\n");
1713 #endif /* TEST_ROUTELIST */
1717 if (routes
->size
== routes
->count
) {
1719 RouteListRef new_routes
;
1722 /* double the size */
1723 old_size
= routes
->size
;
1724 how_many
= old_size
* 2;
1725 new_routes
= (RouteListRef
)
1726 reallocf(routes
, (*info
->list_compute_size
)(how_many
));
1727 if (new_routes
== NULL
) {
1732 ROUTELIST_DEBUG(kDebugFlag8
, "increasing size from %d to %d\n",
1733 old_size
, how_many
);
1734 new_routes
->size
= how_many
;
1735 routes
= new_routes
;
1738 /* add/insert the new route */
1739 this_route
= RouteListAddRouteAtIndex(info
, routes
, this_route
, where
);
1740 this_route
->rank
= this_rank
;
1742 this_route
->control_flags
|= kControlFlagsForce
;
1745 if (RANK_ASSERTION_MASK(this_rank
) == kRankAssertionNever
) {
1746 flags
|= kRouteFlagsIsScoped
;
1748 switch (scope_which
) {
1750 flags
|= kRouteFlagsIsScoped
;
1753 this_route
= RouteListGetRouteAtIndex(info
, routes
, where
+ 1);
1754 flags
|= kRouteFlagsIsScoped
;
1760 if (this_route
!= NULL
&& flags
!= 0) {
1761 this_route
->flags
|= flags
;
1769 * Function: RouteListAddRouteList
1771 * Invoke RouteListAddRoute for each route in the given list
1772 * 'service_routes' combining them into a combined list 'routes'.
1775 * See RouteListAddRoute for more information.
1778 RouteListAddRouteList(RouteListInfoRef info
,
1779 RouteListRef routes
, int init_size
,
1780 RouteListRef service_routes
, Rank rank
,
1786 for (i
= 0, scan
= RouteListGetFirstRoute(info
, service_routes
);
1787 i
< service_routes
->count
;
1788 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1792 && (service_routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
1793 /* only apply rank to first element of the list (default route) */
1797 this_rank
= RANK_INDEX_MASK(rank
) | RANK_ASSERTION_MASK(scan
->rank
);
1799 routes
= RouteListAddRoute(info
, routes
, init_size
, scan
, this_rank
,
1806 RouteAddInterfaceToDescription(RouteRef r
, CFMutableStringRef str
)
1808 char if_name
[IFNAMSIZ
];
1810 if (my_if_indextoname2(r
->ifindex
, if_name
) != NULL
) {
1811 CFStringAppendFormat(str
, NULL
,
1815 if (my_if_indextoname2(r
->exclude_ifindex
, if_name
) != NULL
) {
1816 CFStringAppendFormat(str
, NULL
,
1824 RouteAddFlagsToDescription(RouteRef r
, CFMutableStringRef str
)
1826 if ((r
->flags
& kRouteFlagsIsNULL
) != 0) {
1827 CFStringAppend(str
, CFSTR(" [null]"));
1830 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
1832 switch (rank_assertion
) {
1833 case kRankAssertionFirst
:
1834 CFStringAppend(str
, CFSTR(" [first]"));
1836 case kRankAssertionLast
:
1837 CFStringAppend(str
, CFSTR(" [last]"));
1839 case kRankAssertionNever
:
1840 CFStringAppend(str
, CFSTR(" [never]"));
1845 if ((r
->flags
& kRouteFlagsKernelManaged
) != 0) {
1846 CFStringAppend(str
, CFSTR(" [kern]"));
1848 if ((r
->flags
& kRouteFlagsIsScoped
) != 0) {
1849 CFStringAppend(str
, CFSTR(" [SCOPED]"));
1851 #ifndef TEST_ROUTELIST
1852 if ((r
->control_flags
& kControlFlagsForce
) != 0) {
1853 CFStringAppend(str
, CFSTR(" [force]"));
1860 #if !TARGET_OS_SIMULATOR
1862 RouteListFindRoute(RouteListInfoRef info
, RouteListRef routes
, RouteRef route
)
1865 RouteRef match
= NULL
;
1868 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1870 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1871 if ((*info
->route_equal
)(scan
, route
)) {
1881 kRouteLookupFlagsNone
= 0x0,
1882 kRouteLookupFlagsExcludeInterface
= 0x1
1886 RouteListLookup(RouteListInfoRef info
,
1887 RouteListRef routes
,
1888 const void * address
,
1891 RouteLookupFlags lookup_flags
)
1893 RouteRef best_match
= NULL
;
1897 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1899 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1900 if (scan
->ifindex
== 0 || scan
->exclude_ifindex
!= 0) {
1901 /* ignore exclude routes */
1904 if ((lookup_flags
& kRouteLookupFlagsExcludeInterface
) != 0) {
1905 /* exclude interfaces with the same interface index */
1906 if (ifindex
== scan
->ifindex
) {
1910 else if (ifindex
!= scan
->ifindex
) {
1913 if ((scan
->flags
& kRouteFlagsHasGateway
) != 0
1914 && RouteAddressCompare(info
,
1915 (*info
->route_gateway
)(scan
),
1917 /* skip route whose gateway is the address we're looking for */
1920 if ((scan
->flags
& kRouteFlagsIsHost
) != 0) {
1921 /* if host route and we're looking for an exact match */
1922 if (n_bits
== info
->all_bits_set
1923 && RouteAddressCompare(info
,
1924 (*info
->route_destination
)(scan
),
1926 /* found exact match */
1933 /* verify that address is on the same subnet */
1934 if ((*info
->route_same_subnet
)(scan
, address
) == FALSE
) {
1935 /* different subnet */
1939 if (scan
->prefix_length
== n_bits
) {
1944 if (scan
->prefix_length
> n_bits
) {
1945 /* matched too many bits */
1948 if (best_match
== NULL
1949 || scan
->prefix_length
> best_match
->prefix_length
) {
1953 return (best_match
);
1958 * Function: RouteProcess
1960 * Function to process adding or removing the specified route.
1961 * In the case of adding, that may involve first processing the gateway
1962 * route (recursively).
1965 RouteProcess(RouteRef route
,
1967 RouteListApplyContextRef context
)
1969 RouteLog route_log
= context
->info
->route_log
;
1970 RouteApply route_apply
= context
->info
->route_apply
;
1971 RouteGateway route_gateway
= context
->info
->route_gateway
;
1975 case kRouteCommandAdd
:
1976 if ((route
->control_flags
& kControlFlagsProcessed
) != 0) {
1977 return ((route
->control_flags
& kControlFlagsAdded
) != 0);
1979 route
->control_flags
|= kControlFlagsProcessed
;
1980 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
1982 RouteRef gateway_route
;
1985 = RouteListLookup(context
->info
,
1986 context
->new_routes
,
1987 (*route_gateway
)(route
),
1988 context
->info
->all_bits_set
,
1990 kRouteLookupFlagsNone
);
1991 if (gateway_route
== NULL
) {
1992 (*route_log
)(LOG_NOTICE
, route
, "no gateway route");
1995 #define MAX_RECURSE_DEPTH 10
1996 /* avoid infinite recursion */
1997 if (context
->depth
== MAX_RECURSE_DEPTH
) {
1998 (*route_log
)(LOG_NOTICE
, route
, "routing loop detected, not adding");
2001 /* recurse to add gateway route */
2003 added
= RouteProcess(gateway_route
,
2008 (*route_log
)(LOG_NOTICE
, route
, "failed to add");
2013 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
2014 if (retval
== EEXIST
) {
2015 /* delete and add again */
2016 (void)(*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
2017 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
2022 "failed to add route, %s:",
2024 (*route_log
)(LOG_NOTICE
, route
, NULL
);
2027 case EROUTENOTAPPLIED
:
2028 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2032 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
2033 snprintf(buf
, sizeof(buf
), "%sAdd new[%ld]",
2035 RouteListRouteIndex(context
->info
,
2036 context
->new_routes
,
2038 (*route_log
)(LOG_DEBUG
, route
, buf
);
2040 route
->control_flags
|= kControlFlagsAdded
;
2044 case kRouteCommandRemove
:
2045 retval
= (*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
2049 case EROUTENOTAPPLIED
:
2050 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2054 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
2055 snprintf(buf
, sizeof(buf
), "%sRemove old[%ld]%s",
2057 RouteListRouteIndex(context
->info
,
2058 context
->old_routes
,
2060 (retval
== ESRCH
) ? "(ESRCH)" : "");
2061 (*route_log
)(LOG_DEBUG
, route
, buf
);
2066 "failed to remove route, %s",
2068 (*route_log
)(LOG_NOTICE
, route
, NULL
);
2079 RouteListApply(RouteListInfoRef info
,
2080 RouteListRef old_routes
, RouteListRef new_routes
,
2083 RouteListApplyContext context
;
2087 if (old_routes
== new_routes
&& old_routes
== NULL
) {
2088 /* both old and new are NULL, so there's nothing to do */
2091 memset(&context
, 0, sizeof(context
));
2092 context
.old_routes
= old_routes
;
2093 context
.new_routes
= new_routes
;
2094 context
.sockfd
= sockfd
;
2095 context
.info
= info
;
2096 if (old_routes
!= NULL
) {
2097 for (i
= 0, scan
= RouteListGetFirstRoute(info
, old_routes
);
2098 i
< old_routes
->count
;
2099 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2100 RouteRef new_route
= NULL
;
2102 if (new_routes
!= NULL
) {
2103 new_route
= RouteListFindRoute(info
, new_routes
, scan
);
2105 if (new_route
== NULL
) {
2106 if ((scan
->control_flags
& kControlFlagsAdded
) != 0) {
2107 RouteProcess(scan
, kRouteCommandRemove
, &context
);
2112 if (new_routes
!= NULL
) {
2113 if (old_routes
!= NULL
) {
2114 /* preserve the control flags from any old routes */
2115 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2116 i
< new_routes
->count
;
2117 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2118 RouteRef old_route
= NULL
;
2120 old_route
= RouteListFindRoute(info
, old_routes
, scan
);
2121 if (old_route
!= NULL
) {
2122 if ((scan
->control_flags
& kControlFlagsForce
) == 0) {
2123 /* preserve the control state in the new route */
2125 = ControlFlagsPreserve(old_route
->control_flags
);
2128 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
,
2129 "Re-applying route");
2134 /* add any routes that need to be added */
2135 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2136 i
< new_routes
->count
;
2137 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2138 if ((scan
->control_flags
& kControlFlagsProcessed
) != 0) {
2141 RouteProcess(scan
, kRouteCommandAdd
, &context
);
2147 * Function: RouteListFinalize
2149 * Look for excluded routes. If the excluded route does not have an assigned
2150 * interface, search for a route that *does not* go over the excluded
2153 * If the excluded route does have an assigned interface, search for a route
2154 * that *does* go over the assigned interface.
2156 * Set the gateway on the excluded route to match the gateway of the found
2160 RouteListFinalize(RouteListInfoRef info
, RouteListRef routes
)
2165 if (routes
== NULL
) {
2168 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
2170 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2173 RouteLookupFlags flags
;
2175 if (scan
->exclude_ifindex
== 0) {
2178 if (scan
->ifindex
== 0) {
2179 ifindex
= scan
->exclude_ifindex
;
2180 flags
= kRouteLookupFlagsExcludeInterface
;
2183 ifindex
= scan
->ifindex
;
2184 flags
= kRouteLookupFlagsNone
;
2186 route
= RouteListLookup(info
, routes
,
2187 (*info
->route_destination
)(scan
),
2188 scan
->prefix_length
, ifindex
, flags
);
2189 if (route
== NULL
) {
2190 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
,
2191 "can't resolve excluded route");
2194 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
2195 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)scan
, "Excluded route");
2196 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)route
, "Resolved to");
2198 scan
->ifindex
= route
->ifindex
;
2199 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2200 (*info
->route_set_gateway
)(scan
, (*info
->route_gateway
)(route
));
2201 scan
->flags
|= kRouteFlagsHasGateway
;
2202 if (scan
->prefix_length
== info
->all_bits_set
) {
2203 scan
->flags
|= kRouteFlagsIsHost
;
2207 /* routes directly to interface */
2208 scan
->flags
&= ~(kRouteFlagsHasGateway
| kRouteFlagsIsHost
);
2214 #endif /* !TARGET_OS_SIMULATOR */
2220 #define IPV4_ROUTE_ALL_BITS_SET 32
2222 static __inline__
struct in_addr
2223 subnet_addr(struct in_addr addr
, struct in_addr mask
)
2227 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
2232 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
2234 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
2235 CFStringAppendFormat(str
, NULL
,
2236 CFSTR("Host " IP_FORMAT
),
2240 CFStringAppendFormat(str
, NULL
,
2241 CFSTR("Net " IP_FORMAT
),
2243 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
2246 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
2247 CFStringAppendFormat(str
, NULL
,
2248 CFSTR(" Gate " IP_FORMAT
),
2249 IP_LIST(&r
->gateway
));
2251 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
2252 if (r
->ifa
.s_addr
!= 0) {
2253 CFStringAppendFormat(str
, NULL
,
2254 CFSTR(" Ifa " IP_FORMAT
),
2257 RouteAddFlagsToDescription((RouteRef
)r
, str
);
2262 IPv4RouteCopyDescription(RouteRef r
)
2264 CFMutableStringRef str
;
2266 str
= CFStringCreateMutable(NULL
, 0);
2267 IPv4RouteCopyDescriptionWithString((IPv4RouteRef
)r
, str
);
2271 #ifdef TEST_IPV4_ROUTELIST
2272 static CFMutableStringRef
2273 IPv4RouteListCopyDescription(IPv4RouteListRef routes
);
2276 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2278 CFStringRef str
= IPv4RouteCopyDescription(route
);
2281 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2284 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
2290 static __inline__
void
2291 IPv4RouteListPrint(IPv4RouteListRef routes
)
2293 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2295 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2300 #else /* TEST_IPV4_ROUTELIST */
2302 static __inline__
void
2303 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2305 CFStringRef str
= IPv4RouteCopyDescription(route
);
2308 my_log(level
, "%@", str
);
2311 my_log(level
, "%s: %@", msg
, str
);
2317 #endif /* TEST_IPV4_ROUTELIST */
2320 IPv4RouteIsEqual(RouteRef r_scan
, RouteRef r_route
)
2322 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2323 IPv4RouteRef scan
= (IPv4RouteRef
)r_scan
;
2325 return ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
2326 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
2327 && (scan
->ifindex
== route
->ifindex
)
2328 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
2329 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
2330 && (scan
->flags
== route
->flags
));
2333 static CFMutableStringRef
2334 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
2338 CFMutableStringRef str
;
2340 str
= CFStringCreateMutable(NULL
, 0);
2341 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
2343 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
2344 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
2345 IPv4RouteCopyDescriptionWithString(r
, str
);
2347 CFStringAppend(str
, CFSTR("\n}"));
2352 IPv4RouteListComputeSize(CFIndex n
)
2354 return (offsetof(IPv4RouteList
, list
[n
]));
2358 count_prefix_bits_set(uint32_t n
)
2361 const static int8_t bits
[16] = {
2380 for (count
= 0; n
!= 0; n
>>= 4) {
2381 int nbits
= bits
[n
& 0x0f];
2392 prefix_to_mask32(unsigned int prefix_length
)
2394 if (prefix_length
> 32 || prefix_length
== 0) {
2397 return (0xffffffff << (32 - prefix_length
));
2401 mask_get_prefix_length(struct in_addr mask
)
2405 count
= count_prefix_bits_set(mask
.s_addr
);
2409 val
= prefix_to_mask32(count
);
2410 if (ntohl(mask
.s_addr
) != val
) {
2411 /* expected mask based on prefix length doesn't match */
2419 IPv4RouteSetPrefixLength(IPv4RouteRef route
)
2423 length
= mask_get_prefix_length(route
->mask
);
2427 route
->prefix_length
= length
;
2432 IPv4RouteGateway(RouteRef r_route
)
2434 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2435 return (&route
->gateway
);
2439 IPv4RouteSetGateway(RouteRef r_route
, const void * address
)
2441 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2443 route
->gateway
= *((struct in_addr
*)address
);
2448 IPv4RouteDestination(RouteRef r_route
)
2450 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2451 return (&route
->dest
);
2455 IPv4RouteSameSubnet(RouteRef r_route
, const void * addr
)
2457 const struct in_addr
* address
;
2458 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2460 address
= (const struct in_addr
*)addr
;
2461 return ((address
->s_addr
& route
->mask
.s_addr
) == route
->dest
.s_addr
);
2465 * Define: ROUTE_MSG_ADDRS_SPACE
2467 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2468 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2469 * someone changes the code and doesn't think to modify this.
2471 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2472 + 2 * sizeof(struct sockaddr_dl) \
2475 struct rt_msghdr hdr
;
2476 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2480 * Function: IPv4RouteApply
2482 * Add or remove the specified route to/from the kernel routing table.
2485 IPv4RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
2489 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2492 struct sockaddr_in
* in_p
;
2493 struct sockaddr_dl
* dl_p
;
2497 if (S_netboot
&& route
->dest
.s_addr
== 0) {
2498 /* don't touch the default route */
2499 return (EROUTENOTAPPLIED
);
2501 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
2502 return (EROUTENOTAPPLIED
);
2504 if (route
->ifindex
== 0) {
2506 IP_FORMAT
" no interface specified, ignoring",
2507 IP_LIST(&route
->dest
));
2511 #ifdef TEST_IPV4_ROUTELIST
2513 #else /* TEST_IPV4_ROUTELIST */
2515 #endif /* TEST_IPV4_ROUTELIST */
2517 memset(&rtmsg
, 0, sizeof(rtmsg
));
2518 rtmsg
.hdr
.rtm_type
= cmd
;
2519 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2520 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2521 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
2522 if (route
->ifa
.s_addr
!= 0) {
2523 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
2525 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2526 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
2527 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
2530 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
2531 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
2532 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
2535 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2536 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
2538 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
2539 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
2540 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2543 rtaddr
.ptr
= rtmsg
.addrs
;
2546 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2547 rtaddr
.in_p
->sin_family
= AF_INET
;
2548 rtaddr
.in_p
->sin_addr
= route
->dest
;
2549 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2552 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2553 /* gateway is an IP address */
2554 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2555 rtaddr
.in_p
->sin_family
= AF_INET
;
2556 rtaddr
.in_p
->sin_addr
= route
->gateway
;
2557 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2560 /* gateway is the interface itself */
2561 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2562 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2563 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2564 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2568 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
2569 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2570 rtaddr
.in_p
->sin_family
= AF_INET
;
2571 rtaddr
.in_p
->sin_addr
= route
->mask
;
2572 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2576 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
2577 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2578 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2579 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2580 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2582 /* interface address */
2583 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
2584 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2585 rtaddr
.in_p
->sin_family
= AF_INET
;
2586 rtaddr
.in_p
->sin_addr
= route
->ifa
;
2587 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2590 /* apply the route */
2591 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
2592 rtmsg
.hdr
.rtm_msglen
= len
;
2593 if (write(sockfd
, &rtmsg
, len
) == -1) {
2599 static const RouteListInfo IPv4RouteListInfo
= {
2600 IPv4RouteListComputeSize
,
2605 IPv4RouteSetGateway
,
2606 IPv4RouteDestination
,
2607 IPv4RouteSameSubnet
,
2609 IPv4RouteCopyDescription
,
2612 sizeof(struct in_addr
),
2613 IPV4_ROUTE_ALL_BITS_SET
2616 #if !TARGET_OS_SIMULATOR
2617 static __inline__
void
2618 IPv4RouteListLog(int level
, IPv4RouteListRef routes
)
2620 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2622 my_log(level
, "%@", str
);
2628 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
2631 RouteListApply(&IPv4RouteListInfo
,
2632 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
2638 IPv4RouteListFinalize(IPv4RouteListRef routes
)
2640 RouteListFinalize(&IPv4RouteListInfo
, (RouteListRef
)routes
);
2643 #endif /* !TARGET_OS_SIMULATOR */
2645 #if TEST_IPV4_ROUTELIST
2646 static IPv4RouteListRef
2647 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
2648 IPv4RouteListRef service_routes
, Rank rank
)
2650 return ((IPv4RouteListRef
)
2651 RouteListAddRouteList(&IPv4RouteListInfo
,
2652 (RouteListRef
)routes
, init_size
,
2653 (RouteListRef
)service_routes
, rank
,
2656 #endif /* TEST_IPV4_ROUTELIST */
2659 plist_get_string(CFDictionaryRef dict
, CFStringRef prop_name
,
2660 char * buf
, int buf_size
)
2664 val
= CFDictionaryGetValue(dict
, prop_name
);
2665 if (isA_CFString(val
) == NULL
) {
2668 if (!CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingUTF8
)) {
2675 struct in_addr addr
;
2678 IFIndex exclude_ifindex
;
2679 IPv4RouteRef
* route_p
;
2682 } AddIPv4RouteContext
, * AddIPv4RouteContextRef
;
2685 AddIPv4Route(const void * value
, void * context
)
2687 AddIPv4RouteContextRef ctx
= (AddIPv4RouteContextRef
)context
;
2688 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
2689 IPv4RouteRef r
= *ctx
->route_p
;
2691 dict
= isA_CFDictionary(dict
);
2693 || !dict_get_ip(dict
, kSCPropNetIPv4RouteDestinationAddress
, &r
->dest
)
2694 || !dict_get_ip(dict
, kSCPropNetIPv4RouteSubnetMask
, &r
->mask
)) {
2695 /* one less route than we expected */
2697 my_log(LOG_NOTICE
, "%s route is not a dictionary",
2701 my_log(LOG_NOTICE
, "%s route is invalid, %@",
2706 if (!IPv4RouteSetPrefixLength(r
)) {
2707 my_log(LOG_NOTICE
, "%s route has invalid subnet mask, %@",
2711 r
->rank
= ctx
->rank
;
2712 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
2713 if (ctx
->ifindex
!= 0) {
2714 r
->ifindex
= ctx
->ifindex
;
2716 if (ctx
->exclude_ifindex
== 0
2717 && dict_get_ip(dict
,
2718 kSCPropNetIPv4RouteGatewayAddress
,
2720 r
->flags
|= kRouteFlagsHasGateway
;
2721 if (r
->prefix_length
== IPV4_ROUTE_ALL_BITS_SET
) {
2722 r
->flags
|= kRouteFlagsIsHost
;
2727 char ifname
[IFNAMSIZ
];
2729 if (plist_get_string(dict
, kSCPropNetIPv4RouteInterfaceName
,
2730 ifname
, sizeof(ifname
)) != NULL
) {
2733 ifindex
= my_if_nametoindex(ifname
);
2736 "%s: interface %s does not exist, %@",
2737 ctx
->descr
, ifname
, dict
);
2740 else if (ifindex
== ctx
->ifindex
) {
2742 "%s: interface %s unexpected, %@",
2743 ctx
->descr
, ifname
, dict
);
2746 r
->ifindex
= ifindex
;
2759 confirm_interface_name(CFDictionaryRef dict
, CFStringRef ifname
)
2761 CFStringRef confirmed_ifname
;
2762 boolean_t confirmed
;
2765 = CFDictionaryGetValue(dict
, kSCPropConfirmedInterfaceName
);
2766 if (isA_CFString(confirmed_ifname
) != NULL
) {
2767 confirmed
= CFEqual(confirmed_ifname
, ifname
);
2776 * Function: IPv4RouteListCreateWithDictionary
2779 * Given the service ipv4 entity dictionary, generate the list of routes.
2780 * Currently, this includes just the default route and subnet route,
2781 * if the service has a subnet mask.
2784 * If the passed in route_list is NULL or too small, this routine
2785 * allocates malloc'd memory to hold the routes.
2787 static IPv4RouteListRef
2788 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
2789 CFDictionaryRef dict
,
2790 CFNumberRef rank_assertion
)
2792 boolean_t add_broadcast_multicast
= FALSE
;
2793 boolean_t add_default
= FALSE
;
2794 boolean_t add_router_subnet
= FALSE
;
2795 boolean_t add_subnet
= FALSE
;
2796 struct in_addr addr
= { 0 };
2797 CFArrayRef additional_routes
= NULL
;
2798 CFIndex additional_routes_count
;
2799 boolean_t allow_additional_routes
= FALSE
;
2800 boolean_t exclude_from_nwi
= FALSE
;
2801 CFArrayRef excluded_routes
= NULL
;
2802 CFIndex excluded_routes_count
;
2803 RouteFlags flags
= 0;
2805 char ifname
[IFNAMSIZ
];
2806 CFStringRef ifname_cf
;
2807 struct in_addr mask
= { 0 };
2809 int prefix_length
= 0;
2810 Rank primary_rank
= kRankAssertionDefault
;
2812 Rank rank
= kRankAssertionDefault
;
2813 struct in_addr router
= { 0 };
2814 boolean_t scoped_only
= FALSE
;
2815 struct in_addr subnet
= { 0 };
2820 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
2821 ifname
, sizeof(ifname
));
2822 if (ifname_cf
== NULL
) {
2825 ifindex
= my_if_nametoindex(ifname
);
2827 /* interface doesn't exist */
2830 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
2831 if (!dict_get_ip(dict
, kSCPropNetIPv4Router
, &router
)) {
2832 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
2834 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
2835 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
2837 subnet
= subnet_addr(addr
, mask
);
2838 prefix_length
= mask_get_prefix_length(mask
);
2839 if (prefix_length
< 0) {
2841 "ignoring bad subnet mask "
2843 IP_LIST(&mask
), ifname
);
2850 if (addr
.s_addr
== 0) {
2851 /* invalid/non-existent address */
2854 if (rank_assertion
!= NULL
) {
2855 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
2858 if (router
.s_addr
== 0) {
2859 /* if no router is configured, demote the rank if necessary */
2860 switch (primary_rank
) {
2861 case kRankAssertionLast
:
2862 case kRankAssertionNever
:
2863 case kRankAssertionScoped
:
2864 /* rank is already demoted */
2867 /* demote to RankLast */
2868 primary_rank
= kRankAssertionLast
;
2874 * If the router address is our address and the subnet mask is
2875 * not 255.255.255.255, assume all routes are local to the interface.
2877 if (addr
.s_addr
== router
.s_addr
2878 && mask
.s_addr
!= INADDR_BROADCAST
) {
2879 ; /* all routes local */
2882 flags
|= kRouteFlagsHasGateway
;
2884 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
2885 primary_rank
= kRankAssertionFirst
;
2889 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
2890 exclude_from_nwi
= TRUE
;
2891 flags
|= kRouteFlagsIsNULL
;
2894 switch (primary_rank
) {
2895 case kRankAssertionScoped
:
2896 /* Scoped means all routes for the service get scoped */
2897 primary_rank
= rank
= kRankAssertionNever
;
2898 flags
|= kRouteFlagsIsScoped
;
2901 case kRankAssertionNever
:
2902 /* Never means just the default route gets scoped */
2903 rank
= kRankAssertionLast
;
2904 flags
|= kRouteFlagsIsScoped
;
2907 rank
= primary_rank
;
2911 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2912 add_router_subnet
= TRUE
;
2916 if (ifindex
!= lo0_ifindex()) {
2917 if (router
.s_addr
!= 0) {
2921 add_broadcast_multicast
= TRUE
;
2924 if (allow_additional_routes
) {
2926 = CFDictionaryGetValue(dict
, kSCPropNetIPv4AdditionalRoutes
);
2927 additional_routes
= isA_CFArray(additional_routes
);
2928 if (additional_routes
!= NULL
) {
2929 additional_routes_count
= CFArrayGetCount(additional_routes
);
2930 n
+= additional_routes_count
;
2933 = CFDictionaryGetValue(dict
, kSCPropNetIPv4ExcludedRoutes
);
2934 excluded_routes
= isA_CFArray(excluded_routes
);
2935 if (excluded_routes
!= NULL
) {
2936 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
2937 n
+= excluded_routes_count
;
2940 if (routes
== NULL
|| routes
->size
< n
) {
2941 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
2942 memset(routes
, 0, IPv4RouteListComputeSize(n
));
2946 memset(routes
->list
, 0, sizeof(routes
->list
[0]) * n
);
2949 if (exclude_from_nwi
) {
2950 routes
->flags
|= kRouteListFlagsExcludeNWI
;
2952 else if (scoped_only
) {
2953 routes
->flags
|= kRouteListFlagsScopedOnly
;
2956 /* start at the beginning */
2960 /* add the default route */
2961 routes
->flags
|= kRouteListFlagsHasDefault
;
2962 r
->ifindex
= ifindex
;
2965 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2966 r
->gateway
= router
;
2971 r
->rank
= primary_rank
;
2974 if (add_broadcast_multicast
) {
2975 /* add the broadcast route (rdar://problem/22149738) */
2976 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2977 r
->flags
|= kRouteFlagsIsNULL
;
2979 r
->dest
.s_addr
= INADDR_BROADCAST
;
2980 r
->mask
.s_addr
= INADDR_BROADCAST
;
2981 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2982 r
->ifindex
= ifindex
;
2987 /* add multicast route (rdar://problem/26457121) */
2988 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2989 r
->flags
|= kRouteFlagsIsNULL
;
2991 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
2992 r
->mask
.s_addr
= htonl(IN_CLASSD_NET
);
2993 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSD
;
2994 r
->ifindex
= ifindex
;
3001 /* add the subnet route */
3003 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3004 r
->flags
|= kRouteFlagsIsNULL
;
3006 r
->ifindex
= ifindex
;
3010 r
->prefix_length
= prefix_length
;
3016 /* add the router subnet route */
3017 if (add_router_subnet
) {
3018 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3019 r
->flags
|= kRouteFlagsIsNULL
;
3021 r
->ifindex
= ifindex
;
3024 r
->mask
.s_addr
= INADDR_BROADCAST
;
3025 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
3031 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3032 AddIPv4RouteContext context
;
3034 memset(&context
, 0, sizeof(context
));
3035 context
.count_p
= &routes
->count
;
3036 context
.route_p
= &r
;
3037 context
.rank
= rank
;
3039 /* additional routes */
3040 if (additional_routes
!= NULL
) {
3041 context
.ifindex
= ifindex
;
3042 context
.addr
= addr
;
3043 context
.descr
= "AdditionalRoutes";
3044 CFArrayApplyFunction(additional_routes
,
3045 CFRangeMake(0, additional_routes_count
),
3046 AddIPv4Route
, &context
);
3048 /* excluded routes */
3049 if (excluded_routes
!= NULL
) {
3050 context
.descr
= "ExcludedRoutes";
3051 /* exclude this interface */
3052 context
.ifindex
= 0;
3053 context
.exclude_ifindex
= ifindex
;
3054 CFArrayApplyFunction(excluded_routes
,
3055 CFRangeMake(0, excluded_routes_count
),
3056 AddIPv4Route
, &context
);
3062 #if !TARGET_OS_SIMULATOR
3063 static IPv4RouteListRef
3064 IPv4RouteListCopyMulticastLoopback(void)
3067 IPv4RouteListRef routes
;
3069 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(1));
3070 memset(routes
, 0, IPv4RouteListComputeSize(1));
3071 routes
->count
= routes
->size
= 1;
3074 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
3075 r
->mask
.s_addr
= htonl(IN_CLASSC_NET
);
3076 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSC
;
3077 r
->ifindex
= lo0_ifindex();
3080 #endif /* !TARGET_OS_SIMULATOR */
3085 #define IPV6_ROUTE_ALL_BITS_SET 128
3088 ipv6_prefix_length_is_valid(int prefix_length
)
3090 if (prefix_length
< 0 || prefix_length
> IPV6_ROUTE_ALL_BITS_SET
) {
3097 * from netinet6/in6.c
3100 in6_len2mask(struct in6_addr
* mask
, int len
)
3104 memset(mask
, 0, sizeof(*mask
));
3105 for (i
= 0; i
< len
/ 8; i
++)
3106 mask
->s6_addr
[i
] = 0xff;
3108 mask
->s6_addr
[i
] = (0xff00 >> (len
% 8)) & 0xff;
3112 in6_maskaddr(struct in6_addr
* addr
, const struct in6_addr
* mask
)
3114 for (size_t i
= 0; i
< sizeof(addr
->s6_addr
); i
++) {
3115 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3121 in6_netaddr(struct in6_addr
* addr
, int len
)
3123 struct in6_addr mask
;
3125 in6_len2mask(&mask
, len
);
3126 in6_maskaddr(addr
, &mask
);
3131 in6_addr_scope_linklocal(struct in6_addr
* addr
, IFIndex ifindex
)
3133 if (IN6_IS_ADDR_LINKLOCAL(addr
)) {
3134 addr
->__u6_addr
.__u6_addr16
[1] = htons(ifindex
);
3140 string_append_in6_addr(CFMutableStringRef str
, const struct in6_addr
* addr
)
3142 char ntopbuf
[INET6_ADDRSTRLEN
];
3144 CFStringAppendCString(str
,
3145 inet_ntop(AF_INET6
, addr
, ntopbuf
, sizeof(ntopbuf
)),
3146 kCFStringEncodingASCII
);
3151 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r
, CFMutableStringRef str
)
3153 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
3154 CFStringAppend(str
, CFSTR("Host "));
3155 string_append_in6_addr(str
, &r
->dest
);
3158 CFStringAppend(str
, CFSTR("Net "));
3159 string_append_in6_addr(str
, &r
->dest
);
3160 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
3163 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
3164 CFStringAppend(str
, CFSTR(" Gate "));
3165 string_append_in6_addr(str
, &r
->gateway
);
3167 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
3168 if (!IN6_ARE_ADDR_EQUAL(&r
->ifa
, &in6addr_any
)) {
3169 CFStringAppend(str
, CFSTR(" Ifa "));
3170 string_append_in6_addr(str
, &r
->ifa
);
3172 RouteAddFlagsToDescription((RouteRef
)r
, str
);
3177 IPv6RouteCopyDescription(RouteRef r
)
3179 CFMutableStringRef str
;
3181 str
= CFStringCreateMutable(NULL
, 0);
3182 IPv6RouteCopyDescriptionWithString((IPv6RouteRef
)r
, str
);
3186 static CFMutableStringRef
3187 IPv6RouteListCopyDescription(IPv6RouteListRef routes
)
3191 CFMutableStringRef str
;
3193 str
= CFStringCreateMutable(NULL
, 0);
3194 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv6RouteList[%d]> = {"),
3196 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
3197 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
3198 IPv6RouteCopyDescriptionWithString(r
, str
);
3200 CFStringAppend(str
, CFSTR("\n}"));
3204 #if TEST_IPV6_ROUTELIST
3207 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3209 CFStringRef str
= IPv6RouteCopyDescription(route
);
3212 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3215 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
3221 static __inline__
void
3222 IPv6RouteListPrint(IPv6RouteListRef routes
)
3224 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3226 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3231 #else /* TEST_IPV6_ROUTELIST */
3233 static __inline__
void
3234 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3236 CFStringRef str
= IPv6RouteCopyDescription(route
);
3239 my_log(level
, "%@", str
);
3242 my_log(level
, "%s: %@", msg
, str
);
3248 #endif /* TEST_IPV6_ROUTELIST */
3251 IPv6RouteListComputeSize(CFIndex n
)
3253 return (offsetof(IPv6RouteList
, list
[n
]));
3258 struct in6_addr
* addr
;
3261 IFIndex exclude_ifindex
;
3262 IPv6RouteRef
* route_p
;
3265 } AddIPv6RouteContext
, * AddIPv6RouteContextRef
;
3268 AddIPv6Route(const void * value
, void * context
)
3270 AddIPv6RouteContextRef ctx
= (AddIPv6RouteContextRef
)context
;
3271 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
3272 IPv6RouteRef r
= *ctx
->route_p
;
3274 dict
= isA_CFDictionary(dict
);
3276 || !dict_get_ipv6(dict
, kSCPropNetIPv6RouteDestinationAddress
, &r
->dest
)
3277 || !dict_get_int(dict
, kSCPropNetIPv6RoutePrefixLength
,
3279 || !ipv6_prefix_length_is_valid(r
->prefix_length
)) {
3280 /* one less route than we expected */
3282 my_log(LOG_NOTICE
, "%s route is not a dictionary",
3286 my_log(LOG_NOTICE
, "%s route is invalid, %@",
3291 r
->rank
= ctx
->rank
;
3292 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
3293 if (ctx
->ifindex
!= 0) {
3294 r
->ifindex
= ctx
->ifindex
;
3295 r
->ifa
= *ctx
->addr
;
3296 if (ctx
->exclude_ifindex
== 0
3297 && dict_get_ipv6(dict
,
3298 kSCPropNetIPv6RouteGatewayAddress
,
3300 r
->flags
|= kRouteFlagsHasGateway
;
3301 if (r
->prefix_length
== IPV6_ROUTE_ALL_BITS_SET
) {
3302 r
->flags
|= kRouteFlagsIsHost
;
3307 char ifname
[IFNAMSIZ
];
3309 if (plist_get_string(dict
, kSCPropNetIPv6RouteInterfaceName
,
3310 ifname
, sizeof(ifname
)) != NULL
) {
3313 ifindex
= my_if_nametoindex(ifname
);
3316 "%s: interface %s does not exist, %@",
3317 ctx
->descr
, ifname
, dict
);
3320 else if (ifindex
== ctx
->ifindex
) {
3322 "%s: interface %s unexpected, %@",
3323 ctx
->descr
, ifname
, dict
);
3326 r
->ifindex
= ifindex
;
3339 * Function: IPv6RouteListCreateWithDictionary
3342 * Given the service IPv6 entity dictionary, generate the list of routes.
3345 * If the passed in route_list is NULL or too small, this routine
3346 * allocates malloc'd memory to hold the routes.
3348 static IPv6RouteListRef
3349 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes
,
3350 CFDictionaryRef dict
,
3351 CFNumberRef rank_assertion
)
3353 boolean_t add_default
= FALSE
;
3354 boolean_t add_prefix
= FALSE
;
3355 struct in6_addr addr
;
3356 CFArrayRef additional_routes
= NULL
;
3357 CFIndex additional_routes_count
;
3358 boolean_t allow_additional_routes
= FALSE
;
3359 boolean_t exclude_from_nwi
= FALSE
;
3360 CFArrayRef excluded_routes
= NULL
;
3361 CFIndex excluded_routes_count
;
3362 RouteFlags flags
= 0;
3364 char ifname
[IFNAMSIZ
];
3365 CFStringRef ifname_cf
;
3367 int prefix_length
= 0;
3368 Rank primary_rank
= kRankAssertionDefault
;
3370 Rank rank
= kRankAssertionDefault
;
3371 struct in6_addr router
= in6addr_any
;
3372 boolean_t scoped_only
= FALSE
;
3377 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
3378 ifname
, sizeof(ifname
));
3379 if (ifname_cf
== NULL
) {
3382 ifindex
= my_if_nametoindex(ifname
);
3384 /* interface doesn't exist */
3387 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
3388 if (!dict_get_ipv6(dict
, kSCPropNetIPv6Router
, &router
)) {
3389 (void)dict_get_first_ipv6(dict
, kSCPropNetIPv6DestAddresses
, &router
);
3391 if (dict_get_first_ipv6(dict
, kSCPropNetIPv6Addresses
, &addr
)) {
3392 if (IN6_IS_ADDR_UNSPECIFIED(&addr
)) {
3395 if (dict_get_first_int(dict
, kSCPropNetIPv6PrefixLength
,
3397 && !IN6_IS_ADDR_LINKLOCAL(&addr
)
3398 && ipv6_prefix_length_is_valid(prefix_length
)) {
3410 if (rank_assertion
!= NULL
) {
3411 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
3414 if (!IN6_IS_ADDR_UNSPECIFIED(&router
)) {
3415 if (ifindex
!= lo0_ifindex()) {
3420 * If the router address is our address and the prefix length is
3421 * not 128, assume all routes are local to the interface.
3423 if (IN6_ARE_ADDR_EQUAL(&router
, &addr
)
3424 && prefix_length
!= IPV6_ROUTE_ALL_BITS_SET
) {
3425 ; /* all routes local */
3428 flags
|= kRouteFlagsHasGateway
;
3430 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
3431 primary_rank
= kRankAssertionFirst
;
3434 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
3435 exclude_from_nwi
= TRUE
;
3436 flags
|= kRouteFlagsIsNULL
;
3439 switch (primary_rank
) {
3440 case kRankAssertionScoped
:
3441 /* Scoped means all routes for the service get scoped */
3442 primary_rank
= rank
= kRankAssertionNever
;
3443 flags
|= kRouteFlagsIsScoped
;
3446 case kRankAssertionNever
:
3447 /* Never means just the default route gets scoped */
3448 rank
= kRankAssertionLast
;
3449 flags
|= kRouteFlagsIsScoped
;
3452 rank
= primary_rank
;
3456 if (allow_additional_routes
) {
3458 = CFDictionaryGetValue(dict
, kSCPropNetIPv6AdditionalRoutes
);
3459 additional_routes
= isA_CFArray(additional_routes
);
3460 if (additional_routes
!= NULL
) {
3461 additional_routes_count
= CFArrayGetCount(additional_routes
);
3462 n
+= additional_routes_count
;
3464 excluded_routes
= CFDictionaryGetValue(dict
,
3465 kSCPropNetIPv6ExcludedRoutes
);
3466 excluded_routes
= isA_CFArray(excluded_routes
);
3467 if (excluded_routes
!= NULL
) {
3468 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
3469 n
+= excluded_routes_count
;
3476 /* need IPv6LL subnet route */
3479 if (routes
== NULL
|| routes
->size
< n
) {
3480 routes
= (IPv6RouteListRef
)malloc(IPv6RouteListComputeSize(n
));
3481 memset(routes
, 0, IPv6RouteListComputeSize(n
));
3485 memset(routes
->list
, 0, sizeof(routes
->list
[0]) * n
);
3488 if (exclude_from_nwi
) {
3489 routes
->flags
|= kRouteListFlagsExcludeNWI
;
3491 else if (scoped_only
) {
3492 routes
->flags
|= kRouteListFlagsScopedOnly
;
3495 /* start at the beginning */
3498 /* add the default route */
3499 routes
->flags
|= kRouteListFlagsHasDefault
;
3500 r
->ifindex
= ifindex
;
3503 if ((flags
& kRouteFlagsHasGateway
) != 0) {
3504 r
->gateway
= router
;
3509 r
->rank
= primary_rank
;
3510 r
->flags
|= kRouteFlagsKernelManaged
;
3515 /* add IPv6LL route */
3516 r
->ifindex
= ifindex
;
3517 r
->dest
.s6_addr
[0] = 0xfe;
3518 r
->dest
.s6_addr
[1] = 0x80;
3519 r
->prefix_length
= 64;
3521 r
->flags
|= kRouteFlagsKernelManaged
;
3525 /* add the prefix route(s) */
3527 r
->flags
|= kRouteFlagsKernelManaged
;
3528 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3529 r
->flags
|= kRouteFlagsIsNULL
;
3531 r
->ifindex
= ifindex
;
3534 in6_netaddr(&r
->dest
, prefix_length
);
3535 r
->prefix_length
= prefix_length
;
3541 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3542 AddIPv6RouteContext context
;
3544 memset(&context
, 0, sizeof(context
));
3545 context
.count_p
= &routes
->count
;
3546 context
.route_p
= &r
;
3547 context
.rank
= rank
;
3549 /* additional routes */
3550 if (additional_routes
!= NULL
) {
3551 context
.ifindex
= ifindex
;
3552 context
.addr
= &addr
;
3553 context
.descr
= "AdditionalRoutes";
3554 CFArrayApplyFunction(additional_routes
,
3555 CFRangeMake(0, additional_routes_count
),
3556 AddIPv6Route
, &context
);
3558 /* excluded routes */
3559 if (excluded_routes
!= NULL
) {
3560 context
.descr
= "ExcludedRoutes";
3561 /* exclude this interface */
3562 context
.ifindex
= 0;
3563 context
.exclude_ifindex
= ifindex
;
3564 context
.addr
= NULL
;
3565 CFArrayApplyFunction(excluded_routes
,
3566 CFRangeMake(0, excluded_routes_count
),
3567 AddIPv6Route
, &context
);
3574 IPv6RouteGateway(RouteRef r_route
)
3576 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3577 return (&route
->gateway
);
3581 IPv6RouteSetGateway(RouteRef r_route
, const void * address
)
3583 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3585 route
->gateway
= *((struct in6_addr
*)address
);
3590 IPv6RouteDestination(RouteRef r_route
)
3592 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3593 return (&route
->dest
);
3596 static __inline__
int
3597 in6_addr_cmp(const struct in6_addr
* a
, const struct in6_addr
* b
)
3599 return (memcmp(a
->s6_addr
, b
->s6_addr
, sizeof(struct in6_addr
)));
3603 IPv6RouteIsEqual(RouteRef r_route1
, RouteRef r_route2
)
3605 IPv6RouteRef route1
= (IPv6RouteRef
)r_route1
;
3606 IPv6RouteRef route2
= (IPv6RouteRef
)r_route2
;
3608 return (route1
->prefix_length
== route2
->prefix_length
3609 && route1
->ifindex
== route2
->ifindex
3610 && route1
->flags
== route2
->flags
3611 && in6_addr_cmp(&route1
->dest
, &route2
->dest
) == 0
3612 && in6_addr_cmp(&route1
->ifa
, &route2
->ifa
) == 0
3613 && in6_addr_cmp(&route1
->gateway
, &route2
->gateway
) == 0);
3617 IPv6RouteSameSubnet(RouteRef r_route
, const void * addr
)
3619 const struct in6_addr
* address
= (const struct in6_addr
*)addr
;
3620 struct in6_addr netaddr
;
3621 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3624 in6_netaddr(&netaddr
, route
->prefix_length
);
3625 return (in6_addr_cmp(&netaddr
, &route
->dest
) == 0);
3629 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3632 struct rt_msghdr hdr
;
3633 char addrs
[V6_ROUTE_MSG_ADDRS_SPACE
];
3637 * Function: IPv6RouteApply
3639 * Add or remove the specified route to/from the kernel routing table.
3642 IPv6RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
3646 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3649 struct sockaddr_in6
* in_p
;
3650 struct sockaddr_dl
* dl_p
;
3654 if ((route
->flags
& kRouteFlagsKernelManaged
) != 0) {
3655 /* the kernel manages this route, don't touch it */
3656 return (EROUTENOTAPPLIED
);
3658 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
3659 return (EROUTENOTAPPLIED
);
3661 if (route
->ifindex
== 0) {
3662 IPv6RouteLog(LOG_NOTICE
, (RouteRef
)route
,
3663 "no interface specified");
3667 #if TEST_IPV6_ROUTELIST
3669 #else /* TEST_IPV6_ROUTELIST */
3671 #endif /* TEST_IPV6_ROUTELIST */
3673 memset(&rtmsg
, 0, sizeof(rtmsg
));
3674 rtmsg
.hdr
.rtm_type
= cmd
;
3675 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3676 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3677 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
3678 if (!IN6_IS_ADDR_UNSPECIFIED(&route
->ifa
)) {
3679 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
3681 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3682 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
3683 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
3686 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
3687 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
3688 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
3691 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
3692 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
3694 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
3695 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
3696 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
3699 rtaddr
.ptr
= rtmsg
.addrs
;
3702 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3703 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3704 rtaddr
.in_p
->sin6_addr
= route
->dest
;
3705 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3706 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3709 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
3710 /* gateway is an IP address */
3711 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3712 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3713 rtaddr
.in_p
->sin6_addr
= route
->gateway
;
3714 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3715 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3718 /* gateway is the interface itself */
3719 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3720 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3721 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3722 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3726 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
3727 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3728 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3729 in6_len2mask(&rtaddr
.in_p
->sin6_addr
, route
->prefix_length
);
3730 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3734 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
3735 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3736 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3737 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3738 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3740 /* interface address */
3741 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
3742 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3743 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3744 rtaddr
.in_p
->sin6_addr
= route
->ifa
;
3745 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3748 /* apply the route */
3749 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
3750 rtmsg
.hdr
.rtm_msglen
= len
;
3751 if (write(sockfd
, &rtmsg
, len
) == -1) {
3757 static const RouteListInfo IPv6RouteListInfo
= {
3758 IPv6RouteListComputeSize
,
3763 IPv6RouteSetGateway
,
3764 IPv6RouteDestination
,
3765 IPv6RouteSameSubnet
,
3767 IPv6RouteCopyDescription
,
3770 sizeof(struct in6_addr
),
3771 IPV6_ROUTE_ALL_BITS_SET
3774 #if TEST_IPV6_ROUTELIST
3775 static IPv6RouteListRef
3776 IPv6RouteListAddRouteList(IPv6RouteListRef routes
, int init_size
,
3777 IPv6RouteListRef service_routes
, Rank rank
)
3779 return ((IPv6RouteListRef
)
3780 RouteListAddRouteList(&IPv6RouteListInfo
,
3781 (RouteListRef
)routes
, init_size
,
3782 (RouteListRef
)service_routes
, rank
,
3785 #endif /* TEST_IPV6_ROUTELIST */
3787 #if !TARGET_OS_SIMULATOR
3788 static __inline__
void
3789 IPv6RouteListLog(int level
, IPv6RouteListRef routes
)
3791 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3793 my_log(level
, "%@", str
);
3799 IPv6RouteListFinalize(IPv6RouteListRef routes
)
3801 RouteListFinalize(&IPv6RouteListInfo
, (RouteListRef
)routes
);
3806 IPv6RouteListApply(IPv6RouteListRef old_routes
, IPv6RouteListRef new_routes
,
3809 RouteListApply(&IPv6RouteListInfo
,
3810 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
3814 #endif /* !TARGET_OS_SIMULATOR */
3817 * Function: parseNetworkServiceString
3819 * Parse either of the following two formats:
3820 * <domain>:/Network/Service/<serviceID>
3821 * <domain>:/Network/Service/<serviceID>/<protocol>
3822 * returning <serviceID>, and if available and required, <protocol>.
3824 static CF_RETURNS_RETAINED CFStringRef
3825 parseNetworkServiceString(CFStringRef str
, CFStringRef
*ret_protocol
)
3827 CFArrayRef components
;
3829 CFStringRef protocol
= NULL
;
3830 CFStringRef serviceID
= NULL
;
3833 * str = "<domain>:/Network/Service/<serviceID>"
3835 * str = "<domain>:/Network/Service/<serviceID>/<protocol>"
3837 components
= CFStringCreateArrayBySeparatingStrings(NULL
, str
, CFSTR("/"));
3838 count
= CFArrayGetCount(components
);
3840 /* we have a serviceID */
3841 serviceID
= CFArrayGetValueAtIndex(components
, 3);
3842 CFRetain(serviceID
);
3843 if (count
>= 5 && ret_protocol
!= NULL
) {
3844 /* we have and want a protocol */
3845 protocol
= CFArrayGetValueAtIndex(components
, 4);
3849 if (ret_protocol
!= NULL
) {
3850 *ret_protocol
= protocol
;
3852 my_CFRelease(&components
);
3857 ipdict_is_routable(CFDictionaryRef entity_dict
)
3859 RouteListRef routes
;
3861 routes
= ipdict_get_routelist(entity_dict
);
3862 if (routes
== NULL
) {
3867 if ((routes
->flags
& kRouteListFlagsHasDefault
) == 0) {
3868 // if service has no default route
3872 if ((routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
3873 // if service should be excluded from NWI
3881 __private_extern__ boolean_t
3882 service_is_routable(CFDictionaryRef service_dict
, int af
)
3884 boolean_t contains_protocol
;
3886 CFDictionaryRef entity_dict
;
3888 entity
= (af
== AF_INET
) ? kSCEntNetIPv4
: kSCEntNetIPv6
;
3889 entity_dict
= CFDictionaryGetValue(service_dict
, entity
);
3890 if (entity_dict
== NULL
) {
3894 contains_protocol
= ipdict_is_routable(entity_dict
);
3895 return contains_protocol
;
3899 static CFMutableDictionaryRef
3900 service_dict_copy(CFStringRef serviceID
)
3902 CFDictionaryRef d
= NULL
;
3903 CFMutableDictionaryRef service_dict
;
3905 /* create a modifyable dictionary, a copy or a new one */
3906 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3909 = CFDictionaryCreateMutable(NULL
, 0,
3910 &kCFTypeDictionaryKeyCallBacks
,
3911 &kCFTypeDictionaryValueCallBacks
);
3914 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
3916 return (service_dict
);
3919 __private_extern__ boolean_t
3920 service_is_scoped_only(CFDictionaryRef service_dict
)
3922 nwi_ifstate_t alias
;
3923 CFDictionaryRef dict
;
3924 char ifname
[IFNAMSIZ
];
3925 nwi_ifstate_t ifstate
;
3926 CFStringRef interface
= NULL
;
3928 // get IPv4 (or IPv6) info
3929 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3931 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
3934 // if no connectivity
3939 interface
= ipdict_get_ifname(dict
);
3940 if ((interface
== NULL
) ||
3941 !CFStringGetCString(interface
, ifname
, sizeof(ifname
), kCFStringEncodingUTF8
)) {
3942 // if no interface / interface name
3947 if (S_nwi_state
== NULL
) {
3948 S_nwi_state
= nwi_state_copy();
3952 // get [nwi] interface state
3953 ifstate
= nwi_state_get_ifstate(S_nwi_state
, ifname
);
3954 if (ifstate
== NULL
) {
3957 } else if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3958 // if scoped (i.e. not in list)
3962 // check both both IPv4 and IPv6
3963 alias
= nwi_ifstate_get_alias(ifstate
, ifstate
->af
== AF_INET
? AF_INET6
: AF_INET
);
3964 if (alias
== NULL
) {
3965 // if only one address family
3967 } else if ((alias
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3968 // if scoped (i.e. not in list)
3976 log_service_entity(int level
, CFStringRef serviceID
, CFStringRef entity
,
3977 CFStringRef operation
, CFTypeRef val
)
3979 CFMutableStringRef this_val
= NULL
;
3984 if ((is_ipv4
= CFEqual(entity
, kSCEntNetIPv4
))
3985 || CFEqual(entity
, kSCEntNetIPv6
)) {
3986 RouteListUnion routes
;
3988 routes
.ptr
= ipdict_get_routelist(val
);
3989 if (routes
.ptr
!= NULL
) {
3990 CFDictionaryRef service_dict
= NULL
;
3993 this_val
= IPv4RouteListCopyDescription(routes
.v4
);
3996 this_val
= IPv6RouteListCopyDescription(routes
.v6
);
3998 service_dict
= ipdict_get_service(val
);
3999 if (service_dict
!= NULL
) {
4000 CFStringAppendFormat(this_val
, NULL
,
4001 CFSTR("\n<Service> = %@"),
4009 val
= CFSTR("<none>");
4011 my_log(level
, "serviceID %@ %@ %@ value = %@",
4012 serviceID
, operation
, entity
, val
);
4014 my_CFRelease(&this_val
);
4019 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
4022 boolean_t changed
= FALSE
;
4024 CFMutableDictionaryRef service_dict
;
4026 service_dict
= service_dict_copy(serviceID
);
4027 old_val
= CFDictionaryGetValue(service_dict
, entity
);
4028 if (new_val
== NULL
) {
4029 if (old_val
!= NULL
) {
4030 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4031 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
4032 CFSTR("Removed:"), old_val
);
4034 CFDictionaryRemoveValue(service_dict
, entity
);
4039 if (old_val
== NULL
|| !CFEqual(new_val
, old_val
)) {
4040 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4041 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
4042 CFSTR("Changed: old"), old_val
);
4043 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
4044 CFSTR("Changed: new"), new_val
);
4046 CFDictionarySetValue(service_dict
, entity
, new_val
);
4050 if (CFDictionaryGetCount(service_dict
) == 0) {
4051 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
4054 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
4056 my_CFRelease(&service_dict
);
4060 static CFDictionaryRef
4061 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
4063 CFDictionaryRef service_dict
;
4065 if (S_service_state_dict
== NULL
) {
4068 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
4069 if (service_dict
== NULL
) {
4072 return (CFDictionaryGetValue(service_dict
, entity
));
4075 #if !TARGET_OS_SIMULATOR
4077 service_copy_interface(CFStringRef serviceID
, CFDictionaryRef new_service
)
4079 CFDictionaryRef dict
;
4080 CFStringRef interface
= NULL
;
4082 if (new_service
!= NULL
) {
4083 interface
= ipdict_get_ifname(new_service
);
4085 if (interface
== NULL
) {
4086 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4088 interface
= ipdict_get_ifname(dict
);
4091 if (interface
== NULL
) {
4092 dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4094 interface
= ipdict_get_ifname(dict
);
4097 if (interface
!= NULL
) {
4098 CFRetain(interface
);
4102 #endif /* !TARGET_OS_SIMULATOR */
4105 service_has_clat46_address(CFStringRef serviceID
)
4107 CFDictionaryRef ip_dict
;
4109 ip_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4110 if (ip_dict
!= NULL
) {
4111 CFBooleanRef clat46
= NULL
;
4112 CFDictionaryRef ipv4
;
4114 ipv4
= ipdict_get_service(ip_dict
);
4115 if (isA_CFDictionary(ipv4
) &&
4116 CFDictionaryGetValueIfPresent(ipv4
,
4117 kSCPropNetIPv4CLAT46
,
4118 (const void **)&clat46
) &&
4119 isA_CFBoolean(clat46
)) {
4120 return CFBooleanGetValue(clat46
);
4127 #ifndef kSCPropNetHostname
4128 #define kSCPropNetHostname CFSTR("Hostname")
4133 copy_dhcp_hostname(CFStringRef serviceID
)
4135 CFDictionaryRef dict
= NULL
;
4136 CFStringRef hostname
= NULL
;
4137 CFDictionaryRef service_dict
= NULL
;
4139 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4143 service_dict
= ipdict_get_service(dict
);
4144 if (service_dict
== NULL
) {
4147 hostname
= CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
4148 if (hostname
!= NULL
) {
4154 #if !TARGET_OS_SIMULATOR
4156 static struct in6_addr
*
4157 ipv6_service_get_router(CFDictionaryRef service
,
4158 IFIndex
* ifindex_p
, CFStringRef
* ifname_p
)
4160 IPv6RouteListRef routes
;
4161 struct in6_addr
* router
= NULL
;
4163 routes
= ipdict_get_routelist(service
);
4165 && (routes
->flags
& kRouteListFlagsExcludeNWI
) == 0
4166 && (routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
4167 router
= &routes
->list
[0].gateway
;
4168 if (*ifindex_p
== 0) {
4169 *ifindex_p
= routes
->list
[0].ifindex
;
4171 if (*ifname_p
== NULL
) {
4172 *ifname_p
= ipdict_get_ifname(service
);
4179 ipv6_service_update_router(CFStringRef serviceID
, CFDictionaryRef new_service
)
4181 IFIndex ifindex
= 0;
4182 CFStringRef ifname
= NULL
;
4183 char ntopbuf
[INET6_ADDRSTRLEN
];
4184 CFDictionaryRef old_service
;
4185 struct in6_addr
* old_router
;
4186 struct in6_addr
* new_router
;
4189 old_service
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4190 old_router
= ipv6_service_get_router(old_service
, &ifindex
, &ifname
);
4191 new_router
= ipv6_service_get_router(new_service
, &ifindex
, &ifname
);
4192 if (ifname
== NULL
|| ifindex
== 0) {
4195 s
= inet6_dgram_socket();
4199 /* remove the old router if it was defined */
4200 if (old_router
!= NULL
4201 && (new_router
== NULL
4202 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4203 if (siocdrdel_in6(s
, ifindex
, old_router
) < 0) {
4204 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4205 "siocdrdel_in6(%@, %s) failed: %s",
4207 inet_ntop(AF_INET6
, old_router
,
4208 ntopbuf
, sizeof(ntopbuf
)),
4213 "%@ removed default route %s",
4215 inet_ntop(AF_INET6
, old_router
, ntopbuf
, sizeof(ntopbuf
)));
4218 /* add the new router if it is defined */
4219 if (new_router
!= NULL
4220 && (old_router
== NULL
4221 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4222 if (siocdradd_in6(s
, ifindex
, new_router
, 0) < 0) {
4223 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4224 "siocdradd_in6(%@, %s) failed: %s",
4226 inet_ntop(AF_INET6
, new_router
,
4227 ntopbuf
, sizeof(ntopbuf
)),
4232 "%@ added default route %s",
4234 inet_ntop(AF_INET6
, new_router
, ntopbuf
, sizeof(ntopbuf
)));
4242 #endif /* !TARGET_OS_SIMULATOR */
4244 #define ALLOW_EMPTY_STRING 0x1
4246 static CF_RETURNS_RETAINED CFTypeRef
4247 sanitize_prop(CFTypeRef val
, uint32_t flags
)
4250 if (isA_CFString(val
)) {
4251 CFMutableStringRef str
;
4253 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
4254 CFStringTrimWhitespace(str
);
4255 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
4269 merge_array_prop(CFMutableDictionaryRef dict
,
4271 CFDictionaryRef state_dict
,
4272 CFDictionaryRef setup_dict
,
4276 CFMutableArrayRef merge_prop
;
4277 CFArrayRef setup_prop
= NULL
;
4278 CFArrayRef state_prop
= NULL
;
4280 if (setup_dict
!= NULL
) {
4281 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
4283 if (state_dict
!= NULL
) {
4284 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
4287 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
4291 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4292 if (setup_prop
!= NULL
) {
4296 n
= CFArrayGetCount(setup_prop
);
4297 for (i
= 0; i
< n
; i
++) {
4300 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
4301 val
= sanitize_prop(val
, flags
);
4303 CFArrayAppendValue(merge_prop
, val
);
4308 if (state_prop
!= NULL
4309 && (setup_prop
== NULL
|| S_append_state
)) {
4312 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
4314 n
= CFArrayGetCount(state_prop
);
4315 for (i
= 0; i
< n
; i
++) {
4318 val
= CFArrayGetValueAtIndex(state_prop
, i
);
4319 val
= sanitize_prop(val
, flags
);
4321 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
4322 CFArrayAppendValue(merge_prop
, val
);
4328 if (CFArrayGetCount(merge_prop
) > 0) {
4329 CFDictionarySetValue(dict
, key
, merge_prop
);
4331 CFRelease(merge_prop
);
4336 pick_prop(CFMutableDictionaryRef dict
,
4338 CFDictionaryRef state_dict
,
4339 CFDictionaryRef setup_dict
,
4342 CFTypeRef val
= NULL
;
4344 if (setup_dict
!= NULL
) {
4345 val
= CFDictionaryGetValue(setup_dict
, key
);
4346 val
= sanitize_prop(val
, flags
);
4348 if (val
== NULL
&& state_dict
!= NULL
) {
4349 val
= CFDictionaryGetValue(state_dict
, key
);
4350 val
= sanitize_prop(val
, flags
);
4353 CFDictionarySetValue(dict
, key
, val
);
4361 ** GetEntityChangesFunc functions
4363 #define IPV4_ROUTES_N_STATIC 5
4364 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4365 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4369 #define IPV4_ROUTES_BUF_DECL(routes) \
4370 IPv4RouteListRef routes; \
4371 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4373 routes = (IPv4RouteListRef)(void *)routes_buf; \
4374 routes->size = IPV4_ROUTES_N_STATIC; \
4375 routes->count = 0; \
4379 IPv4RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4382 CFDataRef routes_data
;
4383 IPV4_ROUTES_BUF_DECL(routes
);
4385 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4387 routes_data
= CFDataCreate(NULL
,
4389 IPv4RouteListComputeSize(r
->count
));
4397 return (routes_data
);
4399 #define IPV6_ROUTES_N_STATIC 3
4400 #define IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4401 (roundup(IPv6RouteListComputeSize(IPV6_ROUTES_N_STATIC), \
4405 #define IPV6_ROUTES_BUF_DECL(routes) \
4406 IPv6RouteListRef routes; \
4407 uint32_t routes_buf[IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4409 routes = (IPv6RouteListRef)(void *)routes_buf; \
4410 routes->size = IPV6_ROUTES_N_STATIC; \
4411 routes->count = 0; \
4415 IPv6RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4418 CFDataRef routes_data
;
4419 IPV6_ROUTES_BUF_DECL(routes
);
4421 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4423 routes_data
= CFDataCreate(NULL
,
4425 IPv6RouteListComputeSize(r
->count
));
4433 return (routes_data
);
4436 static CFDictionaryRef
4437 IPDictCreate(int af
, _Nonnull CFDictionaryRef state_dict
,
4438 CFDictionaryRef setup_dict
,
4439 CFNumberRef rank_assertion
)
4441 CFDictionaryRef aggregated_dict
= NULL
;
4442 CFDictionaryRef dict
;
4443 CFMutableDictionaryRef modified_dict
= NULL
;
4444 CFDataRef routes_data
;
4447 if (setup_dict
!= NULL
) {
4448 /* look for keys in Setup: that override/merge with State: */
4449 CFArrayRef additional_routes
;
4450 CFStringRef route_list_prop
;
4453 CFStringRef router_prop
;
4458 router_prop
= kSCPropNetIPv4Router
;
4459 route_list_prop
= kSCPropNetIPv4AdditionalRoutes
;
4463 router_prop
= kSCPropNetIPv6Router
;
4464 route_list_prop
= kSCPropNetIPv6AdditionalRoutes
;
4467 router
= CFDictionaryGetValue(setup_dict
, router_prop
);
4469 && !cfstring_to_ipvx(af
, router
, &router_ip
, sizeof(router_ip
))) {
4473 /* AdditionalRoutes */
4475 = CFDictionaryGetValue(setup_dict
, route_list_prop
);
4476 additional_routes
= isA_CFArray(additional_routes
);
4478 if (router
!= NULL
|| additional_routes
!= NULL
) {
4479 modified_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
4480 if (router
!= NULL
) {
4481 CFDictionarySetValue(modified_dict
,
4485 if (additional_routes
!= NULL
) {
4486 CFArrayRef combined_routes
= NULL
;
4487 CFArrayRef state_routes
;
4490 = CFDictionaryGetValue(state_dict
,
4492 if (isA_CFArray(state_routes
) != NULL
) {
4494 = my_CFArrayCreateCombinedArray(additional_routes
,
4496 additional_routes
= combined_routes
;
4498 CFDictionarySetValue(modified_dict
,
4501 if (combined_routes
!= NULL
) {
4502 CFRelease(combined_routes
);
4505 dict
= modified_dict
;
4510 routes_data
= IPv4RouteListDataCreate(dict
, rank_assertion
);
4514 routes_data
= IPv6RouteListDataCreate(dict
, rank_assertion
);
4517 if (routes_data
!= NULL
) {
4518 aggregated_dict
= ipdict_create(dict
, routes_data
);
4519 CFRelease(routes_data
);
4521 if (modified_dict
!= NULL
) {
4522 CFRelease(modified_dict
);
4524 return (aggregated_dict
);
4528 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4529 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4531 #pragma unused(info)
4532 CFDictionaryRef dict
= NULL
;
4533 boolean_t changed
= FALSE
;
4534 CFNumberRef rank_assertion
= NULL
;
4535 CFDictionaryRef service_options
;
4537 if (state_dict
== NULL
) {
4540 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4541 if (service_options
!= NULL
) {
4543 = CFDictionaryGetValue(service_options
,
4544 kServiceOptionRankAssertion
);
4546 dict
= IPDictCreate(AF_INET
, state_dict
, setup_dict
, rank_assertion
);
4549 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, dict
);
4551 /* clean up the rank too */
4552 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
4554 my_CFRelease(&dict
);
4560 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4561 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4563 #pragma unused(info)
4564 CFDictionaryRef dict
= NULL
;
4565 boolean_t changed
= FALSE
;
4566 #if !TARGET_OS_SIMULATOR
4567 CFStringRef interface
;
4568 #endif /* !TARGET_OS_SIMULATOR */
4569 CFNumberRef rank_assertion
= NULL
;
4570 CFDictionaryRef service_options
;
4572 if (state_dict
== NULL
) {
4575 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4576 if (service_options
!= NULL
) {
4578 = CFDictionaryGetValue(service_options
,
4579 kServiceOptionRankAssertion
);
4582 dict
= IPDictCreate(AF_INET6
, state_dict
, setup_dict
, rank_assertion
);
4586 #if !TARGET_OS_SIMULATOR
4587 interface
= service_copy_interface(serviceID
, dict
);
4588 ipv6_service_update_router(serviceID
, dict
);
4589 #endif /* !TARGET_OS_SIMULATOR */
4591 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, dict
);
4593 #if !TARGET_OS_SIMULATOR
4594 if (interface
!= NULL
) {
4596 CFBooleanRef needs_plat
= NULL
;
4599 // if service is unpublished, cancel the request
4600 set_plat_discovery(kPLATDiscoveryOptionCancel
, interface
);
4601 } else if ((state_dict
!= NULL
) &&
4602 CFDictionaryGetValueIfPresent(state_dict
,
4603 kSCPropNetIPv6PerformPLATDiscovery
,
4604 (const void **)&needs_plat
) &&
4605 isA_CFBoolean(needs_plat
) &&
4606 CFBooleanGetValue(needs_plat
)) {
4607 // perform PLAT discovery
4608 set_plat_discovery(kPLATDiscoveryOptionStart
, interface
);
4610 // IPv6 configuration changed for this interface, poke NAT64
4611 set_plat_discovery(kPLATDiscoveryOptionUpdate
, interface
);
4614 CFRelease(interface
);
4616 #endif /* !TARGET_OS_SIMULATOR */
4619 /* service removed, clean up the rank too */
4620 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
4622 my_CFRelease(&dict
);
4628 __private_extern__ CFDictionaryRef
4629 ipv4_dict_create(CFDictionaryRef state_dict
)
4631 return (IPDictCreate(AF_INET
, state_dict
, NULL
, NULL
));
4634 __private_extern__ CFDictionaryRef
4635 ipv6_dict_create(CFDictionaryRef state_dict
)
4637 return (IPDictCreate(AF_INET6
, state_dict
, NULL
, NULL
));
4640 #endif /* TEST_DNS */
4643 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
4644 CFMutableArrayRef out_servers
, CFStringRef interface
)
4649 count
= CFArrayGetCount(in_servers
);
4650 for (i
= 0; i
< count
; i
++) {
4652 struct in6_addr ipv6_addr
;
4653 struct in_addr ip_addr
;
4655 addr
= CFArrayGetValueAtIndex(in_servers
, i
);
4656 assert(addr
!= NULL
);
4658 if (cfstring_to_ip(addr
, &ip_addr
)) {
4660 if ((active_protos
& kProtocolFlagsIPv4
) == 0
4661 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
4663 "no IPv4 connectivity, "
4664 "ignoring DNS server address " IP_FORMAT
,
4671 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
4673 if ((active_protos
& kProtocolFlagsIPv6
) == 0
4674 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
4675 char ntopbuf
[INET6_ADDRSTRLEN
];
4678 "no IPv6 connectivity, "
4679 "ignoring DNS server address %s",
4680 inet_ntop(AF_INET6
, &ipv6_addr
,
4681 ntopbuf
, sizeof(ntopbuf
)));
4685 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
4686 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
4687 && (interface
!= NULL
)
4688 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
4689 // append interface name to IPv6 link local address
4690 addr
= CFStringCreateWithFormat(NULL
, NULL
,
4699 /* bad IP address */
4700 my_log(LOG_NOTICE
, "ignoring bad DNS server address '%@'", addr
);
4704 /* DNS server is valid and one we want */
4705 CFArrayAppendValue(out_servers
, addr
);
4711 static CF_RETURNS_RETAINED CFArrayRef
4712 order_dns_servers(CFArrayRef servers
, ProtocolFlags active_protos
)
4714 Boolean favor_v4
= FALSE
;
4715 CFMutableArrayRef ordered_servers
;
4716 ProtocolFlags proto_last
= kProtocolFlagsIPv4
;
4717 struct sockaddr_in v4_dns1
= { .sin_family
= AF_INET
,
4718 .sin_len
= sizeof(struct sockaddr_in
) };
4720 struct sockaddr_in6 v6_dns1
= { .sin6_family
= AF_INET6
,
4721 .sin6_len
= sizeof(struct sockaddr_in6
),
4722 .sin6_scope_id
= 0 };
4725 if (((active_protos
& kProtocolFlagsIPv4
) == 0) ||
4726 ((active_protos
& kProtocolFlagsIPv6
) == 0)) {
4727 /* only one protocol */
4728 #ifdef TEST_DNS_ORDER
4729 printf("only one protocol\n");
4730 #endif // TEST_DNS_ORDER
4731 return CFRetain(servers
);
4734 ordered_servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4735 for (CFIndex i
= 0, n
= CFArrayGetCount(servers
); i
< n
; i
++) {
4737 struct in6_addr ia6
;
4738 ProtocolFlags proto
;
4741 server
= CFArrayGetValueAtIndex(servers
, i
);
4742 if (cfstring_to_ip(server
, &ia
)) {
4743 proto
= kProtocolFlagsIPv4
;
4745 v4_dns1
.sin_addr
= ia
;
4747 } else if (cfstring_to_ip6(server
, &ia6
)) {
4748 proto
= kProtocolFlagsIPv6
;
4750 memcpy(&v6_dns1
.sin6_addr
, &ia6
, sizeof(ia6
));
4753 CFRelease(ordered_servers
);
4754 return CFRetain(servers
);
4757 if ((i
> 0) && (proto
!= proto_last
)) {
4758 /* if the protocol of the server addresses changed */
4759 if (((proto
== kProtocolFlagsIPv4
) && (v4_n
== 1)) ||
4760 ((proto
== kProtocolFlagsIPv6
) && (v6_n
== 1))) {
4761 /* if we now have the 1st server address of another protocol */
4762 favor_v4
= (sa_dst_compare_no_dependencies((struct sockaddr
*)&v4_dns1
,
4763 (struct sockaddr
*)&v6_dns1
) >= 0);
4764 #ifdef TEST_DNS_ORDER
4765 char v4_buf
[INET_ADDRSTRLEN
];
4766 char v6_buf
[INET6_ADDRSTRLEN
];
4767 printf("comparing %s vs %s, favoring %s\n",
4768 inet_ntop(v4_dns1
.sin_family
, &v4_dns1
.sin_addr
, v4_buf
, sizeof(v4_buf
)),
4769 inet_ntop(v6_dns1
.sin6_family
, &v6_dns1
.sin6_addr
, v6_buf
, sizeof(v6_buf
)),
4770 favor_v4
? "v4" : "v6");
4771 #endif // TEST_DNS_ORDER
4773 /* if the server addresses array is randomly mixed */
4774 #ifdef TEST_DNS_ORDER
4775 printf("v4/v6 not ordered\n");
4776 #endif // TEST_DNS_ORDER
4777 CFRelease(ordered_servers
);
4778 return CFRetain(servers
);
4783 if ((proto
== kProtocolFlagsIPv4
) && favor_v4
) {
4784 CFArrayInsertValueAtIndex(ordered_servers
, v4_n
- 1, server
);
4785 } else if ((proto
== kProtocolFlagsIPv6
) && !favor_v4
) {
4786 CFArrayInsertValueAtIndex(ordered_servers
, v6_n
- 1, server
);
4788 CFArrayAppendValue(ordered_servers
, server
);
4792 return ordered_servers
;
4796 merge_dns_servers(CFMutableDictionaryRef new_dict
,
4797 CFArrayRef state_servers
,
4798 CFArrayRef setup_servers
,
4800 Boolean trust_state
,
4801 ProtocolFlags active_protos
,
4802 CFStringRef interface
)
4804 CFMutableArrayRef dns_servers
;
4805 Boolean have_dns_setup
= FALSE
;
4807 if (state_servers
== NULL
&& setup_servers
== NULL
) {
4808 /* no DNS servers */
4811 dns_servers
= CFArrayCreateMutable(NULL
, 0,
4812 &kCFTypeArrayCallBacks
);
4813 if (setup_servers
!= NULL
) {
4814 accumulate_dns_servers(setup_servers
, active_protos
,
4815 dns_servers
, interface
);
4816 if (CFArrayGetCount(dns_servers
) > 0) {
4817 have_dns_setup
= TRUE
;
4820 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
4821 && state_servers
!= NULL
) {
4822 CFArrayRef ordered_servers
;
4824 ordered_servers
= order_dns_servers(state_servers
, active_protos
);
4825 accumulate_dns_servers(ordered_servers
, active_protos
,
4827 CFRelease(ordered_servers
);
4831 * Here, we determine whether or not we want all queries for this DNS
4832 * configuration to be bound to the associated network interface.
4834 * For dynamically derived network configurations (i.e. from State:)
4835 * this would be the preferred option using the argument "Hey, the
4836 * server told us to use these servers on this network so let's not
4839 * But, when a DNS configuration has been provided by the user/admin
4840 * via the Network pref pane (i.e. from Setup:) we opt to not force
4841 * binding of the outbound queries. The simplest example why we take
4842 * this stance is with a multi-homing configuration. Consider a system
4843 * with one network service associated with "en0" and a second service
4844 * associated with "en1". The "en0" service has been set higher in
4845 * the network service order so it would be primary but the user/admin
4846 * wants the DNS queries to go to a server only accessible via "en1".
4847 * Without this exception we would take the DNS server addresses from
4848 * the Network pref pane (for "en0") and have the queries bound to
4849 * "en0" where they'd never reach their intended destination (via
4850 * "en1"). So, our exception to the rule is that we will not bind
4851 * user/admin configurations to any specific network interface.
4853 * We also add an exception to the "follow the dynamically derived
4854 * network configuration" path for on-the-fly (no Setup: content)
4857 * But, we add an exception to the exception to support our own
4858 * VPN code. Here, we look for a "ServiceID" property in the DNS
4859 * entity. If present, and if it matches, then we extend our
4860 * trust even when there is no Setup: content.
4862 if (CFArrayGetCount(dns_servers
) != 0) {
4863 CFDictionarySetValue(new_dict
,
4864 kSCPropNetDNSServerAddresses
, dns_servers
);
4865 if ((have_setup
&& !have_dns_setup
) || (!have_setup
&& trust_state
)) {
4866 // if this is a "setup"+"state" service with only "state" DNS content (i.e. no
4867 // setup override) or this is a TRUSTED "state"-only service
4868 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
4872 my_CFRelease(&dns_servers
);
4878 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4879 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4881 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4882 boolean_t changed
= FALSE
;
4884 Boolean have_setup
= FALSE
;
4885 CFStringRef interface
= NULL
;
4886 CFDictionaryRef ipv4
;
4887 CFDictionaryRef ipv6
;
4893 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
4894 { kSCPropNetDNSSortList
, 0, FALSE
},
4895 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4896 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
4898 CFMutableDictionaryRef new_dict
= NULL
;
4899 const CFStringRef pick_list
[] = {
4900 kSCPropNetDNSDomainName
,
4901 kSCPropNetDNSOptions
,
4902 kSCPropNetDNSSearchOrder
,
4903 kSCPropNetDNSServerPort
,
4904 kSCPropNetDNSServerTimeout
,
4905 kSCPropNetDNSServiceIdentifier
,
4906 kSCPropNetDNSSupplementalMatchDomainsNoSearch
,
4908 Boolean trust_state
= FALSE
;
4910 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4911 /* there is no DNS content */
4915 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4916 if (ipdict_is_routable(ipv4
)) {
4917 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
4920 active_protos
|= kProtocolFlagsIPv4
;
4921 interface
= ipdict_get_ifname(ipv4
);
4924 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4925 if (ipdict_is_routable(ipv6
)) {
4927 (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
) != NULL
)) {
4930 active_protos
|= kProtocolFlagsIPv6
;
4931 if (interface
== NULL
) {
4932 interface
= ipdict_get_ifname(ipv6
);
4937 if (active_protos
== kProtocolFlagsNone
) {
4938 /* there is no IPv4 nor IPv6 */
4939 if (state_dict
== NULL
) {
4940 /* ... and no DNS content that we care about */
4946 if (state_dict
!= NULL
) {
4947 CFStringRef state_serviceID
= NULL
;
4949 if (CFDictionaryGetValueIfPresent(state_dict
,
4950 kSCPropNetDNSConfirmedServiceID
,
4951 (const void **)&state_serviceID
) &&
4952 isA_CFString(state_serviceID
) &&
4953 CFEqual(serviceID
, state_serviceID
)) {
4958 /* merge DNS configuration */
4959 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
4960 &kCFTypeDictionaryKeyCallBacks
,
4961 &kCFTypeDictionaryValueCallBacks
);
4963 if (active_protos
== kProtocolFlagsNone
) {
4964 merge_dns_servers(new_dict
,
4965 my_CFDictionaryGetArray(state_dict
,
4966 kSCPropNetDNSServerAddresses
),
4970 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
4974 merge_dns_servers(new_dict
,
4975 my_CFDictionaryGetArray(state_dict
,
4976 kSCPropNetDNSServerAddresses
),
4977 my_CFDictionaryGetArray(setup_dict
,
4978 kSCPropNetDNSServerAddresses
),
4985 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
4986 merge_array_prop(new_dict
,
4990 merge_list
[i
].flags
,
4991 merge_list
[i
].append
);
4994 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5002 if (active_protos
== kProtocolFlagsNone
) {
5003 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
5004 if (CFDictionaryContainsKey(new_dict
,
5005 kSCPropNetDNSSupplementalMatchDomains
)) {
5006 /* only keep State: supplemental */
5007 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
5008 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
5009 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
5010 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
5012 if ((interface
== NULL
) && (setup_dict
== NULL
) && (state_dict
!= NULL
)) {
5014 * for supplemental-only configurations, add any scoped (or
5015 * wild-card "*") interface
5017 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
5019 } else if (CFDictionaryContainsKey(new_dict
, kSCPropNetDNSServiceIdentifier
) &&
5020 (interface
== NULL
) &&
5021 (state_dict
!= NULL
)) {
5022 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
5028 if (CFDictionaryGetCount(new_dict
) == 0) {
5029 my_CFRelease(&new_dict
);
5033 if (interface
!= NULL
) {
5034 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5037 if (S_append_state
) {
5039 * ensure any specified domain name (e.g. the domain returned by
5040 * a DHCP server) is in the search list.
5042 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
5043 if (isA_CFString(domain
)) {
5046 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
5047 if (isA_CFArray(search
) &&
5048 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
5049 CFMutableArrayRef new_search
;
5051 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
5052 CFArrayAppendValue(new_search
, domain
);
5053 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
5054 my_CFRelease(&new_search
);
5061 #if !TARGET_OS_SIMULATOR
5062 if (interface
!= NULL
) {
5063 CFRetain(interface
);
5065 #endif /* !TARGET_OS_SIMULATOR */
5067 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
5069 #if !TARGET_OS_SIMULATOR
5070 if (interface
!= NULL
) {
5072 // DNS configuration changed for this interface, poke NAT64
5073 if ((active_protos
& kProtocolFlagsIPv6
) != 0) {
5074 set_plat_discovery(kPLATDiscoveryOptionUpdate
, interface
);
5077 CFRelease(interface
);
5079 #endif /* !TARGET_OS_SIMULATOR */
5081 my_CFRelease(&new_dict
);
5086 merge_dict(const void *key
, const void *value
, void *context
)
5088 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
5090 CFDictionarySetValue(dict
, key
, value
);
5094 #define PROXY_AUTO_DISCOVERY_URL 252
5096 static CF_RETURNS_RETAINED CFStringRef
5097 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
5099 CFStringRef urlString
= NULL
;
5101 if (dhcp_options
!= NULL
) {
5104 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
5107 const UInt8
*urlBytes
;
5110 urlBytes
= CFDataGetBytePtr(data
);
5111 urlLen
= CFDataGetLength(data
);
5112 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
5113 // remove trailing NUL
5121 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
5123 urlString
= CFURLGetString(url
);
5124 if (urlString
!= NULL
) {
5125 CFRetain(urlString
);
5135 static CF_RETURNS_RETAINED CFStringRef
5139 CFStringRef urlString
= NULL
;
5141 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
5143 urlString
= CFURLGetString(url
);
5144 if (urlString
!= NULL
) {
5145 CFRetain(urlString
);
5154 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5155 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5157 ProtocolFlags active_protos
= kProtocolFlagsNone
;
5158 boolean_t changed
= FALSE
;
5159 CFStringRef interface
= NULL
;
5160 CFDictionaryRef ipv4
;
5161 CFDictionaryRef ipv6
;
5162 CFMutableDictionaryRef new_dict
= NULL
;
5168 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
5169 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
5172 CFStringRef key1
; /* an "enable" key */
5176 { kSCPropNetProxiesFTPEnable
,
5177 kSCPropNetProxiesFTPProxy
,
5178 kSCPropNetProxiesFTPPort
5180 { kSCPropNetProxiesGopherEnable
,
5181 kSCPropNetProxiesGopherProxy
,
5182 kSCPropNetProxiesGopherPort
5184 { kSCPropNetProxiesHTTPEnable
,
5185 kSCPropNetProxiesHTTPProxy
,
5186 kSCPropNetProxiesHTTPPort
5188 { kSCPropNetProxiesHTTPSEnable
,
5189 kSCPropNetProxiesHTTPSProxy
,
5190 kSCPropNetProxiesHTTPSPort
5192 { kSCPropNetProxiesRTSPEnable
,
5193 kSCPropNetProxiesRTSPProxy
,
5194 kSCPropNetProxiesRTSPPort
5196 { kSCPropNetProxiesSOCKSEnable
,
5197 kSCPropNetProxiesSOCKSProxy
,
5198 kSCPropNetProxiesSOCKSPort
5200 { kSCPropNetProxiesTransportConverterEnable
,
5201 kSCPropNetProxiesTransportConverterProxy
,
5202 kSCPropNetProxiesTransportConverterPort
5204 { kSCPropNetProxiesProxyAutoConfigEnable
,
5205 kSCPropNetProxiesProxyAutoConfigURLString
,
5206 kSCPropNetProxiesProxyAutoConfigJavaScript
5208 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5214 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
5215 /* there is no proxy content */
5218 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
5219 if (ipdict_is_routable(ipv4
)) {
5220 active_protos
|= kProtocolFlagsIPv4
;
5221 interface
= ipdict_get_ifname(ipv4
);
5223 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
5224 if (ipdict_is_routable(ipv6
)) {
5225 active_protos
|= kProtocolFlagsIPv6
;
5226 if (interface
== NULL
) {
5227 interface
= ipdict_get_ifname(ipv6
);
5230 if (active_protos
== kProtocolFlagsNone
) {
5231 /* there is no IPv4 nor IPv6 */
5232 if (state_dict
== NULL
) {
5233 /* ... and no proxy content that we care about */
5239 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
5240 CFMutableDictionaryRef setup_copy
;
5243 * Merge the per-service "Setup:" and "State:" proxy information with
5244 * the "Setup:" information always taking precedence. Additionally,
5245 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
5246 * Port) is defined than all of the values for that group will be
5247 * used. That is, we don't allow mixing some of the values from
5248 * the "Setup:" keys and others from the "State:" keys.
5250 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5251 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
5252 merge_array_prop(new_dict
,
5256 merge_list
[i
].flags
,
5257 merge_list
[i
].append
);
5260 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5261 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5262 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
5264 * if a "Setup:" enabled key has been provided than we want to
5265 * ignore all of the "State:" keys
5267 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
5268 if (pick_list
[i
].key2
!= NULL
) {
5269 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
5271 if (pick_list
[i
].key3
!= NULL
) {
5272 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
5274 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
5275 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
5276 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
5278 * if a "Setup:" enabled key has not been provided and we have
5279 * some" "State:" keys than we remove all of of "Setup:" keys
5281 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
5282 if (pick_list
[i
].key2
!= NULL
) {
5283 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
5285 if (pick_list
[i
].key3
!= NULL
) {
5286 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
5291 /* merge the "Setup:" keys */
5292 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
5293 CFRelease(setup_copy
);
5295 else if (setup_dict
!= NULL
) {
5296 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5298 else if (state_dict
!= NULL
) {
5299 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5302 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
5303 CFRelease(new_dict
);
5307 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
5308 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5312 if (new_dict
!= NULL
) {
5313 CFDictionaryRef dhcp_options
;
5315 CFNumberRef wpad
= NULL
;
5316 int wpadEnabled
= 0;
5317 CFStringRef wpadURL
= NULL
;
5319 if (CFDictionaryGetValueIfPresent(new_dict
,
5320 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5321 (const void **)&num
) &&
5322 isA_CFNumber(num
)) {
5323 /* if we have a WPAD key */
5325 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
5326 /* if we don't like the enabled key/value */
5334 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
5335 if (!isA_CFNumber(num
) ||
5336 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
5337 /* if we don't like the enabled key/value */
5344 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
5345 if (pacURL
!= NULL
) {
5346 if (!isA_CFString(pacURL
) || (CFStringGetLength(pacURL
) == 0)) {
5347 /* if we don't like the PAC URL */
5353 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
5354 if (!isA_CFString(pacJS
) || (CFStringGetLength(pacJS
) == 0)) {
5355 /* if we don't have (or like) the PAC JavaScript */
5363 * we already have a PAC URL so disable WPAD.
5370 * if WPAD is enabled and we don't already have a PAC URL then
5371 * we check for a DHCP provided URL. If not available, we use
5372 * a PAC URL pointing to a well-known file (wpad.dat) on a
5373 * well-known host (wpad.<domain>).
5375 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
5376 wpadURL
= wpadURL_dhcp(dhcp_options
);
5377 if (wpadURL
== NULL
) {
5378 wpadURL
= wpadURL_dns();
5380 if (wpadURL
== NULL
) {
5381 wpadEnabled
= 0; /* if we don't have a WPAD URL */
5386 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
5387 CFDictionarySetValue(new_dict
,
5388 kSCPropNetProxiesProxyAutoConfigEnable
,
5391 CFDictionarySetValue(new_dict
,
5392 kSCPropNetProxiesProxyAutoConfigURLString
,
5399 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
5400 CFDictionarySetValue(new_dict
,
5401 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5408 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
5409 my_CFRelease(&new_dict
);
5413 #if !TARGET_OS_IPHONE
5415 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5416 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5418 #pragma unused(info)
5419 boolean_t changed
= FALSE
;
5420 CFMutableDictionaryRef new_dict
= NULL
;
5421 const CFStringRef pick_list
[] = {
5422 kSCPropNetSMBNetBIOSName
,
5423 kSCPropNetSMBNetBIOSNodeType
,
5424 #ifdef ADD_NETBIOS_SCOPE
5425 kSCPropNetSMBNetBIOSScope
,
5426 #endif // ADD_NETBIOS_SCOPE
5427 kSCPropNetSMBWorkgroup
,
5430 if (state_dict
== NULL
&& setup_dict
== NULL
) {
5431 /* there is no SMB */
5434 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
5435 && service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
) {
5436 /* there is no IPv4 or IPv6 */
5440 /* merge SMB configuration */
5441 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5442 &kCFTypeDictionaryKeyCallBacks
,
5443 &kCFTypeDictionaryValueCallBacks
);
5444 merge_array_prop(new_dict
,
5445 kSCPropNetSMBWINSAddresses
,
5450 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5458 if (CFDictionaryGetCount(new_dict
) == 0) {
5459 my_CFRelease(&new_dict
);
5464 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
5465 my_CFRelease(&new_dict
);
5468 #endif /* !TARGET_OS_IPHONE */
5471 services_info_get_interface(CFDictionaryRef services_info
,
5472 CFStringRef serviceID
)
5474 CFStringRef interface
= NULL
;
5475 CFDictionaryRef ipv4_dict
;
5477 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
5479 if (ipv4_dict
!= NULL
) {
5480 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
5483 CFDictionaryRef ipv6_dict
;
5485 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
5487 if (ipv6_dict
!= NULL
) {
5488 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5495 static const struct {
5496 const CFStringRef
* entityName
;
5497 const CFStringRef
* statusKey
;
5498 } transientServiceInfo
[] = {
5499 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
5500 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
5501 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
5505 get_transient_status_changes(CFStringRef serviceID
,
5506 CFDictionaryRef services_info
)
5508 boolean_t changed
= FALSE
;
5510 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5511 CFDictionaryRef dict
;
5512 CFNumberRef status
= NULL
;
5513 CFMutableDictionaryRef ts_dict
= NULL
;
5515 dict
= get_service_state_entity(services_info
, serviceID
,
5516 *transientServiceInfo
[i
].entityName
);
5519 status
= CFDictionaryGetValue(dict
,
5520 *transientServiceInfo
[i
].statusKey
);
5523 if (isA_CFNumber(status
) != NULL
) {
5524 ts_dict
= CFDictionaryCreateMutable(NULL
,
5526 &kCFTypeDictionaryKeyCallBacks
,
5527 &kCFTypeDictionaryValueCallBacks
);
5528 CFDictionaryAddValue(ts_dict
,
5529 *transientServiceInfo
[i
].statusKey
,
5533 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
,
5538 if (ts_dict
!= NULL
) {
5546 if_dict_is_expensive(CFDictionaryRef if_dict
)
5548 boolean_t is_expensive
= FALSE
;
5550 if (isA_CFDictionary(if_dict
) != NULL
) {
5551 CFBooleanRef expensive
;
5552 expensive
= CFDictionaryGetValue(if_dict
, kSCPropNetLinkExpensive
);
5553 if (isA_CFBoolean(expensive
) != NULL
5554 && CFBooleanGetValue(expensive
)) {
5555 is_expensive
= TRUE
;
5558 return is_expensive
;
5562 service_is_expensive(CFStringRef serviceID
, CFDictionaryRef services_info
)
5565 boolean_t is_expensive
= FALSE
;
5567 ifname
= services_info_get_interface(services_info
, serviceID
);
5568 if (ifname
!= NULL
) {
5569 CFDictionaryRef if_dict
;
5572 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5573 if_dict
= CFDictionaryGetValue(services_info
, key
);
5575 is_expensive
= if_dict_is_expensive(if_dict
);
5577 return (is_expensive
);
5581 interface_is_expensive(CFStringRef ifname
)
5583 boolean_t is_expensive
= FALSE
;
5585 if (ifname
!= NULL
) {
5586 CFDictionaryRef if_dict
;
5589 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5590 if_dict
= SCDynamicStoreCopyValue(S_session
, key
);
5592 if (if_dict
!= NULL
) {
5593 is_expensive
= if_dict_is_expensive(if_dict
);
5597 return (is_expensive
);
5601 service_rank_entity_get_index(CFDictionaryRef dict
, CFStringRef serviceID
,
5602 CFStringRef which
, uint32_t * ret_val
)
5604 CFNumberRef service_index
= NULL
;
5607 service_index
= CFDictionaryGetValue(dict
,
5608 kSCPropNetServiceServiceIndex
);
5609 service_index
= isA_CFNumber(service_index
);
5611 if (service_index
!= NULL
) {
5614 if (!CFNumberGetValue(service_index
, kCFNumberSInt32Type
,
5616 || index_val
<= 0) {
5617 /* ServiceIndex must be >= 1 */
5619 "%@%@ ServiceIndex %@ is invalid, ignoring",
5620 which
, serviceID
, service_index
);
5621 service_index
= NULL
;
5623 else if (ret_val
!= NULL
) {
5624 *ret_val
= (uint32_t)index_val
;
5627 return (service_index
);
5631 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
5632 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
5634 boolean_t changed
= FALSE
;
5635 CFStringRef interface
;
5636 boolean_t ip_is_coupled
= FALSE
;
5637 CFMutableDictionaryRef new_dict
= NULL
;
5638 Rank rank_assertion
= kRankAssertionDefault
;
5639 Boolean rank_assertion_is_set
= FALSE
;
5640 CFStringRef setup_rank
= NULL
;
5641 CFStringRef state_rank
= NULL
;
5642 CFNumberRef service_index
= NULL
;
5643 boolean_t use_setup_rank
= TRUE
;
5646 if (setup_options
!= NULL
) {
5647 CFBooleanRef coupled
;
5650 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
5651 setup_rank
= isA_CFString(setup_rank
);
5652 if (setup_rank
!= NULL
&& !use_setup_rank
) {
5653 my_log(LOG_DEBUG
, "%@ ignoring Setup PrimaryRank = %@",
5654 serviceID
, setup_rank
);
5657 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
5658 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5659 ip_is_coupled
= TRUE
;
5662 = service_rank_entity_get_index(setup_options
,
5664 kSCDynamicStoreDomainSetup
,
5667 if (state_options
!= NULL
) {
5668 CFBooleanRef coupled
;
5671 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
5672 state_rank
= isA_CFString(state_rank
);
5673 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
5674 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5675 ip_is_coupled
= TRUE
;
5677 if (service_index
== NULL
) {
5679 = service_rank_entity_get_index(state_options
,
5681 kSCDynamicStoreDomainState
,
5686 if (!ip_is_coupled
) {
5687 ip_is_coupled
= service_is_expensive(serviceID
, services_info
);
5689 if (setup_rank
!= NULL
|| state_rank
!= NULL
) {
5690 /* rank assertion is set on the service */
5691 Rank setup_assertion
;
5692 Boolean setup_assertion_is_set
= FALSE
;
5693 Rank state_assertion
;
5694 Boolean state_assertion_is_set
= FALSE
;
5696 setup_assertion
= PrimaryRankGetRankAssertion(setup_rank
,
5697 &setup_assertion_is_set
);
5698 state_assertion
= PrimaryRankGetRankAssertion(state_rank
,
5699 &state_assertion_is_set
);
5700 if (setup_assertion_is_set
&& state_assertion_is_set
) {
5701 if (setup_assertion
> state_assertion
) {
5702 rank_assertion
= setup_assertion
;
5705 rank_assertion
= state_assertion
;
5707 rank_assertion_is_set
= TRUE
;
5709 else if (setup_assertion_is_set
) {
5710 rank_assertion
= setup_assertion
;
5711 rank_assertion_is_set
= TRUE
;
5713 else if (state_assertion_is_set
) {
5714 rank_assertion
= state_assertion
;
5715 rank_assertion_is_set
= TRUE
;
5719 interface
= services_info_get_interface(services_info
, serviceID
);
5720 if (interface
!= NULL
) {
5721 if (!rank_assertion_is_set
) {
5722 /* check for a rank assertion on the interface */
5723 CFNumberRef if_rank
= NULL
;
5725 if (S_if_rank_dict
!= NULL
) {
5726 if_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
5729 = InterfaceRankGetRankAssertion(if_rank
,
5730 &rank_assertion_is_set
);
5731 #define kNotSetString ((CFTypeRef)CFSTR("not set"))
5733 "serviceID %@ interface %@ rank = 0x%x (%@)%s",
5737 (if_rank
!= NULL
) ? (CFTypeRef
)if_rank
: kNotSetString
,
5738 ip_is_coupled
? " [coupled]" : "");
5742 "serviceID %@ interface %@ rank = 0x%x%s",
5743 serviceID
, interface
, rank_assertion
,
5744 ip_is_coupled
? " [coupled]" : "");
5749 if (service_index
!= NULL
|| rank_assertion_is_set
|| ip_is_coupled
) {
5750 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5751 &kCFTypeDictionaryKeyCallBacks
,
5752 &kCFTypeDictionaryValueCallBacks
);
5753 if (rank_assertion_is_set
) {
5754 CFNumberRef new_rank
;
5756 new_rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
,
5757 (const void *)&rank_assertion
);
5758 CFDictionarySetValue(new_dict
, kServiceOptionRankAssertion
,
5760 CFRelease(new_rank
);
5762 if (ip_is_coupled
) {
5763 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
5765 if (service_index
!= NULL
) {
5766 CFDictionarySetValue(new_dict
, kSCPropNetServiceServiceIndex
,
5770 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
5771 my_CFRelease(&new_dict
);
5776 add_service_keys(CFStringRef serviceID
,
5777 CFMutableArrayRef keys
)
5782 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5783 CFStringRef name
= *entityTypeNames
[i
];
5785 key
= setup_service_key(serviceID
, name
);
5786 my_CFArrayAppendUniqueValue(keys
, key
);
5788 key
= state_service_key(serviceID
, name
);
5789 my_CFArrayAppendUniqueValue(keys
, key
);
5793 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
5794 my_CFArrayAppendUniqueValue(keys
, key
);
5797 key
= setup_service_key(serviceID
, NULL
);
5798 my_CFArrayAppendUniqueValue(keys
, key
);
5800 key
= state_service_key(serviceID
, NULL
);
5801 my_CFArrayAppendUniqueValue(keys
, key
);
5808 add_transient_status_keys(CFStringRef serviceID
,
5809 CFMutableArrayRef keys
)
5811 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5814 key
= state_service_key(serviceID
,
5815 *transientServiceInfo
[i
].entityName
);
5816 my_CFArrayAppendUniqueValue(keys
, key
);
5823 static const CFStringRef
*reachabilitySetupKeys
[] = {
5825 &kSCEntNetInterface
,
5832 add_reachability_patterns(CFMutableArrayRef patterns
)
5834 for (size_t i
= 0; i
< countof(reachabilitySetupKeys
); i
++) {
5835 CFStringRef pattern
;
5836 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
5837 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5844 add_vpn_pattern(CFMutableArrayRef patterns
)
5846 CFStringRef pattern
;
5848 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
5849 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5854 add_interface_link_pattern(CFMutableArrayRef patterns
)
5856 CFStringRef pattern
;
5858 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetLink
);
5859 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5863 static CFDictionaryRef
5864 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
5867 CFMutableArrayRef keys
;
5868 CFDictionaryRef info
;
5869 CFMutableArrayRef patterns
;
5871 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5872 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5874 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
5875 CFArrayAppendValue(keys
, S_multicast_resolvers
);
5876 CFArrayAppendValue(keys
, S_private_resolvers
);
5878 count
= CFArrayGetCount(service_list
);
5879 for (CFIndex s
= 0; s
< count
; s
++) {
5880 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
5882 add_service_keys(serviceID
, keys
);
5883 add_transient_status_keys(serviceID
, keys
);
5886 add_reachability_patterns(patterns
);
5888 add_vpn_pattern(patterns
);
5890 add_interface_link_pattern(patterns
);
5892 info
= SCDynamicStoreCopyMultiple(session
, keys
, patterns
);
5893 my_CFRelease(&keys
);
5894 my_CFRelease(&patterns
);
5898 #if !TARGET_OS_SIMULATOR
5901 set_ipv6_default_interface(IFIndex ifindex
)
5903 struct in6_ndifreq ndifreq
;
5905 boolean_t success
= FALSE
;
5907 memset((char *)&ndifreq
, 0, sizeof(ndifreq
));
5908 strlcpy(ndifreq
.ifname
, kLoopbackInterface
, sizeof(ndifreq
.ifname
));
5910 ndifreq
.ifindex
= ifindex
;
5913 ndifreq
.ifindex
= lo0_ifindex();
5915 sock
= inet6_dgram_socket();
5919 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
5921 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5932 #endif /* !TARGET_OS_SIMULATOR */
5934 #if !TARGET_OS_IPHONE
5935 static __inline__
void
5938 (void)unlink(VAR_RUN_RESOLV_CONF
);
5942 set_dns(CFArrayRef val_search_domains
,
5943 CFStringRef val_domain_name
,
5944 CFArrayRef val_servers
,
5945 CFArrayRef val_sortlist
)
5947 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
5949 /* publish new resolv.conf */
5954 SCPrint(TRUE
, f
, CFSTR("#\n"));
5955 SCPrint(TRUE
, f
, CFSTR("# macOS Notice\n"));
5956 SCPrint(TRUE
, f
, CFSTR("#\n"));
5957 SCPrint(TRUE
, f
, CFSTR("# This file is not consulted for DNS hostname resolution, address\n"));
5958 SCPrint(TRUE
, f
, CFSTR("# resolution, or the DNS query routing mechanism used by most\n"));
5959 SCPrint(TRUE
, f
, CFSTR("# processes on this system.\n"));
5960 SCPrint(TRUE
, f
, CFSTR("#\n"));
5961 SCPrint(TRUE
, f
, CFSTR("# To view the DNS configuration used by this system, use:\n"));
5962 SCPrint(TRUE
, f
, CFSTR("# scutil --dns\n"));
5963 SCPrint(TRUE
, f
, CFSTR("#\n"));
5964 SCPrint(TRUE
, f
, CFSTR("# SEE ALSO\n"));
5965 SCPrint(TRUE
, f
, CFSTR("# dns-sd(1), scutil(8)\n"));
5966 SCPrint(TRUE
, f
, CFSTR("#\n"));
5967 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
5968 SCPrint(TRUE
, f
, CFSTR("#\n"));
5970 if (isA_CFArray(val_search_domains
)) {
5971 SCPrint(TRUE
, f
, CFSTR("search"));
5972 n
= CFArrayGetCount(val_search_domains
);
5973 for (i
= 0; i
< n
; i
++) {
5976 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
5977 if (isA_CFString(domain
)) {
5978 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
5981 SCPrint(TRUE
, f
, CFSTR("\n"));
5983 else if (isA_CFString(val_domain_name
)) {
5984 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
5987 if (isA_CFArray(val_servers
)) {
5988 n
= CFArrayGetCount(val_servers
);
5989 for (i
= 0; i
< n
; i
++) {
5990 CFStringRef nameserver
;
5992 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
5993 if (isA_CFString(nameserver
)) {
5994 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
5999 if (isA_CFArray(val_sortlist
)) {
6000 SCPrint(TRUE
, f
, CFSTR("sortlist"));
6001 n
= CFArrayGetCount(val_sortlist
);
6002 for (i
= 0; i
< n
; i
++) {
6003 CFStringRef address
;
6005 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
6006 if (isA_CFString(address
)) {
6007 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
6010 SCPrint(TRUE
, f
, CFSTR("\n"));
6014 (void)rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
6018 #endif /* !TARGET_OS_IPHONE */
6021 service_get_ip_is_coupled(CFStringRef serviceID
)
6023 CFDictionaryRef dict
;
6024 boolean_t ip_is_coupled
= FALSE
;
6026 dict
= service_dict_get(serviceID
, kSCEntNetService
);
6028 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
6029 ip_is_coupled
= TRUE
;
6032 return (ip_is_coupled
);
6036 my_CFStringCreateWithInAddr(struct in_addr ip
)
6040 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(IP_FORMAT
), IP_LIST(&ip
));
6045 my_CFStringCreateWithIn6Addr(const struct in6_addr
* ip
)
6047 char ntopbuf
[INET6_ADDRSTRLEN
];
6049 (void)inet_ntop(AF_INET6
, ip
, ntopbuf
, sizeof(ntopbuf
));
6050 return (CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), ntopbuf
));
6054 * Function: update_ipv4
6056 * Update the IPv4 configuration based on the latest information.
6057 * Publish the State:/Network/Global/IPv4 information, and update the
6058 * IPv4 routing table.
6061 update_ipv4(CFStringRef primary
,
6062 IPv4RouteListRef new_routelist
,
6063 keyChangeListRef keys
)
6065 #if !TARGET_OS_SIMULATOR
6067 #endif /* !TARGET_OS_SIMULATOR */
6070 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6071 const char * ifn_p
= NULL
;
6072 char ifname
[IFNAMSIZ
];
6074 CFMutableDictionaryRef dict
= NULL
;
6076 dict
= CFDictionaryCreateMutable(NULL
, 0,
6077 &kCFTypeDictionaryKeyCallBacks
,
6078 &kCFTypeDictionaryValueCallBacks
);
6079 /* the first entry is the default route */
6080 r
= new_routelist
->list
;
6081 if (r
->gateway
.s_addr
!= 0) {
6084 str
= my_CFStringCreateWithInAddr(r
->gateway
);
6085 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, str
);
6088 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6089 if (ifn_p
!= NULL
) {
6090 CFStringRef ifname_cf
;
6092 ifname_cf
= CFStringCreateWithCString(NULL
,
6094 kCFStringEncodingASCII
);
6095 if (ifname_cf
!= NULL
) {
6096 CFDictionarySetValue(dict
,
6097 kSCDynamicStorePropNetPrimaryInterface
,
6099 CFRelease(ifname_cf
);
6102 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6104 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
6108 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
6112 #if !TARGET_OS_SIMULATOR
6113 sockfd
= open_routing_socket();
6115 /* go through routelist and bind any unbound routes */
6116 if (new_routelist
!= NULL
) {
6117 IPv4RouteListFinalize(new_routelist
);
6120 /* provide a routelist with just loopback multicast */
6121 new_routelist
= IPv4RouteListCopyMulticastLoopback();
6123 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6124 if (S_ipv4_routelist
== NULL
) {
6125 my_log(LOG_DEBUG
, "Old Routes = <none>");
6128 my_log(LOG_DEBUG
, "Old Routes = ");
6129 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
6131 if (new_routelist
== NULL
) {
6132 my_log(LOG_DEBUG
, "New Routes = <none>");
6135 my_log(LOG_DEBUG
, "New Routes = ");
6136 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
6139 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
, sockfd
);
6142 if (S_ipv4_routelist
!= NULL
) {
6143 free(S_ipv4_routelist
);
6145 S_ipv4_routelist
= new_routelist
;
6146 #else /* !TARGET_OS_SIMULATOR */
6147 if (new_routelist
!= NULL
) {
6148 free(new_routelist
);
6150 #endif /* !TARGET_OS_SIMULATOR */
6156 * Function: update_ipv6
6158 * Update the IPv6 configuration based on the latest information.
6159 * Publish the State:/Network/Global/IPv6 information, and update the
6160 * IPv6 routing table.
6163 update_ipv6(CFStringRef primary
,
6164 IPv6RouteListRef new_routelist
,
6165 keyChangeListRef keys
)
6167 #if !TARGET_OS_SIMULATOR
6169 #endif /* !TARGET_OS_SIMULATOR */
6172 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6173 const char * ifn_p
= NULL
;
6174 char ifname
[IFNAMSIZ
];
6176 CFMutableDictionaryRef dict
= NULL
;
6178 dict
= CFDictionaryCreateMutable(NULL
, 0,
6179 &kCFTypeDictionaryKeyCallBacks
,
6180 &kCFTypeDictionaryValueCallBacks
);
6181 /* the first entry is the default route */
6182 r
= new_routelist
->list
;
6183 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
6186 router
= my_CFStringCreateWithIn6Addr(&r
->gateway
);
6187 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
, router
);
6190 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6191 if (ifn_p
!= NULL
) {
6192 CFStringRef ifname_cf
;
6194 ifname_cf
= CFStringCreateWithCString(NULL
,
6196 kCFStringEncodingASCII
);
6197 if (ifname_cf
!= NULL
) {
6198 CFDictionarySetValue(dict
,
6199 kSCDynamicStorePropNetPrimaryInterface
,
6201 CFRelease(ifname_cf
);
6204 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6206 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
6208 #if !TARGET_OS_SIMULATOR
6209 set_ipv6_default_interface(r
->ifindex
);
6210 #endif /* !TARGET_OS_SIMULATOR */
6213 #if !TARGET_OS_SIMULATOR
6214 set_ipv6_default_interface(0);
6215 #endif /* !TARGET_OS_SIMULATOR */
6216 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
6220 #if !TARGET_OS_SIMULATOR
6221 sockfd
= open_routing_socket();
6223 /* go through routelist and bind any unbound routes */
6224 IPv6RouteListFinalize(new_routelist
);
6225 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6226 if (S_ipv6_routelist
== NULL
) {
6227 my_log(LOG_DEBUG
, "Old Routes = <none>");
6230 my_log(LOG_DEBUG
, "Old Routes = ");
6231 IPv6RouteListLog(LOG_DEBUG
, S_ipv6_routelist
);
6233 if (new_routelist
== NULL
) {
6234 my_log(LOG_DEBUG
, "New Routes = <none>");
6237 my_log(LOG_DEBUG
, "New Routes = ");
6238 IPv6RouteListLog(LOG_DEBUG
, new_routelist
);
6241 IPv6RouteListApply(S_ipv6_routelist
, new_routelist
, sockfd
);
6244 if (S_ipv6_routelist
!= NULL
) {
6245 free(S_ipv6_routelist
);
6247 S_ipv6_routelist
= new_routelist
;
6248 #else /* !TARGET_OS_SIMULATOR */
6249 if (new_routelist
!= NULL
) {
6250 free(new_routelist
);
6252 #endif /* !TARGET_OS_SIMULATOR */
6258 update_dns(CFDictionaryRef services_info
,
6259 CFStringRef primary
,
6260 keyChangeListRef keys
)
6262 #pragma unused(services_info)
6263 #pragma unused(keys)
6264 Boolean changed
= FALSE
;
6265 CFDictionaryRef dict
= NULL
;
6267 if (primary
!= NULL
) {
6268 CFDictionaryRef service_dict
;
6270 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6271 if (service_dict
!= NULL
) {
6272 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6276 if (!_SC_CFEqual(S_dns_primary_dict
, dict
)) {
6280 if (dict
!= NULL
) CFRetain(dict
);
6281 if (S_dns_primary_dict
!= NULL
) CFRelease(S_dns_primary_dict
);
6282 S_dns_primary_dict
= dict
;
6288 update_dns_global_resolver(CFDictionaryRef dict
,
6289 keyChangeListRef keys
)
6291 if (_SC_CFEqual(S_dns_global_dict
, dict
)) {
6296 if (dict
!= NULL
) CFRetain(dict
);
6297 if (S_dns_global_dict
!= NULL
) CFRelease(S_dns_global_dict
);
6298 S_dns_global_dict
= dict
;
6301 #if !TARGET_OS_IPHONE
6303 * remove /etc/resolv.conf
6306 #endif /* !TARGET_OS_IPHONE */
6308 * remove State:/Network/Global/DNS
6310 keyChangeListRemoveValue(keys
, S_state_global_dns
);
6312 CFMutableDictionaryRef new_dict
;
6314 #if !TARGET_OS_IPHONE
6316 * update /etc/resolv.conf
6318 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
6319 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
6320 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
6321 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
6322 #endif /* !TARGET_OS_IPHONE */
6325 * update State:/Network/Global/DNS
6327 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
6328 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
6329 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
6330 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
6331 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
6332 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
6333 CFRelease(new_dict
);
6340 update_dnsinfo(CFDictionaryRef services_info
,
6341 CFStringRef primary
,
6342 keyChangeListRef keys
,
6343 CFArrayRef service_order
)
6346 CFDictionaryRef dict
= NULL
;
6347 CFDictionaryRef globalResolver
= NULL
;
6348 CFArrayRef multicastResolvers
;
6349 CFArrayRef privateResolvers
;
6351 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
6352 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
6354 if (primary
!= NULL
) {
6355 CFDictionaryRef service_dict
;
6357 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6358 if (service_dict
!= NULL
) {
6359 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6363 changed
= dns_configuration_set(dict
,
6364 S_service_state_dict
,
6370 if (!update_dns_global_resolver(globalResolver
, keys
)) {
6372 * There was no change to the default/global resolver
6373 * configuration. Even so, we still want to strobe
6374 * the State:/Network/Global/DNS key to indicate that
6375 * "a" change had occured.
6377 keyChangeListNotifyKey(keys
, S_state_global_dns
);
6380 if (globalResolver
!= NULL
) CFRelease(globalResolver
);
6386 update_nwi(nwi_state_t state
)
6388 unsigned char signature
[CC_SHA256_DIGEST_LENGTH
];
6389 static unsigned char signature_last
[CC_SHA256_DIGEST_LENGTH
];
6391 _nwi_state_compute_sha256_hash(state
, signature
);
6392 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
6393 my_log(LOG_DEBUG
, "Not updating network information");
6397 // save [new] signature
6398 memcpy(signature_last
, signature
, sizeof(signature
));
6400 // save [new] configuration
6401 my_log(LOG_INFO
, "Updating network information");
6402 _nwi_state_log(state
, TRUE
, NULL
);
6404 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
6405 if (!_nwi_state_store(state
)) {
6406 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
6408 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
6414 update_proxies(CFDictionaryRef services_info
,
6415 CFStringRef primary
,
6416 keyChangeListRef keys
,
6417 CFArrayRef service_order
)
6419 Boolean changed
= FALSE
;
6420 CFDictionaryRef dict
= NULL
;
6421 CFDictionaryRef new_dict
;
6423 if (primary
!= NULL
) {
6424 CFDictionaryRef service_dict
;
6426 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6427 if (service_dict
!= NULL
) {
6428 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
6432 new_dict
= proxy_configuration_update(dict
,
6433 S_service_state_dict
,
6436 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
6437 if (new_dict
== NULL
) {
6438 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
6440 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
6445 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
6446 S_proxies_dict
= new_dict
;
6451 #if !TARGET_OS_IPHONE
6453 update_smb(CFDictionaryRef services_info
,
6454 CFStringRef primary
,
6455 keyChangeListRef keys
)
6457 #pragma unused(services_info)
6458 Boolean changed
= FALSE
;
6459 CFDictionaryRef dict
= NULL
;
6461 if (primary
!= NULL
) {
6462 CFDictionaryRef service_dict
;
6464 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6465 if (service_dict
!= NULL
) {
6466 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
6470 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
6472 keyChangeListRemoveValue(keys
, S_state_global_smb
);
6474 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
6479 if (dict
!= NULL
) CFRetain(dict
);
6480 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
6485 #endif /* !TARGET_OS_IPHONE */
6488 get_service_index(CFDictionaryRef rank_entity
,
6489 CFArrayRef order
, CFIndex n_order
, CFStringRef serviceID
)
6492 Rank rank
= kRankIndexMask
;
6493 CFNumberRef service_index
;
6496 = service_rank_entity_get_index(rank_entity
,
6500 if (service_index
!= NULL
) {
6501 /* ServiceIndex specified in service entity */
6504 "%@ specifies ServiceIndex %@, effective index is %d",
6505 serviceID
, service_index
, rank
);
6507 else if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
6508 for (i
= 0; i
< n_order
; i
++) {
6509 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
6514 if (CFEqual(serviceID
, s
)) {
6524 ** Service election:
6527 * Function: rank_dict_get_service_rank
6529 * Retrieve the service rank in the given dictionary.
6532 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
6535 Rank rank_val
= kRankAssertionDefault
;
6537 rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
6538 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
6540 if (!CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
)) {
6541 /* if we don't like the rank value */
6542 rank_val
= kRankAssertionDefault
;
6550 * Function: rank_dict_set_service_rank
6552 * Save the results of ranking the service so we can look it up later without
6553 * repeating all of the ranking code.
6556 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
6557 CFStringRef serviceID
, Rank rank_val
)
6561 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
6563 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
6569 static const CFStringRef
*transientInterfaceEntityNames
[] = {
6575 CollectTransientServices(const void * key
,
6579 #pragma unused(value)
6580 CFStringRef service
= key
;
6581 CFMutableArrayRef vif_setup_keys
= context
;
6583 /* This service is either a vpn type service or a comm center service */
6584 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
6588 for (size_t i
= 0; i
< countof(transientInterfaceEntityNames
); i
++) {
6589 if (CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
6590 my_CFArrayAppendUniqueValue(vif_setup_keys
, service
);
6599 static SCNetworkReachabilityFlags
6600 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
6601 CFStringRef service_id
,
6603 CFStringRef vpn_setup_key
)
6606 CFDictionaryRef dict
;
6607 SCNetworkReachabilityFlags flags
= 0;
6610 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6611 kSCDynamicStoreDomainSetup
,
6613 kSCEntNetInterface
);
6614 dict
= CFDictionaryGetValue(services_info
, key
);
6617 if (isA_CFDictionary(dict
)
6618 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
6620 flags
= (kSCNetworkReachabilityFlagsReachable
6621 | kSCNetworkReachabilityFlagsTransientConnection
6622 | kSCNetworkReachabilityFlagsConnectionRequired
);
6624 if (CFEqual(entity
, kSCEntNetPPP
)) {
6626 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
6628 if (!isA_CFDictionary(p_dict
)) {
6632 // get PPP dial-on-traffic status
6633 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
6634 if (isA_CFNumber(num
)) {
6637 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
6639 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6649 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
6651 Boolean ret
= def_value
;
6656 val
= CFDictionaryGetValue(dict
, key
);
6657 if (isA_CFBoolean(val
) != NULL
) {
6658 ret
= CFBooleanGetValue(val
);
6666 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
6667 SCNetworkReachabilityFlags
*reach_flags_v4
,
6668 SCNetworkReachabilityFlags
*reach_flags_v6
)
6672 CFMutableArrayRef vif_setup_keys
;
6674 vif_setup_keys
= CFArrayCreateMutable(NULL
,
6676 &kCFTypeArrayCallBacks
);
6677 CFDictionaryApplyFunction(services_info
, CollectTransientServices
,
6679 count
= CFArrayGetCount(vif_setup_keys
);
6680 for (i
= 0; i
< count
; i
++) {
6681 CFArrayRef components
= NULL
;
6683 CFStringRef service_id
;
6684 CFStringRef vif_setup_key
;
6686 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
6689 * setup key in the following format:
6690 * Setup:/Network/Service/<Service ID>/<Entity>
6692 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
6694 if (CFArrayGetCount(components
) != 5) {
6695 // invalid Setup key encountered
6699 /* service id is the 3rd element */
6700 service_id
= CFArrayGetValueAtIndex(components
, 3);
6702 /* entity id is the 4th element */
6703 entity
= CFArrayGetValueAtIndex(components
, 4);
6706 if (CFEqual(entity
, kSCEntNetPPP
)) {
6707 SCNetworkReachabilityFlags flags
;
6710 flags
= GetReachabilityFlagsFromVPN(services_info
,
6715 /* Check for the v4 reachability flags */
6716 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6717 kSCDynamicStoreDomainSetup
,
6721 if (CFDictionaryContainsKey(services_info
, key
)) {
6722 *reach_flags_v4
|= flags
;
6723 my_log(LOG_DEBUG
, "Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
6728 /* Check for the v6 reachability flags */
6729 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6730 kSCDynamicStoreDomainSetup
,
6734 if (CFDictionaryContainsKey(services_info
, key
)) {
6735 *reach_flags_v6
|= flags
;
6736 my_log(LOG_DEBUG
, "Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
6741 if (components
!= NULL
) {
6742 CFRelease(components
);
6748 if (components
!= NULL
) {
6749 CFRelease(components
);
6753 CFRelease(vif_setup_keys
);
6757 static SCNetworkReachabilityFlags
6758 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
6760 SCNetworkReachabilityFlags flags
= 0;
6762 if (CFEqual(entity
, kSCEntNetPPP
)) {
6765 /* if we're really UP and RUNNING */
6768 /* if we're effectively UP and RUNNING */
6771 /* if we're not connected at all */
6772 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6774 case PPP_STATERESERVED
:
6775 // if we're not connected at all
6776 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6779 /* if we're in the process of [dis]connecting */
6780 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6784 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
6786 case IPSEC_RUNNING
:
6787 /* if we're really UP and RUNNING */
6790 /* if we're not connected at all */
6791 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6794 /* if we're in the process of [dis]connecting */
6795 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6799 else if (CFEqual(entity
, kSCEntNetVPN
)) {
6802 /* if we're really UP and RUNNING */
6807 case VPN_UNLOADING
:
6808 /* if we're not connected at all */
6809 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6812 /* if we're in the process of [dis]connecting */
6813 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6821 VPNAttributesGet(CFStringRef service_id
,
6822 CFDictionaryRef services_info
,
6823 SCNetworkReachabilityFlags
*flags
,
6824 CFStringRef
*server_address
,
6827 CFDictionaryRef entity_dict
;
6829 CFDictionaryRef p_state
= NULL
;
6831 CFStringRef transient_entity
= NULL
;
6833 if (af
== AF_INET
) {
6834 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv4
);
6836 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv6
);
6838 entity_dict
= ipdict_get_service(entity_dict
);
6839 if (entity_dict
== NULL
) {
6843 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
6844 CFStringRef entity
= *transientServiceInfo
[i
].entityName
;
6846 p_state
= service_dict_get(service_id
, entity
);
6848 /* ensure that this is a VPN Type service */
6849 if (isA_CFDictionary(p_state
)) {
6850 transient_entity
= entity
;
6855 /* Did we find a vpn type service? If not, we are done.*/
6856 if (transient_entity
== NULL
) {
6860 *flags
|= (kSCNetworkReachabilityFlagsReachable
6861 | kSCNetworkReachabilityFlagsTransientConnection
);
6863 /* Get the Server Address */
6864 if (server_address
!= NULL
) {
6865 *server_address
= CFDictionaryGetValue(entity_dict
,
6866 CFSTR("ServerAddress"));
6867 *server_address
= isA_CFString(*server_address
);
6868 if (*server_address
!= NULL
) {
6869 CFRetain(*server_address
);
6874 if (!CFDictionaryGetValueIfPresent(p_state
,
6875 kSCPropNetVPNStatus
, // IPSecStatus, PPPStatus, VPNStatus
6876 (const void **)&num
) ||
6877 !isA_CFNumber(num
) ||
6878 !CFNumberGetValue(num
, kCFNumberIntType
, &status
)) {
6882 *flags
|= GetReachFlagsFromStatus(transient_entity
, status
);
6883 if (CFEqual(transient_entity
, kSCEntNetPPP
)) {
6885 CFDictionaryRef p_setup
;
6888 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6889 kSCDynamicStoreDomainSetup
,
6892 p_setup
= CFDictionaryGetValue(services_info
, key
);
6895 /* get dial-on-traffic status */
6896 if (isA_CFDictionary(p_setup
) &&
6897 CFDictionaryGetValueIfPresent(p_setup
,
6898 kSCPropNetPPPDialOnDemand
,
6899 (const void **)&num
) &&
6900 isA_CFNumber(num
) &&
6901 CFNumberGetValue(num
, kCFNumberIntType
, &ppp_demand
) &&
6902 (ppp_demand
!= 0)) {
6903 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6904 if (status
== PPP_IDLE
) {
6905 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
6913 typedef struct ElectionInfo
{
6919 ElectionResultsRef results
;
6920 CFMutableDictionaryRef rank_dict
;
6921 } ElectionInfo
, * ElectionInfoRef
;
6923 typedef CFDictionaryApplierFunction ElectionFuncRef
;
6926 CandidateRelease(CandidateRef candidate
)
6928 my_CFRelease(&candidate
->serviceID
);
6929 my_CFRelease(&candidate
->if_name
);
6930 my_CFRelease(&candidate
->signature
);
6935 CandidateCopy(CandidateRef dest
, CandidateRef src
)
6938 if (dest
->serviceID
) {
6939 CFRetain(dest
->serviceID
);
6941 if (dest
->if_name
) {
6942 CFRetain(dest
->if_name
);
6944 if(dest
->signature
) {
6945 CFRetain(dest
->signature
);
6950 static ElectionResultsRef
6951 ElectionResultsAlloc(int af
, int size
)
6953 ElectionResultsRef results
;
6955 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
6958 results
->size
= size
;
6963 ElectionResultsRelease(ElectionResultsRef results
)
6968 for (i
= 0, scan
= results
->candidates
;
6971 CandidateRelease(scan
);
6978 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
6983 if (results
== NULL
) {
6984 my_log(level
, "%s: no candidates", prefix
);
6987 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
6988 for (i
= 0, scan
= results
->candidates
;
6991 char ntopbuf
[INET6_ADDRSTRLEN
];
6993 (void)inet_ntop(results
->af
, &scan
->addr
, ntopbuf
, sizeof(ntopbuf
));
6994 my_log(level
, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6995 i
, scan
->if_name
, scan
->serviceID
, ntopbuf
, scan
->rank
,
6996 scan
->ineligible
? " [ineligible]" : "");
7002 * Function: ElectionResultsAddCandidate
7004 * Add the candidate into the election results. Find the insertion point
7005 * by comparing the rank of the candidate with existing entries.
7008 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
7013 if (results
->count
== results
->size
) {
7014 /* this should not happen */
7015 my_log(LOG_NOTICE
, "can't fit another candidate");
7019 /* find the insertion point */
7020 where
= kCFNotFound
;
7021 for (i
= 0; i
< results
->count
; i
++) {
7022 CandidateRef this_candidate
= results
->candidates
+ i
;
7024 if (candidate
->rank
< this_candidate
->rank
) {
7029 /* add it to the end */
7030 if (where
== kCFNotFound
) {
7031 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
7035 /* slide existing entries over */
7036 for (i
= results
->count
; i
> where
; i
--) {
7037 results
->candidates
[i
] = results
->candidates
[i
- 1];
7039 /* insert element */
7040 CandidateCopy(results
->candidates
+ where
, candidate
);
7046 elect_ip(const void * key
, const void * value
, void * context
);
7049 * Function: ElectionResultsCopy
7051 * Visit all of the services and invoke the protocol-specific election
7052 * function. Return the results of the election.
7054 static ElectionResultsRef
7055 ElectionResultsCopy(int af
, CFArrayRef order
)
7060 count
= (int)CFDictionaryGetCount(S_service_state_dict
);
7065 if (af
== AF_INET
) {
7066 info
.entity
= kSCEntNetIPv4
;
7067 info
.rank_dict
= S_ipv4_service_rank_dict
;
7070 info
.entity
= kSCEntNetIPv6
;
7071 info
.rank_dict
= S_ipv6_service_rank_dict
;
7073 info
.results
= ElectionResultsAlloc(af
, count
);
7074 info
.n_services
= count
;
7076 if (order
!= NULL
) {
7077 info
.n_order
= CFArrayGetCount(order
);
7082 CFDictionaryApplyFunction(S_service_state_dict
, elect_ip
, (void *)&info
);
7083 if (info
.results
->count
== 0) {
7084 ElectionResultsRelease(info
.results
);
7085 info
.results
= NULL
;
7087 return (info
.results
);
7091 * Function: ElectionResultsCandidateNeedsDemotion
7093 * Check whether the given candidate requires demotion. A candidate
7094 * might need to be demoted if its IPv4 and IPv6 services must be coupled
7095 * but a higher ranked service has IPv4 or IPv6.
7097 * The converse is also true: if the given candidate has lower rank than
7098 * the other candidate and the other candidate is coupled, this candidate
7099 * needs to be demoted.
7102 ElectionResultsCandidateNeedsDemotion(CandidateRef other_candidate
,
7103 CandidateRef candidate
)
7105 Boolean ret
= FALSE
;
7107 if (other_candidate
== NULL
) {
7108 /* no other candidate */
7111 if (other_candidate
->ineligible
) {
7112 /* other candidate can't become primary */
7115 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
7116 /* the other candidate can't become primary */
7119 if (!candidate
->ip_is_coupled
&& !other_candidate
->ip_is_coupled
) {
7120 /* neither candidate is coupled */
7123 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
7124 /* they are over the same interface, no need to demote */
7127 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
7128 /* avoid creating a feedback loop */
7131 if (candidate
->rank
< other_candidate
->rank
) {
7132 /* we're higher ranked than the other candidate, ignore */
7135 if (candidate
->ip_is_coupled
) {
7136 if (other_candidate
->ip_is_coupled
7137 && candidate
->rank
== other_candidate
->rank
) {
7138 /* same rank as another service that is coupled, ignore */
7142 else if (other_candidate
->ip_is_coupled
) { /* must be true */
7143 if (candidate
->rank
== other_candidate
->rank
) {
7144 /* other candidate will be demoted, so we don't need to */
7147 /* we're lower rank and need to be demoted */
7149 else { /* can't happen, we already tested for this above */
7150 /* neither candidate is coupled */
7162 get_signature_sha256(CFStringRef signature
,
7163 unsigned char * sha256
)
7166 CFDataRef signature_data
;
7168 signature_data
= CFStringCreateExternalRepresentation(NULL
,
7170 kCFStringEncodingUTF8
,
7173 CC_SHA256_Init(&ctx
);
7174 CC_SHA256_Update(&ctx
,
7175 CFDataGetBytePtr(signature_data
),
7176 (CC_LONG
)CFDataGetLength(signature_data
));
7177 CC_SHA256_Final(sha256
, &ctx
);
7179 CFRelease(signature_data
);
7186 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
7187 CandidateRef candidate
, Boolean not_in_list
,
7188 Boolean not_in_iflist
)
7191 char ifname
[IFNAMSIZ
];
7192 nwi_ifstate_t ifstate
;
7194 if (nwi_state
== NULL
) {
7199 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
7200 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
7202 if (not_in_iflist
) {
7203 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST
;
7205 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
7206 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
7208 if ((af
== AF_INET
) && service_has_clat46_address(candidate
->serviceID
)) {
7209 flags
|= NWI_IFSTATE_FLAGS_HAS_CLAT46
;
7211 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
7212 kCFStringEncodingASCII
);
7213 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
7214 char ntopbuf
[INET6_ADDRSTRLEN
];
7216 (void)inet_ntop(af
, &candidate
->addr
, ntopbuf
, sizeof(ntopbuf
));
7218 "Adding IPv%c [%s] %s "
7219 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
7220 ipvx_char(af
), ifname
, ntopbuf
,
7221 flags
, candidate
->rank
, candidate
->reachability_flags
);
7223 ifstate
= nwi_state_add_ifstate(nwi_state
, ifname
, af
, flags
,
7225 (void *)&candidate
->addr
,
7226 (void *)&candidate
->vpn_server_addr
,
7227 candidate
->reachability_flags
);
7228 if (ifstate
!= NULL
&& candidate
->signature
) {
7229 uint8_t hash
[CC_SHA256_DIGEST_LENGTH
];
7231 get_signature_sha256(candidate
->signature
, hash
);
7232 nwi_ifstate_set_signature(ifstate
, hash
);
7239 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
7241 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
7242 CFStringRef vpn_server_address
= NULL
;
7244 assert(candidate
!= NULL
);
7245 assert(services_info
!= NULL
);
7247 VPNAttributesGet(candidate
->serviceID
,
7250 &vpn_server_address
,
7253 candidate
->reachability_flags
= flags
;
7255 if (vpn_server_address
== NULL
) {
7256 memset(&candidate
->vpn_server_addr
, 0, sizeof(candidate
->vpn_server_addr
));
7260 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
),
7261 kCFStringEncodingASCII
);
7262 _SC_string_to_sockaddr(buf
,
7264 (void *)&candidate
->vpn_server_addr
,
7265 sizeof(candidate
->vpn_server_addr
));
7267 CFRelease(vpn_server_address
);
7272 * Function: ElectionResultsGetPrimary
7274 * Use the results of the current protocol and the other protocol to
7275 * determine which service should become primary.
7277 * At the same time, generate the IPv4/IPv6 routing table and
7278 * the nwi_state for the protocol.
7281 ElectionResultsGetPrimary(ElectionResultsRef results
,
7282 CandidateRef other_candidate
,
7283 nwi_state_t nwi_state
, int af
,
7284 RouteListRef
* ret_routes
,
7285 CFDictionaryRef services_info
,
7286 CFSetRef ip_service_changes
)
7288 CandidateRef primary
= NULL
;
7289 Boolean primary_is_null
= FALSE
;
7290 RouteListRef routes
= NULL
;
7292 assert(services_info
!= NULL
);
7294 if (results
!= NULL
) {
7295 CandidateRef deferred
[results
->count
];
7297 CFStringRef entity_name
;
7300 RouteListInfoRef info
;
7305 entity_name
= kSCEntNetIPv4
;
7306 info
= &IPv4RouteListInfo
;
7307 initial_size
= results
->count
* IPV4_ROUTES_N_STATIC
;
7311 entity_name
= kSCEntNetIPv6
;
7312 info
= &IPv6RouteListInfo
;
7313 initial_size
= results
->count
* IPV6_ROUTES_N_STATIC
;
7317 for (i
= 0, scan
= results
->candidates
;
7320 Boolean is_primary
= FALSE
;
7321 CFDictionaryRef service_dict
;
7322 RouteListRef service_routes
;
7323 Boolean skip
= FALSE
;
7325 if (!scan
->ineligible
7327 && RANK_ASSERTION_MASK(scan
->rank
) != kRankAssertionNever
) {
7328 if (ElectionResultsCandidateNeedsDemotion(other_candidate
,
7330 /* demote the service */
7332 "IPv%c over %@ (rank 0x%x) demoted: "
7333 "primary IPv%c %@ (rank 0x%x)",
7334 ipvx_char(af
), scan
->if_name
, scan
->rank
,
7335 ipvx_other_char(af
), other_candidate
->if_name
,
7336 other_candidate
->rank
);
7337 deferred
[deferred_count
++] = scan
;
7345 /* contribute to the routing table */
7346 service_dict
= service_dict_get(scan
->serviceID
, entity_name
);
7347 service_routes
= ipdict_get_routelist(service_dict
);
7348 if (service_routes
!= NULL
) {
7350 Rank rank
= scan
->rank
;
7353 /* routes are RankNever to prevent becoming primary */
7354 rank
= RankMake(rank
, kRankAssertionNever
);
7356 force
= my_CFSetContainsValue(ip_service_changes
,
7358 routes
= RouteListAddRouteList(info
, routes
, initial_size
,
7359 service_routes
, rank
, force
);
7360 if ((service_routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
7368 /* if we're skipping the primary, it's NULL */
7370 primary_is_null
= TRUE
;
7373 else if (!scan
->ineligible
) {
7374 Boolean not_in_iflist
;
7376 add_reachability_flags_to_candidate(scan
, services_info
, af
);
7378 = (service_routes
->flags
& kRouteListFlagsScopedOnly
) != 0;
7379 add_candidate_to_nwi_state(nwi_state
, af
, scan
,
7384 for (i
= 0; i
< deferred_count
; i
++) {
7385 CandidateRef candidate
= deferred
[i
];
7387 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
7388 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, TRUE
, FALSE
);
7391 if (ret_routes
!= NULL
) {
7392 *ret_routes
= routes
;
7394 else if (routes
!= NULL
) {
7397 if (primary_is_null
) {
7406 service_dict_get_signature(CFDictionaryRef service_dict
)
7410 ifname
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7411 if (isA_CFString(ifname
) == NULL
7412 || !confirm_interface_name(service_dict
, ifname
)) {
7415 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
7419 * Function: elect_ip
7421 * Evaluate the service and determine what rank the service should have.
7422 * If it's a suitable candidate, add it to the election results.
7425 elect_ip(const void * key
, const void * value
, void * context
)
7427 CFDictionaryRef all_entities_dict
= (CFDictionaryRef
)value
;
7428 Candidate candidate
;
7430 ElectionInfoRef elect_info
;
7431 CFStringRef if_name
;
7432 CFDictionaryRef ipdict
;
7434 CFDictionaryRef rank_entity
;
7435 RouteListUnion routelist
;
7436 CFDictionaryRef service_dict
;
7438 elect_info
= (ElectionInfoRef
)context
;
7439 ipdict
= CFDictionaryGetValue(all_entities_dict
, elect_info
->entity
);
7440 if (ipdict
!= NULL
) {
7441 routelist
.ptr
= ipdict_get_routelist(ipdict
);
7442 service_dict
= ipdict_get_service(ipdict
);
7445 routelist
.ptr
= NULL
;
7447 if (routelist
.ptr
== NULL
|| service_dict
== NULL
) {
7448 /* no connectivity */
7451 if_name
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7452 if (if_name
== NULL
) {
7453 /* need an interface name */
7456 if (CFEqual(if_name
, CFSTR(kLoopbackInterface
))) {
7457 /* don't process loopback */
7460 memset(&candidate
, 0, sizeof(candidate
));
7461 candidate
.serviceID
= (CFStringRef
)key
;
7462 if ((routelist
.common
->flags
& kRouteListFlagsHasDefault
) == 0) {
7463 /* no default route means it's ineligible to become primary */
7464 candidate
.ineligible
= TRUE
;
7466 rank_entity
= CFDictionaryGetValue(all_entities_dict
, kSCEntNetService
);
7467 candidate
.rank
= get_service_index(rank_entity
,
7468 elect_info
->order
, elect_info
->n_order
,
7469 candidate
.serviceID
);
7470 if (elect_info
->af
== AF_INET
) {
7471 default_rank
= routelist
.v4
->list
->rank
;
7472 candidate
.addr
.v4
= routelist
.v4
->list
->ifa
;
7475 default_rank
= routelist
.v6
->list
->rank
;
7476 candidate
.addr
.v6
= routelist
.v6
->list
->ifa
;
7478 primary_rank
= RANK_ASSERTION_MASK(default_rank
);
7479 if (S_ppp_override_primary
) {
7482 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
7483 kCFStringEncodingASCII
)
7484 && (strncmp(PPP_PREFIX
, ifn
, sizeof(PPP_PREFIX
) - 1) == 0)) {
7485 /* PPP override: make ppp* look the best */
7486 primary_rank
= kRankAssertionFirst
;
7489 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
7490 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
7491 candidate
.if_name
= if_name
;
7492 rank_dict_set_service_rank(elect_info
->rank_dict
,
7493 candidate
.serviceID
, candidate
.rank
);
7494 candidate
.signature
= service_dict_get_signature(service_dict
);
7495 ElectionResultsAddCandidate(elect_info
->results
, &candidate
);
7501 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
7503 uint32_t changed
= 0;
7506 /* update service options first (e.g. rank) */
7507 if (get_rank_changes(serviceID
,
7508 get_service_state_entity(services_info
, serviceID
,
7510 get_service_setup_entity(services_info
, serviceID
,
7513 changed
|= (1 << kEntityTypeServiceOptions
);
7516 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7517 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
7518 GetEntityChangesFuncRef func
;
7521 func
= entityChangeFunc
[i
];
7522 name
= *entityTypeNames
[i
];
7523 if ((*func
)(serviceID
,
7524 get_service_state_entity(services_info
, serviceID
, name
),
7525 get_service_setup_entity(services_info
, serviceID
, name
),
7527 changed
|= (1 << i
);
7531 /* update transient service status */
7532 if (get_transient_status_changes(serviceID
, services_info
)) {
7533 changed
|= (1 << kEntityTypeTransientStatus
);
7540 serviceID_get_ifname(CFStringRef serviceID
)
7542 CFDictionaryRef entity_dict
;
7543 CFStringRef ifname
= NULL
;
7545 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
7546 if (entity_dict
== NULL
) {
7547 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
7549 if (entity_dict
!= NULL
) {
7550 ifname
= ipdict_get_ifname(entity_dict
);
7555 __private_extern__ boolean_t
7556 check_if_service_expensive(CFStringRef serviceID
)
7559 ifname
= serviceID_get_ifname(serviceID
);
7561 return interface_is_expensive(ifname
);
7565 service_order_get(CFDictionaryRef services_info
)
7567 CFArrayRef order
= NULL
;
7568 CFDictionaryRef ipv4_dict
;
7570 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
7571 S_setup_global_ipv4
);
7572 if (ipv4_dict
!= NULL
) {
7573 CFNumberRef ppp_override
;
7576 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
7577 order
= isA_CFArray(order
);
7579 /* get ppp override primary */
7580 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
7581 kSCPropNetPPPOverridePrimary
);
7582 ppp_override
= isA_CFNumber(ppp_override
);
7583 if (ppp_override
!= NULL
) {
7584 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
7586 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
7589 S_ppp_override_primary
= FALSE
;
7595 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
7596 const char * entity
)
7598 boolean_t changed
= FALSE
;
7599 CFStringRef primary
= *primary_p
;
7601 if (new_primary
!= NULL
) {
7602 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
7603 my_log(LOG_INFO
, "%@ is still primary %s", new_primary
, entity
);
7606 my_CFRelease(primary_p
);
7607 *primary_p
= CFRetain(new_primary
);
7608 my_log(LOG_INFO
, "%@ is the new primary %s", new_primary
, entity
);
7612 else if (primary
!= NULL
) {
7613 my_log(LOG_INFO
, "%@ is no longer primary %s", primary
, entity
);
7614 my_CFRelease(primary_p
);
7621 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
7624 if (service_dict_get(serviceID
, entity
) == NULL
) {
7625 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
7627 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
7631 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
7637 #define N_KEYS_VALUES_STATIC 10
7638 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
7641 count
= CFDictionaryGetCount(S_service_state_dict
);
7642 if (count
<= N_KEYS_VALUES_STATIC
) {
7643 keys
= keys_values_buf
;
7645 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
7647 values
= keys
+ count
;
7648 CFDictionaryGetKeysAndValues(S_service_state_dict
,
7649 (const void * *)keys
,
7650 (const void * *)values
);
7652 for (i
= 0; i
< count
; i
++) {
7653 CFDictionaryRef ipdict
= NULL
;
7654 CFStringRef interface
= NULL
;
7655 CFStringRef serviceID
;
7656 CFDictionaryRef service_dict
;
7658 serviceID
= (CFStringRef
)keys
[i
];
7659 service_dict
= (CFDictionaryRef
)values
[i
];
7661 /* check whether service has IPv4 or IPv6 */
7662 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
7663 if (ipdict
== NULL
) {
7664 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
7665 if (ipdict
== NULL
) {
7669 interface
= ipdict_get_ifname(ipdict
);
7670 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
7672 "Found IP service %@ on interface %@",
7674 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
7677 if (keys
!= keys_values_buf
) {
7683 static __inline__
const char *
7684 get_changed_str(CFStringRef serviceID
, CFStringRef entity
,
7685 CFDictionaryRef old_dict
)
7687 CFDictionaryRef new_dict
= NULL
;
7689 if (serviceID
!= NULL
) {
7690 new_dict
= service_dict_get(serviceID
, entity
);
7693 if (old_dict
== NULL
) {
7694 if (new_dict
!= NULL
) {
7698 if (new_dict
== NULL
) {
7700 } else if (!CFEqual(old_dict
, new_dict
)) {
7707 #if !TARGET_OS_SIMULATOR
7710 #define MANAGE_IF_ORDER
7711 #define MANAGE_IF_IOCTL
7712 #endif /* SIOCSIFORDER */
7714 #ifdef SIOCSIFNETSIGNATURE
7715 #define MANAGE_IF_SIGNATURE
7716 #define MANAGE_IF_IOCTL
7717 #endif /* SIOCSIFNETSIGNATURE */
7719 #ifdef MANAGE_IF_IOCTL
7721 inet_dgram_socket(void)
7725 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
7727 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
7732 #endif /* MANAGE_IF_IOCTL */
7734 #ifdef MANAGE_IF_ORDER
7736 interface_order_changed(nwi_state_t old_state
, nwi_state_t new_state
)
7738 if (old_state
== NULL
&& new_state
== NULL
) {
7739 // Both are NULL, nothing changed
7743 if (old_state
== NULL
|| new_state
== NULL
) {
7744 // One is NULL, something changed
7748 if (old_state
->if_list_count
!= new_state
->if_list_count
) {
7749 // Count is different, something changed
7753 if (new_state
->if_list_count
== 0) {
7754 // Count is same and 0, nothing changed
7759 nwi_ifindex_t
*old_scan
;
7760 nwi_ifindex_t
*new_scan
;
7761 for (i
= 0, old_scan
= nwi_state_if_list(old_state
), new_scan
= nwi_state_if_list(new_state
);
7762 i
< new_state
->if_list_count
; i
++, old_scan
++, new_scan
++) {
7763 if (strcmp(old_state
->ifstate_list
[*old_scan
].ifname
, new_state
->ifstate_list
[*new_scan
].ifname
) != 0) {
7764 // Some interface in the list is different, something changed
7769 // Count and contents are the same, nothing changed
7774 update_interface_order(nwi_state_t state
, int sockfd
)
7776 Boolean success
= FALSE
;
7778 // Set interface order into the kernel
7779 struct if_order interface_order
;
7780 interface_order
.ifo_count
= (uint32_t)state
->if_list_count
;
7781 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)calloc((size_t)interface_order
.ifo_count
, sizeof(uint32_t));
7782 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7784 nwi_ifindex_t
*scan
;
7785 for (i
= 0, scan
= nwi_state_if_list(state
);
7786 i
< state
->if_list_count
; i
++, scan
++) {
7787 const char *ifname
= state
->ifstate_list
[*scan
].ifname
;
7788 ((uint32_t *)interface_order
.ifo_ordered_indices
)[i
] = my_if_nametoindex(ifname
);
7791 if (ioctl(sockfd
, SIOCSIFORDER
, &interface_order
) != 0) {
7792 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
));
7794 my_log(LOG_INFO
, "Set kernel interface order for %u interfaces", interface_order
.ifo_count
);
7797 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7798 free((void *)interface_order
.ifo_ordered_indices
);
7799 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)NULL
;
7804 #endif /* MANAGE_IF_ORDER */
7806 #ifdef MANAGE_IF_SIGNATURE
7808 siocsifnetsignature(int s
, const char * ifname
, int af
,
7809 const uint8_t * signature
, size_t signature_length
)
7811 struct if_nsreq nsreq
;
7813 memset(&nsreq
, 0, sizeof(nsreq
));
7814 strlcpy(nsreq
.ifnsr_name
, ifname
, sizeof(nsreq
.ifnsr_name
));
7815 nsreq
.ifnsr_family
= af
;
7816 if (signature_length
> 0) {
7817 if (signature_length
> sizeof(nsreq
.ifnsr_data
)) {
7818 signature_length
= sizeof(nsreq
.ifnsr_data
);
7820 nsreq
.ifnsr_len
= signature_length
;
7821 memcpy(nsreq
.ifnsr_data
, signature
, signature_length
);
7823 return (ioctl(s
, SIOCSIFNETSIGNATURE
, &nsreq
));
7827 process_ifstate_difference(nwi_ifstate_t ifstate
, int af
, int sockfd
)
7829 nwi_ifstate_difference_t diff
;
7830 boolean_t set_signature
= FALSE
;
7831 int signature_length
= 0;
7833 diff
= nwi_ifstate_get_difference(ifstate
);
7835 case knwi_ifstate_difference_changed
:
7836 /* set signature for this interface */
7837 set_signature
= TRUE
;
7838 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_SIGNATURE
) != 0) {
7839 signature_length
= sizeof(ifstate
->signature
);
7842 case knwi_ifstate_difference_removed
:
7843 /* remove signature for this interface */
7844 set_signature
= TRUE
;
7849 if (set_signature
) {
7850 if (siocsifnetsignature(sockfd
, ifstate
->ifname
, af
,
7852 signature_length
) < 0) {
7854 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7855 ifstate
->ifname
, ipvx_char(af
),
7860 my_log(LOG_DEBUG
, "IPv%c Network Signature %s %s",
7862 (signature_length
> 0) ? "Set" : "Cleared",
7864 if (signature_length
> 0
7865 && (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7867 char sig_buf
[signature_length
* 3 + 1];
7870 for (i
= 0; i
< signature_length
; i
++) {
7873 snprintf(byte_buf
, sizeof(byte_buf
),
7874 "%02x ", ifstate
->signature
[i
]);
7875 strlcat(sig_buf
, byte_buf
, sizeof(sig_buf
));
7877 my_log(LOG_DEBUG
, "Signature Bytes: %s", sig_buf
);
7885 process_state_differences(nwi_state_t state
, int af
, int sockfd
)
7891 if (af
== AF_INET
) {
7892 count
= state
->ipv4_count
;
7895 count
= state
->ipv6_count
;
7897 for (i
= 0, scan
= nwi_state_ifstate_list(state
, af
);
7898 i
< count
; i
++, scan
++) {
7899 process_ifstate_difference(scan
, af
, sockfd
);
7903 #endif /* MANAGE_IF_SIGNATURE */
7905 #endif /* !TARGET_OS_SIMULATOR */
7908 process_nwi_changes(CFMutableStringRef log_output
,
7909 nwi_state_t changes_state
,
7910 nwi_state_t new_state
,
7911 nwi_state_t old_state
,
7912 boolean_t dns_changed
,
7913 boolean_t dnsinfo_changed
,
7914 CFDictionaryRef old_primary_dns
,
7915 boolean_t proxy_changed
,
7916 CFDictionaryRef old_primary_proxy
,
7917 boolean_t smb_changed
,
7918 CFDictionaryRef old_primary_smb
)
7920 #ifndef MANAGE_IF_ORDER
7921 #pragma unused(new_state)
7922 #pragma unused(old_state)
7923 #endif // !MANAGE_IF_ORDER
7924 #if TARGET_OS_IPHONE
7925 #pragma unused(smb_changed)
7926 #pragma unused(old_primary_smb)
7927 #endif // TARGET_OS_IPHONE
7929 if (changes_state
!= NULL
) {
7930 const sa_family_t af_list
[] = {AF_INET
, AF_INET6
};
7932 #ifdef MANAGE_IF_IOCTL
7933 int sockfd
= inet_dgram_socket();
7934 #endif /* MANAGE_IF_IOCTL */
7936 #ifdef MANAGE_IF_ORDER
7937 if (interface_order_changed(new_state
, old_state
)) {
7938 update_interface_order(new_state
, sockfd
);
7940 #endif /* MANAGE_IF_ORDER */
7942 for (size_t idx
= 0; idx
< countof(af_list
); idx
++) {
7943 int af
= af_list
[idx
];
7944 CFMutableStringRef changes
= NULL
;
7945 CFMutableStringRef primary_str
= NULL
;
7947 #ifdef MANAGE_IF_SIGNATURE
7948 process_state_differences(changes_state
, af
, sockfd
);
7949 #endif /* MANAGE_IF_SIGNATURE */
7950 scan
= nwi_state_get_first_ifstate(changes_state
, af
);
7951 while (scan
!= NULL
) {
7952 const char * changed_str
;
7954 changed_str
= nwi_ifstate_get_diff_str(scan
);
7955 if (changed_str
!= NULL
) {
7957 const char * addr_str
;
7958 char ntopbuf
[INET6_ADDRSTRLEN
];
7960 address
= (void *)nwi_ifstate_get_address(scan
);
7961 addr_str
= inet_ntop(scan
->af
, address
, ntopbuf
,
7963 if (primary_str
== NULL
) {
7964 primary_str
= CFStringCreateMutable(NULL
, 0);
7965 CFStringAppendFormat(primary_str
, NULL
,
7967 nwi_ifstate_get_ifname(scan
),
7968 changed_str
, addr_str
);
7970 if (changes
== NULL
) {
7971 changes
= CFStringCreateMutable(NULL
, 0);
7973 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
7974 nwi_ifstate_get_ifname(scan
));
7975 if (strcmp(changed_str
, "") != 0) {
7976 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
7977 changed_str
, addr_str
);
7981 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
7984 if (primary_str
!= NULL
) {
7985 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
7986 af
== AF_INET
? "v4" : "v6",
7989 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
7990 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
7993 CFStringAppend(log_output
, CFSTR(")"));
7995 my_CFRelease(&primary_str
);
7996 my_CFRelease(&changes
);
7999 #ifdef MANAGE_IF_IOCTL
8003 #endif /* MANAGE_IF_IOCTL */
8006 if (dns_changed
|| dnsinfo_changed
) {
8009 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
8010 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
8011 str
= "*"; // dnsinfo change w/no change to primary
8013 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
8014 } else if (S_primary_dns
!= NULL
) {
8015 CFStringAppend(log_output
, CFSTR(" DNS"));
8018 if (proxy_changed
) {
8021 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
8022 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
8023 } else if (S_primary_proxies
!= NULL
) {
8024 CFStringAppend(log_output
, CFSTR(" Proxy"));
8027 #if !TARGET_OS_IPHONE
8031 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
8032 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
8033 } else if (S_primary_smb
!= NULL
) {
8034 CFStringAppend(log_output
, CFSTR(" SMB"));
8036 #endif // !TARGET_OS_IPHONE
8042 #pragma mark Network changed notification
8044 static dispatch_queue_t
8045 __network_change_queue()
8047 static dispatch_once_t once
;
8048 static dispatch_queue_t q
;
8050 dispatch_once(&once
, ^{
8051 q
= dispatch_queue_create("network change queue", NULL
);
8057 // Note: must run on __network_change_queue()
8059 post_network_change_when_ready()
8063 dispatch_assert_queue(__network_change_queue());
8065 if (S_network_change_needed
== 0) {
8069 if (!S_network_change_timeout
&&
8070 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
8071 // if we [still] need to wait for the DNS configuration
8072 // or network information changes to be ack'd
8074 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
8075 S_dnsinfo_synced
? "DNS" : "!DNS",
8076 S_nwi_synced
? "nwi" : "!nwi");
8080 // cancel any running timer
8081 if (S_network_change_timer
!= NULL
) {
8082 dispatch_source_cancel(S_network_change_timer
);
8083 dispatch_release(S_network_change_timer
);
8084 S_network_change_timer
= NULL
;
8085 S_network_change_timeout
= FALSE
;
8088 // set (and log?) the post time
8090 struct timeval elapsed
;
8093 (void) gettimeofday(&end
, NULL
);
8094 timersub(&end
, &S_network_change_start
, &elapsed
);
8096 #define QUERY_TIME__FMT "%ld.%6.6d"
8097 #define QUERY_TIME__DIV 1
8100 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
8101 S_network_change_timeout
? "timeout" : "delayed",
8103 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
8104 S_network_change_needed
);
8108 /* We are about to post a network change to everyone, get the agents up to date */
8109 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8110 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
8111 /* Setup or Update config agents */
8112 process_AgentMonitor_DNS();
8114 #endif //!TARGET_OS_SIMULATOR
8116 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
8117 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
8118 if (status
!= NOTIFY_STATUS_OK
) {
8120 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%d", status
);
8124 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
8125 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
8126 if (status
!= NOTIFY_STATUS_OK
) {
8128 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%d", status
);
8132 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
8133 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8134 /* Setup or Update config agents */
8135 process_AgentMonitor_Proxy();
8136 #endif //!TARGET_OS_SIMULATOR
8137 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8138 if (status
!= NOTIFY_STATUS_OK
) {
8140 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%d", status
);
8144 if ((S_network_change_needed
& NETWORK_CHANGE_NAT64
) != 0) {
8145 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8146 // process any NAT64 prefix update requests (and refresh existing prefixes on change)
8147 if (S_nat64_prefix_requests
!= NULL
|| S_nat64_prefix_updates
!= NULL
8148 || S_nat64_cancel_prefix_requests
!= NULL
) {
8149 nat64_configuration_update(S_nat64_prefix_requests
,
8150 S_nat64_prefix_updates
,
8151 S_nat64_cancel_prefix_requests
);
8152 my_CFRelease(&S_nat64_prefix_requests
);
8153 my_CFRelease(&S_nat64_prefix_updates
);
8154 my_CFRelease(&S_nat64_cancel_prefix_requests
);
8156 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8158 S_network_change_needed
&= ~(NETWORK_CHANGE_NAT64
);
8161 if (S_network_change_needed
!= 0) {
8162 // if more than just a NAT64 prefix change
8163 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
8164 if (status
!= NOTIFY_STATUS_OK
) {
8166 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%d", status
);
8170 S_network_change_needed
= 0;
8174 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
8176 // Note: must run on __network_change_queue()
8178 post_network_change(uint32_t change
)
8180 dispatch_assert_queue(__network_change_queue());
8182 if (S_network_change_needed
== 0) {
8183 // set the start time
8184 (void) gettimeofday(&S_network_change_start
, NULL
);
8187 // indicate that we need to post a change for ...
8188 S_network_change_needed
|= change
;
8190 // cancel any running timer
8191 if (S_network_change_timer
!= NULL
) {
8192 dispatch_source_cancel(S_network_change_timer
);
8193 dispatch_release(S_network_change_timer
);
8194 S_network_change_timer
= NULL
;
8195 S_network_change_timeout
= FALSE
;
8198 // if needed, start new timer
8199 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
8200 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
8203 __network_change_queue());
8204 dispatch_source_set_event_handler(S_network_change_timer
, ^{
8205 S_network_change_timeout
= TRUE
;
8206 post_network_change_when_ready();
8208 dispatch_source_set_timer(S_network_change_timer
,
8209 dispatch_time(DISPATCH_TIME_NOW
,
8210 TRAILING_EDGE_TIMEOUT_NSEC
), // start
8211 DISPATCH_TIME_FOREVER
, // interval
8212 10 * NSEC_PER_MSEC
); // leeway
8213 dispatch_resume(S_network_change_timer
);
8216 post_network_change_when_ready();
8222 #pragma mark Process network (SCDynamicStore) changes
8225 IPMonitorProcessChanges(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8226 CFArrayRef if_rank_changes
)
8229 uint32_t changes
= 0;
8230 nwi_state_t changes_state
= NULL
;
8231 boolean_t dns_changed
= FALSE
;
8232 boolean_t dnsinfo_changed
= FALSE
;
8233 boolean_t global_ipv4_changed
= FALSE
;
8234 boolean_t global_ipv6_changed
= FALSE
;
8235 CFMutableSetRef ipv4_service_changes
= NULL
;
8236 CFMutableSetRef ipv6_service_changes
= NULL
;
8239 boolean_t nat64_changed
= FALSE
;
8240 CFMutableStringRef network_change_msg
= NULL
;
8242 nwi_state_t old_nwi_state
= NULL
;
8243 CFDictionaryRef old_primary_dns
= NULL
;
8244 CFDictionaryRef old_primary_proxy
= NULL
;
8245 #if !TARGET_OS_IPHONE
8246 CFDictionaryRef old_primary_smb
= NULL
;
8247 #endif // !TARGET_OS_IPHONE
8248 boolean_t proxies_changed
= FALSE
;
8249 boolean_t reachability_changed
= FALSE
;
8250 CFArrayRef service_order
;
8251 CFMutableArrayRef service_changes
= NULL
;
8252 CFDictionaryRef services_info
= NULL
;
8253 #if !TARGET_OS_IPHONE
8254 boolean_t smb_changed
= FALSE
;
8255 #endif // !TARGET_OS_IPHONE
8257 /* populate name/index cache */
8260 if (changed_keys
!= NULL
) {
8261 count
= CFArrayGetCount(changed_keys
);
8262 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8264 "changed keys %@ (%ld)", changed_keys
, count
);
8267 if (if_rank_changes
== NULL
&& count
== 0) {
8271 if (S_primary_dns
!= NULL
) {
8272 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
8273 if (old_primary_dns
!= NULL
) {
8274 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
8278 if (S_primary_proxies
!= NULL
) {
8280 = service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
8281 if (old_primary_proxy
!= NULL
) {
8282 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
8286 #if !TARGET_OS_IPHONE
8287 if (S_primary_smb
!= NULL
) {
8288 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
8289 if (old_primary_smb
!= NULL
) {
8290 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
8293 #endif // !TARGET_OS_IPHONE
8295 keyChangeListInit(&keys
);
8296 service_changes
= CFArrayCreateMutable(NULL
, 0,
8297 &kCFTypeArrayCallBacks
);
8298 for (CFIndex i
= 0; i
< count
; i
++) {
8300 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8301 CFStringRef interface
= NULL
;
8302 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8304 change
= CFArrayGetValueAtIndex(changed_keys
, i
);
8305 if (CFEqual(change
, S_setup_global_ipv4
)) {
8306 global_ipv4_changed
= TRUE
;
8307 global_ipv6_changed
= TRUE
;
8309 else if (CFEqual(change
, S_multicast_resolvers
)) {
8310 dnsinfo_changed
= TRUE
;
8312 else if (CFEqual(change
, S_private_resolvers
)) {
8313 dnsinfo_changed
= TRUE
;
8315 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
8316 dnsinfo_changed
= TRUE
;
8318 else if (CFStringHasPrefix(change
, S_interface_delegation_prefix
) &&
8319 CFStringHasSuffix(change
, kSCEntNetInterfaceDelegation
)) {
8320 // ensure that we update the reachability flags in the NWI
8321 // state (including the non-interface-specific flags)
8322 reachability_changed
= TRUE
;
8323 // ensure that we update the reachability flags in the DNS
8325 dnsinfo_changed
= TRUE
;
8327 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
8328 CFStringRef protocol
= NULL
;
8329 CFStringRef serviceID
;
8331 serviceID
= parseNetworkServiceString(change
, &protocol
);
8332 if (serviceID
!= NULL
) {
8333 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8334 if (protocol
!= NULL
) {
8335 if (CFEqual(protocol
, kSCEntNetIPv4
)) {
8336 /* IPv4 service changed, remember that */
8337 my_CFSetAddValue(&ipv4_service_changes
, serviceID
);
8339 else if (CFEqual(protocol
, kSCEntNetIPv6
)) {
8340 /* IPv6 service changed, remember that */
8341 my_CFSetAddValue(&ipv6_service_changes
, serviceID
);
8344 CFRelease(serviceID
);
8346 my_CFRelease(&protocol
);
8348 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
8349 CFStringRef serviceID
;
8351 serviceID
= parseNetworkServiceString(change
, NULL
);
8352 if (serviceID
!= NULL
) {
8353 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8354 CFRelease(serviceID
);
8357 for (size_t j
= 0; j
< countof(transientInterfaceEntityNames
); j
++) {
8358 if (CFStringHasSuffix(change
,
8359 *transientInterfaceEntityNames
[j
])) {
8360 reachability_changed
= TRUE
;
8365 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
8366 reachability_changed
= TRUE
;
8369 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8370 else if (is_nat64_prefix_request(change
, &interface
)) {
8371 set_plat_discovery(kPLATDiscoveryOptionStart
, interface
);
8372 nat64_changed
= TRUE
;
8374 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8377 /* determine which serviceIDs are impacted by the interface rank changes */
8378 if (if_rank_changes
!= NULL
) {
8379 n
= CFArrayGetCount(if_rank_changes
);
8380 for (CFIndex i
= 0; i
< n
; i
++) {
8381 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
8383 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8384 my_log(LOG_DEBUG
, "Interface rank changed %@", ifname
);
8386 append_serviceIDs_for_interface(service_changes
, ifname
);
8390 /* grab a snapshot of everything we need */
8391 services_info
= services_info_copy(session
, service_changes
);
8392 assert(services_info
!= NULL
);
8394 /* grab the service order */
8395 service_order
= service_order_get(services_info
);
8396 if (service_order
!= NULL
) {
8397 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8398 my_log(LOG_DEBUG
, "service_order %@ ", service_order
);
8403 * process protocol (v4, v6, rank, ...) and
8404 * configuration (dns, proxies, smb, ...) changes
8406 n
= CFArrayGetCount(service_changes
);
8407 for (CFIndex i
= 0; i
< n
; i
++) {
8409 CFStringRef serviceID
;
8411 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
8412 changes
= service_changed(services_info
, serviceID
);
8413 if (my_CFSetContainsValue(ipv4_service_changes
, serviceID
)) {
8414 changes
|= (1 << kEntityTypeIPv4
);
8416 if (my_CFSetContainsValue(ipv6_service_changes
, serviceID
)) {
8417 changes
|= (1 << kEntityTypeIPv6
);
8419 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
8420 /* if __Service__ (e.g. PrimaryRank) changed */
8421 global_ipv4_changed
= TRUE
;
8422 global_ipv6_changed
= TRUE
;
8425 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
8426 global_ipv4_changed
= TRUE
;
8427 dnsinfo_changed
= TRUE
;
8428 proxies_changed
= TRUE
;
8430 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
8431 global_ipv6_changed
= TRUE
;
8432 dnsinfo_changed
= TRUE
;
8433 proxies_changed
= TRUE
;
8434 nat64_changed
= TRUE
;
8437 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
8438 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
8441 dnsinfo_changed
= TRUE
;
8442 nat64_changed
= TRUE
;
8444 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
8445 proxies_changed
= TRUE
;
8447 #if !TARGET_OS_IPHONE
8448 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
8449 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
8454 if ((changes
& (1 << kEntityTypeTransientStatus
)) != 0
8455 && (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
8456 || service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
)) {
8457 dnsinfo_changed
= TRUE
;
8461 /* ensure S_nwi_state can hold as many services as we have currently */
8462 n_services
= (int)CFDictionaryGetCount(S_service_state_dict
);
8463 old_nwi_state
= nwi_state_make_copy(S_nwi_state
);
8464 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
8466 if (global_ipv4_changed
) {
8467 if (S_ipv4_results
!= NULL
) {
8468 ElectionResultsRelease(S_ipv4_results
);
8471 = ElectionResultsCopy(AF_INET
, service_order
);
8472 ElectionResultsLog(LOG_INFO
, S_ipv4_results
, "IPv4");
8474 if (global_ipv6_changed
) {
8475 if (S_ipv6_results
!= NULL
) {
8476 ElectionResultsRelease(S_ipv6_results
);
8479 = ElectionResultsCopy(AF_INET6
, service_order
);
8480 ElectionResultsLog(LOG_INFO
, S_ipv6_results
, "IPv6");
8482 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
8483 CFStringRef new_primary
;
8484 CFStringRef new_primary_dns
= NULL
;
8485 CFStringRef new_primary_proxies
= NULL
;
8486 #if !TARGET_OS_IPHONE
8487 CFStringRef new_primary_smb
= NULL
;
8488 #endif /* !TARGET_OS_IPHONE */
8489 RouteListUnion new_routelist
;
8490 CandidateRef other_candidate
;
8491 CandidateRef primary_candidate
;
8493 if (S_nwi_state
!= NULL
) {
8494 nwi_state_clear(S_nwi_state
, AF_INET
);
8495 nwi_state_clear(S_nwi_state
, AF_INET6
);
8499 my_log(LOG_DEBUG
, "electing IPv4 primary");
8500 new_routelist
.ptr
= NULL
;
8501 other_candidate
= (S_ipv6_results
!= NULL
) /* get IPv6 primary */
8502 ? S_ipv6_results
->candidates
: NULL
;
8503 primary_candidate
= ElectionResultsGetPrimary(S_ipv4_results
,
8505 S_nwi_state
, AF_INET
,
8506 &new_routelist
.common
,
8508 ipv4_service_changes
);
8509 new_primary
= (primary_candidate
!= NULL
)
8510 ? primary_candidate
->serviceID
: NULL
;
8511 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
8512 update_ipv4(S_primary_ipv4
, new_routelist
.v4
, &keys
);
8515 my_log(LOG_DEBUG
, "electing IPv6 primary");
8516 new_routelist
.ptr
= NULL
;
8517 other_candidate
= primary_candidate
; /* get IPv4 primary */
8518 primary_candidate
= ElectionResultsGetPrimary(S_ipv6_results
,
8520 S_nwi_state
, AF_INET6
,
8521 &new_routelist
.common
,
8523 ipv6_service_changes
);
8524 new_primary
= (primary_candidate
!= NULL
)
8525 ? primary_candidate
->serviceID
: NULL
;
8526 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
8527 update_ipv6(S_primary_ipv6
, new_routelist
.v6
, &keys
);
8529 nwi_state_finalize(S_nwi_state
);
8531 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
8532 /* decide between IPv4 and IPv6 */
8533 if (rank_service_entity(S_ipv4_service_rank_dict
,
8534 S_primary_ipv4
, kSCEntNetDNS
)
8535 <= rank_service_entity(S_ipv6_service_rank_dict
,
8536 S_primary_ipv6
, kSCEntNetDNS
)) {
8537 new_primary_dns
= S_primary_ipv4
;
8540 new_primary_dns
= S_primary_ipv6
;
8542 if (rank_service_entity(S_ipv4_service_rank_dict
,
8543 S_primary_ipv4
, kSCEntNetProxies
)
8544 <= rank_service_entity(S_ipv6_service_rank_dict
,
8545 S_primary_ipv6
, kSCEntNetProxies
)) {
8546 new_primary_proxies
= S_primary_ipv4
;
8549 new_primary_proxies
= S_primary_ipv6
;
8551 #if !TARGET_OS_IPHONE
8552 if (rank_service_entity(S_ipv4_service_rank_dict
,
8553 S_primary_ipv4
, kSCEntNetSMB
)
8554 <= rank_service_entity(S_ipv6_service_rank_dict
,
8555 S_primary_ipv6
, kSCEntNetSMB
)) {
8556 new_primary_smb
= S_primary_ipv4
;
8559 new_primary_smb
= S_primary_ipv6
;
8561 #endif /* !TARGET_OS_IPHONE */
8564 else if (S_primary_ipv6
!= NULL
) {
8565 new_primary_dns
= S_primary_ipv6
;
8566 new_primary_proxies
= S_primary_ipv6
;
8567 #if !TARGET_OS_IPHONE
8568 new_primary_smb
= S_primary_ipv6
;
8569 #endif /* !TARGET_OS_IPHONE */
8571 else if (S_primary_ipv4
!= NULL
) {
8572 new_primary_dns
= S_primary_ipv4
;
8573 new_primary_proxies
= S_primary_ipv4
;
8574 #if !TARGET_OS_IPHONE
8575 new_primary_smb
= S_primary_ipv4
;
8576 #endif /* !TARGET_OS_IPHONE */
8579 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
8581 dnsinfo_changed
= TRUE
;
8583 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
,
8585 proxies_changed
= TRUE
;
8587 #if !TARGET_OS_IPHONE
8588 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
8591 #endif /* !TARGET_OS_IPHONE */
8594 if (!proxies_changed
&& dnsinfo_changed
8595 && ((G_supplemental_proxies_follow_dns
!= NULL
)
8596 && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
8597 proxies_changed
= TRUE
;
8600 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
8602 if (global_ipv4_changed
|| global_ipv6_changed
8603 || dnsinfo_changed
|| reachability_changed
) {
8604 if (S_nwi_state
!= NULL
) {
8605 S_nwi_state
->generation_count
= mach_absolute_time();
8606 if (global_ipv4_changed
|| global_ipv6_changed
8607 || reachability_changed
) {
8608 SCNetworkReachabilityFlags reach_flags_v4
= 0;
8609 SCNetworkReachabilityFlags reach_flags_v6
= 0;
8611 GetReachabilityFlagsFromTransientServices(services_info
,
8615 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
,
8619 /* Update the per-interface generation count */
8620 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
,
8624 if (update_nwi(S_nwi_state
)) {
8625 changes
|= NETWORK_CHANGE_NET
;
8628 * the DNS configuration includes per-resolver configuration
8629 * reachability flags that are based on the nwi state. Let's
8630 * make sure that we check for changes
8632 dnsinfo_changed
= TRUE
;
8636 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
8637 changes
|= NETWORK_CHANGE_DNS
;
8638 dnsinfo_changed
= TRUE
;
8640 dns_changed
= FALSE
;
8643 if (dnsinfo_changed
) {
8644 if (update_dnsinfo(services_info
, S_primary_dns
,
8645 &keys
, service_order
)) {
8646 changes
|= NETWORK_CHANGE_DNS
;
8648 dnsinfo_changed
= FALSE
;
8651 if (proxies_changed
) {
8652 // if proxy change OR supplemental Proxies follow supplemental DNS
8653 if (update_proxies(services_info
, S_primary_proxies
,
8654 &keys
, service_order
)) {
8655 changes
|= NETWORK_CHANGE_PROXY
;
8657 proxies_changed
= FALSE
;
8660 #if !TARGET_OS_IPHONE
8662 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
8663 changes
|= NETWORK_CHANGE_SMB
;
8665 smb_changed
= FALSE
;
8668 #endif /* !TARGET_OS_IPHONE */
8669 if (nat64_changed
) {
8670 changes
|= NETWORK_CHANGE_NAT64
;
8672 my_CFRelease(&service_changes
);
8673 my_CFRelease(&services_info
);
8674 my_CFRelease(&ipv4_service_changes
);
8675 my_CFRelease(&ipv6_service_changes
);
8678 network_change_msg
= CFStringCreateMutable(NULL
, 0);
8679 process_nwi_changes(network_change_msg
,
8688 #if !TARGET_OS_IPHONE
8691 #else // !TARGET_OS_IPHONE
8692 FALSE
, // smb_changed
8693 NULL
// old_primary_smb
8694 #endif // !TARGET_OS_IPHONE
8698 keyChangeListApplyToStore(&keys
, session
);
8699 my_CFRelease(&old_primary_dns
);
8700 my_CFRelease(&old_primary_proxy
);
8701 #if !TARGET_OS_IPHONE
8702 my_CFRelease(&old_primary_smb
);
8703 #endif // !TARGET_OS_IPHONE
8706 dispatch_async(__network_change_queue(), ^{
8707 post_network_change(changes
);
8711 if ((network_change_msg
!= NULL
)
8712 && (CFStringGetLength(network_change_msg
) != 0)) {
8713 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
8714 } else if (keyChangeListActive(&keys
)) {
8715 my_log(LOG_NOTICE
, "network changed");
8716 } else if (nat64_changed
) {
8717 my_log(LOG_NOTICE
, "nat64 update");
8719 my_log(LOG_INFO
, "network event w/no changes");
8722 my_CFRelease(&network_change_msg
);
8724 if (changes_state
!= NULL
) {
8725 nwi_state_free(changes_state
);
8727 if (old_nwi_state
!= NULL
) {
8728 nwi_state_free(old_nwi_state
);
8730 keyChangeListFree(&keys
);
8732 /* release the name/index cache */
8733 my_if_freenameindex();
8739 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8742 #pragma unused(info)
8743 IPMonitorProcessChanges(session
, changed_keys
, NULL
);
8747 #if !TARGET_OS_IPHONE
8748 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_mcx
8750 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_global
8756 static dispatch_queue_t proxy_cb_queue
;
8758 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
8759 _scprefs_observer_watch(PROXY_GLOBAL_OBSERVER_TYPE
,
8760 "com.apple.SystemConfiguration.plist",
8763 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
8764 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8765 /* Setup or Update config agents */
8766 process_AgentMonitor_Proxy();
8767 #endif //!TARGET_OS_SIMULATOR
8768 (void)notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8769 my_log(LOG_INFO
, "Notifying:\n%@",
8770 S_state_global_proxies
);
8775 #if TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST
8778 prefs_changed_callback_init(void)
8782 #else /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8784 #include "IPMonitorControlPrefs.h"
8787 prefs_changed(SCPreferencesRef prefs
)
8789 #pragma unused(prefs)
8790 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
8791 S_IPMonitor_debug
= kDebugFlagDefault
;
8792 S_IPMonitor_verbose
= TRUE
;
8793 my_log(LOG_DEBUG
, "Setting logging verbose mode on");
8795 my_log(LOG_DEBUG
, "Setting logging verbose mode off");
8796 S_IPMonitor_debug
= 0;
8797 S_IPMonitor_verbose
= FALSE
;
8803 prefs_changed_callback_init(void)
8805 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
8806 prefs_changed(NULL
);
8811 #endif /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8813 #if !TARGET_OS_SIMULATOR
8824 struct rt_msghdr
* rtm
;
8825 struct sockaddr_in
*sin
;
8831 mib
[4] = NET_RT_FLAGS
;
8832 mib
[5] = RTF_STATIC
| RTF_DYNAMIC
;
8833 for (i
= 0; i
< 3; i
++) {
8834 if (sysctl(mib
, N_MIB
, NULL
, &needed
, NULL
, 0) < 0) {
8837 if ((buf
= malloc(needed
)) == NULL
) {
8840 if (sysctl(mib
, N_MIB
, buf
, &needed
, NULL
, 0) >= 0) {
8850 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
8853 /* ALIGN: assume kernel provides necessary alignment */
8854 rtm
= (struct rt_msghdr
*)(void *)next
;
8855 sin
= (struct sockaddr_in
*)(rtm
+ 1);
8857 addr
= ntohl(sin
->sin_addr
.s_addr
);
8858 if (IN_LOOPBACK(addr
)) {
8860 "flush_routes: ignoring loopback route");
8863 if (IN_LOCAL_GROUP(addr
)) {
8865 "flush_routes: ignoring multicast route");
8868 rtm
->rtm_type
= RTM_DELETE
;
8869 rtm
->rtm_seq
= ++rtm_seq
;
8870 if (write(s
, rtm
, rtm
->rtm_msglen
) < 0) {
8872 "flush_routes: removing route for "
8873 IP_FORMAT
" failed: %s",
8874 IP_LIST(&sin
->sin_addr
),
8879 "flush_routes: removed route for " IP_FORMAT
,
8880 IP_LIST(&sin
->sin_addr
));
8888 flush_inet_routes(void)
8892 s
= open_routing_socket();
8899 #else /* !TARGET_OS_SIMULATOR */
8902 flush_inet_routes(void)
8906 #endif /* !TARGET_OS_SIMULATOR */
8910 ip_plugin_copy_keys(void)
8912 const void * values
[]
8914 S_setup_global_ipv4
, /* serviceOrder/PPPOverridePrimary */
8915 S_private_resolvers
, /* private DNS config (Back to My Mac) */
8916 S_multicast_resolvers
,/* multicast DNS config (Bonjour/.local) */
8918 const CFIndex values_count
= sizeof(values
) / sizeof(values
[0]);
8920 return (CFArrayCreate(NULL
, values
, values_count
,
8921 &kCFTypeArrayCallBacks
));
8925 ip_plugin_copy_patterns(void)
8927 CFStringRef pattern
;
8928 CFMutableArrayRef patterns
;
8930 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8932 /* State: and Setup: per-service notifications */
8933 add_service_keys(kSCCompAnyRegex
, patterns
);
8935 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
8936 CFArrayAppendValue(patterns
, pattern
);
8939 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
8940 CFArrayAppendValue(patterns
, pattern
);
8943 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
8944 CFArrayAppendValue(patterns
, pattern
);
8947 /* State: per-service PPP/VPN/IPSec status notifications */
8948 add_transient_status_keys(kSCCompAnyRegex
, patterns
);
8950 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8951 /* NAT64 prefix request pattern */
8952 nat64_prefix_request_add_pattern(patterns
);
8953 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8955 /* interface delegation pattern */
8956 pattern
= interface_entity_key_copy(kSCCompAnyRegex
,
8957 kSCEntNetInterfaceDelegation
);
8958 CFArrayAppendValue(patterns
, pattern
);
8966 if (S_is_network_boot() != 0) {
8971 flush_inet_routes();
8975 * Initialize globals
8977 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
8978 IPMonitorNotify
, NULL
);
8979 if (S_session
== NULL
) {
8981 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8982 SCErrorString(SCError()));
8986 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8987 kSCDynamicStoreDomainState
,
8990 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8991 kSCDynamicStoreDomainState
,
8994 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8995 kSCDynamicStoreDomainState
,
8997 S_state_global_proxies
8998 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8999 kSCDynamicStoreDomainState
,
9001 #if !TARGET_OS_IPHONE
9003 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
9004 kSCDynamicStoreDomainState
,
9006 #endif /* !TARGET_OS_IPHONE */
9008 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
9009 kSCDynamicStoreDomainSetup
,
9011 S_state_service_prefix
9012 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
9013 kSCDynamicStoreDomainState
,
9016 S_setup_service_prefix
9017 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
9018 kSCDynamicStoreDomainSetup
,
9021 S_interface_delegation_prefix
9022 = SCDynamicStoreKeyCreateNetworkInterface(NULL
,
9023 kSCDynamicStoreDomainState
);
9024 S_service_state_dict
9025 = CFDictionaryCreateMutable(NULL
, 0,
9026 &kCFTypeDictionaryKeyCallBacks
,
9027 &kCFTypeDictionaryValueCallBacks
);
9028 S_ipv4_service_rank_dict
9029 = CFDictionaryCreateMutable(NULL
, 0,
9030 &kCFTypeDictionaryKeyCallBacks
,
9031 &kCFTypeDictionaryValueCallBacks
);
9032 S_ipv6_service_rank_dict
9033 = CFDictionaryCreateMutable(NULL
, 0,
9034 &kCFTypeDictionaryKeyCallBacks
,
9035 &kCFTypeDictionaryValueCallBacks
);
9036 S_multicast_resolvers
9037 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
9038 kSCDynamicStoreDomainState
,
9040 CFSTR(kDNSServiceCompMulticastDNS
));
9042 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
9043 kSCDynamicStoreDomainState
,
9045 CFSTR(kDNSServiceCompPrivateDNS
));
9046 /* initialize dns configuration */
9047 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
9048 #if !TARGET_OS_IPHONE
9050 #endif /* !TARGET_OS_IPHONE */
9051 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
9053 #if !TARGET_OS_IPHONE
9054 /* initialize SMB configuration */
9055 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
9056 #endif /* !TARGET_OS_IPHONE */
9064 copy_dictionary_keys(CFDictionaryRef dict
)
9067 CFArrayRef ret_keys
;
9069 count
= CFDictionaryGetCount(dict
);
9071 const void * keys
[count
];
9073 CFDictionaryGetKeysAndValues(dict
, keys
, NULL
);
9074 ret_keys
= CFArrayCreate(NULL
, keys
, count
, &kCFTypeArrayCallBacks
);
9083 prime_notifications(CFArrayRef keys
, CFArrayRef patterns
)
9085 CFArrayRef changed_keys
;
9086 CFDictionaryRef info
;
9088 info
= SCDynamicStoreCopyMultiple(S_session
, keys
, patterns
);
9090 my_log(LOG_NOTICE
, "%s: no content", __func__
);
9093 changed_keys
= copy_dictionary_keys(info
);
9095 if (changed_keys
== NULL
) {
9096 my_log(LOG_NOTICE
, "%s: no keys", __func__
);
9100 "IPMonitor prime %ld keys %@",
9101 (long)CFArrayGetCount(changed_keys
), changed_keys
);
9102 IPMonitorProcessChanges(S_session
, changed_keys
, NULL
);
9103 CFRelease(changed_keys
);
9108 initialize_notifications(void)
9111 CFArrayRef patterns
;
9114 /* register for notifications */
9115 keys
= ip_plugin_copy_keys();
9116 patterns
= ip_plugin_copy_patterns();
9117 success
= SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
);
9120 "SCDynamicStoreSetNotificationKeys() failed: %s",
9121 SCErrorString(SCError()));
9124 CFRunLoopSourceRef rls
;
9126 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
9129 "SCDynamicStoreCreateRunLoopSource() failed: %s",
9130 SCErrorString(SCError()));
9133 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
9134 kCFRunLoopDefaultMode
);
9137 /* catch any changes that happened before registering */
9138 prime_notifications(keys
, patterns
);
9142 CFRelease(patterns
);
9148 prime_IPMonitor(void)
9150 /* initialize multicast route */
9151 update_ipv4(NULL
, NULL
, NULL
);
9153 if (S_session
== NULL
) {
9157 /* initialize notifications */
9158 CFRunLoopPerformBlock(CFRunLoopGetCurrent(),
9159 kCFRunLoopDefaultMode
,
9160 ^{ initialize_notifications(); });
9162 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9163 process_AgentMonitor();
9164 #endif // !TARGET_OS_SIMULATOR
9170 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
9174 boolean_t ret
= def
;
9176 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
9178 ret
= CFBooleanGetValue(b
);
9183 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
9184 #include "IPMonitorControlServer.h"
9187 InterfaceRankChanged(void * info
)
9189 #pragma unused(info)
9190 CFDictionaryRef assertions
= NULL
;
9193 changes
= IPMonitorControlServerCopyInterfaceRankInformation(&assertions
);
9194 if (S_if_rank_dict
!= NULL
) {
9195 CFRelease(S_if_rank_dict
);
9197 S_if_rank_dict
= assertions
;
9198 if (changes
!= NULL
) {
9199 IPMonitorProcessChanges(S_session
, NULL
, changes
);
9207 StartIPMonitorControlServer(void)
9209 CFRunLoopSourceContext context
;
9210 CFRunLoopSourceRef rls
;
9212 memset(&context
, 0, sizeof(context
));
9213 context
.perform
= InterfaceRankChanged
;
9214 rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
9215 if (!IPMonitorControlServerStart(CFRunLoopGetCurrent(),
9217 &S_bundle_logging_verbose
)) {
9218 my_log(LOG_ERR
, "IPMonitorControlServerStart failed");
9221 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
9222 kCFRunLoopDefaultMode
);
9228 #endif /* !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
9232 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
9234 CFDictionaryRef info_dict
;
9236 info_dict
= CFBundleGetInfoDictionary(bundle
);
9238 if (info_dict
!= NULL
) {
9240 = S_get_plist_boolean(info_dict
,
9241 CFSTR("AppendStateArrayToSetupArray"),
9244 if (bundleVerbose
) {
9245 S_IPMonitor_debug
= kDebugFlagDefault
;
9246 S_bundle_logging_verbose
= TRUE
;
9247 S_IPMonitor_verbose
= TRUE
;
9250 /* register to receive changes to the "verbose" flag and read the initial setting */
9251 prefs_changed_callback_init();
9253 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
9254 /* start DNS configuration (dnsinfo) server */
9255 load_DNSConfiguration(bundle
, // bundle
9256 ^(Boolean inSync
) { // syncHandler
9257 dispatch_async(__network_change_queue(), ^{
9258 S_dnsinfo_synced
= inSync
;
9261 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
9262 // all of the DNS service ack's should result
9263 // in a [new] network change being posted
9264 post_network_change(NETWORK_CHANGE_DNS
);
9266 post_network_change_when_ready();
9271 /* start Network Information (nwi) server */
9272 load_NetworkInformation(bundle
, // bundle
9273 ^(Boolean inSync
) { // syncHandler
9274 dispatch_async(__network_change_queue(), ^{
9275 S_nwi_synced
= inSync
;
9276 post_network_change_when_ready();
9279 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
9281 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
9282 /* start IPMonitor Control (InterfaceRank) server */
9283 StartIPMonitorControlServer();
9284 #endif /* !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
9286 /* initialize DNS configuration */
9287 dns_configuration_init(bundle
);
9289 /* initialize proxy configuration */
9290 proxy_configuration_init(bundle
);
9294 if (S_session
!= NULL
) {
9295 dns_configuration_monitor(S_session
, IPMonitorNotify
);
9298 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9299 load_hostname(TRUE
);
9300 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
9302 #if !TARGET_OS_IPHONE
9303 load_smb_configuration(TRUE
);
9304 #endif /* !TARGET_OS_IPHONE */
9311 #pragma mark Standalone test code
9314 #ifdef TEST_IPMONITOR
9317 main(int argc
, char **argv
)
9319 _sc_log
= kSCLogDestinationFile
;
9321 S_IPMonitor_debug
= kDebugFlag1
;
9323 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9326 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
9328 S_IPMonitor_debug
= kDebugFlag1
;
9334 #endif /* TEST_IPMONITOR */
9336 #if defined(TEST_ROUTELIST) || defined(TEST_DNS_ORDER)
9338 #if defined(__has_feature)
9339 #if __has_feature(address_sanitizer)
9340 #define TEST_ROUTELIST_RUN_LEAKS FALSE
9341 #endif /* if __has_feature(address_sanitizer) */
9342 #endif /* if defined(__has_feature) */
9344 #ifndef TEST_ROUTELIST_RUN_LEAKS
9345 #define TEST_ROUTELIST_RUN_LEAKS TRUE
9348 static boolean_t S_run_leaks
= TEST_ROUTELIST_RUN_LEAKS
;
9353 printf("\nTest Complete\n");
9357 sprintf(cmd
, "leaks %d 2>&1", getpid());
9366 const char * gateway
;
9367 const char * ifname
;
9372 #if TEST_IPV4_ROUTELIST
9378 const char * router
;
9379 const char * ifname
;
9381 const CFStringRef
* primary_rank
;
9382 struct route
* additional_routes
;
9383 int additional_routes_count
;
9384 struct route
* excluded_routes
;
9385 int excluded_routes_count
;
9386 } IPv4ServiceContents
;
9388 typedef const IPv4ServiceContents
* IPv4ServiceContentsRef
;
9390 struct route loop_routelist
[] = {
9391 { "1.1.1.1", 32, "1.1.1.2", NULL
},
9392 { "1.1.1.2", 32, "1.1.1.3", NULL
},
9393 { "1.1.1.3", 32, "1.1.1.4", NULL
},
9394 { "1.1.1.4", 32, "1.1.1.5", NULL
},
9395 { "1.1.1.5", 32, "1.1.1.6", NULL
},
9396 { "1.1.1.6", 32, "1.1.1.7", NULL
},
9397 { "1.1.1.7", 32, "1.1.1.8", NULL
},
9398 { "1.1.1.8", 32, "1.1.1.9", NULL
},
9399 { "1.1.1.9", 32, "1.1.1.10", NULL
},
9400 { "1.1.1.10", 32, "1.1.1.11", NULL
},
9401 { "1.1.1.11", 32, "1.1.1.1", NULL
},
9404 struct route vpn_routelist
[] = {
9405 { "10.1.3.0", 24, "17.153.46.24", NULL
},
9406 { "10.1.4.0", 24, "17.153.46.24", NULL
},
9407 { "10.1.5.0", 24, "17.153.46.24", NULL
},
9408 { "10.1.6.0", 24, "17.153.46.24", NULL
},
9409 { "10.1.7.0", 24, "17.153.46.24", NULL
},
9410 { "10.16.0.0", 12, "17.153.46.24", NULL
},
9411 { "10.45.0.0", 16, "17.153.46.24", NULL
},
9412 { "10.53.0.0", 16, "17.153.46.24", NULL
},
9413 { "10.70.0.0", 15, "17.153.46.24", NULL
},
9414 { "10.74.0.0", 15, "17.153.46.24", NULL
},
9415 { "10.90.0.0", 15, "17.153.46.24", NULL
},
9416 { "10.91.0.0", 16, "17.153.46.24", NULL
},
9417 { "10.100.0.0", 16, "17.153.46.24", NULL
},
9418 { "10.113.0.0", 16, "17.153.46.24", NULL
},
9419 { "10.128.0.0", 9, "17.153.46.24", NULL
},
9420 { "17.0.0.0", 9, "17.153.46.24", NULL
},
9421 { "17.34.0.0", 16, "17.153.46.24", NULL
},
9422 { "17.112.156.53", 32, "17.153.46.24", NULL
},
9423 { "17.128.0.0", 10, "17.153.46.24", NULL
},
9424 { "17.149.0.121", 32, "17.153.46.24", NULL
},
9425 { "17.149.7.200", 32, "17.153.46.24", NULL
},
9426 { "17.153.46.24", 32, "17.153.46.24", NULL
},
9427 { "17.192.0.0", 12, "17.153.46.24", NULL
},
9428 { "17.208.0.0", 15, "17.153.46.24", NULL
},
9429 { "17.211.0.0", 16, "17.153.46.24", NULL
},
9430 { "17.212.0.0", 14, "17.153.46.24", NULL
},
9431 { "17.216.0.0", 13, "17.153.46.24", NULL
},
9432 { "17.224.0.0", 12, "17.153.46.24", NULL
},
9433 { "17.240.0.0", 16, "17.153.46.24", NULL
},
9434 { "17.241.0.0", 16, "17.153.46.24", NULL
},
9435 { "17.248.0.0", 14, "17.153.46.24", NULL
},
9436 { "17.251.104.200", 32, "17.153.46.24", NULL
},
9437 { "17.252.0.0", 16, "17.153.46.24", NULL
},
9438 { "17.253.0.0", 16, "17.153.46.24", NULL
},
9439 { "17.254.0.0", 16, "17.153.46.24", NULL
},
9440 { "17.255.0.0", 16, "17.153.46.24", NULL
},
9441 { "151.193.141.0", 27, "17.153.46.24", NULL
},
9442 { "172.16.2.0", 24, "17.153.46.24", NULL
},
9443 { "192.35.50.0", 24, "17.153.46.24", NULL
},
9444 { "204.179.20.0", 24, "17.153.46.24", NULL
},
9445 { "206.112.116.0", 24, "17.153.46.24", NULL
},
9448 struct route vpn_routelist_ext
[] = {
9449 { "17.151.63.82", 32, "10.0.0.1", "en0" },
9450 { "17.151.63.81", 32, "17.151.63.81", "en0" },
9451 { "17.151.63.80", 32, NULL
, NULL
},
9452 { "17.1.0.0", 16, NULL
, NULL
},
9453 { "17.2.0.0", 24, NULL
, NULL
},
9454 { "10.0.0.0", 24, NULL
, NULL
},
9458 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
9460 const IPv4ServiceContents en0_10
= {
9461 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9464 const IPv4ServiceContents en0_15
= {
9465 "10.0.0.19", 24, NULL
, "10.0.0.1", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9468 const IPv4ServiceContents en0_30
= {
9469 "10.0.0.11", 24, NULL
, "10.0.0.1", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9472 const IPv4ServiceContents en0_40
= {
9473 "10.0.0.12", 24, NULL
, "10.0.0.1", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9476 const IPv4ServiceContents en0_50
= {
9477 "10.0.0.13", 24, NULL
, "10.0.0.1", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9480 const IPv4ServiceContents en0_110
= {
9481 "192.168.2.10", 24, NULL
, "192.168.2.1", "en0", 110, NULL
, NULL
, 0, NULL
, 0
9484 const IPv4ServiceContents en0_1
= {
9485 "17.202.40.191", 22, NULL
, "17.202.20.1", "en0", 1, NULL
, NULL
, 0, NULL
, 0
9488 const IPv4ServiceContents en1_20
= {
9489 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9492 const IPv4ServiceContents en1_2
= {
9493 "17.202.42.24", 22, NULL
, "17.202.20.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9496 const IPv4ServiceContents en1_125
= {
9497 "192.168.2.20", 24, NULL
, "192.168.2.1", "en1", 125, NULL
, NULL
, 0, NULL
, 0
9500 const IPv4ServiceContents fw0_25
= {
9501 "192.168.2.30", 24, NULL
, "192.168.2.1", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9504 const IPv4ServiceContents fw0_21
= {
9505 "192.168.3.30", 24, NULL
, "192.168.3.1", "fw0", 21, NULL
, NULL
, 0, NULL
, 0
9508 const IPv4ServiceContents ppp0_0_1
= {
9509 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
, NULL
, 0, NULL
, 0
9512 const IPv4ServiceContents utun0
= {
9513 "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
)
9516 const IPv4ServiceContents en0_test6
= {
9517 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 2, NULL
, NULL
, 0, NULL
, 0
9520 const IPv4ServiceContents en1_test6
= {
9521 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 3, NULL
, NULL
, 0, NULL
, 0
9524 const IPv4ServiceContents en2_test6
= {
9525 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9528 const IPv4ServiceContents en0_test7
= {
9529 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 3, NULL
, NULL
, 0, NULL
, 0
9532 const IPv4ServiceContents en1_test7
= {
9533 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9536 const IPv4ServiceContents en2_test7
= {
9537 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9540 const IPv4ServiceContents fw0_test6_and_7
= {
9541 "169.254.11.33", 16, NULL
, NULL
, "fw0", 0x0ffffff, NULL
, NULL
, 0, NULL
, 0
9544 const IPv4ServiceContents en0_10_last
= {
9545 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
, NULL
, 0, NULL
, 0
9548 const IPv4ServiceContents en0_10_never
= {
9549 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9552 const IPv4ServiceContents en1_20_first
= {
9553 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
, NULL
, 0, NULL
, 0
9556 const IPv4ServiceContents en1_20_never
= {
9557 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9560 const IPv4ServiceContents en1_20_other_never
= {
9561 "192.168.2.50", 24, NULL
, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9564 const IPv4ServiceContents en0_linklocal
= {
9565 "169.254.22.44", 16, NULL
, NULL
, "en0", 0xfffff, NULL
, NULL
, 0, NULL
, 0
9568 const IPv4ServiceContents en0_route_loop
= {
9569 "192.168.130.16", 24, NULL
, "192.168.130.1", "en0", 2, NULL
, loop_routelist
, countof(loop_routelist
), NULL
, 0
9574 IPv4ServiceContentsRef test
[];
9575 } IPv4RouteTest
, * IPv4RouteTestRef
;
9577 static IPv4RouteTest test1
= {
9591 static IPv4RouteTest test2
= {
9604 static IPv4RouteTest test3
= {
9621 static IPv4RouteTest test4
= {
9633 static IPv4RouteTest test5
= {
9646 static IPv4RouteTest test6
= {
9657 static IPv4RouteTest test7
= {
9668 static IPv4RouteTest test8
= {
9677 static IPv4RouteTest test9
= {
9687 static IPv4RouteTest test10
= {
9697 static IPv4RouteTest test11
= {
9707 static IPv4RouteTest test12
= {
9716 static IPv4RouteTest test13
= {
9725 static IPv4RouteTest test14
= {
9733 static IPv4RouteTest test15
= {
9741 static IPv4RouteTest test16
= {
9750 static IPv4RouteTest test17
= {
9754 &en1_20_other_never
,
9759 static IPv4RouteTest test18
= {
9767 static IPv4RouteTestRef ipv4_tests
[] = {
9790 ipv4_prefix_length_is_valid(int prefix_length
)
9792 if (prefix_length
< 0 || prefix_length
> IPV4_ROUTE_ALL_BITS_SET
) {
9799 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9802 CFStringRef prop_val
;
9807 prop_val
= CFStringCreateWithCString(NULL
,
9809 kCFStringEncodingASCII
);
9810 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9811 CFRelease(prop_val
);
9816 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9820 CFStringRef prop_val
;
9825 prop_val
= CFStringCreateWithCString(NULL
,
9827 kCFStringEncodingASCII
);
9828 array
= CFArrayCreate(NULL
,
9829 (const void **)&prop_val
, 1,
9830 &kCFTypeArrayCallBacks
);
9831 CFRelease(prop_val
);
9832 CFDictionarySetValue(dict
, prop_name
, array
);
9838 dict_add_ip(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9843 str
= my_CFStringCreateWithInAddr(ip
);
9844 CFDictionarySetValue(dict
, prop_name
, str
);
9850 dict_add_ip_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9856 str
= my_CFStringCreateWithInAddr(ip
);
9857 array
= CFArrayCreate(NULL
,
9858 (const void **)&str
, 1,
9859 &kCFTypeArrayCallBacks
);
9861 CFDictionarySetValue(dict
, prop_name
, array
);
9867 dict_insert_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9868 struct route
* routes
, int routes_count
)
9871 CFMutableArrayRef route_list
;
9872 struct route
* scan
;
9874 if (routes
== NULL
|| routes_count
== 0) {
9877 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9878 &kCFTypeArrayCallBacks
);
9879 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9880 struct in_addr mask
;
9881 CFMutableDictionaryRef route_dict
;
9884 = CFDictionaryCreateMutable(NULL
, 0,
9885 &kCFTypeDictionaryKeyCallBacks
,
9886 &kCFTypeDictionaryValueCallBacks
);
9887 dict_add_string(route_dict
, kSCPropNetIPv4RouteDestinationAddress
,
9889 if (ipv4_prefix_length_is_valid(scan
->prefix_length
)) {
9890 mask
.s_addr
= htonl(prefix_to_mask32(scan
->prefix_length
));
9891 dict_add_ip(route_dict
, kSCPropNetIPv4RouteSubnetMask
, mask
);
9893 dict_add_string(route_dict
, kSCPropNetIPv4RouteGatewayAddress
,
9895 dict_add_string(route_dict
, kSCPropNetIPv4RouteInterfaceName
,
9897 CFArrayAppendValue(route_list
, route_dict
);
9898 CFRelease(route_dict
);
9900 CFDictionarySetValue(dict
, prop_name
, route_list
);
9901 CFRelease(route_list
);
9905 static CFDictionaryRef
9906 make_IPv4_dict(IPv4ServiceContentsRef t
)
9908 CFMutableDictionaryRef dict
;
9910 dict
= CFDictionaryCreateMutable(NULL
, 0,
9911 &kCFTypeDictionaryKeyCallBacks
,
9912 &kCFTypeDictionaryValueCallBacks
);
9913 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
9914 if (ipv4_prefix_length_is_valid(t
->prefix_length
)) {
9915 struct in_addr mask
;
9917 mask
.s_addr
= htonl(prefix_to_mask32(t
->prefix_length
));
9918 dict_add_ip_as_array(dict
, kSCPropNetIPv4SubnetMasks
, mask
);
9920 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
9921 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
9922 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9923 dict_add_string(dict
, kSCPropConfirmedInterfaceName
, t
->ifname
);
9924 dict_insert_routes(dict
, kSCPropNetIPv4AdditionalRoutes
,
9925 t
->additional_routes
, t
->additional_routes_count
);
9926 dict_insert_routes(dict
, kSCPropNetIPv4ExcludedRoutes
,
9927 t
->excluded_routes
, t
->excluded_routes_count
);
9932 kDirectionForwards
= 0,
9933 kDirectionBackwards
= 1
9937 kLogRouteDisabled
= 0,
9938 kLogRouteEnabled
= 1
9941 static IPv4RouteListRef
9942 make_IPv4RouteList_for_test(IPv4RouteListRef list
,
9943 IPv4ServiceContentsRef test
,
9946 CFDictionaryRef dict
;
9949 Rank rank_assertion
= kRankAssertionDefault
;
9950 CFNumberRef rank_assertion_cf
= NULL
;
9951 Boolean rank_assertion_is_set
= FALSE
;
9952 IPv4RouteListRef ret
= NULL
;
9953 IPV4_ROUTES_BUF_DECL(routes
);
9955 dict
= make_IPv4_dict(test
);
9957 fprintf(stderr
, "make_IPv4_dict failed\n");
9960 if (test
->primary_rank
!= NULL
) {
9962 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9963 &rank_assertion_is_set
);
9964 if (rank_assertion_is_set
) {
9966 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9969 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion_cf
);
9970 my_CFRelease(&rank_assertion_cf
);
9972 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
9976 if (rank_assertion
== kRankAssertionScoped
) {
9977 rank_assertion
= kRankAssertionNever
;
9979 rank
= RankMake(test
->rank
, rank_assertion
);
9980 if (log_it
== kLogRouteEnabled
9981 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9984 descr
= IPv4RouteListCopyDescription(r
);
9985 SCPrint(TRUE
, stdout
, CFSTR("Adding %@\n"), descr
);
9988 ret
= IPv4RouteListAddRouteList(list
, 1, r
, rank
);
9996 static IPv4RouteListRef
9997 make_IPv4RouteList(IPv4ServiceContentsRef
* test
, Direction direction
,
10000 IPv4RouteListRef ret
= NULL
;
10001 IPv4ServiceContentsRef
* scan
;
10003 switch (direction
) {
10004 case kDirectionBackwards
:
10005 for (scan
= test
; *scan
!= NULL
; scan
++) {
10006 /* find the end of the list */
10008 for (scan
--; scan
>= test
; scan
--) {
10009 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
10013 case kDirectionForwards
:
10014 for (scan
= test
; *scan
!= NULL
; scan
++) {
10015 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
10019 IPv4RouteListFinalize(ret
);
10023 #define EMPHASIS_CHARS "================="
10026 * Function: routelist_build_test
10028 * Runs through the given set of routes first in the forward direction,
10029 * then again backwards. We should end up with exactly the same set of
10030 * routes at the end.
10033 routelist_build_test(IPv4RouteTestRef test
)
10036 boolean_t ret
= FALSE
;
10037 IPv4RouteListRef routes1
;
10038 IPv4RouteListRef routes2
;
10040 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
10041 EMPHASIS_CHARS
"\n",
10044 routes1
= make_IPv4RouteList(test
->test
, kDirectionForwards
,
10046 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10047 if (routes1
!= NULL
) {
10048 descr
= IPv4RouteListCopyDescription(routes1
);
10049 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10053 routes2
= make_IPv4RouteList(test
->test
, kDirectionBackwards
,
10055 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10056 if (routes2
!= NULL
) {
10057 descr
= IPv4RouteListCopyDescription(routes2
);
10058 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10062 if ((routes1
!= NULL
&& routes2
== NULL
)
10063 || (routes1
== NULL
&& routes2
!= NULL
)) {
10064 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
10065 (routes1
!= NULL
) ? "not " : "",
10066 (routes2
!= NULL
) ? "not " : "");
10068 else if (routes1
!= NULL
&& routes2
!= NULL
) {
10069 /* check if they are different */
10070 if (routes1
->count
!= routes2
->count
) {
10071 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
10072 routes1
->count
, routes2
->count
);
10074 else if (bcmp(routes1
, routes2
,
10075 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
10076 fprintf(stderr
, "routes1 and routes2 are different\n");
10079 printf("routes1 and routes2 are the same\n");
10083 if (routes1
!= NULL
) {
10086 if (routes2
!= NULL
) {
10089 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
10090 EMPHASIS_CHARS
"\n",
10091 test
->name
, ret
? "PASSED" : "FAILED");
10096 apply_test(IPv4RouteTestRef old_test
, IPv4RouteTestRef new_test
)
10098 IPv4RouteListRef new_routes
;
10099 IPv4RouteListRef old_routes
;
10101 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
10102 EMPHASIS_CHARS
"\n",
10103 old_test
->name
, new_test
->name
);
10105 old_routes
= make_IPv4RouteList(old_test
->test
, kDirectionForwards
,
10106 kLogRouteDisabled
);
10107 new_routes
= make_IPv4RouteList(new_test
->test
, kDirectionForwards
,
10108 kLogRouteDisabled
);
10109 if (old_routes
== NULL
) {
10110 printf("No Old Routes\n");
10113 printf("Old routes ('%s') = ", old_test
->name
);
10114 IPv4RouteListPrint(old_routes
);
10117 /* apply the old routes */
10118 IPv4RouteListApply(NULL
, old_routes
, -1);
10120 if (new_routes
== NULL
) {
10121 printf("No New Routes\n");
10124 printf("New Routes ('%s') = ", new_test
->name
);
10125 IPv4RouteListPrint(new_routes
);
10128 /* apply the new routes */
10129 IPv4RouteListApply(old_routes
, new_routes
, -1);
10131 if (old_routes
!= NULL
) {
10134 if (new_routes
!= NULL
) {
10137 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10138 EMPHASIS_CHARS
"\n",
10139 old_test
->name
, new_test
->name
);
10144 main(int argc
, char **argv
)
10146 IPv4RouteTestRef
* test
;
10148 _sc_log
= kSCLogDestinationFile
;
10149 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10150 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10152 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10154 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
10155 if (!routelist_build_test(*test
)) {
10156 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10160 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
10161 IPv4RouteTestRef
* test2
;
10163 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10164 apply_test(*test
, *test2
);
10165 apply_test(*test2
, *test
);
10173 #endif /* TEST_IPV4_ROUTELIST */
10175 #if TEST_IPV6_ROUTELIST
10183 typedef const IPv6Address
* IPv6AddressRef
;
10186 IPv6AddressRef addr
;
10188 const char * router
;
10189 const char * ifname
;
10191 const CFStringRef
* primary_rank
;
10192 struct route
* additional_routes
;
10193 int additional_routes_count
;
10194 struct route
* excluded_routes
;
10195 int excluded_routes_count
;
10196 } IPv6ServiceContents
;
10198 typedef const IPv6ServiceContents
* IPv6ServiceContentsRef
;
10200 struct route loop_routelist
[] = {
10201 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
10202 "2620:149:4:f01:225:ff:fecc:89a2", NULL
},
10203 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
10204 "2620:149:4:f01:225:ff:fecc:89a3", NULL
},
10205 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
10206 "2620:149:4:f01:225:ff:fecc:89a4", NULL
},
10207 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
10208 "2620:149:4:f01:225:ff:fecc:89a5", NULL
},
10209 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
10210 "2620:149:4:f01:225:ff:fecc:89a6", NULL
},
10211 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
10212 "2620:149:4:f01:225:ff:fecc:89a7", NULL
},
10213 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
10214 "2620:149:4:f01:225:ff:fecc:89a8", NULL
},
10215 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
10216 "2620:149:4:f01:225:ff:fecc:89a9", NULL
},
10217 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
10218 "2620:149:4:f01:225:ff:fecc:89aa", NULL
},
10219 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
10220 "2620:149:4:f01:225:ff:fecc:89ab", NULL
},
10221 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
10222 "2620:149:4:f01:225:ff:fecc:89a1", NULL
},
10225 struct route vpn_routelist
[] = {
10226 { "2010:470:1f05:3cb::", 64,
10227 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10228 { "2010:222:3fa5:acb::", 48,
10229 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10230 { "2010:222:3fa5:1234::", 40,
10231 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10232 { "2010:222:3fa5:5678::", 40,
10236 struct route vpn_routelist_ext
[] = {
10237 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL
, NULL
},
10240 struct route en1_routelist_ext
[] = {
10241 { "2020:299:abcd:ef12::", 64, NULL
, NULL
},
10245 static const IPv6Address en0_addr1
[] = {
10246 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL
},
10247 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL
}
10250 static const IPv6Address en0_addr2
[] = {
10251 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL
},
10252 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL
}
10255 static const IPv6Address en0_addr3
[] = {
10256 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL
},
10257 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL
}
10260 static const IPv6Address en0_addr4
[] = {
10261 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL
},
10262 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL
}
10265 static const IPv6Address en0_addr5
[] = {
10266 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL
},
10267 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL
}
10270 static const IPv6Address en0_addr6
[] = {
10271 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL
},
10274 static const IPv6Address en0_lladdr
[] = {
10275 { "fe80::cabc:c8ff:fe96:96af", 64, NULL
}
10278 static const IPv6Address en1_addr
[] = {
10279 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL
},
10280 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL
}
10283 static const IPv6Address utun0_addr
[] = {
10284 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL
},
10287 static const IPv6Address fw0_addr1
[] = {
10288 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL
},
10289 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL
}
10293 * address+address-count
10294 * router ifname pri rank additional-routes+count excluded-routes+count
10297 static const IPv6ServiceContents en0_10
= {
10298 en0_addr1
, countof(en0_addr1
),
10299 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
10302 static const IPv6ServiceContents en0_15
= {
10303 en0_addr2
, countof(en0_addr2
),
10304 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL
, NULL
, 0, NULL
, 0
10307 static const IPv6ServiceContents en0_30
= {
10308 en0_addr3
, countof(en0_addr3
),
10309 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL
, NULL
, 0, NULL
, 0
10312 static const IPv6ServiceContents en0_40
= {
10313 en0_addr4
, countof(en0_addr4
),
10314 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL
, NULL
, 0, NULL
, 0
10317 static const IPv6ServiceContents en0_50
= {
10318 en0_addr5
, countof(en0_addr5
),
10319 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL
, NULL
, 0, NULL
, 0
10322 static const IPv6ServiceContents en0_10_a
= {
10323 en0_addr6
, countof(en0_addr6
),
10324 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
10327 static const IPv6ServiceContents fw0_25
= {
10328 fw0_addr1
, countof(fw0_addr1
),
10329 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
10332 static const IPv6ServiceContents en1_20
= {
10333 en1_addr
, countof(en1_addr
),
10334 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL
, NULL
, 0, NULL
, 0
10337 static const IPv6ServiceContents en1_10_ext
= {
10338 en1_addr
, countof(en1_addr
),
10339 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL
, NULL
, 0,
10340 en1_routelist_ext
, countof(en1_routelist_ext
)
10343 static const IPv6ServiceContents en0_0_lladdr
= {
10344 en0_lladdr
, countof(en0_lladdr
),
10345 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL
, NULL
, 0, NULL
, 0
10348 static const IPv6ServiceContents en0_loop
= {
10349 en0_addr1
, countof(en0_addr1
),
10350 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
,
10351 loop_routelist
, countof(loop_routelist
), NULL
, 0
10354 static const IPv6ServiceContents utun0
= {
10355 utun0_addr
, countof(utun0_addr
),
10356 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL
,
10357 vpn_routelist
, countof(vpn_routelist
),
10358 vpn_routelist_ext
, countof(vpn_routelist_ext
),
10363 IPv6ServiceContentsRef test
[];
10364 } IPv6RouteTest
, * IPv6RouteTestRef
;
10366 static IPv6RouteTest test1
= {
10380 static IPv6RouteTest test2
= {
10393 static IPv6RouteTest test3
= {
10402 static IPv6RouteTest test4
= {
10411 static IPv6RouteTest test5
= {
10423 static IPv6RouteTestRef ipv6_tests
[] = {
10434 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10437 CFStringRef prop_val
;
10442 prop_val
= CFStringCreateWithCString(NULL
,
10444 kCFStringEncodingASCII
);
10445 CFDictionarySetValue(dict
, prop_name
, prop_val
);
10446 CFRelease(prop_val
);
10451 dict_add_int(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10456 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10457 CFDictionarySetValue(dict
, prop_name
, num
);
10463 dict_insert_v6_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10464 struct route
* routes
, int routes_count
)
10467 CFMutableArrayRef route_list
;
10468 struct route
* scan
;
10470 if (routes
== NULL
|| routes_count
== 0) {
10473 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
10474 &kCFTypeArrayCallBacks
);
10475 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
10476 CFMutableDictionaryRef route_dict
;
10478 route_dict
= CFDictionaryCreateMutable(NULL
, 0,
10479 &kCFTypeDictionaryKeyCallBacks
,
10480 &kCFTypeDictionaryValueCallBacks
);
10481 dict_add_string(route_dict
, kSCPropNetIPv6RouteDestinationAddress
,
10483 dict_add_int(route_dict
, kSCPropNetIPv6PrefixLength
,
10484 scan
->prefix_length
);
10485 dict_add_string(route_dict
, kSCPropNetIPv6RouteGatewayAddress
,
10487 dict_add_string(route_dict
, kSCPropNetIPv6RouteInterfaceName
,
10489 CFArrayAppendValue(route_list
, route_dict
);
10490 CFRelease(route_dict
);
10492 CFDictionarySetValue(dict
, prop_name
, route_list
);
10493 CFRelease(route_list
);
10498 array_add_string(CFMutableArrayRef array
, const char * c_str
)
10502 str
= CFStringCreateWithCString(NULL
,
10504 kCFStringEncodingUTF8
);
10505 CFArrayAppendValue(array
, str
);
10511 array_add_int(CFMutableArrayRef array
, int int_val
)
10515 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10516 CFArrayAppendValue(array
, num
);
10522 dict_add_ipv6_addressing(CFMutableDictionaryRef dict
,
10523 IPv6AddressRef list
, int list_count
)
10525 CFMutableArrayRef addr
= NULL
;
10526 CFMutableArrayRef dest
= NULL
;
10528 CFMutableArrayRef prefix
= NULL
;
10529 IPv6AddressRef scan
;
10531 if (list
== NULL
|| list_count
== 0) {
10534 for (i
= 0, scan
= list
; i
< list_count
; i
++, scan
++) {
10535 if (scan
->addr
!= NULL
) {
10536 if (addr
== NULL
) {
10537 addr
= CFArrayCreateMutable(NULL
, list_count
,
10538 &kCFTypeArrayCallBacks
);
10540 array_add_string(addr
, scan
->addr
);
10542 if (scan
->prefix_length
>= 0) {
10543 if (prefix
== NULL
) {
10544 prefix
= CFArrayCreateMutable(NULL
, list_count
,
10545 &kCFTypeArrayCallBacks
);
10547 array_add_int(prefix
, scan
->prefix_length
);
10549 if (scan
->dest
!= NULL
) {
10550 if (dest
== NULL
) {
10551 dest
= CFArrayCreateMutable(NULL
, list_count
,
10552 &kCFTypeArrayCallBacks
);
10554 array_add_string(dest
, scan
->dest
);
10557 if (addr
!= NULL
) {
10558 CFDictionarySetValue(dict
, kSCPropNetIPv6Addresses
, addr
);
10561 if (dest
!= NULL
) {
10562 CFDictionarySetValue(dict
, kSCPropNetIPv6DestAddresses
, dest
);
10565 if (prefix
!= NULL
) {
10566 CFDictionarySetValue(dict
, kSCPropNetIPv6PrefixLength
, prefix
);
10572 static CFDictionaryRef
10573 make_IPv6_dict(IPv6ServiceContentsRef t
)
10575 CFMutableDictionaryRef dict
;
10577 dict
= CFDictionaryCreateMutable(NULL
, 0,
10578 &kCFTypeDictionaryKeyCallBacks
,
10579 &kCFTypeDictionaryValueCallBacks
);
10580 dict_add_ipv6_addressing(dict
, t
->addr
, t
->addr_count
);
10581 dict_add_string(dict
, kSCPropNetIPv6Router
, t
->router
);
10582 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
10583 dict_insert_v6_routes(dict
, kSCPropNetIPv6AdditionalRoutes
,
10584 t
->additional_routes
, t
->additional_routes_count
);
10585 dict_insert_v6_routes(dict
, kSCPropNetIPv6ExcludedRoutes
,
10586 t
->excluded_routes
, t
->excluded_routes_count
);
10591 kDirectionForwards
= 0,
10592 kDirectionBackwards
= 1
10596 kLogRouteDisabled
= 0,
10597 kLogRouteEnabled
= 1
10600 static IPv6RouteListRef
10601 make_IPv6RouteList_for_test(IPv6RouteListRef list
,
10602 IPv6ServiceContentsRef test
,
10605 CFDictionaryRef dict
;
10606 IPv6RouteListRef r
;
10608 Rank rank_assertion
= kRankAssertionDefault
;
10609 CFNumberRef rank_assertion_cf
= NULL
;
10610 Boolean rank_assertion_is_set
= FALSE
;
10611 IPv6RouteListRef ret
= NULL
;
10612 IPV6_ROUTES_BUF_DECL(routes
);
10614 dict
= make_IPv6_dict(test
);
10615 if (dict
== NULL
) {
10616 fprintf(stderr
, "make_IPv6_dict failed\n");
10619 if (test
->primary_rank
!= NULL
) {
10621 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
10622 &rank_assertion_is_set
);
10623 if (rank_assertion_is_set
) {
10625 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
10628 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion_cf
);
10629 my_CFRelease(&rank_assertion_cf
);
10631 fprintf(stderr
, "IPv6RouteListCreateWithDictionary failed\n");
10635 if (rank_assertion
== kRankAssertionScoped
) {
10636 rank_assertion
= kRankAssertionNever
;
10638 rank
= RankMake(test
->rank
, rank_assertion
);
10639 if (log_it
== kLogRouteEnabled
10640 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10643 descr
= IPv6RouteListCopyDescription(r
);
10644 SCPrint(TRUE
, stdout
, CFSTR("Adding %@\n"), descr
);
10647 ret
= IPv6RouteListAddRouteList(list
, 1, r
, rank
);
10655 static IPv6RouteListRef
10656 make_IPv6RouteList(IPv6ServiceContentsRef
* test
, Direction direction
,
10659 IPv6RouteListRef ret
= NULL
;
10660 IPv6ServiceContentsRef
* scan
;
10662 switch (direction
) {
10663 case kDirectionBackwards
:
10664 for (scan
= test
; *scan
!= NULL
; scan
++) {
10665 /* find the end of the list */
10667 for (scan
--; scan
>= test
; scan
--) {
10668 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10672 case kDirectionForwards
:
10673 for (scan
= test
; *scan
!= NULL
; scan
++) {
10674 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10678 IPv6RouteListFinalize(ret
);
10682 #define EMPHASIS_CHARS "================="
10685 * Function: routelist_build_test
10687 * Runs through the given set of routes first in the forward direction,
10688 * then again backwards. We should end up with exactly the same set of
10689 * routes at the end.
10692 routelist_build_test(IPv6RouteTestRef test
)
10695 boolean_t ret
= FALSE
;
10696 IPv6RouteListRef routes1
;
10697 IPv6RouteListRef routes2
;
10699 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
10700 EMPHASIS_CHARS
"\n",
10703 routes1
= make_IPv6RouteList(test
->test
, kDirectionForwards
,
10705 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10706 if (routes1
!= NULL
) {
10707 descr
= IPv6RouteListCopyDescription(routes1
);
10708 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10712 routes2
= make_IPv6RouteList(test
->test
, kDirectionBackwards
,
10714 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10715 if (routes2
!= NULL
) {
10716 descr
= IPv6RouteListCopyDescription(routes2
);
10717 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10721 if ((routes1
!= NULL
&& routes2
== NULL
)
10722 || (routes1
== NULL
&& routes2
!= NULL
)) {
10723 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
10724 (routes1
!= NULL
) ? "not " : "",
10725 (routes2
!= NULL
) ? "not " : "");
10727 else if (routes1
!= NULL
&& routes2
!= NULL
) {
10728 /* check if they are different */
10729 if (routes1
->count
!= routes2
->count
) {
10730 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
10731 routes1
->count
, routes2
->count
);
10733 else if (bcmp(routes1
, routes2
,
10734 IPv6RouteListComputeSize(routes1
->count
)) != 0) {
10735 fprintf(stderr
, "routes1 and routes2 are different\n");
10738 printf("routes1 and routes2 are the same\n");
10742 if (routes1
!= NULL
) {
10745 if (routes2
!= NULL
) {
10748 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
10749 EMPHASIS_CHARS
"\n",
10750 test
->name
, ret
? "PASSED" : "FAILED");
10755 apply_test(IPv6RouteTestRef old_test
, IPv6RouteTestRef new_test
)
10757 IPv6RouteListRef new_routes
;
10758 IPv6RouteListRef old_routes
;
10760 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
10761 EMPHASIS_CHARS
"\n",
10762 old_test
->name
, new_test
->name
);
10764 old_routes
= make_IPv6RouteList(old_test
->test
, kDirectionForwards
,
10765 kLogRouteDisabled
);
10766 new_routes
= make_IPv6RouteList(new_test
->test
, kDirectionForwards
,
10767 kLogRouteDisabled
);
10768 if (old_routes
== NULL
) {
10769 printf("No Old Routes\n");
10772 printf("Old routes ('%s') = ", old_test
->name
);
10773 IPv6RouteListPrint(old_routes
);
10776 /* apply the old routes */
10777 IPv6RouteListApply(NULL
, old_routes
, -1);
10778 if (new_routes
== NULL
) {
10779 printf("No New Routes\n");
10782 printf("New Routes ('%s') = ", new_test
->name
);
10783 IPv6RouteListPrint(new_routes
);
10786 /* apply the new routes */
10787 IPv6RouteListApply(old_routes
, new_routes
, -1);
10788 if (old_routes
!= NULL
) {
10791 if (new_routes
!= NULL
) {
10794 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10795 EMPHASIS_CHARS
"\n",
10796 old_test
->name
, new_test
->name
);
10801 main(int argc
, char **argv
)
10803 IPv6RouteTestRef
* test
;
10805 _sc_log
= kSCLogDestinationFile
;
10806 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10807 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10809 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10811 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10812 if (!routelist_build_test(*test
)) {
10813 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10817 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10818 IPv6RouteTestRef
* test2
;
10820 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10821 apply_test(*test
, *test2
);
10822 apply_test(*test2
, *test
);
10830 #endif /* TEST_IPV6_ROUTELIST */
10832 #ifdef TEST_DNS_ORDER
10834 #define kProtocolFlagsIPv4v6 (kProtocolFlagsIPv4 | kProtocolFlagsIPv6)
10836 #define V4_ADDR_LOOP CFSTR("127.0.0.1")
10837 #define V4_ADDR_1 CFSTR("192.168.1.1")
10838 #define V4_ADDR_2 CFSTR("192.168.1.2")
10839 #define V4_ADDR_3 CFSTR("8.8.8.8")
10840 #define V4_ADDR_4 CFSTR("8.8.4.4")
10842 #define V6_ADDR_LOOP CFSTR("::1")
10843 #define V6_ADDR_1 CFSTR("fe80::0123:4567:89ab:cdef%en0")
10844 #define V6_ADDR_2 CFSTR("fd00::2acf:e9ff:fe14:8c59")
10845 #define V6_ADDR_3 CFSTR("2001:4860:4860::8888")
10849 const ProtocolFlags flags
;
10850 const CFStringRef server_addrs
[];
10851 } DNSOrderTest
, * DNSOrderTestRef
;
10853 static DNSOrderTest test0a
= {
10855 kProtocolFlagsIPv4
,
10857 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, NULL
10861 static DNSOrderTest test0b
= {
10863 kProtocolFlagsIPv6
,
10865 V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10869 static DNSOrderTest test1a
= {
10871 kProtocolFlagsIPv4v6
,
10873 V4_ADDR_1
, V6_ADDR_1
, NULL
10877 static DNSOrderTest test2a
= {
10879 kProtocolFlagsIPv4v6
,
10881 V4_ADDR_1
, V6_ADDR_2
, NULL
10885 static DNSOrderTest test3a
= {
10887 kProtocolFlagsIPv4v6
,
10889 V4_ADDR_1
, V6_ADDR_3
, NULL
10893 static DNSOrderTest test1b
= {
10895 kProtocolFlagsIPv4v6
,
10897 V4_ADDR_3
, V6_ADDR_1
, NULL
10901 static DNSOrderTest test2b
= {
10903 kProtocolFlagsIPv4v6
,
10905 V4_ADDR_3
, V6_ADDR_2
, NULL
10909 static DNSOrderTest test3b
= {
10911 kProtocolFlagsIPv4v6
,
10913 V4_ADDR_3
, V6_ADDR_3
, NULL
10917 static DNSOrderTest test1c
= {
10919 kProtocolFlagsIPv4v6
,
10921 V6_ADDR_1
, V4_ADDR_1
, NULL
10925 static DNSOrderTest test2c
= {
10927 kProtocolFlagsIPv4v6
,
10929 V6_ADDR_2
, V4_ADDR_1
, NULL
10933 static DNSOrderTest test3c
= {
10935 kProtocolFlagsIPv4v6
,
10937 V6_ADDR_3
, V4_ADDR_1
, NULL
10941 static DNSOrderTest test1d
= {
10943 kProtocolFlagsIPv4v6
,
10945 V6_ADDR_1
, V4_ADDR_3
, NULL
10949 static DNSOrderTest test2d
= {
10951 kProtocolFlagsIPv4v6
,
10953 V6_ADDR_2
, V4_ADDR_3
, NULL
10957 static DNSOrderTest test3d
= {
10959 kProtocolFlagsIPv4v6
,
10961 V6_ADDR_3
, V4_ADDR_3
, NULL
10965 static DNSOrderTest test4
= {
10967 kProtocolFlagsIPv4v6
,
10969 V4_ADDR_LOOP
, V4_ADDR_3
, V6_ADDR_2
, NULL
10973 static DNSOrderTest test5
= {
10975 kProtocolFlagsIPv4v6
,
10977 V4_ADDR_3
, V6_ADDR_LOOP
, V6_ADDR_2
, NULL
10981 static DNSOrderTest test6
= {
10983 kProtocolFlagsIPv4v6
,
10985 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10989 static DNSOrderTest test7
= {
10991 kProtocolFlagsIPv4v6
,
10993 V4_ADDR_1
, V6_ADDR_1
, V4_ADDR_3
, V6_ADDR_2
, NULL
10997 static DNSOrderTestRef dns_order_tests
[] = {
10999 &test1a
, &test2a
, &test3a
,
11000 &test1b
, &test2b
, &test3b
,
11001 &test1c
, &test2c
, &test3c
,
11002 &test1d
, &test2d
, &test3d
,
11010 #define EMPHASIS_CHARS "================="
11013 apply_order(CFArrayRef servers
, ProtocolFlags flags
)
11015 CFArrayRef ordered_servers
;
11017 ordered_servers
= order_dns_servers(servers
, flags
);
11018 if (ordered_servers
!= NULL
) {
11019 SCPrint(TRUE
, stdout
, CFSTR("After :\n%@\n"), ordered_servers
);
11020 CFRelease(ordered_servers
);
11022 printf("FAIL: No ordered servers\n");
11029 apply_test(DNSOrderTestRef test
)
11031 CFMutableArrayRef servers
;
11033 printf("\n" EMPHASIS_CHARS
"> '%s' Begin <" EMPHASIS_CHARS
"\n\n", test
->name
);
11035 servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
11036 for (int i
= 0; test
->server_addrs
[i
] != NULL
; i
++) {
11037 CFStringRef server_addr
= test
->server_addrs
[i
];
11039 CFArrayAppendValue(servers
, server_addr
);
11042 SCPrint(TRUE
, stdout
, CFSTR("Before :\n%@\n"), servers
);
11044 apply_order(servers
, test
->flags
);
11046 CFRelease(servers
);
11052 main(int argc
, char **argv
)
11055 _sc_log
= kSCLogDestinationFile
;
11056 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
11057 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
11059 S_IPMonitor_debug
= (uint32
)strtoul(argv
[1], NULL
, 0);
11062 for (DNSOrderTestRef
* test
= dns_order_tests
; *test
!= NULL
; test
++) {
11070 #endif /* TEST_DNS_ORDER */