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
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
5177 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
5178 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
5179 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
5180 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
5181 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
5182 { kSCPropNetProxiesProxyAutoConfigEnable
,
5183 kSCPropNetProxiesProxyAutoConfigURLString
,
5184 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
5185 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5190 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
5191 /* there is no proxy content */
5194 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
5195 if (ipdict_is_routable(ipv4
)) {
5196 active_protos
|= kProtocolFlagsIPv4
;
5197 interface
= ipdict_get_ifname(ipv4
);
5199 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
5200 if (ipdict_is_routable(ipv6
)) {
5201 active_protos
|= kProtocolFlagsIPv6
;
5202 if (interface
== NULL
) {
5203 interface
= ipdict_get_ifname(ipv6
);
5206 if (active_protos
== kProtocolFlagsNone
) {
5207 /* there is no IPv4 nor IPv6 */
5208 if (state_dict
== NULL
) {
5209 /* ... and no proxy content that we care about */
5215 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
5216 CFMutableDictionaryRef setup_copy
;
5219 * Merge the per-service "Setup:" and "State:" proxy information with
5220 * the "Setup:" information always taking precedence. Additionally,
5221 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
5222 * Port) is defined than all of the values for that group will be
5223 * used. That is, we don't allow mixing some of the values from
5224 * the "Setup:" keys and others from the "State:" keys.
5226 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5227 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
5228 merge_array_prop(new_dict
,
5232 merge_list
[i
].flags
,
5233 merge_list
[i
].append
);
5236 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5237 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5238 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
5240 * if a "Setup:" enabled key has been provided than we want to
5241 * ignore all of the "State:" keys
5243 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
5244 if (pick_list
[i
].key2
!= NULL
) {
5245 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
5247 if (pick_list
[i
].key3
!= NULL
) {
5248 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
5250 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
5251 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
5252 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
5254 * if a "Setup:" enabled key has not been provided and we have
5255 * some" "State:" keys than we remove all of of "Setup:" keys
5257 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
5258 if (pick_list
[i
].key2
!= NULL
) {
5259 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
5261 if (pick_list
[i
].key3
!= NULL
) {
5262 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
5267 /* merge the "Setup:" keys */
5268 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
5269 CFRelease(setup_copy
);
5271 else if (setup_dict
!= NULL
) {
5272 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5274 else if (state_dict
!= NULL
) {
5275 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5278 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
5279 CFRelease(new_dict
);
5283 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
5284 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5288 if (new_dict
!= NULL
) {
5289 CFDictionaryRef dhcp_options
;
5291 CFNumberRef wpad
= NULL
;
5292 int wpadEnabled
= 0;
5293 CFStringRef wpadURL
= NULL
;
5295 if (CFDictionaryGetValueIfPresent(new_dict
,
5296 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5297 (const void **)&num
) &&
5298 isA_CFNumber(num
)) {
5299 /* if we have a WPAD key */
5301 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
5302 /* if we don't like the enabled key/value */
5310 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
5311 if (!isA_CFNumber(num
) ||
5312 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
5313 /* if we don't like the enabled key/value */
5320 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
5321 if (pacURL
!= NULL
) {
5322 if (!isA_CFString(pacURL
) || (CFStringGetLength(pacURL
) == 0)) {
5323 /* if we don't like the PAC URL */
5329 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
5330 if (!isA_CFString(pacJS
) || (CFStringGetLength(pacJS
) == 0)) {
5331 /* if we don't have (or like) the PAC JavaScript */
5339 * we already have a PAC URL so disable WPAD.
5346 * if WPAD is enabled and we don't already have a PAC URL then
5347 * we check for a DHCP provided URL. If not available, we use
5348 * a PAC URL pointing to a well-known file (wpad.dat) on a
5349 * well-known host (wpad.<domain>).
5351 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
5352 wpadURL
= wpadURL_dhcp(dhcp_options
);
5353 if (wpadURL
== NULL
) {
5354 wpadURL
= wpadURL_dns();
5356 if (wpadURL
== NULL
) {
5357 wpadEnabled
= 0; /* if we don't have a WPAD URL */
5362 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
5363 CFDictionarySetValue(new_dict
,
5364 kSCPropNetProxiesProxyAutoConfigEnable
,
5367 CFDictionarySetValue(new_dict
,
5368 kSCPropNetProxiesProxyAutoConfigURLString
,
5375 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
5376 CFDictionarySetValue(new_dict
,
5377 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5384 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
5385 my_CFRelease(&new_dict
);
5389 #if !TARGET_OS_IPHONE
5391 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5392 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5394 #pragma unused(info)
5395 boolean_t changed
= FALSE
;
5396 CFMutableDictionaryRef new_dict
= NULL
;
5397 const CFStringRef pick_list
[] = {
5398 kSCPropNetSMBNetBIOSName
,
5399 kSCPropNetSMBNetBIOSNodeType
,
5400 #ifdef ADD_NETBIOS_SCOPE
5401 kSCPropNetSMBNetBIOSScope
,
5402 #endif // ADD_NETBIOS_SCOPE
5403 kSCPropNetSMBWorkgroup
,
5406 if (state_dict
== NULL
&& setup_dict
== NULL
) {
5407 /* there is no SMB */
5410 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
5411 && service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
) {
5412 /* there is no IPv4 or IPv6 */
5416 /* merge SMB configuration */
5417 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5418 &kCFTypeDictionaryKeyCallBacks
,
5419 &kCFTypeDictionaryValueCallBacks
);
5420 merge_array_prop(new_dict
,
5421 kSCPropNetSMBWINSAddresses
,
5426 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5434 if (CFDictionaryGetCount(new_dict
) == 0) {
5435 my_CFRelease(&new_dict
);
5440 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
5441 my_CFRelease(&new_dict
);
5444 #endif /* !TARGET_OS_IPHONE */
5447 services_info_get_interface(CFDictionaryRef services_info
,
5448 CFStringRef serviceID
)
5450 CFStringRef interface
= NULL
;
5451 CFDictionaryRef ipv4_dict
;
5453 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
5455 if (ipv4_dict
!= NULL
) {
5456 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
5459 CFDictionaryRef ipv6_dict
;
5461 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
5463 if (ipv6_dict
!= NULL
) {
5464 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5471 static const struct {
5472 const CFStringRef
* entityName
;
5473 const CFStringRef
* statusKey
;
5474 } transientServiceInfo
[] = {
5475 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
5476 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
5477 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
5481 get_transient_status_changes(CFStringRef serviceID
,
5482 CFDictionaryRef services_info
)
5484 boolean_t changed
= FALSE
;
5486 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5487 CFDictionaryRef dict
;
5488 CFNumberRef status
= NULL
;
5489 CFMutableDictionaryRef ts_dict
= NULL
;
5491 dict
= get_service_state_entity(services_info
, serviceID
,
5492 *transientServiceInfo
[i
].entityName
);
5495 status
= CFDictionaryGetValue(dict
,
5496 *transientServiceInfo
[i
].statusKey
);
5499 if (isA_CFNumber(status
) != NULL
) {
5500 ts_dict
= CFDictionaryCreateMutable(NULL
,
5502 &kCFTypeDictionaryKeyCallBacks
,
5503 &kCFTypeDictionaryValueCallBacks
);
5504 CFDictionaryAddValue(ts_dict
,
5505 *transientServiceInfo
[i
].statusKey
,
5509 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
,
5514 if (ts_dict
!= NULL
) {
5522 if_dict_is_expensive(CFDictionaryRef if_dict
)
5524 boolean_t is_expensive
= FALSE
;
5526 if (isA_CFDictionary(if_dict
) != NULL
) {
5527 CFBooleanRef expensive
;
5528 expensive
= CFDictionaryGetValue(if_dict
, kSCPropNetLinkExpensive
);
5529 if (isA_CFBoolean(expensive
) != NULL
5530 && CFBooleanGetValue(expensive
)) {
5531 is_expensive
= TRUE
;
5534 return is_expensive
;
5538 service_is_expensive(CFStringRef serviceID
, CFDictionaryRef services_info
)
5541 boolean_t is_expensive
= FALSE
;
5543 ifname
= services_info_get_interface(services_info
, serviceID
);
5544 if (ifname
!= NULL
) {
5545 CFDictionaryRef if_dict
;
5548 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5549 if_dict
= CFDictionaryGetValue(services_info
, key
);
5551 is_expensive
= if_dict_is_expensive(if_dict
);
5553 return (is_expensive
);
5557 interface_is_expensive(CFStringRef ifname
)
5559 boolean_t is_expensive
= FALSE
;
5561 if (ifname
!= NULL
) {
5562 CFDictionaryRef if_dict
;
5565 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5566 if_dict
= SCDynamicStoreCopyValue(S_session
, key
);
5568 if (if_dict
!= NULL
) {
5569 is_expensive
= if_dict_is_expensive(if_dict
);
5573 return (is_expensive
);
5577 service_rank_entity_get_index(CFDictionaryRef dict
, CFStringRef serviceID
,
5578 CFStringRef which
, uint32_t * ret_val
)
5580 CFNumberRef service_index
= NULL
;
5583 service_index
= CFDictionaryGetValue(dict
,
5584 kSCPropNetServiceServiceIndex
);
5585 service_index
= isA_CFNumber(service_index
);
5587 if (service_index
!= NULL
) {
5590 if (!CFNumberGetValue(service_index
, kCFNumberSInt32Type
,
5592 || index_val
<= 0) {
5593 /* ServiceIndex must be >= 1 */
5595 "%@%@ ServiceIndex %@ is invalid, ignoring",
5596 which
, serviceID
, service_index
);
5597 service_index
= NULL
;
5599 else if (ret_val
!= NULL
) {
5600 *ret_val
= (uint32_t)index_val
;
5603 return (service_index
);
5607 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
5608 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
5610 boolean_t changed
= FALSE
;
5611 CFStringRef interface
;
5612 boolean_t ip_is_coupled
= FALSE
;
5613 CFMutableDictionaryRef new_dict
= NULL
;
5614 Rank rank_assertion
= kRankAssertionDefault
;
5615 Boolean rank_assertion_is_set
= FALSE
;
5616 CFStringRef setup_rank
= NULL
;
5617 CFStringRef state_rank
= NULL
;
5618 CFNumberRef service_index
= NULL
;
5619 boolean_t use_setup_rank
= TRUE
;
5622 if (setup_options
!= NULL
) {
5623 CFBooleanRef coupled
;
5626 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
5627 setup_rank
= isA_CFString(setup_rank
);
5628 if (setup_rank
!= NULL
&& !use_setup_rank
) {
5629 my_log(LOG_DEBUG
, "%@ ignoring Setup PrimaryRank = %@",
5630 serviceID
, setup_rank
);
5633 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
5634 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5635 ip_is_coupled
= TRUE
;
5638 = service_rank_entity_get_index(setup_options
,
5640 kSCDynamicStoreDomainSetup
,
5643 if (state_options
!= NULL
) {
5644 CFBooleanRef coupled
;
5647 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
5648 state_rank
= isA_CFString(state_rank
);
5649 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
5650 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5651 ip_is_coupled
= TRUE
;
5653 if (service_index
== NULL
) {
5655 = service_rank_entity_get_index(state_options
,
5657 kSCDynamicStoreDomainState
,
5662 if (!ip_is_coupled
) {
5663 ip_is_coupled
= service_is_expensive(serviceID
, services_info
);
5665 if (setup_rank
!= NULL
|| state_rank
!= NULL
) {
5666 /* rank assertion is set on the service */
5667 Rank setup_assertion
;
5668 Boolean setup_assertion_is_set
= FALSE
;
5669 Rank state_assertion
;
5670 Boolean state_assertion_is_set
= FALSE
;
5672 setup_assertion
= PrimaryRankGetRankAssertion(setup_rank
,
5673 &setup_assertion_is_set
);
5674 state_assertion
= PrimaryRankGetRankAssertion(state_rank
,
5675 &state_assertion_is_set
);
5676 if (setup_assertion_is_set
&& state_assertion_is_set
) {
5677 if (setup_assertion
> state_assertion
) {
5678 rank_assertion
= setup_assertion
;
5681 rank_assertion
= state_assertion
;
5683 rank_assertion_is_set
= TRUE
;
5685 else if (setup_assertion_is_set
) {
5686 rank_assertion
= setup_assertion
;
5687 rank_assertion_is_set
= TRUE
;
5689 else if (state_assertion_is_set
) {
5690 rank_assertion
= state_assertion
;
5691 rank_assertion_is_set
= TRUE
;
5695 interface
= services_info_get_interface(services_info
, serviceID
);
5696 if (interface
!= NULL
) {
5697 if (!rank_assertion_is_set
) {
5698 /* check for a rank assertion on the interface */
5699 CFNumberRef if_rank
= NULL
;
5701 if (S_if_rank_dict
!= NULL
) {
5702 if_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
5705 = InterfaceRankGetRankAssertion(if_rank
,
5706 &rank_assertion_is_set
);
5707 #define kNotSetString ((CFTypeRef)CFSTR("not set"))
5709 "serviceID %@ interface %@ rank = 0x%x (%@)%s",
5713 (if_rank
!= NULL
) ? (CFTypeRef
)if_rank
: kNotSetString
,
5714 ip_is_coupled
? " [coupled]" : "");
5718 "serviceID %@ interface %@ rank = 0x%x%s",
5719 serviceID
, interface
, rank_assertion
,
5720 ip_is_coupled
? " [coupled]" : "");
5725 if (service_index
!= NULL
|| rank_assertion_is_set
|| ip_is_coupled
) {
5726 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5727 &kCFTypeDictionaryKeyCallBacks
,
5728 &kCFTypeDictionaryValueCallBacks
);
5729 if (rank_assertion_is_set
) {
5730 CFNumberRef new_rank
;
5732 new_rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
,
5733 (const void *)&rank_assertion
);
5734 CFDictionarySetValue(new_dict
, kServiceOptionRankAssertion
,
5736 CFRelease(new_rank
);
5738 if (ip_is_coupled
) {
5739 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
5741 if (service_index
!= NULL
) {
5742 CFDictionarySetValue(new_dict
, kSCPropNetServiceServiceIndex
,
5746 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
5747 my_CFRelease(&new_dict
);
5752 add_service_keys(CFStringRef serviceID
,
5753 CFMutableArrayRef keys
)
5758 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5759 CFStringRef name
= *entityTypeNames
[i
];
5761 key
= setup_service_key(serviceID
, name
);
5762 my_CFArrayAppendUniqueValue(keys
, key
);
5764 key
= state_service_key(serviceID
, name
);
5765 my_CFArrayAppendUniqueValue(keys
, key
);
5769 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
5770 my_CFArrayAppendUniqueValue(keys
, key
);
5773 key
= setup_service_key(serviceID
, NULL
);
5774 my_CFArrayAppendUniqueValue(keys
, key
);
5776 key
= state_service_key(serviceID
, NULL
);
5777 my_CFArrayAppendUniqueValue(keys
, key
);
5784 add_transient_status_keys(CFStringRef serviceID
,
5785 CFMutableArrayRef keys
)
5787 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5790 key
= state_service_key(serviceID
,
5791 *transientServiceInfo
[i
].entityName
);
5792 my_CFArrayAppendUniqueValue(keys
, key
);
5799 static const CFStringRef
*reachabilitySetupKeys
[] = {
5801 &kSCEntNetInterface
,
5808 add_reachability_patterns(CFMutableArrayRef patterns
)
5810 for (size_t i
= 0; i
< countof(reachabilitySetupKeys
); i
++) {
5811 CFStringRef pattern
;
5812 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
5813 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5820 add_vpn_pattern(CFMutableArrayRef patterns
)
5822 CFStringRef pattern
;
5824 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
5825 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5830 add_interface_link_pattern(CFMutableArrayRef patterns
)
5832 CFStringRef pattern
;
5834 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetLink
);
5835 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5839 static CFDictionaryRef
5840 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
5843 CFMutableArrayRef keys
;
5844 CFDictionaryRef info
;
5845 CFMutableArrayRef patterns
;
5847 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5848 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5850 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
5851 CFArrayAppendValue(keys
, S_multicast_resolvers
);
5852 CFArrayAppendValue(keys
, S_private_resolvers
);
5854 count
= CFArrayGetCount(service_list
);
5855 for (CFIndex s
= 0; s
< count
; s
++) {
5856 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
5858 add_service_keys(serviceID
, keys
);
5859 add_transient_status_keys(serviceID
, keys
);
5862 add_reachability_patterns(patterns
);
5864 add_vpn_pattern(patterns
);
5866 add_interface_link_pattern(patterns
);
5868 info
= SCDynamicStoreCopyMultiple(session
, keys
, patterns
);
5869 my_CFRelease(&keys
);
5870 my_CFRelease(&patterns
);
5874 #if !TARGET_OS_SIMULATOR
5877 set_ipv6_default_interface(IFIndex ifindex
)
5879 struct in6_ndifreq ndifreq
;
5881 boolean_t success
= FALSE
;
5883 memset((char *)&ndifreq
, 0, sizeof(ndifreq
));
5884 strlcpy(ndifreq
.ifname
, kLoopbackInterface
, sizeof(ndifreq
.ifname
));
5886 ndifreq
.ifindex
= ifindex
;
5889 ndifreq
.ifindex
= lo0_ifindex();
5891 sock
= inet6_dgram_socket();
5895 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
5897 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5908 #endif /* !TARGET_OS_SIMULATOR */
5910 #if !TARGET_OS_IPHONE
5911 static __inline__
void
5914 (void)unlink(VAR_RUN_RESOLV_CONF
);
5918 set_dns(CFArrayRef val_search_domains
,
5919 CFStringRef val_domain_name
,
5920 CFArrayRef val_servers
,
5921 CFArrayRef val_sortlist
)
5923 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
5925 /* publish new resolv.conf */
5930 SCPrint(TRUE
, f
, CFSTR("#\n"));
5931 SCPrint(TRUE
, f
, CFSTR("# macOS Notice\n"));
5932 SCPrint(TRUE
, f
, CFSTR("#\n"));
5933 SCPrint(TRUE
, f
, CFSTR("# This file is not consulted for DNS hostname resolution, address\n"));
5934 SCPrint(TRUE
, f
, CFSTR("# resolution, or the DNS query routing mechanism used by most\n"));
5935 SCPrint(TRUE
, f
, CFSTR("# processes on this system.\n"));
5936 SCPrint(TRUE
, f
, CFSTR("#\n"));
5937 SCPrint(TRUE
, f
, CFSTR("# To view the DNS configuration used by this system, use:\n"));
5938 SCPrint(TRUE
, f
, CFSTR("# scutil --dns\n"));
5939 SCPrint(TRUE
, f
, CFSTR("#\n"));
5940 SCPrint(TRUE
, f
, CFSTR("# SEE ALSO\n"));
5941 SCPrint(TRUE
, f
, CFSTR("# dns-sd(1), scutil(8)\n"));
5942 SCPrint(TRUE
, f
, CFSTR("#\n"));
5943 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
5944 SCPrint(TRUE
, f
, CFSTR("#\n"));
5946 if (isA_CFArray(val_search_domains
)) {
5947 SCPrint(TRUE
, f
, CFSTR("search"));
5948 n
= CFArrayGetCount(val_search_domains
);
5949 for (i
= 0; i
< n
; i
++) {
5952 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
5953 if (isA_CFString(domain
)) {
5954 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
5957 SCPrint(TRUE
, f
, CFSTR("\n"));
5959 else if (isA_CFString(val_domain_name
)) {
5960 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
5963 if (isA_CFArray(val_servers
)) {
5964 n
= CFArrayGetCount(val_servers
);
5965 for (i
= 0; i
< n
; i
++) {
5966 CFStringRef nameserver
;
5968 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
5969 if (isA_CFString(nameserver
)) {
5970 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
5975 if (isA_CFArray(val_sortlist
)) {
5976 SCPrint(TRUE
, f
, CFSTR("sortlist"));
5977 n
= CFArrayGetCount(val_sortlist
);
5978 for (i
= 0; i
< n
; i
++) {
5979 CFStringRef address
;
5981 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
5982 if (isA_CFString(address
)) {
5983 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
5986 SCPrint(TRUE
, f
, CFSTR("\n"));
5990 (void)rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
5994 #endif /* !TARGET_OS_IPHONE */
5997 service_get_ip_is_coupled(CFStringRef serviceID
)
5999 CFDictionaryRef dict
;
6000 boolean_t ip_is_coupled
= FALSE
;
6002 dict
= service_dict_get(serviceID
, kSCEntNetService
);
6004 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
6005 ip_is_coupled
= TRUE
;
6008 return (ip_is_coupled
);
6012 my_CFStringCreateWithInAddr(struct in_addr ip
)
6016 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(IP_FORMAT
), IP_LIST(&ip
));
6021 my_CFStringCreateWithIn6Addr(const struct in6_addr
* ip
)
6023 char ntopbuf
[INET6_ADDRSTRLEN
];
6025 (void)inet_ntop(AF_INET6
, ip
, ntopbuf
, sizeof(ntopbuf
));
6026 return (CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), ntopbuf
));
6030 * Function: update_ipv4
6032 * Update the IPv4 configuration based on the latest information.
6033 * Publish the State:/Network/Global/IPv4 information, and update the
6034 * IPv4 routing table.
6037 update_ipv4(CFStringRef primary
,
6038 IPv4RouteListRef new_routelist
,
6039 keyChangeListRef keys
)
6041 #if !TARGET_OS_SIMULATOR
6043 #endif /* !TARGET_OS_SIMULATOR */
6046 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6047 const char * ifn_p
= NULL
;
6048 char ifname
[IFNAMSIZ
];
6050 CFMutableDictionaryRef dict
= NULL
;
6052 dict
= CFDictionaryCreateMutable(NULL
, 0,
6053 &kCFTypeDictionaryKeyCallBacks
,
6054 &kCFTypeDictionaryValueCallBacks
);
6055 /* the first entry is the default route */
6056 r
= new_routelist
->list
;
6057 if (r
->gateway
.s_addr
!= 0) {
6060 str
= my_CFStringCreateWithInAddr(r
->gateway
);
6061 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, str
);
6064 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6065 if (ifn_p
!= NULL
) {
6066 CFStringRef ifname_cf
;
6068 ifname_cf
= CFStringCreateWithCString(NULL
,
6070 kCFStringEncodingASCII
);
6071 if (ifname_cf
!= NULL
) {
6072 CFDictionarySetValue(dict
,
6073 kSCDynamicStorePropNetPrimaryInterface
,
6075 CFRelease(ifname_cf
);
6078 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6080 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
6084 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
6088 #if !TARGET_OS_SIMULATOR
6089 sockfd
= open_routing_socket();
6091 /* go through routelist and bind any unbound routes */
6092 if (new_routelist
!= NULL
) {
6093 IPv4RouteListFinalize(new_routelist
);
6096 /* provide a routelist with just loopback multicast */
6097 new_routelist
= IPv4RouteListCopyMulticastLoopback();
6099 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6100 if (S_ipv4_routelist
== NULL
) {
6101 my_log(LOG_DEBUG
, "Old Routes = <none>");
6104 my_log(LOG_DEBUG
, "Old Routes = ");
6105 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
6107 if (new_routelist
== NULL
) {
6108 my_log(LOG_DEBUG
, "New Routes = <none>");
6111 my_log(LOG_DEBUG
, "New Routes = ");
6112 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
6115 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
, sockfd
);
6118 if (S_ipv4_routelist
!= NULL
) {
6119 free(S_ipv4_routelist
);
6121 S_ipv4_routelist
= new_routelist
;
6122 #else /* !TARGET_OS_SIMULATOR */
6123 if (new_routelist
!= NULL
) {
6124 free(new_routelist
);
6126 #endif /* !TARGET_OS_SIMULATOR */
6132 * Function: update_ipv6
6134 * Update the IPv6 configuration based on the latest information.
6135 * Publish the State:/Network/Global/IPv6 information, and update the
6136 * IPv6 routing table.
6139 update_ipv6(CFStringRef primary
,
6140 IPv6RouteListRef new_routelist
,
6141 keyChangeListRef keys
)
6143 #if !TARGET_OS_SIMULATOR
6145 #endif /* !TARGET_OS_SIMULATOR */
6148 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6149 const char * ifn_p
= NULL
;
6150 char ifname
[IFNAMSIZ
];
6152 CFMutableDictionaryRef dict
= NULL
;
6154 dict
= CFDictionaryCreateMutable(NULL
, 0,
6155 &kCFTypeDictionaryKeyCallBacks
,
6156 &kCFTypeDictionaryValueCallBacks
);
6157 /* the first entry is the default route */
6158 r
= new_routelist
->list
;
6159 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
6162 router
= my_CFStringCreateWithIn6Addr(&r
->gateway
);
6163 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
, router
);
6166 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6167 if (ifn_p
!= NULL
) {
6168 CFStringRef ifname_cf
;
6170 ifname_cf
= CFStringCreateWithCString(NULL
,
6172 kCFStringEncodingASCII
);
6173 if (ifname_cf
!= NULL
) {
6174 CFDictionarySetValue(dict
,
6175 kSCDynamicStorePropNetPrimaryInterface
,
6177 CFRelease(ifname_cf
);
6180 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6182 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
6184 #if !TARGET_OS_SIMULATOR
6185 set_ipv6_default_interface(r
->ifindex
);
6186 #endif /* !TARGET_OS_SIMULATOR */
6189 #if !TARGET_OS_SIMULATOR
6190 set_ipv6_default_interface(0);
6191 #endif /* !TARGET_OS_SIMULATOR */
6192 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
6196 #if !TARGET_OS_SIMULATOR
6197 sockfd
= open_routing_socket();
6199 /* go through routelist and bind any unbound routes */
6200 IPv6RouteListFinalize(new_routelist
);
6201 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6202 if (S_ipv6_routelist
== NULL
) {
6203 my_log(LOG_DEBUG
, "Old Routes = <none>");
6206 my_log(LOG_DEBUG
, "Old Routes = ");
6207 IPv6RouteListLog(LOG_DEBUG
, S_ipv6_routelist
);
6209 if (new_routelist
== NULL
) {
6210 my_log(LOG_DEBUG
, "New Routes = <none>");
6213 my_log(LOG_DEBUG
, "New Routes = ");
6214 IPv6RouteListLog(LOG_DEBUG
, new_routelist
);
6217 IPv6RouteListApply(S_ipv6_routelist
, new_routelist
, sockfd
);
6220 if (S_ipv6_routelist
!= NULL
) {
6221 free(S_ipv6_routelist
);
6223 S_ipv6_routelist
= new_routelist
;
6224 #else /* !TARGET_OS_SIMULATOR */
6225 if (new_routelist
!= NULL
) {
6226 free(new_routelist
);
6228 #endif /* !TARGET_OS_SIMULATOR */
6234 update_dns(CFDictionaryRef services_info
,
6235 CFStringRef primary
,
6236 keyChangeListRef keys
)
6238 #pragma unused(services_info)
6239 #pragma unused(keys)
6240 Boolean changed
= FALSE
;
6241 CFDictionaryRef dict
= NULL
;
6243 if (primary
!= NULL
) {
6244 CFDictionaryRef service_dict
;
6246 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6247 if (service_dict
!= NULL
) {
6248 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6252 if (!_SC_CFEqual(S_dns_primary_dict
, dict
)) {
6256 if (dict
!= NULL
) CFRetain(dict
);
6257 if (S_dns_primary_dict
!= NULL
) CFRelease(S_dns_primary_dict
);
6258 S_dns_primary_dict
= dict
;
6264 update_dns_global_resolver(CFDictionaryRef dict
,
6265 keyChangeListRef keys
)
6267 if (_SC_CFEqual(S_dns_global_dict
, dict
)) {
6272 if (dict
!= NULL
) CFRetain(dict
);
6273 if (S_dns_global_dict
!= NULL
) CFRelease(S_dns_global_dict
);
6274 S_dns_global_dict
= dict
;
6277 #if !TARGET_OS_IPHONE
6279 * remove /etc/resolv.conf
6282 #endif /* !TARGET_OS_IPHONE */
6284 * remove State:/Network/Global/DNS
6286 keyChangeListRemoveValue(keys
, S_state_global_dns
);
6288 CFMutableDictionaryRef new_dict
;
6290 #if !TARGET_OS_IPHONE
6292 * update /etc/resolv.conf
6294 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
6295 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
6296 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
6297 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
6298 #endif /* !TARGET_OS_IPHONE */
6301 * update State:/Network/Global/DNS
6303 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
6304 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
6305 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
6306 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
6307 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
6308 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
6309 CFRelease(new_dict
);
6316 update_dnsinfo(CFDictionaryRef services_info
,
6317 CFStringRef primary
,
6318 keyChangeListRef keys
,
6319 CFArrayRef service_order
)
6322 CFDictionaryRef dict
= NULL
;
6323 CFDictionaryRef globalResolver
= NULL
;
6324 CFArrayRef multicastResolvers
;
6325 CFArrayRef privateResolvers
;
6327 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
6328 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
6330 if (primary
!= NULL
) {
6331 CFDictionaryRef service_dict
;
6333 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6334 if (service_dict
!= NULL
) {
6335 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6339 changed
= dns_configuration_set(dict
,
6340 S_service_state_dict
,
6346 if (!update_dns_global_resolver(globalResolver
, keys
)) {
6348 * There was no change to the default/global resolver
6349 * configuration. Even so, we still want to strobe
6350 * the State:/Network/Global/DNS key to indicate that
6351 * "a" change had occured.
6353 keyChangeListNotifyKey(keys
, S_state_global_dns
);
6356 if (globalResolver
!= NULL
) CFRelease(globalResolver
);
6362 update_nwi(nwi_state_t state
)
6364 unsigned char signature
[CC_SHA256_DIGEST_LENGTH
];
6365 static unsigned char signature_last
[CC_SHA256_DIGEST_LENGTH
];
6367 _nwi_state_compute_sha256_hash(state
, signature
);
6368 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
6369 my_log(LOG_DEBUG
, "Not updating network information");
6373 // save [new] signature
6374 memcpy(signature_last
, signature
, sizeof(signature
));
6376 // save [new] configuration
6377 my_log(LOG_INFO
, "Updating network information");
6378 _nwi_state_log(state
, TRUE
, NULL
);
6380 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
6381 if (!_nwi_state_store(state
)) {
6382 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
6384 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
6390 update_proxies(CFDictionaryRef services_info
,
6391 CFStringRef primary
,
6392 keyChangeListRef keys
,
6393 CFArrayRef service_order
)
6395 Boolean changed
= FALSE
;
6396 CFDictionaryRef dict
= NULL
;
6397 CFDictionaryRef new_dict
;
6399 if (primary
!= NULL
) {
6400 CFDictionaryRef service_dict
;
6402 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6403 if (service_dict
!= NULL
) {
6404 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
6408 new_dict
= proxy_configuration_update(dict
,
6409 S_service_state_dict
,
6412 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
6413 if (new_dict
== NULL
) {
6414 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
6416 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
6421 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
6422 S_proxies_dict
= new_dict
;
6427 #if !TARGET_OS_IPHONE
6429 update_smb(CFDictionaryRef services_info
,
6430 CFStringRef primary
,
6431 keyChangeListRef keys
)
6433 #pragma unused(services_info)
6434 Boolean changed
= FALSE
;
6435 CFDictionaryRef dict
= NULL
;
6437 if (primary
!= NULL
) {
6438 CFDictionaryRef service_dict
;
6440 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6441 if (service_dict
!= NULL
) {
6442 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
6446 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
6448 keyChangeListRemoveValue(keys
, S_state_global_smb
);
6450 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
6455 if (dict
!= NULL
) CFRetain(dict
);
6456 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
6461 #endif /* !TARGET_OS_IPHONE */
6464 get_service_index(CFDictionaryRef rank_entity
,
6465 CFArrayRef order
, CFIndex n_order
, CFStringRef serviceID
)
6468 Rank rank
= kRankIndexMask
;
6469 CFNumberRef service_index
;
6472 = service_rank_entity_get_index(rank_entity
,
6476 if (service_index
!= NULL
) {
6477 /* ServiceIndex specified in service entity */
6480 "%@ specifies ServiceIndex %@, effective index is %d",
6481 serviceID
, service_index
, rank
);
6483 else if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
6484 for (i
= 0; i
< n_order
; i
++) {
6485 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
6490 if (CFEqual(serviceID
, s
)) {
6500 ** Service election:
6503 * Function: rank_dict_get_service_rank
6505 * Retrieve the service rank in the given dictionary.
6508 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
6511 Rank rank_val
= kRankAssertionDefault
;
6513 rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
6514 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
6516 if (!CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
)) {
6517 /* if we don't like the rank value */
6518 rank_val
= kRankAssertionDefault
;
6526 * Function: rank_dict_set_service_rank
6528 * Save the results of ranking the service so we can look it up later without
6529 * repeating all of the ranking code.
6532 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
6533 CFStringRef serviceID
, Rank rank_val
)
6537 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
6539 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
6545 static const CFStringRef
*transientInterfaceEntityNames
[] = {
6551 CollectTransientServices(const void * key
,
6555 #pragma unused(value)
6556 CFStringRef service
= key
;
6557 CFMutableArrayRef vif_setup_keys
= context
;
6559 /* This service is either a vpn type service or a comm center service */
6560 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
6564 for (size_t i
= 0; i
< countof(transientInterfaceEntityNames
); i
++) {
6565 if (CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
6566 my_CFArrayAppendUniqueValue(vif_setup_keys
, service
);
6575 static SCNetworkReachabilityFlags
6576 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
6577 CFStringRef service_id
,
6579 CFStringRef vpn_setup_key
)
6582 CFDictionaryRef dict
;
6583 SCNetworkReachabilityFlags flags
= 0;
6586 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6587 kSCDynamicStoreDomainSetup
,
6589 kSCEntNetInterface
);
6590 dict
= CFDictionaryGetValue(services_info
, key
);
6593 if (isA_CFDictionary(dict
)
6594 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
6596 flags
= (kSCNetworkReachabilityFlagsReachable
6597 | kSCNetworkReachabilityFlagsTransientConnection
6598 | kSCNetworkReachabilityFlagsConnectionRequired
);
6600 if (CFEqual(entity
, kSCEntNetPPP
)) {
6602 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
6604 if (!isA_CFDictionary(p_dict
)) {
6608 // get PPP dial-on-traffic status
6609 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
6610 if (isA_CFNumber(num
)) {
6613 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
6615 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6625 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
6627 Boolean ret
= def_value
;
6632 val
= CFDictionaryGetValue(dict
, key
);
6633 if (isA_CFBoolean(val
) != NULL
) {
6634 ret
= CFBooleanGetValue(val
);
6642 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
6643 SCNetworkReachabilityFlags
*reach_flags_v4
,
6644 SCNetworkReachabilityFlags
*reach_flags_v6
)
6648 CFMutableArrayRef vif_setup_keys
;
6650 vif_setup_keys
= CFArrayCreateMutable(NULL
,
6652 &kCFTypeArrayCallBacks
);
6653 CFDictionaryApplyFunction(services_info
, CollectTransientServices
,
6655 count
= CFArrayGetCount(vif_setup_keys
);
6656 for (i
= 0; i
< count
; i
++) {
6657 CFArrayRef components
= NULL
;
6659 CFStringRef service_id
;
6660 CFStringRef vif_setup_key
;
6662 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
6665 * setup key in the following format:
6666 * Setup:/Network/Service/<Service ID>/<Entity>
6668 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
6670 if (CFArrayGetCount(components
) != 5) {
6671 // invalid Setup key encountered
6675 /* service id is the 3rd element */
6676 service_id
= CFArrayGetValueAtIndex(components
, 3);
6678 /* entity id is the 4th element */
6679 entity
= CFArrayGetValueAtIndex(components
, 4);
6682 if (CFEqual(entity
, kSCEntNetPPP
)) {
6683 SCNetworkReachabilityFlags flags
;
6686 flags
= GetReachabilityFlagsFromVPN(services_info
,
6691 /* Check for the v4 reachability flags */
6692 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6693 kSCDynamicStoreDomainSetup
,
6697 if (CFDictionaryContainsKey(services_info
, key
)) {
6698 *reach_flags_v4
|= flags
;
6699 my_log(LOG_DEBUG
, "Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
6704 /* Check for the v6 reachability flags */
6705 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6706 kSCDynamicStoreDomainSetup
,
6710 if (CFDictionaryContainsKey(services_info
, key
)) {
6711 *reach_flags_v6
|= flags
;
6712 my_log(LOG_DEBUG
, "Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
6717 if (components
!= NULL
) {
6718 CFRelease(components
);
6724 if (components
!= NULL
) {
6725 CFRelease(components
);
6729 CFRelease(vif_setup_keys
);
6733 static SCNetworkReachabilityFlags
6734 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
6736 SCNetworkReachabilityFlags flags
= 0;
6738 if (CFEqual(entity
, kSCEntNetPPP
)) {
6741 /* if we're really UP and RUNNING */
6744 /* if we're effectively UP and RUNNING */
6747 /* if we're not connected at all */
6748 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6750 case PPP_STATERESERVED
:
6751 // if we're not connected at all
6752 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6755 /* if we're in the process of [dis]connecting */
6756 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6760 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
6762 case IPSEC_RUNNING
:
6763 /* if we're really UP and RUNNING */
6766 /* if we're not connected at all */
6767 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6770 /* if we're in the process of [dis]connecting */
6771 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6775 else if (CFEqual(entity
, kSCEntNetVPN
)) {
6778 /* if we're really UP and RUNNING */
6783 case VPN_UNLOADING
:
6784 /* if we're not connected at all */
6785 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6788 /* if we're in the process of [dis]connecting */
6789 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6797 VPNAttributesGet(CFStringRef service_id
,
6798 CFDictionaryRef services_info
,
6799 SCNetworkReachabilityFlags
*flags
,
6800 CFStringRef
*server_address
,
6803 CFDictionaryRef entity_dict
;
6805 CFDictionaryRef p_state
= NULL
;
6807 CFStringRef transient_entity
= NULL
;
6809 if (af
== AF_INET
) {
6810 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv4
);
6812 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv6
);
6814 entity_dict
= ipdict_get_service(entity_dict
);
6815 if (entity_dict
== NULL
) {
6819 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
6820 CFStringRef entity
= *transientServiceInfo
[i
].entityName
;
6822 p_state
= service_dict_get(service_id
, entity
);
6824 /* ensure that this is a VPN Type service */
6825 if (isA_CFDictionary(p_state
)) {
6826 transient_entity
= entity
;
6831 /* Did we find a vpn type service? If not, we are done.*/
6832 if (transient_entity
== NULL
) {
6836 *flags
|= (kSCNetworkReachabilityFlagsReachable
6837 | kSCNetworkReachabilityFlagsTransientConnection
);
6839 /* Get the Server Address */
6840 if (server_address
!= NULL
) {
6841 *server_address
= CFDictionaryGetValue(entity_dict
,
6842 CFSTR("ServerAddress"));
6843 *server_address
= isA_CFString(*server_address
);
6844 if (*server_address
!= NULL
) {
6845 CFRetain(*server_address
);
6850 if (!CFDictionaryGetValueIfPresent(p_state
,
6851 kSCPropNetVPNStatus
, // IPSecStatus, PPPStatus, VPNStatus
6852 (const void **)&num
) ||
6853 !isA_CFNumber(num
) ||
6854 !CFNumberGetValue(num
, kCFNumberIntType
, &status
)) {
6858 *flags
|= GetReachFlagsFromStatus(transient_entity
, status
);
6859 if (CFEqual(transient_entity
, kSCEntNetPPP
)) {
6861 CFDictionaryRef p_setup
;
6864 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6865 kSCDynamicStoreDomainSetup
,
6868 p_setup
= CFDictionaryGetValue(services_info
, key
);
6871 /* get dial-on-traffic status */
6872 if (isA_CFDictionary(p_setup
) &&
6873 CFDictionaryGetValueIfPresent(p_setup
,
6874 kSCPropNetPPPDialOnDemand
,
6875 (const void **)&num
) &&
6876 isA_CFNumber(num
) &&
6877 CFNumberGetValue(num
, kCFNumberIntType
, &ppp_demand
) &&
6878 (ppp_demand
!= 0)) {
6879 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6880 if (status
== PPP_IDLE
) {
6881 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
6889 typedef struct ElectionInfo
{
6895 ElectionResultsRef results
;
6896 CFMutableDictionaryRef rank_dict
;
6897 } ElectionInfo
, * ElectionInfoRef
;
6899 typedef CFDictionaryApplierFunction ElectionFuncRef
;
6902 CandidateRelease(CandidateRef candidate
)
6904 my_CFRelease(&candidate
->serviceID
);
6905 my_CFRelease(&candidate
->if_name
);
6906 my_CFRelease(&candidate
->signature
);
6911 CandidateCopy(CandidateRef dest
, CandidateRef src
)
6914 if (dest
->serviceID
) {
6915 CFRetain(dest
->serviceID
);
6917 if (dest
->if_name
) {
6918 CFRetain(dest
->if_name
);
6920 if(dest
->signature
) {
6921 CFRetain(dest
->signature
);
6926 static ElectionResultsRef
6927 ElectionResultsAlloc(int af
, int size
)
6929 ElectionResultsRef results
;
6931 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
6934 results
->size
= size
;
6939 ElectionResultsRelease(ElectionResultsRef results
)
6944 for (i
= 0, scan
= results
->candidates
;
6947 CandidateRelease(scan
);
6954 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
6959 if (results
== NULL
) {
6960 my_log(level
, "%s: no candidates", prefix
);
6963 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
6964 for (i
= 0, scan
= results
->candidates
;
6967 char ntopbuf
[INET6_ADDRSTRLEN
];
6969 (void)inet_ntop(results
->af
, &scan
->addr
, ntopbuf
, sizeof(ntopbuf
));
6970 my_log(level
, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6971 i
, scan
->if_name
, scan
->serviceID
, ntopbuf
, scan
->rank
,
6972 scan
->ineligible
? " [ineligible]" : "");
6978 * Function: ElectionResultsAddCandidate
6980 * Add the candidate into the election results. Find the insertion point
6981 * by comparing the rank of the candidate with existing entries.
6984 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
6989 if (results
->count
== results
->size
) {
6990 /* this should not happen */
6991 my_log(LOG_NOTICE
, "can't fit another candidate");
6995 /* find the insertion point */
6996 where
= kCFNotFound
;
6997 for (i
= 0; i
< results
->count
; i
++) {
6998 CandidateRef this_candidate
= results
->candidates
+ i
;
7000 if (candidate
->rank
< this_candidate
->rank
) {
7005 /* add it to the end */
7006 if (where
== kCFNotFound
) {
7007 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
7011 /* slide existing entries over */
7012 for (i
= results
->count
; i
> where
; i
--) {
7013 results
->candidates
[i
] = results
->candidates
[i
- 1];
7015 /* insert element */
7016 CandidateCopy(results
->candidates
+ where
, candidate
);
7022 elect_ip(const void * key
, const void * value
, void * context
);
7025 * Function: ElectionResultsCopy
7027 * Visit all of the services and invoke the protocol-specific election
7028 * function. Return the results of the election.
7030 static ElectionResultsRef
7031 ElectionResultsCopy(int af
, CFArrayRef order
)
7036 count
= (int)CFDictionaryGetCount(S_service_state_dict
);
7041 if (af
== AF_INET
) {
7042 info
.entity
= kSCEntNetIPv4
;
7043 info
.rank_dict
= S_ipv4_service_rank_dict
;
7046 info
.entity
= kSCEntNetIPv6
;
7047 info
.rank_dict
= S_ipv6_service_rank_dict
;
7049 info
.results
= ElectionResultsAlloc(af
, count
);
7050 info
.n_services
= count
;
7052 if (order
!= NULL
) {
7053 info
.n_order
= CFArrayGetCount(order
);
7058 CFDictionaryApplyFunction(S_service_state_dict
, elect_ip
, (void *)&info
);
7059 if (info
.results
->count
== 0) {
7060 ElectionResultsRelease(info
.results
);
7061 info
.results
= NULL
;
7063 return (info
.results
);
7067 * Function: ElectionResultsCandidateNeedsDemotion
7069 * Check whether the given candidate requires demotion. A candidate
7070 * might need to be demoted if its IPv4 and IPv6 services must be coupled
7071 * but a higher ranked service has IPv4 or IPv6.
7073 * The converse is also true: if the given candidate has lower rank than
7074 * the other candidate and the other candidate is coupled, this candidate
7075 * needs to be demoted.
7078 ElectionResultsCandidateNeedsDemotion(CandidateRef other_candidate
,
7079 CandidateRef candidate
)
7081 Boolean ret
= FALSE
;
7083 if (other_candidate
== NULL
) {
7084 /* no other candidate */
7087 if (other_candidate
->ineligible
) {
7088 /* other candidate can't become primary */
7091 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
7092 /* the other candidate can't become primary */
7095 if (!candidate
->ip_is_coupled
&& !other_candidate
->ip_is_coupled
) {
7096 /* neither candidate is coupled */
7099 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
7100 /* they are over the same interface, no need to demote */
7103 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
7104 /* avoid creating a feedback loop */
7107 if (candidate
->rank
< other_candidate
->rank
) {
7108 /* we're higher ranked than the other candidate, ignore */
7111 if (candidate
->ip_is_coupled
) {
7112 if (other_candidate
->ip_is_coupled
7113 && candidate
->rank
== other_candidate
->rank
) {
7114 /* same rank as another service that is coupled, ignore */
7118 else if (other_candidate
->ip_is_coupled
) { /* must be true */
7119 if (candidate
->rank
== other_candidate
->rank
) {
7120 /* other candidate will be demoted, so we don't need to */
7123 /* we're lower rank and need to be demoted */
7125 else { /* can't happen, we already tested for this above */
7126 /* neither candidate is coupled */
7138 get_signature_sha256(CFStringRef signature
,
7139 unsigned char * sha256
)
7142 CFDataRef signature_data
;
7144 signature_data
= CFStringCreateExternalRepresentation(NULL
,
7146 kCFStringEncodingUTF8
,
7149 CC_SHA256_Init(&ctx
);
7150 CC_SHA256_Update(&ctx
,
7151 CFDataGetBytePtr(signature_data
),
7152 (CC_LONG
)CFDataGetLength(signature_data
));
7153 CC_SHA256_Final(sha256
, &ctx
);
7155 CFRelease(signature_data
);
7162 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
7163 CandidateRef candidate
, Boolean not_in_list
,
7164 Boolean not_in_iflist
)
7167 char ifname
[IFNAMSIZ
];
7168 nwi_ifstate_t ifstate
;
7170 if (nwi_state
== NULL
) {
7175 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
7176 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
7178 if (not_in_iflist
) {
7179 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST
;
7181 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
7182 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
7184 if ((af
== AF_INET
) && service_has_clat46_address(candidate
->serviceID
)) {
7185 flags
|= NWI_IFSTATE_FLAGS_HAS_CLAT46
;
7187 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
7188 kCFStringEncodingASCII
);
7189 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
7190 char ntopbuf
[INET6_ADDRSTRLEN
];
7192 (void)inet_ntop(af
, &candidate
->addr
, ntopbuf
, sizeof(ntopbuf
));
7194 "Adding IPv%c [%s] %s "
7195 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
7196 ipvx_char(af
), ifname
, ntopbuf
,
7197 flags
, candidate
->rank
, candidate
->reachability_flags
);
7199 ifstate
= nwi_state_add_ifstate(nwi_state
, ifname
, af
, flags
,
7201 (void *)&candidate
->addr
,
7202 (void *)&candidate
->vpn_server_addr
,
7203 candidate
->reachability_flags
);
7204 if (ifstate
!= NULL
&& candidate
->signature
) {
7205 uint8_t hash
[CC_SHA256_DIGEST_LENGTH
];
7207 get_signature_sha256(candidate
->signature
, hash
);
7208 nwi_ifstate_set_signature(ifstate
, hash
);
7215 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
7217 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
7218 CFStringRef vpn_server_address
= NULL
;
7220 assert(candidate
!= NULL
);
7221 assert(services_info
!= NULL
);
7223 VPNAttributesGet(candidate
->serviceID
,
7226 &vpn_server_address
,
7229 candidate
->reachability_flags
= flags
;
7231 if (vpn_server_address
== NULL
) {
7232 memset(&candidate
->vpn_server_addr
, 0, sizeof(candidate
->vpn_server_addr
));
7236 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
),
7237 kCFStringEncodingASCII
);
7238 _SC_string_to_sockaddr(buf
,
7240 (void *)&candidate
->vpn_server_addr
,
7241 sizeof(candidate
->vpn_server_addr
));
7243 CFRelease(vpn_server_address
);
7248 * Function: ElectionResultsGetPrimary
7250 * Use the results of the current protocol and the other protocol to
7251 * determine which service should become primary.
7253 * At the same time, generate the IPv4/IPv6 routing table and
7254 * the nwi_state for the protocol.
7257 ElectionResultsGetPrimary(ElectionResultsRef results
,
7258 CandidateRef other_candidate
,
7259 nwi_state_t nwi_state
, int af
,
7260 RouteListRef
* ret_routes
,
7261 CFDictionaryRef services_info
,
7262 CFSetRef ip_service_changes
)
7264 CandidateRef primary
= NULL
;
7265 Boolean primary_is_null
= FALSE
;
7266 RouteListRef routes
= NULL
;
7268 assert(services_info
!= NULL
);
7270 if (results
!= NULL
) {
7271 CandidateRef deferred
[results
->count
];
7273 CFStringRef entity_name
;
7276 RouteListInfoRef info
;
7281 entity_name
= kSCEntNetIPv4
;
7282 info
= &IPv4RouteListInfo
;
7283 initial_size
= results
->count
* IPV4_ROUTES_N_STATIC
;
7287 entity_name
= kSCEntNetIPv6
;
7288 info
= &IPv6RouteListInfo
;
7289 initial_size
= results
->count
* IPV6_ROUTES_N_STATIC
;
7293 for (i
= 0, scan
= results
->candidates
;
7296 Boolean is_primary
= FALSE
;
7297 CFDictionaryRef service_dict
;
7298 RouteListRef service_routes
;
7299 Boolean skip
= FALSE
;
7301 if (!scan
->ineligible
7303 && RANK_ASSERTION_MASK(scan
->rank
) != kRankAssertionNever
) {
7304 if (ElectionResultsCandidateNeedsDemotion(other_candidate
,
7306 /* demote the service */
7308 "IPv%c over %@ (rank 0x%x) demoted: "
7309 "primary IPv%c %@ (rank 0x%x)",
7310 ipvx_char(af
), scan
->if_name
, scan
->rank
,
7311 ipvx_other_char(af
), other_candidate
->if_name
,
7312 other_candidate
->rank
);
7313 deferred
[deferred_count
++] = scan
;
7321 /* contribute to the routing table */
7322 service_dict
= service_dict_get(scan
->serviceID
, entity_name
);
7323 service_routes
= ipdict_get_routelist(service_dict
);
7324 if (service_routes
!= NULL
) {
7326 Rank rank
= scan
->rank
;
7329 /* routes are RankNever to prevent becoming primary */
7330 rank
= RankMake(rank
, kRankAssertionNever
);
7332 force
= my_CFSetContainsValue(ip_service_changes
,
7334 routes
= RouteListAddRouteList(info
, routes
, initial_size
,
7335 service_routes
, rank
, force
);
7336 if ((service_routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
7344 /* if we're skipping the primary, it's NULL */
7346 primary_is_null
= TRUE
;
7349 else if (!scan
->ineligible
) {
7350 Boolean not_in_iflist
;
7352 add_reachability_flags_to_candidate(scan
, services_info
, af
);
7354 = (service_routes
->flags
& kRouteListFlagsScopedOnly
) != 0;
7355 add_candidate_to_nwi_state(nwi_state
, af
, scan
,
7360 for (i
= 0; i
< deferred_count
; i
++) {
7361 CandidateRef candidate
= deferred
[i
];
7363 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
7364 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, TRUE
, FALSE
);
7367 if (ret_routes
!= NULL
) {
7368 *ret_routes
= routes
;
7370 else if (routes
!= NULL
) {
7373 if (primary_is_null
) {
7382 service_dict_get_signature(CFDictionaryRef service_dict
)
7386 ifname
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7387 if (isA_CFString(ifname
) == NULL
7388 || !confirm_interface_name(service_dict
, ifname
)) {
7391 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
7395 * Function: elect_ip
7397 * Evaluate the service and determine what rank the service should have.
7398 * If it's a suitable candidate, add it to the election results.
7401 elect_ip(const void * key
, const void * value
, void * context
)
7403 CFDictionaryRef all_entities_dict
= (CFDictionaryRef
)value
;
7404 Candidate candidate
;
7406 ElectionInfoRef elect_info
;
7407 CFStringRef if_name
;
7408 CFDictionaryRef ipdict
;
7410 CFDictionaryRef rank_entity
;
7411 RouteListUnion routelist
;
7412 CFDictionaryRef service_dict
;
7414 elect_info
= (ElectionInfoRef
)context
;
7415 ipdict
= CFDictionaryGetValue(all_entities_dict
, elect_info
->entity
);
7416 if (ipdict
!= NULL
) {
7417 routelist
.ptr
= ipdict_get_routelist(ipdict
);
7418 service_dict
= ipdict_get_service(ipdict
);
7421 routelist
.ptr
= NULL
;
7423 if (routelist
.ptr
== NULL
|| service_dict
== NULL
) {
7424 /* no connectivity */
7427 if_name
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7428 if (if_name
== NULL
) {
7429 /* need an interface name */
7432 if (CFEqual(if_name
, CFSTR(kLoopbackInterface
))) {
7433 /* don't process loopback */
7436 memset(&candidate
, 0, sizeof(candidate
));
7437 candidate
.serviceID
= (CFStringRef
)key
;
7438 if ((routelist
.common
->flags
& kRouteListFlagsHasDefault
) == 0) {
7439 /* no default route means it's ineligible to become primary */
7440 candidate
.ineligible
= TRUE
;
7442 rank_entity
= CFDictionaryGetValue(all_entities_dict
, kSCEntNetService
);
7443 candidate
.rank
= get_service_index(rank_entity
,
7444 elect_info
->order
, elect_info
->n_order
,
7445 candidate
.serviceID
);
7446 if (elect_info
->af
== AF_INET
) {
7447 default_rank
= routelist
.v4
->list
->rank
;
7448 candidate
.addr
.v4
= routelist
.v4
->list
->ifa
;
7451 default_rank
= routelist
.v6
->list
->rank
;
7452 candidate
.addr
.v6
= routelist
.v6
->list
->ifa
;
7454 primary_rank
= RANK_ASSERTION_MASK(default_rank
);
7455 if (S_ppp_override_primary
) {
7458 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
7459 kCFStringEncodingASCII
)
7460 && (strncmp(PPP_PREFIX
, ifn
, sizeof(PPP_PREFIX
) - 1) == 0)) {
7461 /* PPP override: make ppp* look the best */
7462 primary_rank
= kRankAssertionFirst
;
7465 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
7466 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
7467 candidate
.if_name
= if_name
;
7468 rank_dict_set_service_rank(elect_info
->rank_dict
,
7469 candidate
.serviceID
, candidate
.rank
);
7470 candidate
.signature
= service_dict_get_signature(service_dict
);
7471 ElectionResultsAddCandidate(elect_info
->results
, &candidate
);
7477 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
7479 uint32_t changed
= 0;
7482 /* update service options first (e.g. rank) */
7483 if (get_rank_changes(serviceID
,
7484 get_service_state_entity(services_info
, serviceID
,
7486 get_service_setup_entity(services_info
, serviceID
,
7489 changed
|= (1 << kEntityTypeServiceOptions
);
7492 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7493 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
7494 GetEntityChangesFuncRef func
;
7497 func
= entityChangeFunc
[i
];
7498 name
= *entityTypeNames
[i
];
7499 if ((*func
)(serviceID
,
7500 get_service_state_entity(services_info
, serviceID
, name
),
7501 get_service_setup_entity(services_info
, serviceID
, name
),
7503 changed
|= (1 << i
);
7507 /* update transient service status */
7508 if (get_transient_status_changes(serviceID
, services_info
)) {
7509 changed
|= (1 << kEntityTypeTransientStatus
);
7516 serviceID_get_ifname(CFStringRef serviceID
)
7518 CFDictionaryRef entity_dict
;
7519 CFStringRef ifname
= NULL
;
7521 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
7522 if (entity_dict
== NULL
) {
7523 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
7525 if (entity_dict
!= NULL
) {
7526 ifname
= ipdict_get_ifname(entity_dict
);
7531 __private_extern__ boolean_t
7532 check_if_service_expensive(CFStringRef serviceID
)
7535 ifname
= serviceID_get_ifname(serviceID
);
7537 return interface_is_expensive(ifname
);
7541 service_order_get(CFDictionaryRef services_info
)
7543 CFArrayRef order
= NULL
;
7544 CFDictionaryRef ipv4_dict
;
7546 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
7547 S_setup_global_ipv4
);
7548 if (ipv4_dict
!= NULL
) {
7549 CFNumberRef ppp_override
;
7552 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
7553 order
= isA_CFArray(order
);
7555 /* get ppp override primary */
7556 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
7557 kSCPropNetPPPOverridePrimary
);
7558 ppp_override
= isA_CFNumber(ppp_override
);
7559 if (ppp_override
!= NULL
) {
7560 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
7562 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
7565 S_ppp_override_primary
= FALSE
;
7571 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
7572 const char * entity
)
7574 boolean_t changed
= FALSE
;
7575 CFStringRef primary
= *primary_p
;
7577 if (new_primary
!= NULL
) {
7578 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
7579 my_log(LOG_INFO
, "%@ is still primary %s", new_primary
, entity
);
7582 my_CFRelease(primary_p
);
7583 *primary_p
= CFRetain(new_primary
);
7584 my_log(LOG_INFO
, "%@ is the new primary %s", new_primary
, entity
);
7588 else if (primary
!= NULL
) {
7589 my_log(LOG_INFO
, "%@ is no longer primary %s", primary
, entity
);
7590 my_CFRelease(primary_p
);
7597 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
7600 if (service_dict_get(serviceID
, entity
) == NULL
) {
7601 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
7603 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
7607 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
7613 #define N_KEYS_VALUES_STATIC 10
7614 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
7617 count
= CFDictionaryGetCount(S_service_state_dict
);
7618 if (count
<= N_KEYS_VALUES_STATIC
) {
7619 keys
= keys_values_buf
;
7621 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
7623 values
= keys
+ count
;
7624 CFDictionaryGetKeysAndValues(S_service_state_dict
,
7625 (const void * *)keys
,
7626 (const void * *)values
);
7628 for (i
= 0; i
< count
; i
++) {
7629 CFDictionaryRef ipdict
= NULL
;
7630 CFStringRef interface
= NULL
;
7631 CFStringRef serviceID
;
7632 CFDictionaryRef service_dict
;
7634 serviceID
= (CFStringRef
)keys
[i
];
7635 service_dict
= (CFDictionaryRef
)values
[i
];
7637 /* check whether service has IPv4 or IPv6 */
7638 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
7639 if (ipdict
== NULL
) {
7640 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
7641 if (ipdict
== NULL
) {
7645 interface
= ipdict_get_ifname(ipdict
);
7646 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
7648 "Found IP service %@ on interface %@",
7650 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
7653 if (keys
!= keys_values_buf
) {
7659 static __inline__
const char *
7660 get_changed_str(CFStringRef serviceID
, CFStringRef entity
,
7661 CFDictionaryRef old_dict
)
7663 CFDictionaryRef new_dict
= NULL
;
7665 if (serviceID
!= NULL
) {
7666 new_dict
= service_dict_get(serviceID
, entity
);
7669 if (old_dict
== NULL
) {
7670 if (new_dict
!= NULL
) {
7674 if (new_dict
== NULL
) {
7676 } else if (!CFEqual(old_dict
, new_dict
)) {
7683 #if !TARGET_OS_SIMULATOR
7686 #define MANAGE_IF_ORDER
7687 #define MANAGE_IF_IOCTL
7688 #endif /* SIOCSIFORDER */
7690 #ifdef SIOCSIFNETSIGNATURE
7691 #define MANAGE_IF_SIGNATURE
7692 #define MANAGE_IF_IOCTL
7693 #endif /* SIOCSIFNETSIGNATURE */
7695 #ifdef MANAGE_IF_IOCTL
7697 inet_dgram_socket(void)
7701 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
7703 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
7708 #endif /* MANAGE_IF_IOCTL */
7710 #ifdef MANAGE_IF_ORDER
7712 interface_order_changed(nwi_state_t old_state
, nwi_state_t new_state
)
7714 if (old_state
== NULL
&& new_state
== NULL
) {
7715 // Both are NULL, nothing changed
7719 if (old_state
== NULL
|| new_state
== NULL
) {
7720 // One is NULL, something changed
7724 if (old_state
->if_list_count
!= new_state
->if_list_count
) {
7725 // Count is different, something changed
7729 if (new_state
->if_list_count
== 0) {
7730 // Count is same and 0, nothing changed
7735 nwi_ifindex_t
*old_scan
;
7736 nwi_ifindex_t
*new_scan
;
7737 for (i
= 0, old_scan
= nwi_state_if_list(old_state
), new_scan
= nwi_state_if_list(new_state
);
7738 i
< new_state
->if_list_count
; i
++, old_scan
++, new_scan
++) {
7739 if (strcmp(old_state
->ifstate_list
[*old_scan
].ifname
, new_state
->ifstate_list
[*new_scan
].ifname
) != 0) {
7740 // Some interface in the list is different, something changed
7745 // Count and contents are the same, nothing changed
7750 update_interface_order(nwi_state_t state
, int sockfd
)
7752 Boolean success
= FALSE
;
7754 // Set interface order into the kernel
7755 struct if_order interface_order
;
7756 interface_order
.ifo_count
= (uint32_t)state
->if_list_count
;
7757 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)calloc((size_t)interface_order
.ifo_count
, sizeof(uint32_t));
7758 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7760 nwi_ifindex_t
*scan
;
7761 for (i
= 0, scan
= nwi_state_if_list(state
);
7762 i
< state
->if_list_count
; i
++, scan
++) {
7763 const char *ifname
= state
->ifstate_list
[*scan
].ifname
;
7764 ((uint32_t *)interface_order
.ifo_ordered_indices
)[i
] = my_if_nametoindex(ifname
);
7767 if (ioctl(sockfd
, SIOCSIFORDER
, &interface_order
) != 0) {
7768 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
));
7770 my_log(LOG_INFO
, "Set kernel interface order for %u interfaces", interface_order
.ifo_count
);
7773 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7774 free((void *)interface_order
.ifo_ordered_indices
);
7775 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)NULL
;
7780 #endif /* MANAGE_IF_ORDER */
7782 #ifdef MANAGE_IF_SIGNATURE
7784 siocsifnetsignature(int s
, const char * ifname
, int af
,
7785 const uint8_t * signature
, size_t signature_length
)
7787 struct if_nsreq nsreq
;
7789 memset(&nsreq
, 0, sizeof(nsreq
));
7790 strlcpy(nsreq
.ifnsr_name
, ifname
, sizeof(nsreq
.ifnsr_name
));
7791 nsreq
.ifnsr_family
= af
;
7792 if (signature_length
> 0) {
7793 if (signature_length
> sizeof(nsreq
.ifnsr_data
)) {
7794 signature_length
= sizeof(nsreq
.ifnsr_data
);
7796 nsreq
.ifnsr_len
= signature_length
;
7797 memcpy(nsreq
.ifnsr_data
, signature
, signature_length
);
7799 return (ioctl(s
, SIOCSIFNETSIGNATURE
, &nsreq
));
7803 process_ifstate_difference(nwi_ifstate_t ifstate
, int af
, int sockfd
)
7805 nwi_ifstate_difference_t diff
;
7806 boolean_t set_signature
= FALSE
;
7807 int signature_length
= 0;
7809 diff
= nwi_ifstate_get_difference(ifstate
);
7811 case knwi_ifstate_difference_changed
:
7812 /* set signature for this interface */
7813 set_signature
= TRUE
;
7814 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_SIGNATURE
) != 0) {
7815 signature_length
= sizeof(ifstate
->signature
);
7818 case knwi_ifstate_difference_removed
:
7819 /* remove signature for this interface */
7820 set_signature
= TRUE
;
7825 if (set_signature
) {
7826 if (siocsifnetsignature(sockfd
, ifstate
->ifname
, af
,
7828 signature_length
) < 0) {
7830 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7831 ifstate
->ifname
, ipvx_char(af
),
7836 my_log(LOG_DEBUG
, "IPv%c Network Signature %s %s",
7838 (signature_length
> 0) ? "Set" : "Cleared",
7840 if (signature_length
> 0
7841 && (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7843 char sig_buf
[signature_length
* 3 + 1];
7846 for (i
= 0; i
< signature_length
; i
++) {
7849 snprintf(byte_buf
, sizeof(byte_buf
),
7850 "%02x ", ifstate
->signature
[i
]);
7851 strlcat(sig_buf
, byte_buf
, sizeof(sig_buf
));
7853 my_log(LOG_DEBUG
, "Signature Bytes: %s", sig_buf
);
7861 process_state_differences(nwi_state_t state
, int af
, int sockfd
)
7867 if (af
== AF_INET
) {
7868 count
= state
->ipv4_count
;
7871 count
= state
->ipv6_count
;
7873 for (i
= 0, scan
= nwi_state_ifstate_list(state
, af
);
7874 i
< count
; i
++, scan
++) {
7875 process_ifstate_difference(scan
, af
, sockfd
);
7879 #endif /* MANAGE_IF_SIGNATURE */
7881 #endif /* !TARGET_OS_SIMULATOR */
7884 process_nwi_changes(CFMutableStringRef log_output
,
7885 nwi_state_t changes_state
,
7886 nwi_state_t new_state
,
7887 nwi_state_t old_state
,
7888 boolean_t dns_changed
,
7889 boolean_t dnsinfo_changed
,
7890 CFDictionaryRef old_primary_dns
,
7891 boolean_t proxy_changed
,
7892 CFDictionaryRef old_primary_proxy
,
7893 boolean_t smb_changed
,
7894 CFDictionaryRef old_primary_smb
)
7896 #ifndef MANAGE_IF_ORDER
7897 #pragma unused(new_state)
7898 #pragma unused(old_state)
7899 #endif // !MANAGE_IF_ORDER
7900 #if TARGET_OS_IPHONE
7901 #pragma unused(smb_changed)
7902 #pragma unused(old_primary_smb)
7903 #endif // TARGET_OS_IPHONE
7905 if (changes_state
!= NULL
) {
7906 const sa_family_t af_list
[] = {AF_INET
, AF_INET6
};
7908 #ifdef MANAGE_IF_IOCTL
7909 int sockfd
= inet_dgram_socket();
7910 #endif /* MANAGE_IF_IOCTL */
7912 #ifdef MANAGE_IF_ORDER
7913 if (interface_order_changed(new_state
, old_state
)) {
7914 update_interface_order(new_state
, sockfd
);
7916 #endif /* MANAGE_IF_ORDER */
7918 for (size_t idx
= 0; idx
< countof(af_list
); idx
++) {
7919 int af
= af_list
[idx
];
7920 CFMutableStringRef changes
= NULL
;
7921 CFMutableStringRef primary_str
= NULL
;
7923 #ifdef MANAGE_IF_SIGNATURE
7924 process_state_differences(changes_state
, af
, sockfd
);
7925 #endif /* MANAGE_IF_SIGNATURE */
7926 scan
= nwi_state_get_first_ifstate(changes_state
, af
);
7927 while (scan
!= NULL
) {
7928 const char * changed_str
;
7930 changed_str
= nwi_ifstate_get_diff_str(scan
);
7931 if (changed_str
!= NULL
) {
7933 const char * addr_str
;
7934 char ntopbuf
[INET6_ADDRSTRLEN
];
7936 address
= (void *)nwi_ifstate_get_address(scan
);
7937 addr_str
= inet_ntop(scan
->af
, address
, ntopbuf
,
7939 if (primary_str
== NULL
) {
7940 primary_str
= CFStringCreateMutable(NULL
, 0);
7941 CFStringAppendFormat(primary_str
, NULL
,
7943 nwi_ifstate_get_ifname(scan
),
7944 changed_str
, addr_str
);
7946 if (changes
== NULL
) {
7947 changes
= CFStringCreateMutable(NULL
, 0);
7949 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
7950 nwi_ifstate_get_ifname(scan
));
7951 if (strcmp(changed_str
, "") != 0) {
7952 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
7953 changed_str
, addr_str
);
7957 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
7960 if (primary_str
!= NULL
) {
7961 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
7962 af
== AF_INET
? "v4" : "v6",
7965 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
7966 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
7969 CFStringAppend(log_output
, CFSTR(")"));
7971 my_CFRelease(&primary_str
);
7972 my_CFRelease(&changes
);
7975 #ifdef MANAGE_IF_IOCTL
7979 #endif /* MANAGE_IF_IOCTL */
7982 if (dns_changed
|| dnsinfo_changed
) {
7985 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
7986 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
7987 str
= "*"; // dnsinfo change w/no change to primary
7989 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
7990 } else if (S_primary_dns
!= NULL
) {
7991 CFStringAppend(log_output
, CFSTR(" DNS"));
7994 if (proxy_changed
) {
7997 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
7998 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
7999 } else if (S_primary_proxies
!= NULL
) {
8000 CFStringAppend(log_output
, CFSTR(" Proxy"));
8003 #if !TARGET_OS_IPHONE
8007 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
8008 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
8009 } else if (S_primary_smb
!= NULL
) {
8010 CFStringAppend(log_output
, CFSTR(" SMB"));
8012 #endif // !TARGET_OS_IPHONE
8018 #pragma mark Network changed notification
8020 static dispatch_queue_t
8021 __network_change_queue()
8023 static dispatch_once_t once
;
8024 static dispatch_queue_t q
;
8026 dispatch_once(&once
, ^{
8027 q
= dispatch_queue_create("network change queue", NULL
);
8033 // Note: must run on __network_change_queue()
8035 post_network_change_when_ready()
8039 dispatch_assert_queue(__network_change_queue());
8041 if (S_network_change_needed
== 0) {
8045 if (!S_network_change_timeout
&&
8046 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
8047 // if we [still] need to wait for the DNS configuration
8048 // or network information changes to be ack'd
8050 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
8051 S_dnsinfo_synced
? "DNS" : "!DNS",
8052 S_nwi_synced
? "nwi" : "!nwi");
8056 // cancel any running timer
8057 if (S_network_change_timer
!= NULL
) {
8058 dispatch_source_cancel(S_network_change_timer
);
8059 dispatch_release(S_network_change_timer
);
8060 S_network_change_timer
= NULL
;
8061 S_network_change_timeout
= FALSE
;
8064 // set (and log?) the post time
8066 struct timeval elapsed
;
8069 (void) gettimeofday(&end
, NULL
);
8070 timersub(&end
, &S_network_change_start
, &elapsed
);
8072 #define QUERY_TIME__FMT "%ld.%6.6d"
8073 #define QUERY_TIME__DIV 1
8076 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
8077 S_network_change_timeout
? "timeout" : "delayed",
8079 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
8080 S_network_change_needed
);
8084 /* We are about to post a network change to everyone, get the agents up to date */
8085 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8086 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
8087 /* Setup or Update config agents */
8088 process_AgentMonitor_DNS();
8090 #endif //!TARGET_OS_SIMULATOR
8092 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
8093 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
8094 if (status
!= NOTIFY_STATUS_OK
) {
8096 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%d", status
);
8100 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
8101 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
8102 if (status
!= NOTIFY_STATUS_OK
) {
8104 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%d", status
);
8108 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
8109 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8110 /* Setup or Update config agents */
8111 process_AgentMonitor_Proxy();
8112 #endif //!TARGET_OS_SIMULATOR
8113 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8114 if (status
!= NOTIFY_STATUS_OK
) {
8116 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%d", status
);
8120 if ((S_network_change_needed
& NETWORK_CHANGE_NAT64
) != 0) {
8121 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8122 // process any NAT64 prefix update requests (and refresh existing prefixes on change)
8123 if (S_nat64_prefix_requests
!= NULL
|| S_nat64_prefix_updates
!= NULL
8124 || S_nat64_cancel_prefix_requests
!= NULL
) {
8125 nat64_configuration_update(S_nat64_prefix_requests
,
8126 S_nat64_prefix_updates
,
8127 S_nat64_cancel_prefix_requests
);
8128 my_CFRelease(&S_nat64_prefix_requests
);
8129 my_CFRelease(&S_nat64_prefix_updates
);
8130 my_CFRelease(&S_nat64_cancel_prefix_requests
);
8132 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8134 S_network_change_needed
&= ~(NETWORK_CHANGE_NAT64
);
8137 if (S_network_change_needed
!= 0) {
8138 // if more than just a NAT64 prefix change
8139 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
8140 if (status
!= NOTIFY_STATUS_OK
) {
8142 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%d", status
);
8146 S_network_change_needed
= 0;
8150 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
8152 // Note: must run on __network_change_queue()
8154 post_network_change(uint32_t change
)
8156 dispatch_assert_queue(__network_change_queue());
8158 if (S_network_change_needed
== 0) {
8159 // set the start time
8160 (void) gettimeofday(&S_network_change_start
, NULL
);
8163 // indicate that we need to post a change for ...
8164 S_network_change_needed
|= change
;
8166 // cancel any running timer
8167 if (S_network_change_timer
!= NULL
) {
8168 dispatch_source_cancel(S_network_change_timer
);
8169 dispatch_release(S_network_change_timer
);
8170 S_network_change_timer
= NULL
;
8171 S_network_change_timeout
= FALSE
;
8174 // if needed, start new timer
8175 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
8176 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
8179 __network_change_queue());
8180 dispatch_source_set_event_handler(S_network_change_timer
, ^{
8181 S_network_change_timeout
= TRUE
;
8182 post_network_change_when_ready();
8184 dispatch_source_set_timer(S_network_change_timer
,
8185 dispatch_time(DISPATCH_TIME_NOW
,
8186 TRAILING_EDGE_TIMEOUT_NSEC
), // start
8187 DISPATCH_TIME_FOREVER
, // interval
8188 10 * NSEC_PER_MSEC
); // leeway
8189 dispatch_resume(S_network_change_timer
);
8192 post_network_change_when_ready();
8198 #pragma mark Process network (SCDynamicStore) changes
8201 IPMonitorProcessChanges(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8202 CFArrayRef if_rank_changes
)
8205 uint32_t changes
= 0;
8206 nwi_state_t changes_state
= NULL
;
8207 boolean_t dns_changed
= FALSE
;
8208 boolean_t dnsinfo_changed
= FALSE
;
8209 boolean_t global_ipv4_changed
= FALSE
;
8210 boolean_t global_ipv6_changed
= FALSE
;
8211 CFMutableSetRef ipv4_service_changes
= NULL
;
8212 CFMutableSetRef ipv6_service_changes
= NULL
;
8215 boolean_t nat64_changed
= FALSE
;
8216 CFMutableStringRef network_change_msg
= NULL
;
8218 nwi_state_t old_nwi_state
= NULL
;
8219 CFDictionaryRef old_primary_dns
= NULL
;
8220 CFDictionaryRef old_primary_proxy
= NULL
;
8221 #if !TARGET_OS_IPHONE
8222 CFDictionaryRef old_primary_smb
= NULL
;
8223 #endif // !TARGET_OS_IPHONE
8224 boolean_t proxies_changed
= FALSE
;
8225 boolean_t reachability_changed
= FALSE
;
8226 CFArrayRef service_order
;
8227 CFMutableArrayRef service_changes
= NULL
;
8228 CFDictionaryRef services_info
= NULL
;
8229 #if !TARGET_OS_IPHONE
8230 boolean_t smb_changed
= FALSE
;
8231 #endif // !TARGET_OS_IPHONE
8233 /* populate name/index cache */
8236 if (changed_keys
!= NULL
) {
8237 count
= CFArrayGetCount(changed_keys
);
8238 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8240 "changed keys %@ (%ld)", changed_keys
, count
);
8243 if (if_rank_changes
== NULL
&& count
== 0) {
8247 if (S_primary_dns
!= NULL
) {
8248 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
8249 if (old_primary_dns
!= NULL
) {
8250 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
8254 if (S_primary_proxies
!= NULL
) {
8256 = service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
8257 if (old_primary_proxy
!= NULL
) {
8258 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
8262 #if !TARGET_OS_IPHONE
8263 if (S_primary_smb
!= NULL
) {
8264 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
8265 if (old_primary_smb
!= NULL
) {
8266 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
8269 #endif // !TARGET_OS_IPHONE
8271 keyChangeListInit(&keys
);
8272 service_changes
= CFArrayCreateMutable(NULL
, 0,
8273 &kCFTypeArrayCallBacks
);
8274 for (CFIndex i
= 0; i
< count
; i
++) {
8276 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8277 CFStringRef interface
= NULL
;
8278 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8280 change
= CFArrayGetValueAtIndex(changed_keys
, i
);
8281 if (CFEqual(change
, S_setup_global_ipv4
)) {
8282 global_ipv4_changed
= TRUE
;
8283 global_ipv6_changed
= TRUE
;
8285 else if (CFEqual(change
, S_multicast_resolvers
)) {
8286 dnsinfo_changed
= TRUE
;
8288 else if (CFEqual(change
, S_private_resolvers
)) {
8289 dnsinfo_changed
= TRUE
;
8291 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
8292 dnsinfo_changed
= TRUE
;
8294 else if (CFStringHasPrefix(change
, S_interface_delegation_prefix
) &&
8295 CFStringHasSuffix(change
, kSCEntNetInterfaceDelegation
)) {
8296 // ensure that we update the reachability flags in the NWI
8297 // state (including the non-interface-specific flags)
8298 reachability_changed
= TRUE
;
8299 // ensure that we update the reachability flags in the DNS
8301 dnsinfo_changed
= TRUE
;
8303 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
8304 CFStringRef protocol
= NULL
;
8305 CFStringRef serviceID
;
8307 serviceID
= parseNetworkServiceString(change
, &protocol
);
8308 if (serviceID
!= NULL
) {
8309 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8310 if (protocol
!= NULL
) {
8311 if (CFEqual(protocol
, kSCEntNetIPv4
)) {
8312 /* IPv4 service changed, remember that */
8313 my_CFSetAddValue(&ipv4_service_changes
, serviceID
);
8315 else if (CFEqual(protocol
, kSCEntNetIPv6
)) {
8316 /* IPv6 service changed, remember that */
8317 my_CFSetAddValue(&ipv6_service_changes
, serviceID
);
8320 CFRelease(serviceID
);
8322 my_CFRelease(&protocol
);
8324 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
8325 CFStringRef serviceID
;
8327 serviceID
= parseNetworkServiceString(change
, NULL
);
8328 if (serviceID
!= NULL
) {
8329 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8330 CFRelease(serviceID
);
8333 for (size_t j
= 0; j
< countof(transientInterfaceEntityNames
); j
++) {
8334 if (CFStringHasSuffix(change
,
8335 *transientInterfaceEntityNames
[j
])) {
8336 reachability_changed
= TRUE
;
8341 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
8342 reachability_changed
= TRUE
;
8345 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8346 else if (is_nat64_prefix_request(change
, &interface
)) {
8347 set_plat_discovery(kPLATDiscoveryOptionStart
, interface
);
8348 nat64_changed
= TRUE
;
8350 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8353 /* determine which serviceIDs are impacted by the interface rank changes */
8354 if (if_rank_changes
!= NULL
) {
8355 n
= CFArrayGetCount(if_rank_changes
);
8356 for (CFIndex i
= 0; i
< n
; i
++) {
8357 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
8359 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8360 my_log(LOG_DEBUG
, "Interface rank changed %@", ifname
);
8362 append_serviceIDs_for_interface(service_changes
, ifname
);
8366 /* grab a snapshot of everything we need */
8367 services_info
= services_info_copy(session
, service_changes
);
8368 assert(services_info
!= NULL
);
8370 /* grab the service order */
8371 service_order
= service_order_get(services_info
);
8372 if (service_order
!= NULL
) {
8373 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8374 my_log(LOG_DEBUG
, "service_order %@ ", service_order
);
8379 * process protocol (v4, v6, rank, ...) and
8380 * configuration (dns, proxies, smb, ...) changes
8382 n
= CFArrayGetCount(service_changes
);
8383 for (CFIndex i
= 0; i
< n
; i
++) {
8385 CFStringRef serviceID
;
8387 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
8388 changes
= service_changed(services_info
, serviceID
);
8389 if (my_CFSetContainsValue(ipv4_service_changes
, serviceID
)) {
8390 changes
|= (1 << kEntityTypeIPv4
);
8392 if (my_CFSetContainsValue(ipv6_service_changes
, serviceID
)) {
8393 changes
|= (1 << kEntityTypeIPv6
);
8395 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
8396 /* if __Service__ (e.g. PrimaryRank) changed */
8397 global_ipv4_changed
= TRUE
;
8398 global_ipv6_changed
= TRUE
;
8401 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
8402 global_ipv4_changed
= TRUE
;
8403 dnsinfo_changed
= TRUE
;
8404 proxies_changed
= TRUE
;
8406 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
8407 global_ipv6_changed
= TRUE
;
8408 dnsinfo_changed
= TRUE
;
8409 proxies_changed
= TRUE
;
8410 nat64_changed
= TRUE
;
8413 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
8414 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
8417 dnsinfo_changed
= TRUE
;
8418 nat64_changed
= TRUE
;
8420 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
8421 proxies_changed
= TRUE
;
8423 #if !TARGET_OS_IPHONE
8424 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
8425 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
8430 if ((changes
& (1 << kEntityTypeTransientStatus
)) != 0
8431 && (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
8432 || service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
)) {
8433 dnsinfo_changed
= TRUE
;
8437 /* ensure S_nwi_state can hold as many services as we have currently */
8438 n_services
= (int)CFDictionaryGetCount(S_service_state_dict
);
8439 old_nwi_state
= nwi_state_make_copy(S_nwi_state
);
8440 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
8442 if (global_ipv4_changed
) {
8443 if (S_ipv4_results
!= NULL
) {
8444 ElectionResultsRelease(S_ipv4_results
);
8447 = ElectionResultsCopy(AF_INET
, service_order
);
8448 ElectionResultsLog(LOG_INFO
, S_ipv4_results
, "IPv4");
8450 if (global_ipv6_changed
) {
8451 if (S_ipv6_results
!= NULL
) {
8452 ElectionResultsRelease(S_ipv6_results
);
8455 = ElectionResultsCopy(AF_INET6
, service_order
);
8456 ElectionResultsLog(LOG_INFO
, S_ipv6_results
, "IPv6");
8458 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
8459 CFStringRef new_primary
;
8460 CFStringRef new_primary_dns
= NULL
;
8461 CFStringRef new_primary_proxies
= NULL
;
8462 #if !TARGET_OS_IPHONE
8463 CFStringRef new_primary_smb
= NULL
;
8464 #endif /* !TARGET_OS_IPHONE */
8465 RouteListUnion new_routelist
;
8466 CandidateRef other_candidate
;
8467 CandidateRef primary_candidate
;
8469 if (S_nwi_state
!= NULL
) {
8470 nwi_state_clear(S_nwi_state
, AF_INET
);
8471 nwi_state_clear(S_nwi_state
, AF_INET6
);
8475 my_log(LOG_DEBUG
, "electing IPv4 primary");
8476 new_routelist
.ptr
= NULL
;
8477 other_candidate
= (S_ipv6_results
!= NULL
) /* get IPv6 primary */
8478 ? S_ipv6_results
->candidates
: NULL
;
8479 primary_candidate
= ElectionResultsGetPrimary(S_ipv4_results
,
8481 S_nwi_state
, AF_INET
,
8482 &new_routelist
.common
,
8484 ipv4_service_changes
);
8485 new_primary
= (primary_candidate
!= NULL
)
8486 ? primary_candidate
->serviceID
: NULL
;
8487 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
8488 update_ipv4(S_primary_ipv4
, new_routelist
.v4
, &keys
);
8491 my_log(LOG_DEBUG
, "electing IPv6 primary");
8492 new_routelist
.ptr
= NULL
;
8493 other_candidate
= primary_candidate
; /* get IPv4 primary */
8494 primary_candidate
= ElectionResultsGetPrimary(S_ipv6_results
,
8496 S_nwi_state
, AF_INET6
,
8497 &new_routelist
.common
,
8499 ipv6_service_changes
);
8500 new_primary
= (primary_candidate
!= NULL
)
8501 ? primary_candidate
->serviceID
: NULL
;
8502 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
8503 update_ipv6(S_primary_ipv6
, new_routelist
.v6
, &keys
);
8505 nwi_state_finalize(S_nwi_state
);
8507 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
8508 /* decide between IPv4 and IPv6 */
8509 if (rank_service_entity(S_ipv4_service_rank_dict
,
8510 S_primary_ipv4
, kSCEntNetDNS
)
8511 <= rank_service_entity(S_ipv6_service_rank_dict
,
8512 S_primary_ipv6
, kSCEntNetDNS
)) {
8513 new_primary_dns
= S_primary_ipv4
;
8516 new_primary_dns
= S_primary_ipv6
;
8518 if (rank_service_entity(S_ipv4_service_rank_dict
,
8519 S_primary_ipv4
, kSCEntNetProxies
)
8520 <= rank_service_entity(S_ipv6_service_rank_dict
,
8521 S_primary_ipv6
, kSCEntNetProxies
)) {
8522 new_primary_proxies
= S_primary_ipv4
;
8525 new_primary_proxies
= S_primary_ipv6
;
8527 #if !TARGET_OS_IPHONE
8528 if (rank_service_entity(S_ipv4_service_rank_dict
,
8529 S_primary_ipv4
, kSCEntNetSMB
)
8530 <= rank_service_entity(S_ipv6_service_rank_dict
,
8531 S_primary_ipv6
, kSCEntNetSMB
)) {
8532 new_primary_smb
= S_primary_ipv4
;
8535 new_primary_smb
= S_primary_ipv6
;
8537 #endif /* !TARGET_OS_IPHONE */
8540 else if (S_primary_ipv6
!= NULL
) {
8541 new_primary_dns
= S_primary_ipv6
;
8542 new_primary_proxies
= S_primary_ipv6
;
8543 #if !TARGET_OS_IPHONE
8544 new_primary_smb
= S_primary_ipv6
;
8545 #endif /* !TARGET_OS_IPHONE */
8547 else if (S_primary_ipv4
!= NULL
) {
8548 new_primary_dns
= S_primary_ipv4
;
8549 new_primary_proxies
= S_primary_ipv4
;
8550 #if !TARGET_OS_IPHONE
8551 new_primary_smb
= S_primary_ipv4
;
8552 #endif /* !TARGET_OS_IPHONE */
8555 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
8557 dnsinfo_changed
= TRUE
;
8559 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
,
8561 proxies_changed
= TRUE
;
8563 #if !TARGET_OS_IPHONE
8564 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
8567 #endif /* !TARGET_OS_IPHONE */
8570 if (!proxies_changed
&& dnsinfo_changed
8571 && ((G_supplemental_proxies_follow_dns
!= NULL
)
8572 && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
8573 proxies_changed
= TRUE
;
8576 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
8578 if (global_ipv4_changed
|| global_ipv6_changed
8579 || dnsinfo_changed
|| reachability_changed
) {
8580 if (S_nwi_state
!= NULL
) {
8581 S_nwi_state
->generation_count
= mach_absolute_time();
8582 if (global_ipv4_changed
|| global_ipv6_changed
8583 || reachability_changed
) {
8584 SCNetworkReachabilityFlags reach_flags_v4
= 0;
8585 SCNetworkReachabilityFlags reach_flags_v6
= 0;
8587 GetReachabilityFlagsFromTransientServices(services_info
,
8591 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
,
8595 /* Update the per-interface generation count */
8596 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
,
8600 if (update_nwi(S_nwi_state
)) {
8601 changes
|= NETWORK_CHANGE_NET
;
8604 * the DNS configuration includes per-resolver configuration
8605 * reachability flags that are based on the nwi state. Let's
8606 * make sure that we check for changes
8608 dnsinfo_changed
= TRUE
;
8612 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
8613 changes
|= NETWORK_CHANGE_DNS
;
8614 dnsinfo_changed
= TRUE
;
8616 dns_changed
= FALSE
;
8619 if (dnsinfo_changed
) {
8620 if (update_dnsinfo(services_info
, S_primary_dns
,
8621 &keys
, service_order
)) {
8622 changes
|= NETWORK_CHANGE_DNS
;
8624 dnsinfo_changed
= FALSE
;
8627 if (proxies_changed
) {
8628 // if proxy change OR supplemental Proxies follow supplemental DNS
8629 if (update_proxies(services_info
, S_primary_proxies
,
8630 &keys
, service_order
)) {
8631 changes
|= NETWORK_CHANGE_PROXY
;
8633 proxies_changed
= FALSE
;
8636 #if !TARGET_OS_IPHONE
8638 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
8639 changes
|= NETWORK_CHANGE_SMB
;
8641 smb_changed
= FALSE
;
8644 #endif /* !TARGET_OS_IPHONE */
8645 if (nat64_changed
) {
8646 changes
|= NETWORK_CHANGE_NAT64
;
8648 my_CFRelease(&service_changes
);
8649 my_CFRelease(&services_info
);
8650 my_CFRelease(&ipv4_service_changes
);
8651 my_CFRelease(&ipv6_service_changes
);
8654 network_change_msg
= CFStringCreateMutable(NULL
, 0);
8655 process_nwi_changes(network_change_msg
,
8664 #if !TARGET_OS_IPHONE
8667 #else // !TARGET_OS_IPHONE
8668 FALSE
, // smb_changed
8669 NULL
// old_primary_smb
8670 #endif // !TARGET_OS_IPHONE
8674 keyChangeListApplyToStore(&keys
, session
);
8675 my_CFRelease(&old_primary_dns
);
8676 my_CFRelease(&old_primary_proxy
);
8677 #if !TARGET_OS_IPHONE
8678 my_CFRelease(&old_primary_smb
);
8679 #endif // !TARGET_OS_IPHONE
8682 dispatch_async(__network_change_queue(), ^{
8683 post_network_change(changes
);
8687 if ((network_change_msg
!= NULL
)
8688 && (CFStringGetLength(network_change_msg
) != 0)) {
8689 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
8690 } else if (keyChangeListActive(&keys
)) {
8691 my_log(LOG_NOTICE
, "network changed");
8692 } else if (nat64_changed
) {
8693 my_log(LOG_NOTICE
, "nat64 update");
8695 my_log(LOG_INFO
, "network event w/no changes");
8698 my_CFRelease(&network_change_msg
);
8700 if (changes_state
!= NULL
) {
8701 nwi_state_free(changes_state
);
8703 if (old_nwi_state
!= NULL
) {
8704 nwi_state_free(old_nwi_state
);
8706 keyChangeListFree(&keys
);
8708 /* release the name/index cache */
8709 my_if_freenameindex();
8715 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8718 #pragma unused(info)
8719 IPMonitorProcessChanges(session
, changed_keys
, NULL
);
8723 #if !TARGET_OS_IPHONE
8724 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_mcx
8726 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_global
8732 static dispatch_queue_t proxy_cb_queue
;
8734 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
8735 _scprefs_observer_watch(PROXY_GLOBAL_OBSERVER_TYPE
,
8736 "com.apple.SystemConfiguration.plist",
8739 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
8740 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8741 /* Setup or Update config agents */
8742 process_AgentMonitor_Proxy();
8743 #endif //!TARGET_OS_SIMULATOR
8744 (void)notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8745 my_log(LOG_INFO
, "Notifying:\n%@",
8746 S_state_global_proxies
);
8751 #if TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST
8754 prefs_changed_callback_init(void)
8758 #else /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8760 #include "IPMonitorControlPrefs.h"
8763 prefs_changed(SCPreferencesRef prefs
)
8765 #pragma unused(prefs)
8766 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
8767 S_IPMonitor_debug
= kDebugFlagDefault
;
8768 S_IPMonitor_verbose
= TRUE
;
8769 my_log(LOG_DEBUG
, "Setting logging verbose mode on");
8771 my_log(LOG_DEBUG
, "Setting logging verbose mode off");
8772 S_IPMonitor_debug
= 0;
8773 S_IPMonitor_verbose
= FALSE
;
8779 prefs_changed_callback_init(void)
8781 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
8782 prefs_changed(NULL
);
8787 #endif /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8789 #if !TARGET_OS_SIMULATOR
8800 struct rt_msghdr
* rtm
;
8801 struct sockaddr_in
*sin
;
8807 mib
[4] = NET_RT_FLAGS
;
8808 mib
[5] = RTF_STATIC
| RTF_DYNAMIC
;
8809 for (i
= 0; i
< 3; i
++) {
8810 if (sysctl(mib
, N_MIB
, NULL
, &needed
, NULL
, 0) < 0) {
8813 if ((buf
= malloc(needed
)) == NULL
) {
8816 if (sysctl(mib
, N_MIB
, buf
, &needed
, NULL
, 0) >= 0) {
8826 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
8829 /* ALIGN: assume kernel provides necessary alignment */
8830 rtm
= (struct rt_msghdr
*)(void *)next
;
8831 sin
= (struct sockaddr_in
*)(rtm
+ 1);
8833 addr
= ntohl(sin
->sin_addr
.s_addr
);
8834 if (IN_LOOPBACK(addr
)) {
8836 "flush_routes: ignoring loopback route");
8839 if (IN_LOCAL_GROUP(addr
)) {
8841 "flush_routes: ignoring multicast route");
8844 rtm
->rtm_type
= RTM_DELETE
;
8845 rtm
->rtm_seq
= ++rtm_seq
;
8846 if (write(s
, rtm
, rtm
->rtm_msglen
) < 0) {
8848 "flush_routes: removing route for "
8849 IP_FORMAT
" failed: %s",
8850 IP_LIST(&sin
->sin_addr
),
8855 "flush_routes: removed route for " IP_FORMAT
,
8856 IP_LIST(&sin
->sin_addr
));
8864 flush_inet_routes(void)
8868 s
= open_routing_socket();
8875 #else /* !TARGET_OS_SIMULATOR */
8878 flush_inet_routes(void)
8882 #endif /* !TARGET_OS_SIMULATOR */
8886 ip_plugin_copy_keys(void)
8888 const void * values
[]
8890 S_setup_global_ipv4
, /* serviceOrder/PPPOverridePrimary */
8891 S_private_resolvers
, /* private DNS config (Back to My Mac) */
8892 S_multicast_resolvers
,/* multicast DNS config (Bonjour/.local) */
8894 const CFIndex values_count
= sizeof(values
) / sizeof(values
[0]);
8896 return (CFArrayCreate(NULL
, values
, values_count
,
8897 &kCFTypeArrayCallBacks
));
8901 ip_plugin_copy_patterns(void)
8903 CFStringRef pattern
;
8904 CFMutableArrayRef patterns
;
8906 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8908 /* State: and Setup: per-service notifications */
8909 add_service_keys(kSCCompAnyRegex
, patterns
);
8911 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
8912 CFArrayAppendValue(patterns
, pattern
);
8915 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
8916 CFArrayAppendValue(patterns
, pattern
);
8919 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
8920 CFArrayAppendValue(patterns
, pattern
);
8923 /* State: per-service PPP/VPN/IPSec status notifications */
8924 add_transient_status_keys(kSCCompAnyRegex
, patterns
);
8926 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8927 /* NAT64 prefix request pattern */
8928 nat64_prefix_request_add_pattern(patterns
);
8929 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8931 /* interface delegation pattern */
8932 pattern
= interface_entity_key_copy(kSCCompAnyRegex
,
8933 kSCEntNetInterfaceDelegation
);
8934 CFArrayAppendValue(patterns
, pattern
);
8942 if (S_is_network_boot() != 0) {
8947 flush_inet_routes();
8951 * Initialize globals
8953 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
8954 IPMonitorNotify
, NULL
);
8955 if (S_session
== NULL
) {
8957 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8958 SCErrorString(SCError()));
8962 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8963 kSCDynamicStoreDomainState
,
8966 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8967 kSCDynamicStoreDomainState
,
8970 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8971 kSCDynamicStoreDomainState
,
8973 S_state_global_proxies
8974 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8975 kSCDynamicStoreDomainState
,
8977 #if !TARGET_OS_IPHONE
8979 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8980 kSCDynamicStoreDomainState
,
8982 #endif /* !TARGET_OS_IPHONE */
8984 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8985 kSCDynamicStoreDomainSetup
,
8987 S_state_service_prefix
8988 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8989 kSCDynamicStoreDomainState
,
8992 S_setup_service_prefix
8993 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8994 kSCDynamicStoreDomainSetup
,
8997 S_interface_delegation_prefix
8998 = SCDynamicStoreKeyCreateNetworkInterface(NULL
,
8999 kSCDynamicStoreDomainState
);
9000 S_service_state_dict
9001 = CFDictionaryCreateMutable(NULL
, 0,
9002 &kCFTypeDictionaryKeyCallBacks
,
9003 &kCFTypeDictionaryValueCallBacks
);
9004 S_ipv4_service_rank_dict
9005 = CFDictionaryCreateMutable(NULL
, 0,
9006 &kCFTypeDictionaryKeyCallBacks
,
9007 &kCFTypeDictionaryValueCallBacks
);
9008 S_ipv6_service_rank_dict
9009 = CFDictionaryCreateMutable(NULL
, 0,
9010 &kCFTypeDictionaryKeyCallBacks
,
9011 &kCFTypeDictionaryValueCallBacks
);
9012 S_multicast_resolvers
9013 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
9014 kSCDynamicStoreDomainState
,
9016 CFSTR(kDNSServiceCompMulticastDNS
));
9018 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
9019 kSCDynamicStoreDomainState
,
9021 CFSTR(kDNSServiceCompPrivateDNS
));
9022 /* initialize dns configuration */
9023 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
9024 #if !TARGET_OS_IPHONE
9026 #endif /* !TARGET_OS_IPHONE */
9027 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
9029 #if !TARGET_OS_IPHONE
9030 /* initialize SMB configuration */
9031 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
9032 #endif /* !TARGET_OS_IPHONE */
9040 copy_dictionary_keys(CFDictionaryRef dict
)
9043 CFArrayRef ret_keys
;
9045 count
= CFDictionaryGetCount(dict
);
9047 const void * keys
[count
];
9049 CFDictionaryGetKeysAndValues(dict
, keys
, NULL
);
9050 ret_keys
= CFArrayCreate(NULL
, keys
, count
, &kCFTypeArrayCallBacks
);
9059 prime_notifications(CFArrayRef keys
, CFArrayRef patterns
)
9061 CFArrayRef changed_keys
;
9062 CFDictionaryRef info
;
9064 info
= SCDynamicStoreCopyMultiple(S_session
, keys
, patterns
);
9066 my_log(LOG_NOTICE
, "%s: no content", __func__
);
9069 changed_keys
= copy_dictionary_keys(info
);
9071 if (changed_keys
== NULL
) {
9072 my_log(LOG_NOTICE
, "%s: no keys", __func__
);
9076 "IPMonitor prime %ld keys %@",
9077 (long)CFArrayGetCount(changed_keys
), changed_keys
);
9078 IPMonitorProcessChanges(S_session
, changed_keys
, NULL
);
9079 CFRelease(changed_keys
);
9084 initialize_notifications(void)
9087 CFArrayRef patterns
;
9090 /* register for notifications */
9091 keys
= ip_plugin_copy_keys();
9092 patterns
= ip_plugin_copy_patterns();
9093 success
= SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
);
9096 "SCDynamicStoreSetNotificationKeys() failed: %s",
9097 SCErrorString(SCError()));
9100 CFRunLoopSourceRef rls
;
9102 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
9105 "SCDynamicStoreCreateRunLoopSource() failed: %s",
9106 SCErrorString(SCError()));
9109 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
9110 kCFRunLoopDefaultMode
);
9113 /* catch any changes that happened before registering */
9114 prime_notifications(keys
, patterns
);
9118 CFRelease(patterns
);
9124 prime_IPMonitor(void)
9126 /* initialize multicast route */
9127 update_ipv4(NULL
, NULL
, NULL
);
9129 if (S_session
== NULL
) {
9133 /* initialize notifications */
9134 CFRunLoopPerformBlock(CFRunLoopGetCurrent(),
9135 kCFRunLoopDefaultMode
,
9136 ^{ initialize_notifications(); });
9138 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9139 process_AgentMonitor();
9140 #endif // !TARGET_OS_SIMULATOR
9146 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
9150 boolean_t ret
= def
;
9152 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
9154 ret
= CFBooleanGetValue(b
);
9159 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
9160 #include "IPMonitorControlServer.h"
9163 InterfaceRankChanged(void * info
)
9165 #pragma unused(info)
9166 CFDictionaryRef assertions
= NULL
;
9169 changes
= IPMonitorControlServerCopyInterfaceRankInformation(&assertions
);
9170 if (S_if_rank_dict
!= NULL
) {
9171 CFRelease(S_if_rank_dict
);
9173 S_if_rank_dict
= assertions
;
9174 if (changes
!= NULL
) {
9175 IPMonitorProcessChanges(S_session
, NULL
, changes
);
9183 StartIPMonitorControlServer(void)
9185 CFRunLoopSourceContext context
;
9186 CFRunLoopSourceRef rls
;
9188 memset(&context
, 0, sizeof(context
));
9189 context
.perform
= InterfaceRankChanged
;
9190 rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
9191 if (!IPMonitorControlServerStart(CFRunLoopGetCurrent(),
9193 &S_bundle_logging_verbose
)) {
9194 my_log(LOG_ERR
, "IPMonitorControlServerStart failed");
9197 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
9198 kCFRunLoopDefaultMode
);
9204 #endif /* !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
9208 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
9210 CFDictionaryRef info_dict
;
9212 info_dict
= CFBundleGetInfoDictionary(bundle
);
9214 if (info_dict
!= NULL
) {
9216 = S_get_plist_boolean(info_dict
,
9217 CFSTR("AppendStateArrayToSetupArray"),
9220 if (bundleVerbose
) {
9221 S_IPMonitor_debug
= kDebugFlagDefault
;
9222 S_bundle_logging_verbose
= TRUE
;
9223 S_IPMonitor_verbose
= TRUE
;
9226 /* register to receive changes to the "verbose" flag and read the initial setting */
9227 prefs_changed_callback_init();
9229 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
9230 /* start DNS configuration (dnsinfo) server */
9231 load_DNSConfiguration(bundle
, // bundle
9232 ^(Boolean inSync
) { // syncHandler
9233 dispatch_async(__network_change_queue(), ^{
9234 S_dnsinfo_synced
= inSync
;
9237 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
9238 // all of the DNS service ack's should result
9239 // in a [new] network change being posted
9240 post_network_change(NETWORK_CHANGE_DNS
);
9242 post_network_change_when_ready();
9247 /* start Network Information (nwi) server */
9248 load_NetworkInformation(bundle
, // bundle
9249 ^(Boolean inSync
) { // syncHandler
9250 dispatch_async(__network_change_queue(), ^{
9251 S_nwi_synced
= inSync
;
9252 post_network_change_when_ready();
9255 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
9257 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
9258 /* start IPMonitor Control (InterfaceRank) server */
9259 StartIPMonitorControlServer();
9260 #endif /* !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
9262 /* initialize DNS configuration */
9263 dns_configuration_init(bundle
);
9265 /* initialize proxy configuration */
9266 proxy_configuration_init(bundle
);
9270 if (S_session
!= NULL
) {
9271 dns_configuration_monitor(S_session
, IPMonitorNotify
);
9274 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9275 load_hostname(TRUE
);
9276 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
9278 #if !TARGET_OS_IPHONE
9279 load_smb_configuration(TRUE
);
9280 #endif /* !TARGET_OS_IPHONE */
9287 #pragma mark Standalone test code
9290 #ifdef TEST_IPMONITOR
9293 main(int argc
, char **argv
)
9295 _sc_log
= kSCLogDestinationFile
;
9297 S_IPMonitor_debug
= kDebugFlag1
;
9299 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9302 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
9304 S_IPMonitor_debug
= kDebugFlag1
;
9310 #endif /* TEST_IPMONITOR */
9312 #if defined(TEST_ROUTELIST) || defined(TEST_DNS_ORDER)
9314 #if defined(__has_feature)
9315 #if __has_feature(address_sanitizer)
9316 #define TEST_ROUTELIST_RUN_LEAKS FALSE
9317 #endif /* if __has_feature(address_sanitizer) */
9318 #endif /* if defined(__has_feature) */
9320 #ifndef TEST_ROUTELIST_RUN_LEAKS
9321 #define TEST_ROUTELIST_RUN_LEAKS TRUE
9324 static boolean_t S_run_leaks
= TEST_ROUTELIST_RUN_LEAKS
;
9329 printf("\nTest Complete\n");
9333 sprintf(cmd
, "leaks %d 2>&1", getpid());
9342 const char * gateway
;
9343 const char * ifname
;
9348 #if TEST_IPV4_ROUTELIST
9354 const char * router
;
9355 const char * ifname
;
9357 const CFStringRef
* primary_rank
;
9358 struct route
* additional_routes
;
9359 int additional_routes_count
;
9360 struct route
* excluded_routes
;
9361 int excluded_routes_count
;
9362 } IPv4ServiceContents
;
9364 typedef const IPv4ServiceContents
* IPv4ServiceContentsRef
;
9366 struct route loop_routelist
[] = {
9367 { "1.1.1.1", 32, "1.1.1.2", NULL
},
9368 { "1.1.1.2", 32, "1.1.1.3", NULL
},
9369 { "1.1.1.3", 32, "1.1.1.4", NULL
},
9370 { "1.1.1.4", 32, "1.1.1.5", NULL
},
9371 { "1.1.1.5", 32, "1.1.1.6", NULL
},
9372 { "1.1.1.6", 32, "1.1.1.7", NULL
},
9373 { "1.1.1.7", 32, "1.1.1.8", NULL
},
9374 { "1.1.1.8", 32, "1.1.1.9", NULL
},
9375 { "1.1.1.9", 32, "1.1.1.10", NULL
},
9376 { "1.1.1.10", 32, "1.1.1.11", NULL
},
9377 { "1.1.1.11", 32, "1.1.1.1", NULL
},
9380 struct route vpn_routelist
[] = {
9381 { "10.1.3.0", 24, "17.153.46.24", NULL
},
9382 { "10.1.4.0", 24, "17.153.46.24", NULL
},
9383 { "10.1.5.0", 24, "17.153.46.24", NULL
},
9384 { "10.1.6.0", 24, "17.153.46.24", NULL
},
9385 { "10.1.7.0", 24, "17.153.46.24", NULL
},
9386 { "10.16.0.0", 12, "17.153.46.24", NULL
},
9387 { "10.45.0.0", 16, "17.153.46.24", NULL
},
9388 { "10.53.0.0", 16, "17.153.46.24", NULL
},
9389 { "10.70.0.0", 15, "17.153.46.24", NULL
},
9390 { "10.74.0.0", 15, "17.153.46.24", NULL
},
9391 { "10.90.0.0", 15, "17.153.46.24", NULL
},
9392 { "10.91.0.0", 16, "17.153.46.24", NULL
},
9393 { "10.100.0.0", 16, "17.153.46.24", NULL
},
9394 { "10.113.0.0", 16, "17.153.46.24", NULL
},
9395 { "10.128.0.0", 9, "17.153.46.24", NULL
},
9396 { "17.0.0.0", 9, "17.153.46.24", NULL
},
9397 { "17.34.0.0", 16, "17.153.46.24", NULL
},
9398 { "17.112.156.53", 32, "17.153.46.24", NULL
},
9399 { "17.128.0.0", 10, "17.153.46.24", NULL
},
9400 { "17.149.0.121", 32, "17.153.46.24", NULL
},
9401 { "17.149.7.200", 32, "17.153.46.24", NULL
},
9402 { "17.153.46.24", 32, "17.153.46.24", NULL
},
9403 { "17.192.0.0", 12, "17.153.46.24", NULL
},
9404 { "17.208.0.0", 15, "17.153.46.24", NULL
},
9405 { "17.211.0.0", 16, "17.153.46.24", NULL
},
9406 { "17.212.0.0", 14, "17.153.46.24", NULL
},
9407 { "17.216.0.0", 13, "17.153.46.24", NULL
},
9408 { "17.224.0.0", 12, "17.153.46.24", NULL
},
9409 { "17.240.0.0", 16, "17.153.46.24", NULL
},
9410 { "17.241.0.0", 16, "17.153.46.24", NULL
},
9411 { "17.248.0.0", 14, "17.153.46.24", NULL
},
9412 { "17.251.104.200", 32, "17.153.46.24", NULL
},
9413 { "17.252.0.0", 16, "17.153.46.24", NULL
},
9414 { "17.253.0.0", 16, "17.153.46.24", NULL
},
9415 { "17.254.0.0", 16, "17.153.46.24", NULL
},
9416 { "17.255.0.0", 16, "17.153.46.24", NULL
},
9417 { "151.193.141.0", 27, "17.153.46.24", NULL
},
9418 { "172.16.2.0", 24, "17.153.46.24", NULL
},
9419 { "192.35.50.0", 24, "17.153.46.24", NULL
},
9420 { "204.179.20.0", 24, "17.153.46.24", NULL
},
9421 { "206.112.116.0", 24, "17.153.46.24", NULL
},
9424 struct route vpn_routelist_ext
[] = {
9425 { "17.151.63.82", 32, "10.0.0.1", "en0" },
9426 { "17.151.63.81", 32, "17.151.63.81", "en0" },
9427 { "17.151.63.80", 32, NULL
, NULL
},
9428 { "17.1.0.0", 16, NULL
, NULL
},
9429 { "17.2.0.0", 24, NULL
, NULL
},
9430 { "10.0.0.0", 24, NULL
, NULL
},
9434 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
9436 const IPv4ServiceContents en0_10
= {
9437 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9440 const IPv4ServiceContents en0_15
= {
9441 "10.0.0.19", 24, NULL
, "10.0.0.1", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9444 const IPv4ServiceContents en0_30
= {
9445 "10.0.0.11", 24, NULL
, "10.0.0.1", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9448 const IPv4ServiceContents en0_40
= {
9449 "10.0.0.12", 24, NULL
, "10.0.0.1", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9452 const IPv4ServiceContents en0_50
= {
9453 "10.0.0.13", 24, NULL
, "10.0.0.1", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9456 const IPv4ServiceContents en0_110
= {
9457 "192.168.2.10", 24, NULL
, "192.168.2.1", "en0", 110, NULL
, NULL
, 0, NULL
, 0
9460 const IPv4ServiceContents en0_1
= {
9461 "17.202.40.191", 22, NULL
, "17.202.20.1", "en0", 1, NULL
, NULL
, 0, NULL
, 0
9464 const IPv4ServiceContents en1_20
= {
9465 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9468 const IPv4ServiceContents en1_2
= {
9469 "17.202.42.24", 22, NULL
, "17.202.20.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9472 const IPv4ServiceContents en1_125
= {
9473 "192.168.2.20", 24, NULL
, "192.168.2.1", "en1", 125, NULL
, NULL
, 0, NULL
, 0
9476 const IPv4ServiceContents fw0_25
= {
9477 "192.168.2.30", 24, NULL
, "192.168.2.1", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9480 const IPv4ServiceContents fw0_21
= {
9481 "192.168.3.30", 24, NULL
, "192.168.3.1", "fw0", 21, NULL
, NULL
, 0, NULL
, 0
9484 const IPv4ServiceContents ppp0_0_1
= {
9485 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
, NULL
, 0, NULL
, 0
9488 const IPv4ServiceContents utun0
= {
9489 "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
)
9492 const IPv4ServiceContents en0_test6
= {
9493 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 2, NULL
, NULL
, 0, NULL
, 0
9496 const IPv4ServiceContents en1_test6
= {
9497 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 3, NULL
, NULL
, 0, NULL
, 0
9500 const IPv4ServiceContents en2_test6
= {
9501 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9504 const IPv4ServiceContents en0_test7
= {
9505 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 3, NULL
, NULL
, 0, NULL
, 0
9508 const IPv4ServiceContents en1_test7
= {
9509 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9512 const IPv4ServiceContents en2_test7
= {
9513 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9516 const IPv4ServiceContents fw0_test6_and_7
= {
9517 "169.254.11.33", 16, NULL
, NULL
, "fw0", 0x0ffffff, NULL
, NULL
, 0, NULL
, 0
9520 const IPv4ServiceContents en0_10_last
= {
9521 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
, NULL
, 0, NULL
, 0
9524 const IPv4ServiceContents en0_10_never
= {
9525 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9528 const IPv4ServiceContents en1_20_first
= {
9529 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
, NULL
, 0, NULL
, 0
9532 const IPv4ServiceContents en1_20_never
= {
9533 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9536 const IPv4ServiceContents en1_20_other_never
= {
9537 "192.168.2.50", 24, NULL
, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9540 const IPv4ServiceContents en0_linklocal
= {
9541 "169.254.22.44", 16, NULL
, NULL
, "en0", 0xfffff, NULL
, NULL
, 0, NULL
, 0
9544 const IPv4ServiceContents en0_route_loop
= {
9545 "192.168.130.16", 24, NULL
, "192.168.130.1", "en0", 2, NULL
, loop_routelist
, countof(loop_routelist
), NULL
, 0
9550 IPv4ServiceContentsRef test
[];
9551 } IPv4RouteTest
, * IPv4RouteTestRef
;
9553 static IPv4RouteTest test1
= {
9567 static IPv4RouteTest test2
= {
9580 static IPv4RouteTest test3
= {
9597 static IPv4RouteTest test4
= {
9609 static IPv4RouteTest test5
= {
9622 static IPv4RouteTest test6
= {
9633 static IPv4RouteTest test7
= {
9644 static IPv4RouteTest test8
= {
9653 static IPv4RouteTest test9
= {
9663 static IPv4RouteTest test10
= {
9673 static IPv4RouteTest test11
= {
9683 static IPv4RouteTest test12
= {
9692 static IPv4RouteTest test13
= {
9701 static IPv4RouteTest test14
= {
9709 static IPv4RouteTest test15
= {
9717 static IPv4RouteTest test16
= {
9726 static IPv4RouteTest test17
= {
9730 &en1_20_other_never
,
9735 static IPv4RouteTest test18
= {
9743 static IPv4RouteTestRef ipv4_tests
[] = {
9766 ipv4_prefix_length_is_valid(int prefix_length
)
9768 if (prefix_length
< 0 || prefix_length
> IPV4_ROUTE_ALL_BITS_SET
) {
9775 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9778 CFStringRef prop_val
;
9783 prop_val
= CFStringCreateWithCString(NULL
,
9785 kCFStringEncodingASCII
);
9786 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9787 CFRelease(prop_val
);
9792 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9796 CFStringRef prop_val
;
9801 prop_val
= CFStringCreateWithCString(NULL
,
9803 kCFStringEncodingASCII
);
9804 array
= CFArrayCreate(NULL
,
9805 (const void **)&prop_val
, 1,
9806 &kCFTypeArrayCallBacks
);
9807 CFRelease(prop_val
);
9808 CFDictionarySetValue(dict
, prop_name
, array
);
9814 dict_add_ip(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9819 str
= my_CFStringCreateWithInAddr(ip
);
9820 CFDictionarySetValue(dict
, prop_name
, str
);
9826 dict_add_ip_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9832 str
= my_CFStringCreateWithInAddr(ip
);
9833 array
= CFArrayCreate(NULL
,
9834 (const void **)&str
, 1,
9835 &kCFTypeArrayCallBacks
);
9837 CFDictionarySetValue(dict
, prop_name
, array
);
9843 dict_insert_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9844 struct route
* routes
, int routes_count
)
9847 CFMutableArrayRef route_list
;
9848 struct route
* scan
;
9850 if (routes
== NULL
|| routes_count
== 0) {
9853 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9854 &kCFTypeArrayCallBacks
);
9855 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9856 struct in_addr mask
;
9857 CFMutableDictionaryRef route_dict
;
9860 = CFDictionaryCreateMutable(NULL
, 0,
9861 &kCFTypeDictionaryKeyCallBacks
,
9862 &kCFTypeDictionaryValueCallBacks
);
9863 dict_add_string(route_dict
, kSCPropNetIPv4RouteDestinationAddress
,
9865 if (ipv4_prefix_length_is_valid(scan
->prefix_length
)) {
9866 mask
.s_addr
= htonl(prefix_to_mask32(scan
->prefix_length
));
9867 dict_add_ip(route_dict
, kSCPropNetIPv4RouteSubnetMask
, mask
);
9869 dict_add_string(route_dict
, kSCPropNetIPv4RouteGatewayAddress
,
9871 dict_add_string(route_dict
, kSCPropNetIPv4RouteInterfaceName
,
9873 CFArrayAppendValue(route_list
, route_dict
);
9874 CFRelease(route_dict
);
9876 CFDictionarySetValue(dict
, prop_name
, route_list
);
9877 CFRelease(route_list
);
9881 static CFDictionaryRef
9882 make_IPv4_dict(IPv4ServiceContentsRef t
)
9884 CFMutableDictionaryRef dict
;
9886 dict
= CFDictionaryCreateMutable(NULL
, 0,
9887 &kCFTypeDictionaryKeyCallBacks
,
9888 &kCFTypeDictionaryValueCallBacks
);
9889 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
9890 if (ipv4_prefix_length_is_valid(t
->prefix_length
)) {
9891 struct in_addr mask
;
9893 mask
.s_addr
= htonl(prefix_to_mask32(t
->prefix_length
));
9894 dict_add_ip_as_array(dict
, kSCPropNetIPv4SubnetMasks
, mask
);
9896 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
9897 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
9898 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9899 dict_add_string(dict
, kSCPropConfirmedInterfaceName
, t
->ifname
);
9900 dict_insert_routes(dict
, kSCPropNetIPv4AdditionalRoutes
,
9901 t
->additional_routes
, t
->additional_routes_count
);
9902 dict_insert_routes(dict
, kSCPropNetIPv4ExcludedRoutes
,
9903 t
->excluded_routes
, t
->excluded_routes_count
);
9908 kDirectionForwards
= 0,
9909 kDirectionBackwards
= 1
9913 kLogRouteDisabled
= 0,
9914 kLogRouteEnabled
= 1
9917 static IPv4RouteListRef
9918 make_IPv4RouteList_for_test(IPv4RouteListRef list
,
9919 IPv4ServiceContentsRef test
,
9922 CFDictionaryRef dict
;
9925 Rank rank_assertion
= kRankAssertionDefault
;
9926 CFNumberRef rank_assertion_cf
= NULL
;
9927 Boolean rank_assertion_is_set
= FALSE
;
9928 IPv4RouteListRef ret
= NULL
;
9929 IPV4_ROUTES_BUF_DECL(routes
);
9931 dict
= make_IPv4_dict(test
);
9933 fprintf(stderr
, "make_IPv4_dict failed\n");
9936 if (test
->primary_rank
!= NULL
) {
9938 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9939 &rank_assertion_is_set
);
9940 if (rank_assertion_is_set
) {
9942 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9945 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion_cf
);
9946 my_CFRelease(&rank_assertion_cf
);
9948 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
9952 if (rank_assertion
== kRankAssertionScoped
) {
9953 rank_assertion
= kRankAssertionNever
;
9955 rank
= RankMake(test
->rank
, rank_assertion
);
9956 if (log_it
== kLogRouteEnabled
9957 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9960 descr
= IPv4RouteListCopyDescription(r
);
9961 SCPrint(TRUE
, stdout
, CFSTR("Adding %@\n"), descr
);
9964 ret
= IPv4RouteListAddRouteList(list
, 1, r
, rank
);
9972 static IPv4RouteListRef
9973 make_IPv4RouteList(IPv4ServiceContentsRef
* test
, Direction direction
,
9976 IPv4RouteListRef ret
= NULL
;
9977 IPv4ServiceContentsRef
* scan
;
9979 switch (direction
) {
9980 case kDirectionBackwards
:
9981 for (scan
= test
; *scan
!= NULL
; scan
++) {
9982 /* find the end of the list */
9984 for (scan
--; scan
>= test
; scan
--) {
9985 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9989 case kDirectionForwards
:
9990 for (scan
= test
; *scan
!= NULL
; scan
++) {
9991 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9995 IPv4RouteListFinalize(ret
);
9999 #define EMPHASIS_CHARS "================="
10002 * Function: routelist_build_test
10004 * Runs through the given set of routes first in the forward direction,
10005 * then again backwards. We should end up with exactly the same set of
10006 * routes at the end.
10009 routelist_build_test(IPv4RouteTestRef test
)
10012 boolean_t ret
= FALSE
;
10013 IPv4RouteListRef routes1
;
10014 IPv4RouteListRef routes2
;
10016 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
10017 EMPHASIS_CHARS
"\n",
10020 routes1
= make_IPv4RouteList(test
->test
, kDirectionForwards
,
10022 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10023 if (routes1
!= NULL
) {
10024 descr
= IPv4RouteListCopyDescription(routes1
);
10025 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10029 routes2
= make_IPv4RouteList(test
->test
, kDirectionBackwards
,
10031 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10032 if (routes2
!= NULL
) {
10033 descr
= IPv4RouteListCopyDescription(routes2
);
10034 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10038 if ((routes1
!= NULL
&& routes2
== NULL
)
10039 || (routes1
== NULL
&& routes2
!= NULL
)) {
10040 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
10041 (routes1
!= NULL
) ? "not " : "",
10042 (routes2
!= NULL
) ? "not " : "");
10044 else if (routes1
!= NULL
&& routes2
!= NULL
) {
10045 /* check if they are different */
10046 if (routes1
->count
!= routes2
->count
) {
10047 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
10048 routes1
->count
, routes2
->count
);
10050 else if (bcmp(routes1
, routes2
,
10051 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
10052 fprintf(stderr
, "routes1 and routes2 are different\n");
10055 printf("routes1 and routes2 are the same\n");
10059 if (routes1
!= NULL
) {
10062 if (routes2
!= NULL
) {
10065 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
10066 EMPHASIS_CHARS
"\n",
10067 test
->name
, ret
? "PASSED" : "FAILED");
10072 apply_test(IPv4RouteTestRef old_test
, IPv4RouteTestRef new_test
)
10074 IPv4RouteListRef new_routes
;
10075 IPv4RouteListRef old_routes
;
10077 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
10078 EMPHASIS_CHARS
"\n",
10079 old_test
->name
, new_test
->name
);
10081 old_routes
= make_IPv4RouteList(old_test
->test
, kDirectionForwards
,
10082 kLogRouteDisabled
);
10083 new_routes
= make_IPv4RouteList(new_test
->test
, kDirectionForwards
,
10084 kLogRouteDisabled
);
10085 if (old_routes
== NULL
) {
10086 printf("No Old Routes\n");
10089 printf("Old routes ('%s') = ", old_test
->name
);
10090 IPv4RouteListPrint(old_routes
);
10093 /* apply the old routes */
10094 IPv4RouteListApply(NULL
, old_routes
, -1);
10096 if (new_routes
== NULL
) {
10097 printf("No New Routes\n");
10100 printf("New Routes ('%s') = ", new_test
->name
);
10101 IPv4RouteListPrint(new_routes
);
10104 /* apply the new routes */
10105 IPv4RouteListApply(old_routes
, new_routes
, -1);
10107 if (old_routes
!= NULL
) {
10110 if (new_routes
!= NULL
) {
10113 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10114 EMPHASIS_CHARS
"\n",
10115 old_test
->name
, new_test
->name
);
10120 main(int argc
, char **argv
)
10122 IPv4RouteTestRef
* test
;
10124 _sc_log
= kSCLogDestinationFile
;
10125 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10126 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10128 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10130 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
10131 if (!routelist_build_test(*test
)) {
10132 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10136 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
10137 IPv4RouteTestRef
* test2
;
10139 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10140 apply_test(*test
, *test2
);
10141 apply_test(*test2
, *test
);
10149 #endif /* TEST_IPV4_ROUTELIST */
10151 #if TEST_IPV6_ROUTELIST
10159 typedef const IPv6Address
* IPv6AddressRef
;
10162 IPv6AddressRef addr
;
10164 const char * router
;
10165 const char * ifname
;
10167 const CFStringRef
* primary_rank
;
10168 struct route
* additional_routes
;
10169 int additional_routes_count
;
10170 struct route
* excluded_routes
;
10171 int excluded_routes_count
;
10172 } IPv6ServiceContents
;
10174 typedef const IPv6ServiceContents
* IPv6ServiceContentsRef
;
10176 struct route loop_routelist
[] = {
10177 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
10178 "2620:149:4:f01:225:ff:fecc:89a2", NULL
},
10179 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
10180 "2620:149:4:f01:225:ff:fecc:89a3", NULL
},
10181 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
10182 "2620:149:4:f01:225:ff:fecc:89a4", NULL
},
10183 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
10184 "2620:149:4:f01:225:ff:fecc:89a5", NULL
},
10185 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
10186 "2620:149:4:f01:225:ff:fecc:89a6", NULL
},
10187 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
10188 "2620:149:4:f01:225:ff:fecc:89a7", NULL
},
10189 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
10190 "2620:149:4:f01:225:ff:fecc:89a8", NULL
},
10191 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
10192 "2620:149:4:f01:225:ff:fecc:89a9", NULL
},
10193 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
10194 "2620:149:4:f01:225:ff:fecc:89aa", NULL
},
10195 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
10196 "2620:149:4:f01:225:ff:fecc:89ab", NULL
},
10197 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
10198 "2620:149:4:f01:225:ff:fecc:89a1", NULL
},
10201 struct route vpn_routelist
[] = {
10202 { "2010:470:1f05:3cb::", 64,
10203 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10204 { "2010:222:3fa5:acb::", 48,
10205 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10206 { "2010:222:3fa5:1234::", 40,
10207 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10208 { "2010:222:3fa5:5678::", 40,
10212 struct route vpn_routelist_ext
[] = {
10213 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL
, NULL
},
10216 struct route en1_routelist_ext
[] = {
10217 { "2020:299:abcd:ef12::", 64, NULL
, NULL
},
10221 static const IPv6Address en0_addr1
[] = {
10222 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL
},
10223 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL
}
10226 static const IPv6Address en0_addr2
[] = {
10227 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL
},
10228 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL
}
10231 static const IPv6Address en0_addr3
[] = {
10232 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL
},
10233 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL
}
10236 static const IPv6Address en0_addr4
[] = {
10237 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL
},
10238 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL
}
10241 static const IPv6Address en0_addr5
[] = {
10242 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL
},
10243 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL
}
10246 static const IPv6Address en0_addr6
[] = {
10247 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL
},
10250 static const IPv6Address en0_lladdr
[] = {
10251 { "fe80::cabc:c8ff:fe96:96af", 64, NULL
}
10254 static const IPv6Address en1_addr
[] = {
10255 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL
},
10256 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL
}
10259 static const IPv6Address utun0_addr
[] = {
10260 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL
},
10263 static const IPv6Address fw0_addr1
[] = {
10264 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL
},
10265 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL
}
10269 * address+address-count
10270 * router ifname pri rank additional-routes+count excluded-routes+count
10273 static const IPv6ServiceContents en0_10
= {
10274 en0_addr1
, countof(en0_addr1
),
10275 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
10278 static const IPv6ServiceContents en0_15
= {
10279 en0_addr2
, countof(en0_addr2
),
10280 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL
, NULL
, 0, NULL
, 0
10283 static const IPv6ServiceContents en0_30
= {
10284 en0_addr3
, countof(en0_addr3
),
10285 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL
, NULL
, 0, NULL
, 0
10288 static const IPv6ServiceContents en0_40
= {
10289 en0_addr4
, countof(en0_addr4
),
10290 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL
, NULL
, 0, NULL
, 0
10293 static const IPv6ServiceContents en0_50
= {
10294 en0_addr5
, countof(en0_addr5
),
10295 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL
, NULL
, 0, NULL
, 0
10298 static const IPv6ServiceContents en0_10_a
= {
10299 en0_addr6
, countof(en0_addr6
),
10300 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
10303 static const IPv6ServiceContents fw0_25
= {
10304 fw0_addr1
, countof(fw0_addr1
),
10305 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
10308 static const IPv6ServiceContents en1_20
= {
10309 en1_addr
, countof(en1_addr
),
10310 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL
, NULL
, 0, NULL
, 0
10313 static const IPv6ServiceContents en1_10_ext
= {
10314 en1_addr
, countof(en1_addr
),
10315 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL
, NULL
, 0,
10316 en1_routelist_ext
, countof(en1_routelist_ext
)
10319 static const IPv6ServiceContents en0_0_lladdr
= {
10320 en0_lladdr
, countof(en0_lladdr
),
10321 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL
, NULL
, 0, NULL
, 0
10324 static const IPv6ServiceContents en0_loop
= {
10325 en0_addr1
, countof(en0_addr1
),
10326 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
,
10327 loop_routelist
, countof(loop_routelist
), NULL
, 0
10330 static const IPv6ServiceContents utun0
= {
10331 utun0_addr
, countof(utun0_addr
),
10332 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL
,
10333 vpn_routelist
, countof(vpn_routelist
),
10334 vpn_routelist_ext
, countof(vpn_routelist_ext
),
10339 IPv6ServiceContentsRef test
[];
10340 } IPv6RouteTest
, * IPv6RouteTestRef
;
10342 static IPv6RouteTest test1
= {
10356 static IPv6RouteTest test2
= {
10369 static IPv6RouteTest test3
= {
10378 static IPv6RouteTest test4
= {
10387 static IPv6RouteTest test5
= {
10399 static IPv6RouteTestRef ipv6_tests
[] = {
10410 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10413 CFStringRef prop_val
;
10418 prop_val
= CFStringCreateWithCString(NULL
,
10420 kCFStringEncodingASCII
);
10421 CFDictionarySetValue(dict
, prop_name
, prop_val
);
10422 CFRelease(prop_val
);
10427 dict_add_int(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10432 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10433 CFDictionarySetValue(dict
, prop_name
, num
);
10439 dict_insert_v6_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10440 struct route
* routes
, int routes_count
)
10443 CFMutableArrayRef route_list
;
10444 struct route
* scan
;
10446 if (routes
== NULL
|| routes_count
== 0) {
10449 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
10450 &kCFTypeArrayCallBacks
);
10451 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
10452 CFMutableDictionaryRef route_dict
;
10454 route_dict
= CFDictionaryCreateMutable(NULL
, 0,
10455 &kCFTypeDictionaryKeyCallBacks
,
10456 &kCFTypeDictionaryValueCallBacks
);
10457 dict_add_string(route_dict
, kSCPropNetIPv6RouteDestinationAddress
,
10459 dict_add_int(route_dict
, kSCPropNetIPv6PrefixLength
,
10460 scan
->prefix_length
);
10461 dict_add_string(route_dict
, kSCPropNetIPv6RouteGatewayAddress
,
10463 dict_add_string(route_dict
, kSCPropNetIPv6RouteInterfaceName
,
10465 CFArrayAppendValue(route_list
, route_dict
);
10466 CFRelease(route_dict
);
10468 CFDictionarySetValue(dict
, prop_name
, route_list
);
10469 CFRelease(route_list
);
10474 array_add_string(CFMutableArrayRef array
, const char * c_str
)
10478 str
= CFStringCreateWithCString(NULL
,
10480 kCFStringEncodingUTF8
);
10481 CFArrayAppendValue(array
, str
);
10487 array_add_int(CFMutableArrayRef array
, int int_val
)
10491 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10492 CFArrayAppendValue(array
, num
);
10498 dict_add_ipv6_addressing(CFMutableDictionaryRef dict
,
10499 IPv6AddressRef list
, int list_count
)
10501 CFMutableArrayRef addr
= NULL
;
10502 CFMutableArrayRef dest
= NULL
;
10504 CFMutableArrayRef prefix
= NULL
;
10505 IPv6AddressRef scan
;
10507 if (list
== NULL
|| list_count
== 0) {
10510 for (i
= 0, scan
= list
; i
< list_count
; i
++, scan
++) {
10511 if (scan
->addr
!= NULL
) {
10512 if (addr
== NULL
) {
10513 addr
= CFArrayCreateMutable(NULL
, list_count
,
10514 &kCFTypeArrayCallBacks
);
10516 array_add_string(addr
, scan
->addr
);
10518 if (scan
->prefix_length
>= 0) {
10519 if (prefix
== NULL
) {
10520 prefix
= CFArrayCreateMutable(NULL
, list_count
,
10521 &kCFTypeArrayCallBacks
);
10523 array_add_int(prefix
, scan
->prefix_length
);
10525 if (scan
->dest
!= NULL
) {
10526 if (dest
== NULL
) {
10527 dest
= CFArrayCreateMutable(NULL
, list_count
,
10528 &kCFTypeArrayCallBacks
);
10530 array_add_string(dest
, scan
->dest
);
10533 if (addr
!= NULL
) {
10534 CFDictionarySetValue(dict
, kSCPropNetIPv6Addresses
, addr
);
10537 if (dest
!= NULL
) {
10538 CFDictionarySetValue(dict
, kSCPropNetIPv6DestAddresses
, dest
);
10541 if (prefix
!= NULL
) {
10542 CFDictionarySetValue(dict
, kSCPropNetIPv6PrefixLength
, prefix
);
10548 static CFDictionaryRef
10549 make_IPv6_dict(IPv6ServiceContentsRef t
)
10551 CFMutableDictionaryRef dict
;
10553 dict
= CFDictionaryCreateMutable(NULL
, 0,
10554 &kCFTypeDictionaryKeyCallBacks
,
10555 &kCFTypeDictionaryValueCallBacks
);
10556 dict_add_ipv6_addressing(dict
, t
->addr
, t
->addr_count
);
10557 dict_add_string(dict
, kSCPropNetIPv6Router
, t
->router
);
10558 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
10559 dict_insert_v6_routes(dict
, kSCPropNetIPv6AdditionalRoutes
,
10560 t
->additional_routes
, t
->additional_routes_count
);
10561 dict_insert_v6_routes(dict
, kSCPropNetIPv6ExcludedRoutes
,
10562 t
->excluded_routes
, t
->excluded_routes_count
);
10567 kDirectionForwards
= 0,
10568 kDirectionBackwards
= 1
10572 kLogRouteDisabled
= 0,
10573 kLogRouteEnabled
= 1
10576 static IPv6RouteListRef
10577 make_IPv6RouteList_for_test(IPv6RouteListRef list
,
10578 IPv6ServiceContentsRef test
,
10581 CFDictionaryRef dict
;
10582 IPv6RouteListRef r
;
10584 Rank rank_assertion
= kRankAssertionDefault
;
10585 CFNumberRef rank_assertion_cf
= NULL
;
10586 Boolean rank_assertion_is_set
= FALSE
;
10587 IPv6RouteListRef ret
= NULL
;
10588 IPV6_ROUTES_BUF_DECL(routes
);
10590 dict
= make_IPv6_dict(test
);
10591 if (dict
== NULL
) {
10592 fprintf(stderr
, "make_IPv6_dict failed\n");
10595 if (test
->primary_rank
!= NULL
) {
10597 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
10598 &rank_assertion_is_set
);
10599 if (rank_assertion_is_set
) {
10601 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
10604 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion_cf
);
10605 my_CFRelease(&rank_assertion_cf
);
10607 fprintf(stderr
, "IPv6RouteListCreateWithDictionary failed\n");
10611 if (rank_assertion
== kRankAssertionScoped
) {
10612 rank_assertion
= kRankAssertionNever
;
10614 rank
= RankMake(test
->rank
, rank_assertion
);
10615 if (log_it
== kLogRouteEnabled
10616 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10619 descr
= IPv6RouteListCopyDescription(r
);
10620 SCPrint(TRUE
, stdout
, CFSTR("Adding %@\n"), descr
);
10623 ret
= IPv6RouteListAddRouteList(list
, 1, r
, rank
);
10631 static IPv6RouteListRef
10632 make_IPv6RouteList(IPv6ServiceContentsRef
* test
, Direction direction
,
10635 IPv6RouteListRef ret
= NULL
;
10636 IPv6ServiceContentsRef
* scan
;
10638 switch (direction
) {
10639 case kDirectionBackwards
:
10640 for (scan
= test
; *scan
!= NULL
; scan
++) {
10641 /* find the end of the list */
10643 for (scan
--; scan
>= test
; scan
--) {
10644 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10648 case kDirectionForwards
:
10649 for (scan
= test
; *scan
!= NULL
; scan
++) {
10650 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10654 IPv6RouteListFinalize(ret
);
10658 #define EMPHASIS_CHARS "================="
10661 * Function: routelist_build_test
10663 * Runs through the given set of routes first in the forward direction,
10664 * then again backwards. We should end up with exactly the same set of
10665 * routes at the end.
10668 routelist_build_test(IPv6RouteTestRef test
)
10671 boolean_t ret
= FALSE
;
10672 IPv6RouteListRef routes1
;
10673 IPv6RouteListRef routes2
;
10675 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
10676 EMPHASIS_CHARS
"\n",
10679 routes1
= make_IPv6RouteList(test
->test
, kDirectionForwards
,
10681 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10682 if (routes1
!= NULL
) {
10683 descr
= IPv6RouteListCopyDescription(routes1
);
10684 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10688 routes2
= make_IPv6RouteList(test
->test
, kDirectionBackwards
,
10690 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10691 if (routes2
!= NULL
) {
10692 descr
= IPv6RouteListCopyDescription(routes2
);
10693 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10697 if ((routes1
!= NULL
&& routes2
== NULL
)
10698 || (routes1
== NULL
&& routes2
!= NULL
)) {
10699 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
10700 (routes1
!= NULL
) ? "not " : "",
10701 (routes2
!= NULL
) ? "not " : "");
10703 else if (routes1
!= NULL
&& routes2
!= NULL
) {
10704 /* check if they are different */
10705 if (routes1
->count
!= routes2
->count
) {
10706 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
10707 routes1
->count
, routes2
->count
);
10709 else if (bcmp(routes1
, routes2
,
10710 IPv6RouteListComputeSize(routes1
->count
)) != 0) {
10711 fprintf(stderr
, "routes1 and routes2 are different\n");
10714 printf("routes1 and routes2 are the same\n");
10718 if (routes1
!= NULL
) {
10721 if (routes2
!= NULL
) {
10724 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
10725 EMPHASIS_CHARS
"\n",
10726 test
->name
, ret
? "PASSED" : "FAILED");
10731 apply_test(IPv6RouteTestRef old_test
, IPv6RouteTestRef new_test
)
10733 IPv6RouteListRef new_routes
;
10734 IPv6RouteListRef old_routes
;
10736 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
10737 EMPHASIS_CHARS
"\n",
10738 old_test
->name
, new_test
->name
);
10740 old_routes
= make_IPv6RouteList(old_test
->test
, kDirectionForwards
,
10741 kLogRouteDisabled
);
10742 new_routes
= make_IPv6RouteList(new_test
->test
, kDirectionForwards
,
10743 kLogRouteDisabled
);
10744 if (old_routes
== NULL
) {
10745 printf("No Old Routes\n");
10748 printf("Old routes ('%s') = ", old_test
->name
);
10749 IPv6RouteListPrint(old_routes
);
10752 /* apply the old routes */
10753 IPv6RouteListApply(NULL
, old_routes
, -1);
10754 if (new_routes
== NULL
) {
10755 printf("No New Routes\n");
10758 printf("New Routes ('%s') = ", new_test
->name
);
10759 IPv6RouteListPrint(new_routes
);
10762 /* apply the new routes */
10763 IPv6RouteListApply(old_routes
, new_routes
, -1);
10764 if (old_routes
!= NULL
) {
10767 if (new_routes
!= NULL
) {
10770 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10771 EMPHASIS_CHARS
"\n",
10772 old_test
->name
, new_test
->name
);
10777 main(int argc
, char **argv
)
10779 IPv6RouteTestRef
* test
;
10781 _sc_log
= kSCLogDestinationFile
;
10782 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10783 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10785 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10787 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10788 if (!routelist_build_test(*test
)) {
10789 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10793 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10794 IPv6RouteTestRef
* test2
;
10796 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10797 apply_test(*test
, *test2
);
10798 apply_test(*test2
, *test
);
10806 #endif /* TEST_IPV6_ROUTELIST */
10808 #ifdef TEST_DNS_ORDER
10810 #define kProtocolFlagsIPv4v6 (kProtocolFlagsIPv4 | kProtocolFlagsIPv6)
10812 #define V4_ADDR_LOOP CFSTR("127.0.0.1")
10813 #define V4_ADDR_1 CFSTR("192.168.1.1")
10814 #define V4_ADDR_2 CFSTR("192.168.1.2")
10815 #define V4_ADDR_3 CFSTR("8.8.8.8")
10816 #define V4_ADDR_4 CFSTR("8.8.4.4")
10818 #define V6_ADDR_LOOP CFSTR("::1")
10819 #define V6_ADDR_1 CFSTR("fe80::0123:4567:89ab:cdef%en0")
10820 #define V6_ADDR_2 CFSTR("fd00::2acf:e9ff:fe14:8c59")
10821 #define V6_ADDR_3 CFSTR("2001:4860:4860::8888")
10825 const ProtocolFlags flags
;
10826 const CFStringRef server_addrs
[];
10827 } DNSOrderTest
, * DNSOrderTestRef
;
10829 static DNSOrderTest test0a
= {
10831 kProtocolFlagsIPv4
,
10833 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, NULL
10837 static DNSOrderTest test0b
= {
10839 kProtocolFlagsIPv6
,
10841 V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10845 static DNSOrderTest test1a
= {
10847 kProtocolFlagsIPv4v6
,
10849 V4_ADDR_1
, V6_ADDR_1
, NULL
10853 static DNSOrderTest test2a
= {
10855 kProtocolFlagsIPv4v6
,
10857 V4_ADDR_1
, V6_ADDR_2
, NULL
10861 static DNSOrderTest test3a
= {
10863 kProtocolFlagsIPv4v6
,
10865 V4_ADDR_1
, V6_ADDR_3
, NULL
10869 static DNSOrderTest test1b
= {
10871 kProtocolFlagsIPv4v6
,
10873 V4_ADDR_3
, V6_ADDR_1
, NULL
10877 static DNSOrderTest test2b
= {
10879 kProtocolFlagsIPv4v6
,
10881 V4_ADDR_3
, V6_ADDR_2
, NULL
10885 static DNSOrderTest test3b
= {
10887 kProtocolFlagsIPv4v6
,
10889 V4_ADDR_3
, V6_ADDR_3
, NULL
10893 static DNSOrderTest test1c
= {
10895 kProtocolFlagsIPv4v6
,
10897 V6_ADDR_1
, V4_ADDR_1
, NULL
10901 static DNSOrderTest test2c
= {
10903 kProtocolFlagsIPv4v6
,
10905 V6_ADDR_2
, V4_ADDR_1
, NULL
10909 static DNSOrderTest test3c
= {
10911 kProtocolFlagsIPv4v6
,
10913 V6_ADDR_3
, V4_ADDR_1
, NULL
10917 static DNSOrderTest test1d
= {
10919 kProtocolFlagsIPv4v6
,
10921 V6_ADDR_1
, V4_ADDR_3
, NULL
10925 static DNSOrderTest test2d
= {
10927 kProtocolFlagsIPv4v6
,
10929 V6_ADDR_2
, V4_ADDR_3
, NULL
10933 static DNSOrderTest test3d
= {
10935 kProtocolFlagsIPv4v6
,
10937 V6_ADDR_3
, V4_ADDR_3
, NULL
10941 static DNSOrderTest test4
= {
10943 kProtocolFlagsIPv4v6
,
10945 V4_ADDR_LOOP
, V4_ADDR_3
, V6_ADDR_2
, NULL
10949 static DNSOrderTest test5
= {
10951 kProtocolFlagsIPv4v6
,
10953 V4_ADDR_3
, V6_ADDR_LOOP
, V6_ADDR_2
, NULL
10957 static DNSOrderTest test6
= {
10959 kProtocolFlagsIPv4v6
,
10961 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10965 static DNSOrderTest test7
= {
10967 kProtocolFlagsIPv4v6
,
10969 V4_ADDR_1
, V6_ADDR_1
, V4_ADDR_3
, V6_ADDR_2
, NULL
10973 static DNSOrderTestRef dns_order_tests
[] = {
10975 &test1a
, &test2a
, &test3a
,
10976 &test1b
, &test2b
, &test3b
,
10977 &test1c
, &test2c
, &test3c
,
10978 &test1d
, &test2d
, &test3d
,
10986 #define EMPHASIS_CHARS "================="
10989 apply_order(CFArrayRef servers
, ProtocolFlags flags
)
10991 CFArrayRef ordered_servers
;
10993 ordered_servers
= order_dns_servers(servers
, flags
);
10994 if (ordered_servers
!= NULL
) {
10995 SCPrint(TRUE
, stdout
, CFSTR("After :\n%@\n"), ordered_servers
);
10996 CFRelease(ordered_servers
);
10998 printf("FAIL: No ordered servers\n");
11005 apply_test(DNSOrderTestRef test
)
11007 CFMutableArrayRef servers
;
11009 printf("\n" EMPHASIS_CHARS
"> '%s' Begin <" EMPHASIS_CHARS
"\n\n", test
->name
);
11011 servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
11012 for (int i
= 0; test
->server_addrs
[i
] != NULL
; i
++) {
11013 CFStringRef server_addr
= test
->server_addrs
[i
];
11015 CFArrayAppendValue(servers
, server_addr
);
11018 SCPrint(TRUE
, stdout
, CFSTR("Before :\n%@\n"), servers
);
11020 apply_order(servers
, test
->flags
);
11022 CFRelease(servers
);
11028 main(int argc
, char **argv
)
11031 _sc_log
= kSCLogDestinationFile
;
11032 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
11033 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
11035 S_IPMonitor_debug
= (uint32
)strtoul(argv
[1], NULL
, 0);
11038 for (DNSOrderTestRef
* test
= dns_order_tests
; *test
!= NULL
; test
++) {
11046 #endif /* TEST_DNS_ORDER */