2 * Copyright (c) 2000-2019 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/sa_compare.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"
99 #include "serviceIDNumber.h"
101 #include <SystemConfiguration/SystemConfiguration.h>
102 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
103 #include <SystemConfiguration/SCValidation.h>
104 #include <SystemConfiguration/scprefs_observer.h>
105 #include <SystemConfiguration/SCPrivate.h>
106 #include "SCNetworkReachabilityInternal.h"
107 #include "SCNetworkSignaturePrivate.h"
109 #include "dnsinfo_server.h"
111 #include <ppp/PPPControllerPriv.h>
114 #include <dns_sd_private.h>
116 #include <network_information.h>
117 #include "network_state_information_priv.h"
118 #include "network_state_information_logging.h"
119 #include "network_information_server.h"
120 #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,
412 #define ROUTE_COMMON \
415 IFIndex exclude_ifindex; \
418 ControlFlags control_flags; \
419 serviceIDNumber sidn;
425 #define PREFIX_LENGTH_IN_CLASSC 24
426 #define PREFIX_LENGTH_IN_CLASSD 4
432 struct in_addr gateway
;
434 } IPv4Route
, * IPv4RouteRef
;
438 struct in6_addr dest
;
439 struct in6_addr gateway
;
441 } IPv6Route
, * IPv6RouteRef
;
443 typedef CF_ENUM(uint16_t, RouteListFlags
) {
444 kRouteListFlagsExcludeNWI
= 0x0001,
445 kRouteListFlagsHasDefault
= 0x0002,
446 kRouteListFlagsScopedOnly
= 0x0004
449 #define ROUTELIST_COMMON \
452 RouteListFlags flags;
456 } RouteListCommon
, * RouteListRef
;
460 IPv4Route list
[1]; /* variable length */
461 } IPv4RouteList
, * IPv4RouteListRef
;
465 IPv6Route list
[1]; /* variable length */
466 } IPv6RouteList
, * IPv6RouteListRef
;
481 * Election Information
482 * - information about the current best services
490 struct sockaddr_in v4
;
491 struct sockaddr_in6 v6
;
494 typedef struct Candidate
{
495 CFStringRef serviceID
;
498 boolean_t ip_is_coupled
;
499 boolean_t ineligible
;
500 SCNetworkReachabilityFlags reachability_flags
;
502 in_sockaddr vpn_server_addr
;
503 CFStringRef signature
;
504 } Candidate
, * CandidateRef
;
506 typedef struct ElectionResults
{
510 Candidate candidates
[1];
511 } ElectionResults
, * ElectionResultsRef
;
513 static __inline__
size_t
514 ElectionResultsComputeSize(unsigned int n
)
516 return (offsetof(ElectionResults
, candidates
[n
]));
523 static __inline__ Rank
524 RankMake(uint32_t service_index
, Rank primary_rank
)
526 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
530 InterfaceRankGetRankAssertion(CFNumberRef rank_cf
, Boolean
* ret_is_set
)
532 SCNetworkServicePrimaryRank if_rank
;
533 Boolean is_set
= FALSE
;
534 Rank rank
= kRankAssertionDefault
;
537 && CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &if_rank
)
538 && if_rank
!= kSCNetworkServicePrimaryRankDefault
) {
539 if (if_rank
== kSCNetworkServicePrimaryRankFirst
) {
540 rank
= kRankAssertionFirst
;
543 rank
= RANK_ASSERTION_MAKE(if_rank
);
547 if (ret_is_set
!= NULL
) {
548 *ret_is_set
= is_set
;
554 PrimaryRankGetRankAssertion(CFStringRef rank_str
, Boolean
* is_set
)
557 const CFStringRef
* name
;
560 { &kSCValNetServicePrimaryRankFirst
, kRankAssertionFirst
},
561 { &kSCValNetServicePrimaryRankLast
, kRankAssertionLast
},
562 { &kSCValNetServicePrimaryRankNever
, kRankAssertionNever
},
563 { &kSCValNetServicePrimaryRankScoped
, kRankAssertionScoped
}
566 if (rank_str
!= NULL
) {
567 for (size_t i
= 0; i
< countof(values
); i
++) {
568 if (CFEqual(rank_str
, *(values
[i
].name
))) {
569 if (is_set
!= NULL
) {
572 return (values
[i
].rank_assertion
);
576 if (is_set
!= NULL
) {
579 return (kRankAssertionDefault
);
582 /* SCDynamicStore session */
583 static SCDynamicStoreRef S_session
= NULL
;
585 /* debug output flags */
586 static uint32_t S_IPMonitor_debug
= 0;
587 static Boolean S_IPMonitor_verbose
= FALSE
;
589 /* are we netbooted? If so, don't touch the default route */
590 static boolean_t S_netboot
= FALSE
;
592 /* dictionary to hold per-service state: key is the serviceID */
593 static CFMutableDictionaryRef S_service_state_dict
;
595 /* dictionaries to hold per-service rank: key is the serviceID */
596 static CFMutableDictionaryRef S_ipv4_service_rank_dict
;
597 static CFMutableDictionaryRef S_ipv6_service_rank_dict
;
599 /* dictionary to hold per-interface rank information: key is the ifname */
600 static CFDictionaryRef S_if_rank_dict
;
602 /* if set, a PPP interface overrides the primary */
603 static boolean_t S_ppp_override_primary
= FALSE
;
605 /* the current primary serviceID's */
606 static CFStringRef S_primary_ipv4
= NULL
;
607 static CFStringRef S_primary_ipv6
= NULL
;
608 static CFStringRef S_primary_dns
= NULL
;
609 static CFStringRef S_primary_proxies
= NULL
;
611 /* the current election results */
612 static ElectionResultsRef S_ipv4_results
;
613 static ElectionResultsRef S_ipv6_results
;
615 static CFStringRef S_state_global_ipv4
= NULL
;
616 static CFStringRef S_state_global_ipv6
= NULL
;
617 static CFStringRef S_state_global_dns
= NULL
;
618 static CFStringRef S_state_global_proxies
= NULL
;
619 static CFStringRef S_state_service_prefix
= NULL
;
620 static CFStringRef S_setup_global_ipv4
= NULL
;
621 static CFStringRef S_setup_service_prefix
= NULL
;
623 static CFStringRef S_interface_delegation_prefix
= NULL
;
625 static CFStringRef S_multicast_resolvers
= NULL
;
626 static CFStringRef S_private_resolvers
= NULL
;
628 #if !TARGET_OS_SIMULATOR
629 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
630 static IPv6RouteListRef S_ipv6_routelist
= NULL
;
631 #endif /* !TARGET_OS_SIMULATOR */
633 static boolean_t S_append_state
= FALSE
;
635 static CFDictionaryRef S_dns_dict
= NULL
;
637 static Boolean S_dnsinfo_synced
= TRUE
;
639 static nwi_state_t S_nwi_state
= NULL
;
640 static Boolean S_nwi_synced
= TRUE
;
642 static CFDictionaryRef S_proxies_dict
= NULL
;
644 // Note: access should be gated with __network_change_queue()
645 static uint32_t S_network_change_needed
= 0;
646 #define NETWORK_CHANGE_NET 1<<0
647 #define NETWORK_CHANGE_DNS 1<<1
648 #define NETWORK_CHANGE_PROXY 1<<2
649 #if !TARGET_OS_IPHONE
650 #define NETWORK_CHANGE_SMB 1<<3
651 #endif /* !TARGET_OS_IPHONE */
652 #define NETWORK_CHANGE_NAT64 1<<4
653 static struct timeval S_network_change_start
;
654 static Boolean S_network_change_timeout
= FALSE
;
655 static dispatch_source_t S_network_change_timer
= NULL
;
657 #if !TARGET_OS_IPHONE
658 static CFStringRef S_primary_smb
= NULL
;
659 static CFStringRef S_state_global_smb
= NULL
;
660 static CFDictionaryRef S_smb_dict
= NULL
;
661 #endif /* !TARGET_OS_IPHONE */
663 #if !TARGET_OS_IPHONE
664 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
665 #endif /* !TARGET_OS_IPHONE */
668 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
669 #endif /* KERN_NETBOOT */
672 ** entityType*, GetEntityChanges*
673 ** - definitions for the entity types we handle
680 #if !TARGET_OS_IPHONE
682 #endif /* !TARGET_OS_IPHONE */
684 kEntityTypeTransientStatus
,
685 kEntityTypeServiceOptions
= 31
688 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
689 &kSCEntNetIPv4
, /* 0 */
690 &kSCEntNetIPv6
, /* 1 */
691 &kSCEntNetDNS
, /* 2 */
692 &kSCEntNetProxies
, /* 3 */
693 #if !TARGET_OS_IPHONE
694 &kSCEntNetSMB
, /* 4 */
695 #endif /* !TARGET_OS_IPHONE */
700 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
);
702 static __inline__
char
705 return ((af
== AF_INET
) ? '4' : '6');
708 static __inline__
char
709 ipvx_other_char(int af
)
711 return ((af
== AF_INET
) ? '6' : '4');
715 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
717 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
718 * Routes CFData containing IPv4RouteList/IPv6RouteList
719 * Service dictionary containing kSCEntNetIPv[46] service entity
721 #define kIPDictRoutes CFSTR("Routes") /* data */
722 #define kIPDictService CFSTR("Service") /* dict */
724 static CFDictionaryRef
725 ipdict_create(CFDictionaryRef dict
, CFDataRef routes_data
)
730 keys
[0] = kIPDictService
;
732 keys
[1] = kIPDictRoutes
;
733 values
[1] = routes_data
;
734 return (CFDictionaryCreate(NULL
,
735 (const void * *)keys
,
738 &kCFTypeDictionaryKeyCallBacks
,
739 &kCFTypeDictionaryValueCallBacks
));
743 ipdict_get_routelist(CFDictionaryRef dict
)
745 void * routes_list
= NULL
;
750 routes
= CFDictionaryGetValue(dict
, kIPDictRoutes
);
751 if (routes
!= NULL
) {
752 routes_list
= (void *)CFDataGetBytePtr(routes
);
755 return (routes_list
);
758 static CFDictionaryRef
759 ipdict_get_service(CFDictionaryRef dict
)
761 CFDictionaryRef ip_dict
= NULL
;
764 ip_dict
= CFDictionaryGetValue(dict
, kIPDictService
);
770 ipdict_get_ifname(CFDictionaryRef dict
)
772 CFStringRef ifname
= NULL
;
773 CFDictionaryRef ip_dict
;
775 ip_dict
= ipdict_get_service(dict
);
776 if (ip_dict
!= NULL
) {
777 ifname
= CFDictionaryGetValue(ip_dict
, kSCPropInterfaceName
);
782 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
783 CFDictionaryRef state_dict
,
784 CFDictionaryRef setup_dict
,
785 CFDictionaryRef info
);
786 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
788 static GetEntityChangesFunc get_ipv4_changes
;
789 static GetEntityChangesFunc get_ipv6_changes
;
790 static GetEntityChangesFunc get_dns_changes
;
791 static GetEntityChangesFunc get_proxies_changes
;
792 #if !TARGET_OS_IPHONE
793 static GetEntityChangesFunc get_smb_changes
;
794 #endif /* !TARGET_OS_IPHONE */
796 static __inline__
void
797 my_CFRelease(void * t
)
799 void * * obj
= (void * *)t
;
809 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
812 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
814 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
815 get_ipv4_changes
, /* 0 */
816 get_ipv6_changes
, /* 1 */
817 get_dns_changes
, /* 2 */
818 get_proxies_changes
,/* 3 */
819 #if !TARGET_OS_IPHONE
820 get_smb_changes
, /* 4 */
821 #endif /* !TARGET_OS_IPHONE */
826 ** - mechanism to do an atomic update of the SCDynamicStore
827 ** when the content needs to be changed across multiple functions
830 CFMutableArrayRef notify
;
831 CFMutableArrayRef remove
;
832 CFMutableDictionaryRef set
;
833 } keyChangeList
, * keyChangeListRef
;
836 keyChangeListInit(keyChangeListRef keys
)
838 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
839 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
840 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
841 &kCFTypeDictionaryKeyCallBacks
,
842 &kCFTypeDictionaryValueCallBacks
);
847 keyChangeListFree(keyChangeListRef keys
)
849 my_CFRelease(&keys
->notify
);
850 my_CFRelease(&keys
->remove
);
851 my_CFRelease(&keys
->set
);
856 keyChangeListActive(keyChangeListRef keys
)
858 return ((CFDictionaryGetCount(keys
->set
) > 0) ||
859 (CFArrayGetCount(keys
->remove
) > 0) ||
860 (CFArrayGetCount(keys
->notify
) > 0));
864 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
866 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
871 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
873 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
874 CFDictionaryRemoveValue(keys
->set
, key
);
879 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
881 my_CFArrayRemoveValue(keys
->remove
, key
);
882 CFDictionarySetValue(keys
->set
, key
, value
);
887 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
889 CFArrayRef notify
= keys
->notify
;
890 CFArrayRef remove
= keys
->remove
;
891 CFDictionaryRef set
= keys
->set
;
893 if (CFArrayGetCount(notify
) == 0) {
896 if (CFArrayGetCount(remove
) == 0) {
899 if (CFDictionaryGetCount(set
) == 0) {
902 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
905 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
907 my_log(LOG_DEBUG
, "Setting:\n%@", set
);
909 if (remove
!= NULL
) {
910 my_log(LOG_DEBUG
, "Removing:\n%@", remove
);
912 if (notify
!= NULL
) {
913 my_log(LOG_DEBUG
, "Notifying:\n%@", notify
);
916 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
929 mib
[1] = KERN_NETBOOT
;
930 len
= sizeof(netboot
);
931 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
935 static int rtm_seq
= 0;
937 #if !TARGET_OS_SIMULATOR
939 open_routing_socket(void)
943 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
944 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
949 static __inline__
int
950 inet6_dgram_socket(void)
954 sockfd
= socket(AF_INET6
, SOCK_DGRAM
, 0);
956 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
963 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
965 struct in6_defrouter dr
;
966 struct sockaddr_in6
* sin6
;
968 memset(&dr
, 0, sizeof(dr
));
970 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
971 sin6
->sin6_family
= AF_INET6
;
972 sin6
->sin6_addr
= *addr
;
974 dr
.if_index
= if_index
;
975 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
979 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
981 struct in6_defrouter dr
;
982 struct sockaddr_in6
* sin6
;
984 memset(&dr
, 0, sizeof(dr
));
986 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
987 sin6
->sin6_family
= AF_INET6
;
988 sin6
->sin6_addr
= *addr
;
989 dr
.if_index
= if_index
;
990 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
993 #endif /* !TARGET_OS_SIMULATOR */
996 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
998 CFIndex n
= CFArrayGetCount(arr
);
1000 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
1003 CFArrayAppendValue(arr
, new);
1008 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
1012 i
= CFArrayGetFirstIndexOfValue(arr
,
1013 CFRangeMake(0, CFArrayGetCount(arr
)),
1015 if (i
!= kCFNotFound
) {
1016 CFArrayRemoveValueAtIndex(arr
, i
);
1022 my_CFArrayCreateCombinedArray(CFArrayRef array1
, CFArrayRef array2
)
1024 CFMutableArrayRef combined
;
1026 combined
= CFArrayCreateMutableCopy(NULL
, 0, array1
);
1027 CFArrayAppendArray(combined
,
1029 CFRangeMake(0, CFArrayGetCount(array2
)));
1033 static CFDictionaryRef
1034 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
1036 if (isA_CFDictionary(dict
) == NULL
) {
1039 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
1043 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
1045 if (isA_CFDictionary(dict
) == NULL
) {
1048 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
1051 #if !TARGET_OS_SIMULATOR
1053 typedef CF_ENUM(uint16_t, PLATDiscoveryOption
) {
1054 kPLATDiscoveryOptionStart
,
1055 kPLATDiscoveryOptionUpdate
,
1056 kPLATDiscoveryOptionCancel
1060 my_CFSetAddValue(CFMutableSetRef
* set_p
, CFTypeRef value
)
1062 if (*set_p
== NULL
) {
1063 *set_p
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1065 CFSetAddValue(*set_p
, value
);
1069 my_CFSetRemoveValue(CFMutableSetRef
* set_p
, CFTypeRef value
)
1071 if (*set_p
== NULL
) {
1074 CFSetRemoveValue(*set_p
, value
);
1075 if (CFSetGetCount(*set_p
) == 0) {
1076 my_CFRelease(set_p
);
1081 my_CFSetContainsValue(CFSetRef set
, CFTypeRef value
)
1086 return (CFSetContainsValue(set
, value
));
1089 // Note: must only accessed on __network_change_queue()
1090 static CFMutableSetRef S_nat64_cancel_prefix_requests
;
1091 static CFMutableSetRef S_nat64_prefix_updates
;
1092 static CFMutableSetRef S_nat64_prefix_requests
;
1095 set_plat_discovery_locked(PLATDiscoveryOption option
, CFStringRef interface
)
1098 case kPLATDiscoveryOptionStart
:
1099 my_log(LOG_DEBUG
, "NAT64 Start %@", interface
);
1100 my_CFSetAddValue(&S_nat64_prefix_requests
, interface
);
1101 my_CFSetRemoveValue(&S_nat64_prefix_updates
, interface
);
1102 my_CFSetRemoveValue(&S_nat64_cancel_prefix_requests
, interface
);
1104 case kPLATDiscoveryOptionUpdate
:
1105 my_log(LOG_DEBUG
, "NAT64 Update %@", interface
);
1106 if (!my_CFSetContainsValue(S_nat64_prefix_requests
, interface
)) {
1107 my_CFSetAddValue(&S_nat64_prefix_updates
, interface
);
1109 my_CFSetRemoveValue(&S_nat64_cancel_prefix_requests
, interface
);
1111 case kPLATDiscoveryOptionCancel
:
1112 my_log(LOG_DEBUG
, "NAT64 Cancel %@", interface
);
1113 my_CFSetRemoveValue(&S_nat64_prefix_requests
, interface
);
1114 my_CFSetRemoveValue(&S_nat64_prefix_updates
, interface
);
1115 my_CFSetAddValue(&S_nat64_cancel_prefix_requests
, interface
);
1123 set_plat_discovery(PLATDiscoveryOption option
, CFStringRef interface
)
1125 CFRetain(interface
);
1126 dispatch_async(__network_change_queue(), ^{
1127 set_plat_discovery_locked(option
, interface
);
1128 CFRelease(interface
);
1133 #endif /* !TARGET_OS_SIMULATOR */
1136 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, size_t addr_size
)
1140 if (isA_CFString(str
) == NULL
) {
1146 if (addr_size
< sizeof(struct in_addr
)) {
1151 if (addr_size
< sizeof(struct in6_addr
)) {
1158 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
1159 if (inet_pton(family
, buf
, addr
) == 1) {
1163 memset(addr
, 0, addr_size
);
1169 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
1171 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
1176 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
1178 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
1182 cfnumber_to_int(CFNumberRef num
, int * int_val
)
1184 if (isA_CFNumber(num
) == NULL
) {
1187 return (CFNumberGetValue(num
, kCFNumberIntType
, int_val
));
1190 static CF_RETURNS_RETAINED CFStringRef
1191 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
1193 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1194 kSCDynamicStoreDomainSetup
,
1199 static CF_RETURNS_RETAINED CFStringRef
1200 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
1202 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1203 kSCDynamicStoreDomainState
,
1209 interface_entity_key_copy(CFStringRef ifname
, CFStringRef entity
)
1211 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1212 kSCDynamicStoreDomainState
,
1217 static CFDictionaryRef
1218 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1221 CFStringRef setup_key
;
1222 CFDictionaryRef setup_dict
;
1224 setup_key
= setup_service_key(serviceID
, entity
);
1225 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
1226 my_CFRelease(&setup_key
);
1227 return (setup_dict
);
1230 static CFDictionaryRef
1231 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1234 CFStringRef state_key
;
1235 CFDictionaryRef state_dict
;
1237 state_key
= state_service_key(serviceID
, entity
);
1238 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
1239 my_CFRelease(&state_key
);
1240 return (state_dict
);
1244 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1248 ip_list
= CFDictionaryGetValue(dict
, prop
);
1249 if (isA_CFArray(ip_list
) != NULL
1250 && CFArrayGetCount(ip_list
) > 0
1251 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1258 dict_get_first_ipv6(CFDictionaryRef dict
, CFStringRef prop
,
1259 struct in6_addr
* ip_p
)
1263 ip_list
= CFDictionaryGetValue(dict
, prop
);
1264 if (isA_CFArray(ip_list
) != NULL
1265 && CFArrayGetCount(ip_list
) > 0
1266 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1273 dict_get_first_int(CFDictionaryRef dict
, CFStringRef prop
,
1278 list
= CFDictionaryGetValue(dict
, prop
);
1279 if (isA_CFArray(list
) != NULL
1280 && CFArrayGetCount(list
) > 0
1281 && cfnumber_to_int(CFArrayGetValueAtIndex(list
, 0), val
)) {
1288 dict_get_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1292 val
= CFDictionaryGetValue(dict
, prop
);
1293 return (cfstring_to_ip(val
, ip_p
));
1297 dict_get_ipv6(CFDictionaryRef dict
, CFStringRef prop
, struct in6_addr
* ip_p
)
1301 val
= CFDictionaryGetValue(dict
, prop
);
1302 return (cfstring_to_ip6(val
, ip_p
));
1306 dict_get_int(CFDictionaryRef dict
, CFStringRef prop
, int * intval
)
1310 val
= CFDictionaryGetValue(dict
, prop
);
1311 return (cfnumber_to_int(val
, intval
));
1315 get_override_primary(CFDictionaryRef dict
)
1319 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
1320 if (isA_CFNumber(override
) != NULL
) {
1323 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
1328 else if (isA_CFBoolean(override
) != NULL
) {
1329 if (CFBooleanGetValue(override
)) {
1341 (*RouteListComputeSize
)(CFIndex n
);
1344 (*RouteIsEqual
)(RouteRef a
, RouteRef b
);
1347 (*RouteApply
)(RouteRef route
, int cmd
, int sockfd
);
1349 typedef const void *
1350 (*RouteGateway
)(RouteRef route
);
1353 (*RouteSetGateway
)(RouteRef route
, const void * address
);
1355 typedef const void *
1356 (*RouteDestination
)(RouteRef route
);
1359 (*RouteSameSubnet
)(RouteRef route
, const void * address
);
1362 (*RouteCopyDescription
)(RouteRef route
);
1365 (*RouteLog
)(int priority
, RouteRef route
, const char * msg
);
1368 RouteListComputeSize list_compute_size
;
1370 RouteIsEqual route_equal
;
1371 RouteApply route_apply
;
1372 RouteGateway route_gateway
;
1373 RouteSetGateway route_set_gateway
;
1374 RouteDestination route_destination
;
1375 RouteSameSubnet route_same_subnet
;
1377 RouteCopyDescription route_copy_description
;
1384 typedef const RouteListInfo
* RouteListInfoRef
;
1387 RouteListInfoRef info
;
1388 RouteListRef old_routes
;
1389 RouteListRef new_routes
;
1392 } RouteListApplyContext
, * RouteListApplyContextRef
;
1396 RouteAddressCompare(RouteListInfoRef info
,
1400 return (memcmp(addr1
, addr2
, info
->address_size
));
1404 RouteCompare(RouteListInfoRef info
,
1405 RouteRef a
, Rank a_rank
,
1406 RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
1409 RouteDestination route_destination
;
1410 RouteCopyDescription route_copy_description
;
1413 route_destination
= info
->route_destination
;
1414 route_copy_description
= info
->route_copy_description
;
1415 cmp
= RouteAddressCompare(info
,
1416 (*route_destination
)(a
),
1417 (*route_destination
)(b
));
1419 cmp
= a
->prefix_length
- b
->prefix_length
;
1421 int index_cmp
= a
->ifindex
- b
->ifindex
;
1423 if (index_cmp
== 0) {
1426 else if ((a
->ifindex
== 0 || b
->ifindex
== 0)
1427 && (a
->flags
& kRouteFlagsIsScoped
) == 0
1428 && (b
->flags
& kRouteFlagsIsScoped
) == 0) {
1430 * Either of the routes specifies no interface and neither
1431 * route is scoped. Claim they are equal to eliminate the
1438 cmp
= RankCompare(a_rank
, b_rank
);
1445 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
1453 else if (cmp
== 0) {
1459 a_str
= (*route_copy_description
)(a
);
1460 b_str
= (*route_copy_description
)(b
);
1461 my_log(LOG_DEBUG
, "%@ rank 0x%x %c %@ rank 0x%x",
1462 a_str
, a_rank
, ch
, b_str
, b_rank
);
1470 RouteListGetRouteAtIndexSimple(RouteListInfoRef info
, RouteListRef routes
,
1473 return ((void *)routes
+ (*info
->list_compute_size
)(where
));
1477 RouteListGetRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1480 if (routes
->count
== 0
1481 || where
>= routes
->count
) {
1484 return (RouteListGetRouteAtIndexSimple(info
, routes
, where
));
1488 RouteListGetFirstRoute(RouteListInfoRef info
, RouteListRef routes
)
1490 return (RouteListGetRouteAtIndexSimple(info
, routes
, 0));
1493 #if !TARGET_OS_SIMULATOR
1495 RouteListRouteIndex(RouteListInfoRef info
, RouteListRef routes
,
1498 return (((void *)route
1499 - (void *)RouteListGetFirstRoute(info
, routes
))
1500 / info
->element_size
);
1502 #endif /* !TARGET_OS_SIMULATOR */
1505 RouteGetNextRoute(RouteListInfoRef info
, RouteRef route
)
1507 return ((RouteRef
)(((void *)route
) + info
->element_size
));
1511 RouteListAddRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1512 RouteRef this_route
, CFIndex where
)
1514 RouteRef insert_route
;
1516 if (where
== kCFNotFound
) {
1517 /* add it to the end */
1519 = RouteListGetRouteAtIndexSimple(info
, routes
, routes
->count
);
1522 /* make space at [where] */
1523 insert_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1524 memcpy((void *)insert_route
+ info
->element_size
,
1526 info
->element_size
* (routes
->count
- where
));
1528 /* copy the route */
1529 memcpy(insert_route
, this_route
, info
->element_size
);
1531 return (insert_route
);
1535 RouteListRemoveRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1538 if (routes
->count
== 0
1539 || where
>= routes
->count
) {
1543 if (where
== routes
->count
) {
1544 /* last slot, decrementing gets rid of it */
1547 RouteRef remove_route
;
1549 remove_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1550 memcpy(remove_route
,
1551 (void *)remove_route
+ info
->element_size
,
1552 info
->element_size
* (routes
->count
- where
));
1558 * Function: RouteListAddRoute
1561 * Add the given route to the list of routes, eliminating lower-ranked
1562 * duplicates on the same interface, and marking any lower ranked duplicates
1563 * on other interfaces with kRouteFlagsIsScoped.
1565 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1568 * Route list updated with the given route, possibly a different pointer,
1569 * due to using realloc'd memory.
1579 RouteListAddRoute(RouteListInfoRef info
,
1580 RouteListRef routes
, int init_size
,
1581 RouteRef this_route
, Rank this_rank
)
1584 RouteRef first_scan
= NULL
;
1587 Scope scope_which
= kScopeNone
;
1588 CFIndex where
= kCFNotFound
;
1590 if (routes
== NULL
) {
1591 size_t alloc_size
= (*info
->list_compute_size
)(init_size
);
1593 routes
= (RouteListRef
)malloc(alloc_size
);
1594 memset(routes
, 0, alloc_size
);
1595 routes
->size
= init_size
;
1597 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1599 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1601 boolean_t same_dest
;
1603 cmp
= RouteCompare(info
, this_route
, this_rank
, scan
, scan
->rank
,
1605 if (same_dest
&& (first_scan
== NULL
)) {
1609 if (where
== kCFNotFound
) {
1611 && (first_scan
!= NULL
)
1612 && (first_scan
->flags
& kRouteFlagsIsScoped
) == 0) {
1613 if ((scan
->flags
& kRouteFlagsIsScoped
) != 0) {
1614 ROUTELIST_DEBUG(kDebugFlag8
,
1615 "Hit 1: set scope on self\n");
1616 scope_which
= kScopeThis
;
1619 ROUTELIST_DEBUG(kDebugFlag8
,
1620 "Hit 2: set scope on next\n");
1621 scope_which
= kScopeNext
;
1624 /* remember our insertion point, but keep going to find a dup */
1628 else if (cmp
== 0) {
1631 if (where
!= kCFNotFound
1632 && scan
->ifindex
== this_route
->ifindex
1633 && scan
->exclude_ifindex
== 0
1634 && this_route
->exclude_ifindex
== 0) {
1635 /* this route is a duplicate */
1636 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 3: removing [%ld]\n", i
);
1637 RouteListRemoveRouteAtIndex(info
, routes
, i
);
1641 * this_route is "better" than scan if this_route is not excluded
1642 * and scan is excluded or this_route sorts ahead of scan
1644 if (this_route
->exclude_ifindex
== 0
1645 && (scan
->exclude_ifindex
!= 0 || this_rank
< scan
->rank
)) {
1646 IFIndex ifindex
= 0;
1647 boolean_t is_scoped
= FALSE
;
1649 if (scan
->flags
& kRouteFlagsIsScoped
) {
1652 if (this_rank
< scan
->rank
) {
1653 ROUTELIST_DEBUG(kDebugFlag8
,
1654 "Hit 4a: replacing [%ld]"
1655 " rank 0x%x < 0x%x\n",
1656 i
, this_rank
, scan
->rank
);
1659 ROUTELIST_DEBUG(kDebugFlag8
,
1660 "Hit 4b: replacing [%ld] excluded route\n",
1663 if (scan
->ifindex
!= 0) {
1664 ifindex
= scan
->ifindex
;
1666 else if (this_route
->ifindex
!= 0) {
1667 ifindex
= this_route
->ifindex
;
1669 memcpy(scan
, this_route
, info
->element_size
);
1670 scan
->rank
= this_rank
;
1671 scan
->ifindex
= ifindex
;
1672 scan
->exclude_ifindex
= 0;
1674 /* preserve whether route was scoped */
1675 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 5: preserved scope\n");
1676 scan
->flags
|= kRouteFlagsIsScoped
;
1684 if (scope_which
== kScopeNone
) {
1685 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 6: set scope on self\n");
1686 scope_which
= kScopeThis
;
1689 #ifdef TEST_ROUTELIST
1690 else if (where
!= kCFNotFound
) {
1691 /* not possible because we maintain a sorted list */
1693 "Hit 7: moved past routes - can't happen\n");
1697 #endif /* TEST_ROUTELIST */
1701 if (routes
->size
== routes
->count
) {
1703 RouteListRef new_routes
;
1706 /* double the size */
1707 old_size
= routes
->size
;
1708 how_many
= old_size
* 2;
1709 new_routes
= (RouteListRef
)
1710 reallocf(routes
, (*info
->list_compute_size
)(how_many
));
1711 if (new_routes
== NULL
) {
1716 ROUTELIST_DEBUG(kDebugFlag8
, "increasing size from %d to %d\n",
1717 old_size
, how_many
);
1718 new_routes
->size
= how_many
;
1719 routes
= new_routes
;
1722 /* add/insert the new route */
1723 this_route
= RouteListAddRouteAtIndex(info
, routes
, this_route
, where
);
1724 this_route
->rank
= this_rank
;
1726 if (RANK_ASSERTION_MASK(this_rank
) == kRankAssertionNever
) {
1727 flags
|= kRouteFlagsIsScoped
;
1729 switch (scope_which
) {
1731 flags
|= kRouteFlagsIsScoped
;
1734 this_route
= RouteListGetRouteAtIndex(info
, routes
, where
+ 1);
1735 flags
|= kRouteFlagsIsScoped
;
1741 if (this_route
!= NULL
&& flags
!= 0) {
1742 this_route
->flags
|= flags
;
1750 * Function: RouteListAddRouteList
1752 * Invoke RouteListAddRoute for each route in the given list
1753 * 'service_routes' combining them into a combined list 'routes'.
1756 * See RouteListAddRoute for more information.
1759 RouteListAddRouteList(RouteListInfoRef info
,
1760 RouteListRef routes
, int init_size
,
1761 RouteListRef service_routes
, Rank rank
)
1766 for (i
= 0, scan
= RouteListGetFirstRoute(info
, service_routes
);
1767 i
< service_routes
->count
;
1768 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1772 && (service_routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
1773 /* only apply rank to first element of the list (default route) */
1777 this_rank
= RANK_INDEX_MASK(rank
) | RANK_ASSERTION_MASK(scan
->rank
);
1779 routes
= RouteListAddRoute(info
, routes
, init_size
, scan
, this_rank
);
1785 RouteAddInterfaceToDescription(RouteRef r
, CFMutableStringRef str
)
1787 char if_name
[IFNAMSIZ
];
1789 if (my_if_indextoname2(r
->ifindex
, if_name
) != NULL
) {
1790 CFStringAppendFormat(str
, NULL
,
1794 if (my_if_indextoname2(r
->exclude_ifindex
, if_name
) != NULL
) {
1795 CFStringAppendFormat(str
, NULL
,
1803 RouteAddFlagsToDescription(RouteRef r
, CFMutableStringRef str
)
1805 if ((r
->flags
& kRouteFlagsIsNULL
) != 0) {
1806 CFStringAppend(str
, CFSTR(" [null]"));
1809 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
1811 switch (rank_assertion
) {
1812 case kRankAssertionFirst
:
1813 CFStringAppend(str
, CFSTR(" [first]"));
1815 case kRankAssertionLast
:
1816 CFStringAppend(str
, CFSTR(" [last]"));
1818 case kRankAssertionNever
:
1819 CFStringAppend(str
, CFSTR(" [never]"));
1824 if ((r
->flags
& kRouteFlagsKernelManaged
) != 0) {
1825 CFStringAppend(str
, CFSTR(" [kern]"));
1827 if ((r
->flags
& kRouteFlagsIsScoped
) != 0) {
1828 CFStringAppend(str
, CFSTR(" [SCOPED]"));
1834 #if !TARGET_OS_SIMULATOR
1836 RouteListFindRoute(RouteListInfoRef info
, RouteListRef routes
, RouteRef route
)
1839 RouteRef match
= NULL
;
1842 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1844 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1845 if ((*info
->route_equal
)(scan
, route
)) {
1855 kRouteLookupFlagsNone
= 0x0,
1856 kRouteLookupFlagsExcludeInterface
= 0x1
1860 RouteListLookup(RouteListInfoRef info
,
1861 RouteListRef routes
,
1862 const void * address
,
1865 RouteLookupFlags lookup_flags
)
1867 RouteRef best_match
= NULL
;
1871 for (i
= 0, candidate
= RouteListGetFirstRoute(info
, routes
);
1873 i
++, candidate
= RouteGetNextRoute(info
, candidate
)) {
1874 if (candidate
->ifindex
== 0 || candidate
->exclude_ifindex
!= 0) {
1875 /* ignore exclude routes */
1878 if ((lookup_flags
& kRouteLookupFlagsExcludeInterface
) != 0) {
1879 /* exclude interfaces with the same interface index */
1880 if (ifindex
== candidate
->ifindex
) {
1884 else if (ifindex
!= candidate
->ifindex
) {
1887 if ((candidate
->flags
& kRouteFlagsHasGateway
) != 0
1888 && RouteAddressCompare(info
,
1889 (*info
->route_gateway
)(candidate
),
1891 /* skip route whose gateway is the address we're looking for */
1894 if ((candidate
->flags
& kRouteFlagsIsHost
) != 0) {
1895 /* if host route and we're looking for an exact match */
1896 if (n_bits
== info
->all_bits_set
1897 && RouteAddressCompare(info
,
1898 (*info
->route_destination
)(candidate
),
1900 /* found exact match */
1901 best_match
= candidate
;
1907 /* verify that address is on the same subnet */
1908 if ((*info
->route_same_subnet
)(candidate
, address
) == FALSE
) {
1909 /* different subnet */
1913 if (candidate
->prefix_length
== n_bits
) {
1915 best_match
= candidate
;
1918 if (candidate
->prefix_length
> n_bits
) {
1919 /* matched too many bits */
1922 if (best_match
== NULL
1923 || candidate
->prefix_length
> best_match
->prefix_length
) {
1924 best_match
= candidate
;
1927 return (best_match
);
1932 * Function: RouteProcess
1934 * Function to process adding or removing the specified route.
1935 * In the case of adding, that may involve first processing the gateway
1936 * route (recursively).
1939 RouteProcess(RouteRef route
,
1941 RouteListApplyContextRef context
)
1943 RouteLog route_log
= context
->info
->route_log
;
1944 RouteApply route_apply
= context
->info
->route_apply
;
1945 RouteGateway route_gateway
= context
->info
->route_gateway
;
1949 case kRouteCommandAdd
:
1950 if ((route
->control_flags
& kControlFlagsProcessed
) != 0) {
1951 return ((route
->control_flags
& kControlFlagsAdded
) != 0);
1953 route
->control_flags
|= kControlFlagsProcessed
;
1954 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
1956 RouteRef gateway_route
;
1959 = RouteListLookup(context
->info
,
1960 context
->new_routes
,
1961 (*route_gateway
)(route
),
1962 context
->info
->all_bits_set
,
1964 kRouteLookupFlagsNone
);
1965 if (gateway_route
== NULL
) {
1966 (*route_log
)(LOG_NOTICE
, route
, "no gateway route");
1969 #define MAX_RECURSE_DEPTH 10
1970 /* avoid infinite recursion */
1971 if (context
->depth
== MAX_RECURSE_DEPTH
) {
1972 (*route_log
)(LOG_NOTICE
, route
, "routing loop detected, not adding");
1975 /* recurse to add gateway route */
1977 added
= RouteProcess(gateway_route
,
1982 (*route_log
)(LOG_NOTICE
, route
, "failed to add");
1987 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1988 if (retval
== EEXIST
) {
1989 /* delete and add again */
1990 (void)(*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
1991 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1996 "failed to add route, %s:",
1998 (*route_log
)(LOG_NOTICE
, route
, NULL
);
2001 case EROUTENOTAPPLIED
:
2002 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2006 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
2007 snprintf(buf
, sizeof(buf
), "%sAdd new[%ld]",
2009 RouteListRouteIndex(context
->info
,
2010 context
->new_routes
,
2012 (*route_log
)(LOG_DEBUG
, route
, buf
);
2014 route
->control_flags
|= kControlFlagsAdded
;
2018 case kRouteCommandRemove
:
2019 retval
= (*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
2023 case EROUTENOTAPPLIED
:
2024 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2028 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
2029 snprintf(buf
, sizeof(buf
), "%sRemove old[%ld]%s",
2031 RouteListRouteIndex(context
->info
,
2032 context
->old_routes
,
2034 (retval
== ESRCH
) ? "(ESRCH)" : "");
2035 (*route_log
)(LOG_DEBUG
, route
, buf
);
2040 "failed to remove route, %s",
2042 (*route_log
)(LOG_NOTICE
, route
, NULL
);
2053 RouteListApply(RouteListInfoRef info
,
2054 RouteListRef old_routes
, RouteListRef new_routes
,
2057 RouteListApplyContext context
;
2061 if (old_routes
== new_routes
&& old_routes
== NULL
) {
2062 /* both old and new are NULL, so there's nothing to do */
2065 memset(&context
, 0, sizeof(context
));
2066 context
.old_routes
= old_routes
;
2067 context
.new_routes
= new_routes
;
2068 context
.sockfd
= sockfd
;
2069 context
.info
= info
;
2070 if (old_routes
!= NULL
) {
2071 for (i
= 0, scan
= RouteListGetFirstRoute(info
, old_routes
);
2072 i
< old_routes
->count
;
2073 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2074 RouteRef new_route
= NULL
;
2076 if (new_routes
!= NULL
) {
2077 new_route
= RouteListFindRoute(info
, new_routes
, scan
);
2079 if (new_route
== NULL
) {
2080 if ((scan
->control_flags
& kControlFlagsAdded
) != 0) {
2081 RouteProcess(scan
, kRouteCommandRemove
, &context
);
2086 if (new_routes
!= NULL
) {
2087 if (old_routes
!= NULL
) {
2088 /* preserve the control flags from any old routes */
2089 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2090 i
< new_routes
->count
;
2091 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2092 RouteRef old_route
= NULL
;
2094 old_route
= RouteListFindRoute(info
, old_routes
, scan
);
2095 if (old_route
!= NULL
&& scan
->sidn
== old_route
->sidn
) {
2096 /* preserve the control state in the new route */
2097 scan
->control_flags
= old_route
->control_flags
;
2101 /* add any routes that need to be added */
2102 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2103 i
< new_routes
->count
;
2104 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2105 if ((scan
->control_flags
& kControlFlagsProcessed
) != 0) {
2108 RouteProcess(scan
, kRouteCommandAdd
, &context
);
2114 * Function: RouteListFinalize
2116 * Look for excluded routes. If the excluded route does not have an assigned
2117 * interface, search for a route that *does not* go over the excluded
2120 * If the excluded route does have an assigned interface, search for a route
2121 * that *does* go over the assigned interface.
2123 * Set the gateway on the excluded route to match the gateway of the found
2127 RouteListFinalize(RouteListInfoRef info
, RouteListRef routes
)
2132 if (routes
== NULL
) {
2135 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
2137 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2140 RouteLookupFlags flags
;
2142 if (scan
->exclude_ifindex
== 0) {
2145 if (scan
->ifindex
== 0) {
2146 ifindex
= scan
->exclude_ifindex
;
2147 flags
= kRouteLookupFlagsExcludeInterface
;
2150 ifindex
= scan
->ifindex
;
2151 flags
= kRouteLookupFlagsNone
;
2153 route
= RouteListLookup(info
, routes
,
2154 (*info
->route_destination
)(scan
),
2155 scan
->prefix_length
, ifindex
, flags
);
2156 if (route
== NULL
) {
2157 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
, "can't resolve excluded route");
2160 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
2161 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)scan
, "Excluded route");
2162 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)route
, "Resolved to");
2164 scan
->ifindex
= route
->ifindex
;
2165 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2166 (*info
->route_set_gateway
)(scan
, (*info
->route_gateway
)(route
));
2167 scan
->flags
|= kRouteFlagsHasGateway
;
2168 if (scan
->prefix_length
== info
->all_bits_set
) {
2169 scan
->flags
|= kRouteFlagsIsHost
;
2173 /* routes directly to interface */
2174 scan
->flags
&= ~(kRouteFlagsHasGateway
| kRouteFlagsIsHost
);
2180 #endif /* !TARGET_OS_SIMULATOR */
2186 #define IPV4_ROUTE_ALL_BITS_SET 32
2188 static __inline__
struct in_addr
2189 subnet_addr(struct in_addr addr
, struct in_addr mask
)
2193 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
2198 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
2200 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
2201 CFStringAppendFormat(str
, NULL
,
2202 CFSTR("Host " IP_FORMAT
),
2206 CFStringAppendFormat(str
, NULL
,
2207 CFSTR("Net " IP_FORMAT
),
2209 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
2212 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
2213 CFStringAppendFormat(str
, NULL
,
2214 CFSTR(" Gate " IP_FORMAT
),
2215 IP_LIST(&r
->gateway
));
2217 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
2218 if (r
->ifa
.s_addr
!= 0) {
2219 CFStringAppendFormat(str
, NULL
,
2220 CFSTR(" Ifa " IP_FORMAT
),
2223 #if !TEST_IPV4_ROUTELIST
2224 CFStringAppendFormat(str
, NULL
,
2225 CFSTR(" <SID %ld>"),
2228 RouteAddFlagsToDescription((RouteRef
)r
, str
);
2233 IPv4RouteCopyDescription(RouteRef r
)
2235 CFMutableStringRef str
;
2237 str
= CFStringCreateMutable(NULL
, 0);
2238 IPv4RouteCopyDescriptionWithString((IPv4RouteRef
)r
, str
);
2242 #ifdef TEST_IPV4_ROUTELIST
2243 static CFMutableStringRef
2244 IPv4RouteListCopyDescription(IPv4RouteListRef routes
);
2247 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2249 CFStringRef str
= IPv4RouteCopyDescription(route
);
2252 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2255 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
2261 static __inline__
void
2262 IPv4RouteListPrint(IPv4RouteListRef routes
)
2264 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2266 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2271 #else /* TEST_IPV4_ROUTELIST */
2273 static __inline__
void
2274 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2276 CFStringRef str
= IPv4RouteCopyDescription(route
);
2279 my_log(level
, "%@", str
);
2282 my_log(level
, "%s: %@", msg
, str
);
2288 #endif /* TEST_IPV4_ROUTELIST */
2291 IPv4RouteIsEqual(RouteRef r_scan
, RouteRef r_route
)
2293 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2294 IPv4RouteRef scan
= (IPv4RouteRef
)r_scan
;
2296 return ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
2297 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
2298 && (scan
->ifindex
== route
->ifindex
)
2299 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
2300 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
2301 && (scan
->flags
== route
->flags
));
2304 static CFMutableStringRef
2305 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
2309 CFMutableStringRef str
;
2311 str
= CFStringCreateMutable(NULL
, 0);
2312 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
2314 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
2315 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
2316 IPv4RouteCopyDescriptionWithString(r
, str
);
2318 CFStringAppend(str
, CFSTR("\n}"));
2323 IPv4RouteListComputeSize(CFIndex n
)
2325 return (offsetof(IPv4RouteList
, list
[n
]));
2329 count_prefix_bits_set(uint32_t n
)
2332 const static int8_t bits
[16] = {
2351 for (count
= 0; n
!= 0; n
>>= 4) {
2352 int nbits
= bits
[n
& 0x0f];
2363 prefix_to_mask32(unsigned int prefix_length
)
2365 if (prefix_length
> 32 || prefix_length
== 0) {
2368 return (0xffffffff << (32 - prefix_length
));
2372 mask_get_prefix_length(struct in_addr mask
)
2376 count
= count_prefix_bits_set(mask
.s_addr
);
2380 val
= prefix_to_mask32(count
);
2381 if (ntohl(mask
.s_addr
) != val
) {
2382 /* expected mask based on prefix length doesn't match */
2390 IPv4RouteSetPrefixLength(IPv4RouteRef route
)
2394 length
= mask_get_prefix_length(route
->mask
);
2398 route
->prefix_length
= length
;
2403 IPv4RouteGateway(RouteRef r_route
)
2405 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2406 return (&route
->gateway
);
2410 IPv4RouteSetGateway(RouteRef r_route
, const void * address
)
2412 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2414 route
->gateway
= *((struct in_addr
*)address
);
2419 IPv4RouteDestination(RouteRef r_route
)
2421 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2422 return (&route
->dest
);
2426 IPv4RouteSameSubnet(RouteRef r_route
, const void * addr
)
2428 const struct in_addr
* address
;
2429 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2431 address
= (const struct in_addr
*)addr
;
2432 return ((address
->s_addr
& route
->mask
.s_addr
) == route
->dest
.s_addr
);
2436 * Define: ROUTE_MSG_ADDRS_SPACE
2438 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2439 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2440 * someone changes the code and doesn't think to modify this.
2442 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2443 + 2 * sizeof(struct sockaddr_dl) \
2446 struct rt_msghdr hdr
;
2447 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2451 * Function: IPv4RouteApply
2453 * Add or remove the specified route to/from the kernel routing table.
2456 IPv4RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
2460 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2463 struct sockaddr_in
* in_p
;
2464 struct sockaddr_dl
* dl_p
;
2468 if (S_netboot
&& route
->dest
.s_addr
== 0) {
2469 /* don't touch the default route */
2470 return (EROUTENOTAPPLIED
);
2472 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
2473 return (EROUTENOTAPPLIED
);
2475 if (route
->ifindex
== 0) {
2477 IP_FORMAT
" no interface specified, ignoring",
2478 IP_LIST(&route
->dest
));
2482 #ifdef TEST_IPV4_ROUTELIST
2484 #else /* TEST_IPV4_ROUTELIST */
2486 #endif /* TEST_IPV4_ROUTELIST */
2488 memset(&rtmsg
, 0, sizeof(rtmsg
));
2489 rtmsg
.hdr
.rtm_type
= cmd
;
2490 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2491 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2492 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
2493 if (route
->ifa
.s_addr
!= 0) {
2494 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
2496 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2497 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
2498 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
2501 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
2502 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
2503 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
2506 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2507 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
2509 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
2510 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
2511 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2514 rtaddr
.ptr
= rtmsg
.addrs
;
2517 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2518 rtaddr
.in_p
->sin_family
= AF_INET
;
2519 rtaddr
.in_p
->sin_addr
= route
->dest
;
2520 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2523 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2524 /* gateway is an IP address */
2525 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2526 rtaddr
.in_p
->sin_family
= AF_INET
;
2527 rtaddr
.in_p
->sin_addr
= route
->gateway
;
2528 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2531 /* gateway is the interface itself */
2532 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2533 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2534 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2535 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2539 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
2540 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2541 rtaddr
.in_p
->sin_family
= AF_INET
;
2542 rtaddr
.in_p
->sin_addr
= route
->mask
;
2543 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2547 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
2548 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2549 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2550 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2551 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2553 /* interface address */
2554 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
2555 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2556 rtaddr
.in_p
->sin_family
= AF_INET
;
2557 rtaddr
.in_p
->sin_addr
= route
->ifa
;
2558 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2561 /* apply the route */
2562 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
2563 rtmsg
.hdr
.rtm_msglen
= len
;
2564 if (write(sockfd
, &rtmsg
, len
) == -1) {
2570 static const RouteListInfo IPv4RouteListInfo
= {
2571 IPv4RouteListComputeSize
,
2576 IPv4RouteSetGateway
,
2577 IPv4RouteDestination
,
2578 IPv4RouteSameSubnet
,
2580 IPv4RouteCopyDescription
,
2583 sizeof(struct in_addr
),
2584 IPV4_ROUTE_ALL_BITS_SET
2587 #if !TARGET_OS_SIMULATOR
2588 static __inline__
void
2589 IPv4RouteListLog(int level
, IPv4RouteListRef routes
)
2591 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2593 my_log(level
, "%@", str
);
2599 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
2602 RouteListApply(&IPv4RouteListInfo
,
2603 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
2609 IPv4RouteListFinalize(IPv4RouteListRef routes
)
2611 RouteListFinalize(&IPv4RouteListInfo
, (RouteListRef
)routes
);
2614 #endif /* !TARGET_OS_SIMULATOR */
2616 #if TEST_IPV4_ROUTELIST
2617 static IPv4RouteListRef
2618 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
2619 IPv4RouteListRef service_routes
, Rank rank
)
2621 return ((IPv4RouteListRef
)
2622 RouteListAddRouteList(&IPv4RouteListInfo
,
2623 (RouteListRef
)routes
, init_size
,
2624 (RouteListRef
)service_routes
, rank
));
2626 #endif /* TEST_IPV4_ROUTELIST */
2629 plist_get_string(CFDictionaryRef dict
, CFStringRef prop_name
,
2630 char * buf
, int buf_size
)
2634 val
= CFDictionaryGetValue(dict
, prop_name
);
2635 if (isA_CFString(val
) == NULL
) {
2638 if (!CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingUTF8
)) {
2645 struct in_addr addr
;
2648 IFIndex exclude_ifindex
;
2649 IPv4RouteRef
* route_p
;
2652 serviceIDNumber sidn
;
2653 } AddIPv4RouteContext
, * AddIPv4RouteContextRef
;
2656 AddIPv4Route(const void * value
, void * context
)
2658 AddIPv4RouteContextRef ctx
= (AddIPv4RouteContextRef
)context
;
2659 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
2660 IPv4RouteRef r
= *ctx
->route_p
;
2662 dict
= isA_CFDictionary(dict
);
2664 || !dict_get_ip(dict
, kSCPropNetIPv4RouteDestinationAddress
, &r
->dest
)
2665 || !dict_get_ip(dict
, kSCPropNetIPv4RouteSubnetMask
, &r
->mask
)) {
2666 /* one less route than we expected */
2668 my_log(LOG_NOTICE
, "%s route is not a dictionary",
2672 my_log(LOG_NOTICE
, "%s route is invalid, %@",
2677 if (!IPv4RouteSetPrefixLength(r
)) {
2678 my_log(LOG_NOTICE
, "%s route has invalid subnet mask, %@",
2682 r
->rank
= ctx
->rank
;
2683 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
2684 r
->sidn
= ctx
->sidn
;
2685 if (ctx
->ifindex
!= 0) {
2686 r
->ifindex
= ctx
->ifindex
;
2688 if (ctx
->exclude_ifindex
== 0
2689 && dict_get_ip(dict
,
2690 kSCPropNetIPv4RouteGatewayAddress
,
2692 r
->flags
|= kRouteFlagsHasGateway
;
2693 if (r
->prefix_length
== IPV4_ROUTE_ALL_BITS_SET
) {
2694 r
->flags
|= kRouteFlagsIsHost
;
2699 char ifname
[IFNAMSIZ
];
2701 if (plist_get_string(dict
, kSCPropNetIPv4RouteInterfaceName
,
2702 ifname
, sizeof(ifname
)) != NULL
) {
2705 ifindex
= my_if_nametoindex(ifname
);
2708 "%s: interface %s does not exist, %@",
2709 ctx
->descr
, ifname
, dict
);
2712 else if (ifindex
== ctx
->ifindex
) {
2714 "%s: interface %s unexpected, %@",
2715 ctx
->descr
, ifname
, dict
);
2718 r
->ifindex
= ifindex
;
2731 confirm_interface_name(CFDictionaryRef dict
, CFStringRef ifname
)
2733 CFStringRef confirmed_ifname
;
2734 boolean_t confirmed
;
2737 = CFDictionaryGetValue(dict
, kSCPropConfirmedInterfaceName
);
2738 if (isA_CFString(confirmed_ifname
) != NULL
) {
2739 confirmed
= CFEqual(confirmed_ifname
, ifname
);
2748 * Function: IPv4RouteListCreateWithDictionary
2751 * Given the service ipv4 entity dictionary, generate the list of routes.
2752 * Currently, this includes just the default route and subnet route,
2753 * if the service has a subnet mask.
2756 * If the passed in route_list is NULL or too small, this routine
2757 * allocates malloc'd memory to hold the routes.
2759 static IPv4RouteListRef
2760 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
2761 CFDictionaryRef dict
,
2762 CFNumberRef rank_assertion
,
2763 serviceIDNumber sidn
)
2765 boolean_t add_broadcast_multicast
= FALSE
;
2766 boolean_t add_default
= FALSE
;
2767 boolean_t add_router_subnet
= FALSE
;
2768 boolean_t add_subnet
= FALSE
;
2769 struct in_addr addr
= { 0 };
2770 CFArrayRef additional_routes
= NULL
;
2771 CFIndex additional_routes_count
;
2772 boolean_t allow_additional_routes
= FALSE
;
2773 boolean_t exclude_from_nwi
= FALSE
;
2774 CFArrayRef excluded_routes
= NULL
;
2775 CFIndex excluded_routes_count
;
2776 RouteFlags flags
= 0;
2778 char ifname
[IFNAMSIZ
];
2779 CFStringRef ifname_cf
;
2780 struct in_addr mask
= { 0 };
2782 int prefix_length
= 0;
2783 Rank primary_rank
= kRankAssertionDefault
;
2785 Rank rank
= kRankAssertionDefault
;
2786 struct in_addr router
= { 0 };
2787 boolean_t scoped_only
= FALSE
;
2788 struct in_addr subnet
= { 0 };
2793 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
2794 ifname
, sizeof(ifname
));
2795 if (ifname_cf
== NULL
) {
2798 ifindex
= my_if_nametoindex(ifname
);
2800 /* interface doesn't exist */
2803 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
2804 if (!dict_get_ip(dict
, kSCPropNetIPv4Router
, &router
)) {
2805 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
2807 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
2808 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
2810 subnet
= subnet_addr(addr
, mask
);
2811 prefix_length
= mask_get_prefix_length(mask
);
2812 if (prefix_length
< 0) {
2814 "ignoring bad subnet mask "
2816 IP_LIST(&mask
), ifname
);
2823 if (addr
.s_addr
== 0) {
2824 /* invalid/non-existent address */
2827 if (rank_assertion
!= NULL
) {
2828 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
2831 if (router
.s_addr
== 0) {
2832 /* if no router is configured, demote the rank if necessary */
2833 switch (primary_rank
) {
2834 case kRankAssertionLast
:
2835 case kRankAssertionNever
:
2836 case kRankAssertionScoped
:
2837 /* rank is already demoted */
2840 /* demote to RankLast */
2841 primary_rank
= kRankAssertionLast
;
2847 * If the router address is our address and the subnet mask is
2848 * not 255.255.255.255, assume all routes are local to the interface.
2850 if (addr
.s_addr
== router
.s_addr
2851 && mask
.s_addr
!= INADDR_BROADCAST
) {
2852 ; /* all routes local */
2855 flags
|= kRouteFlagsHasGateway
;
2857 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
2858 primary_rank
= kRankAssertionFirst
;
2862 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
2863 exclude_from_nwi
= TRUE
;
2864 flags
|= kRouteFlagsIsNULL
;
2867 switch (primary_rank
) {
2868 case kRankAssertionScoped
:
2869 /* Scoped means all routes for the service get scoped */
2870 primary_rank
= rank
= kRankAssertionNever
;
2871 flags
|= kRouteFlagsIsScoped
;
2874 case kRankAssertionNever
:
2875 /* Never means just the default route gets scoped */
2876 rank
= kRankAssertionLast
;
2877 flags
|= kRouteFlagsIsScoped
;
2880 rank
= primary_rank
;
2884 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2885 add_router_subnet
= TRUE
;
2889 if (ifindex
!= lo0_ifindex()) {
2890 if (router
.s_addr
!= 0) {
2894 add_broadcast_multicast
= TRUE
;
2897 if (allow_additional_routes
) {
2899 = CFDictionaryGetValue(dict
, kSCPropNetIPv4AdditionalRoutes
);
2900 additional_routes
= isA_CFArray(additional_routes
);
2901 if (additional_routes
!= NULL
) {
2902 additional_routes_count
= CFArrayGetCount(additional_routes
);
2903 n
+= additional_routes_count
;
2906 = CFDictionaryGetValue(dict
, kSCPropNetIPv4ExcludedRoutes
);
2907 excluded_routes
= isA_CFArray(excluded_routes
);
2908 if (excluded_routes
!= NULL
) {
2909 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
2910 n
+= excluded_routes_count
;
2913 if (routes
== NULL
|| routes
->size
< n
) {
2914 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
2915 memset(routes
, 0, IPv4RouteListComputeSize(n
));
2919 memset(routes
->list
, 0, sizeof(routes
->list
[0]) * n
);
2922 if (exclude_from_nwi
) {
2923 routes
->flags
|= kRouteListFlagsExcludeNWI
;
2925 else if (scoped_only
) {
2926 routes
->flags
|= kRouteListFlagsScopedOnly
;
2929 /* start at the beginning */
2933 /* add the default route */
2934 routes
->flags
|= kRouteListFlagsHasDefault
;
2936 r
->ifindex
= ifindex
;
2939 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2940 r
->gateway
= router
;
2945 r
->rank
= primary_rank
;
2948 if (add_broadcast_multicast
) {
2949 /* add the broadcast route (rdar://problem/22149738) */
2950 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2951 r
->flags
|= kRouteFlagsIsNULL
;
2953 r
->dest
.s_addr
= INADDR_BROADCAST
;
2954 r
->mask
.s_addr
= INADDR_BROADCAST
;
2955 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2956 r
->ifindex
= ifindex
;
2962 /* add multicast route (rdar://problem/26457121) */
2963 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2964 r
->flags
|= kRouteFlagsIsNULL
;
2966 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
2967 r
->mask
.s_addr
= htonl(IN_CLASSD_NET
);
2968 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSD
;
2969 r
->ifindex
= ifindex
;
2977 /* add the subnet route */
2979 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2980 r
->flags
|= kRouteFlagsIsNULL
;
2982 r
->ifindex
= ifindex
;
2987 r
->prefix_length
= prefix_length
;
2993 /* add the router subnet route */
2994 if (add_router_subnet
) {
2995 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2996 r
->flags
|= kRouteFlagsIsNULL
;
2998 r
->ifindex
= ifindex
;
3002 r
->mask
.s_addr
= INADDR_BROADCAST
;
3003 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
3009 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3010 AddIPv4RouteContext context
;
3012 memset(&context
, 0, sizeof(context
));
3013 context
.count_p
= &routes
->count
;
3014 context
.route_p
= &r
;
3015 context
.rank
= rank
;
3017 /* additional routes */
3018 if (additional_routes
!= NULL
) {
3019 context
.ifindex
= ifindex
;
3020 context
.addr
= addr
;
3021 context
.descr
= "AdditionalRoutes";
3022 context
.sidn
= sidn
;
3023 CFArrayApplyFunction(additional_routes
,
3024 CFRangeMake(0, additional_routes_count
),
3025 AddIPv4Route
, &context
);
3027 /* excluded routes */
3028 if (excluded_routes
!= NULL
) {
3029 context
.descr
= "ExcludedRoutes";
3030 /* exclude this interface */
3031 context
.ifindex
= 0;
3032 context
.exclude_ifindex
= ifindex
;
3033 context
.sidn
= sidn
;
3034 CFArrayApplyFunction(excluded_routes
,
3035 CFRangeMake(0, excluded_routes_count
),
3036 AddIPv4Route
, &context
);
3042 #if !TARGET_OS_SIMULATOR
3043 static IPv4RouteListRef
3044 IPv4RouteListCopyMulticastLoopback(void)
3047 IPv4RouteListRef routes
;
3049 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(1));
3050 memset(routes
, 0, IPv4RouteListComputeSize(1));
3051 routes
->count
= routes
->size
= 1;
3054 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
3055 r
->mask
.s_addr
= htonl(IN_CLASSC_NET
);
3056 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSC
;
3057 r
->ifindex
= lo0_ifindex();
3058 r
->sidn
= kserviceIDNumberZero
;
3061 #endif /* !TARGET_OS_SIMULATOR */
3066 #define IPV6_ROUTE_ALL_BITS_SET 128
3069 ipv6_prefix_length_is_valid(int prefix_length
)
3071 if (prefix_length
< 0 || prefix_length
> IPV6_ROUTE_ALL_BITS_SET
) {
3078 * from netinet6/in6.c
3081 in6_len2mask(struct in6_addr
* mask
, int len
)
3085 memset(mask
, 0, sizeof(*mask
));
3086 for (i
= 0; i
< len
/ 8; i
++)
3087 mask
->s6_addr
[i
] = 0xff;
3089 mask
->s6_addr
[i
] = (0xff00 >> (len
% 8)) & 0xff;
3093 in6_maskaddr(struct in6_addr
* addr
, const struct in6_addr
* mask
)
3095 for (size_t i
= 0; i
< sizeof(addr
->s6_addr
); i
++) {
3096 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3102 in6_netaddr(struct in6_addr
* addr
, int len
)
3104 struct in6_addr mask
;
3106 in6_len2mask(&mask
, len
);
3107 in6_maskaddr(addr
, &mask
);
3112 in6_addr_scope_linklocal(struct in6_addr
* addr
, IFIndex ifindex
)
3114 if (IN6_IS_ADDR_LINKLOCAL(addr
)) {
3115 addr
->__u6_addr
.__u6_addr16
[1] = htons(ifindex
);
3121 string_append_in6_addr(CFMutableStringRef str
, const struct in6_addr
* addr
)
3123 char ntopbuf
[INET6_ADDRSTRLEN
];
3125 CFStringAppendCString(str
,
3126 inet_ntop(AF_INET6
, addr
, ntopbuf
, sizeof(ntopbuf
)),
3127 kCFStringEncodingASCII
);
3132 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r
, CFMutableStringRef str
)
3134 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
3135 CFStringAppend(str
, CFSTR("Host "));
3136 string_append_in6_addr(str
, &r
->dest
);
3139 CFStringAppend(str
, CFSTR("Net "));
3140 string_append_in6_addr(str
, &r
->dest
);
3141 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
3144 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
3145 CFStringAppend(str
, CFSTR(" Gate "));
3146 string_append_in6_addr(str
, &r
->gateway
);
3148 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
3149 if (!IN6_ARE_ADDR_EQUAL(&r
->ifa
, &in6addr_any
)) {
3150 CFStringAppend(str
, CFSTR(" Ifa "));
3151 string_append_in6_addr(str
, &r
->ifa
);
3153 #if !TEST_IPV6_ROUTELIST
3154 CFStringAppendFormat(str
, NULL
,
3155 CFSTR(" <SID %ld>"),
3158 RouteAddFlagsToDescription((RouteRef
)r
, str
);
3163 IPv6RouteCopyDescription(RouteRef r
)
3165 CFMutableStringRef str
;
3167 str
= CFStringCreateMutable(NULL
, 0);
3168 IPv6RouteCopyDescriptionWithString((IPv6RouteRef
)r
, str
);
3172 static CFMutableStringRef
3173 IPv6RouteListCopyDescription(IPv6RouteListRef routes
)
3177 CFMutableStringRef str
;
3179 str
= CFStringCreateMutable(NULL
, 0);
3180 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv6RouteList[%d]> = {"),
3182 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
3183 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
3184 IPv6RouteCopyDescriptionWithString(r
, str
);
3186 CFStringAppend(str
, CFSTR("\n}"));
3190 #if TEST_IPV6_ROUTELIST
3193 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3195 CFStringRef str
= IPv6RouteCopyDescription(route
);
3198 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3201 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
3207 static __inline__
void
3208 IPv6RouteListPrint(IPv6RouteListRef routes
)
3210 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3212 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3217 #else /* TEST_IPV6_ROUTELIST */
3219 static __inline__
void
3220 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3222 CFStringRef str
= IPv6RouteCopyDescription(route
);
3225 my_log(level
, "%@", str
);
3228 my_log(level
, "%s: %@", msg
, str
);
3234 #endif /* TEST_IPV6_ROUTELIST */
3237 IPv6RouteListComputeSize(CFIndex n
)
3239 return (offsetof(IPv6RouteList
, list
[n
]));
3244 struct in6_addr
* addr
;
3247 IFIndex exclude_ifindex
;
3248 IPv6RouteRef
* route_p
;
3251 serviceIDNumber sidn
;
3252 } AddIPv6RouteContext
, * AddIPv6RouteContextRef
;
3255 AddIPv6Route(const void * value
, void * context
)
3257 AddIPv6RouteContextRef ctx
= (AddIPv6RouteContextRef
)context
;
3258 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
3259 IPv6RouteRef r
= *ctx
->route_p
;
3261 dict
= isA_CFDictionary(dict
);
3263 || !dict_get_ipv6(dict
, kSCPropNetIPv6RouteDestinationAddress
, &r
->dest
)
3264 || !dict_get_int(dict
, kSCPropNetIPv6RoutePrefixLength
,
3266 || !ipv6_prefix_length_is_valid(r
->prefix_length
)) {
3267 /* one less route than we expected */
3269 my_log(LOG_NOTICE
, "%s route is not a dictionary",
3273 my_log(LOG_NOTICE
, "%s route is invalid, %@",
3278 r
->rank
= ctx
->rank
;
3279 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
3280 r
->sidn
= ctx
->sidn
;
3281 if (ctx
->ifindex
!= 0) {
3282 r
->ifindex
= ctx
->ifindex
;
3283 r
->ifa
= *ctx
->addr
;
3284 if (ctx
->exclude_ifindex
== 0
3285 && dict_get_ipv6(dict
,
3286 kSCPropNetIPv6RouteGatewayAddress
,
3288 r
->flags
|= kRouteFlagsHasGateway
;
3289 if (r
->prefix_length
== IPV6_ROUTE_ALL_BITS_SET
) {
3290 r
->flags
|= kRouteFlagsIsHost
;
3295 char ifname
[IFNAMSIZ
];
3297 if (plist_get_string(dict
, kSCPropNetIPv6RouteInterfaceName
,
3298 ifname
, sizeof(ifname
)) != NULL
) {
3301 ifindex
= my_if_nametoindex(ifname
);
3304 "%s: interface %s does not exist, %@",
3305 ctx
->descr
, ifname
, dict
);
3308 else if (ifindex
== ctx
->ifindex
) {
3310 "%s: interface %s unexpected, %@",
3311 ctx
->descr
, ifname
, dict
);
3314 r
->ifindex
= ifindex
;
3327 * Function: IPv6RouteListCreateWithDictionary
3330 * Given the service IPv6 entity dictionary, generate the list of routes.
3333 * If the passed in route_list is NULL or too small, this routine
3334 * allocates malloc'd memory to hold the routes.
3336 static IPv6RouteListRef
3337 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes
,
3338 CFDictionaryRef dict
,
3339 CFNumberRef rank_assertion
,
3340 serviceIDNumber sidn
)
3342 boolean_t add_default
= FALSE
;
3343 boolean_t add_prefix
= FALSE
;
3344 struct in6_addr addr
;
3345 CFArrayRef additional_routes
= NULL
;
3346 CFIndex additional_routes_count
;
3347 boolean_t allow_additional_routes
= FALSE
;
3348 boolean_t exclude_from_nwi
= FALSE
;
3349 CFArrayRef excluded_routes
= NULL
;
3350 CFIndex excluded_routes_count
;
3351 RouteFlags flags
= 0;
3353 char ifname
[IFNAMSIZ
];
3354 CFStringRef ifname_cf
;
3356 int prefix_length
= 0;
3357 Rank primary_rank
= kRankAssertionDefault
;
3359 Rank rank
= kRankAssertionDefault
;
3360 struct in6_addr router
= in6addr_any
;
3361 boolean_t scoped_only
= FALSE
;
3366 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
3367 ifname
, sizeof(ifname
));
3368 if (ifname_cf
== NULL
) {
3371 ifindex
= my_if_nametoindex(ifname
);
3373 /* interface doesn't exist */
3376 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
3377 if (!dict_get_ipv6(dict
, kSCPropNetIPv6Router
, &router
)) {
3378 (void)dict_get_first_ipv6(dict
, kSCPropNetIPv6DestAddresses
, &router
);
3380 if (dict_get_first_ipv6(dict
, kSCPropNetIPv6Addresses
, &addr
)) {
3381 if (IN6_IS_ADDR_UNSPECIFIED(&addr
)) {
3384 if (dict_get_first_int(dict
, kSCPropNetIPv6PrefixLength
,
3386 && !IN6_IS_ADDR_LINKLOCAL(&addr
)
3387 && ipv6_prefix_length_is_valid(prefix_length
)) {
3399 if (rank_assertion
!= NULL
) {
3400 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
3403 if (!IN6_IS_ADDR_UNSPECIFIED(&router
)) {
3404 if (ifindex
!= lo0_ifindex()) {
3409 * If the router address is our address and the prefix length is
3410 * not 128, assume all routes are local to the interface.
3412 if (IN6_ARE_ADDR_EQUAL(&router
, &addr
)
3413 && prefix_length
!= IPV6_ROUTE_ALL_BITS_SET
) {
3414 ; /* all routes local */
3417 flags
|= kRouteFlagsHasGateway
;
3419 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
3420 primary_rank
= kRankAssertionFirst
;
3423 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
3424 exclude_from_nwi
= TRUE
;
3425 flags
|= kRouteFlagsIsNULL
;
3428 switch (primary_rank
) {
3429 case kRankAssertionScoped
:
3430 /* Scoped means all routes for the service get scoped */
3431 primary_rank
= rank
= kRankAssertionNever
;
3432 flags
|= kRouteFlagsIsScoped
;
3435 case kRankAssertionNever
:
3436 /* Never means just the default route gets scoped */
3437 rank
= kRankAssertionLast
;
3438 flags
|= kRouteFlagsIsScoped
;
3441 rank
= primary_rank
;
3445 if (allow_additional_routes
) {
3447 = CFDictionaryGetValue(dict
, kSCPropNetIPv6AdditionalRoutes
);
3448 additional_routes
= isA_CFArray(additional_routes
);
3449 if (additional_routes
!= NULL
) {
3450 additional_routes_count
= CFArrayGetCount(additional_routes
);
3451 n
+= additional_routes_count
;
3453 excluded_routes
= CFDictionaryGetValue(dict
,
3454 kSCPropNetIPv6ExcludedRoutes
);
3455 excluded_routes
= isA_CFArray(excluded_routes
);
3456 if (excluded_routes
!= NULL
) {
3457 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
3458 n
+= excluded_routes_count
;
3465 /* need IPv6LL subnet route */
3468 if (routes
== NULL
|| routes
->size
< n
) {
3469 routes
= (IPv6RouteListRef
)malloc(IPv6RouteListComputeSize(n
));
3470 memset(routes
, 0, IPv6RouteListComputeSize(n
));
3474 memset(routes
->list
, 0, sizeof(routes
->list
[0]) * n
);
3477 if (exclude_from_nwi
) {
3478 routes
->flags
|= kRouteListFlagsExcludeNWI
;
3480 else if (scoped_only
) {
3481 routes
->flags
|= kRouteListFlagsScopedOnly
;
3484 /* start at the beginning */
3487 /* add the default route */
3488 routes
->flags
|= kRouteListFlagsHasDefault
;
3489 r
->ifindex
= ifindex
;
3493 if ((flags
& kRouteFlagsHasGateway
) != 0) {
3494 r
->gateway
= router
;
3499 r
->rank
= primary_rank
;
3500 r
->flags
|= kRouteFlagsKernelManaged
;
3505 /* add IPv6LL route */
3506 r
->ifindex
= ifindex
;
3508 r
->dest
.s6_addr
[0] = 0xfe;
3509 r
->dest
.s6_addr
[1] = 0x80;
3510 r
->prefix_length
= 64;
3512 r
->flags
|= kRouteFlagsKernelManaged
;
3516 /* add the prefix route(s) */
3518 r
->flags
|= kRouteFlagsKernelManaged
;
3519 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3520 r
->flags
|= kRouteFlagsIsNULL
;
3522 r
->ifindex
= ifindex
;
3526 in6_netaddr(&r
->dest
, prefix_length
);
3527 r
->prefix_length
= prefix_length
;
3533 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3534 AddIPv6RouteContext context
;
3536 memset(&context
, 0, sizeof(context
));
3537 context
.count_p
= &routes
->count
;
3538 context
.route_p
= &r
;
3539 context
.rank
= rank
;
3541 /* additional routes */
3542 if (additional_routes
!= NULL
) {
3543 context
.ifindex
= ifindex
;
3544 context
.addr
= &addr
;
3545 context
.descr
= "AdditionalRoutes";
3546 context
.sidn
= sidn
;
3547 CFArrayApplyFunction(additional_routes
,
3548 CFRangeMake(0, additional_routes_count
),
3549 AddIPv6Route
, &context
);
3551 /* excluded routes */
3552 if (excluded_routes
!= NULL
) {
3553 context
.descr
= "ExcludedRoutes";
3554 /* exclude this interface */
3555 context
.ifindex
= 0;
3556 context
.exclude_ifindex
= ifindex
;
3557 context
.addr
= NULL
;
3558 context
.sidn
= sidn
;
3559 CFArrayApplyFunction(excluded_routes
,
3560 CFRangeMake(0, excluded_routes_count
),
3561 AddIPv6Route
, &context
);
3568 IPv6RouteGateway(RouteRef r_route
)
3570 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3571 return (&route
->gateway
);
3575 IPv6RouteSetGateway(RouteRef r_route
, const void * address
)
3577 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3579 route
->gateway
= *((struct in6_addr
*)address
);
3584 IPv6RouteDestination(RouteRef r_route
)
3586 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3587 return (&route
->dest
);
3590 static __inline__
int
3591 in6_addr_cmp(const struct in6_addr
* a
, const struct in6_addr
* b
)
3593 return (memcmp(a
->s6_addr
, b
->s6_addr
, sizeof(struct in6_addr
)));
3597 IPv6RouteIsEqual(RouteRef r_route1
, RouteRef r_route2
)
3599 IPv6RouteRef route1
= (IPv6RouteRef
)r_route1
;
3600 IPv6RouteRef route2
= (IPv6RouteRef
)r_route2
;
3602 return (route1
->prefix_length
== route2
->prefix_length
3603 && route1
->ifindex
== route2
->ifindex
3604 && route1
->flags
== route2
->flags
3605 && in6_addr_cmp(&route1
->dest
, &route2
->dest
) == 0
3606 && in6_addr_cmp(&route1
->ifa
, &route2
->ifa
) == 0
3607 && in6_addr_cmp(&route1
->gateway
, &route2
->gateway
) == 0);
3611 IPv6RouteSameSubnet(RouteRef r_route
, const void * addr
)
3613 const struct in6_addr
* address
= (const struct in6_addr
*)addr
;
3614 struct in6_addr netaddr
;
3615 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3618 in6_netaddr(&netaddr
, route
->prefix_length
);
3619 return (in6_addr_cmp(&netaddr
, &route
->dest
) == 0);
3623 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3626 struct rt_msghdr hdr
;
3627 char addrs
[V6_ROUTE_MSG_ADDRS_SPACE
];
3631 * Function: IPv6RouteApply
3633 * Add or remove the specified route to/from the kernel routing table.
3636 IPv6RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
3640 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3643 struct sockaddr_in6
* in_p
;
3644 struct sockaddr_dl
* dl_p
;
3648 if ((route
->flags
& kRouteFlagsKernelManaged
) != 0) {
3649 /* the kernel manages this route, don't touch it */
3650 return (EROUTENOTAPPLIED
);
3652 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
3653 return (EROUTENOTAPPLIED
);
3655 if (route
->ifindex
== 0) {
3656 IPv6RouteLog(LOG_NOTICE
, (RouteRef
)route
,
3657 "no interface specified");
3661 #if TEST_IPV6_ROUTELIST
3663 #else /* TEST_IPV6_ROUTELIST */
3665 #endif /* TEST_IPV6_ROUTELIST */
3667 memset(&rtmsg
, 0, sizeof(rtmsg
));
3668 rtmsg
.hdr
.rtm_type
= cmd
;
3669 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3670 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3671 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
3672 if (!IN6_IS_ADDR_UNSPECIFIED(&route
->ifa
)) {
3673 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
3675 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3676 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
3677 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
3680 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
3681 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
3682 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
3685 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
3686 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
3688 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
3689 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
3690 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
3693 rtaddr
.ptr
= rtmsg
.addrs
;
3696 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3697 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3698 rtaddr
.in_p
->sin6_addr
= route
->dest
;
3699 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3700 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3703 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
3704 /* gateway is an IP address */
3705 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3706 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3707 rtaddr
.in_p
->sin6_addr
= route
->gateway
;
3708 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3709 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3712 /* gateway is the interface itself */
3713 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3714 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3715 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3716 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3720 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
3721 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3722 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3723 in6_len2mask(&rtaddr
.in_p
->sin6_addr
, route
->prefix_length
);
3724 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3728 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
3729 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3730 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3731 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3732 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3734 /* interface address */
3735 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
3736 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3737 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3738 rtaddr
.in_p
->sin6_addr
= route
->ifa
;
3739 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3742 /* apply the route */
3743 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
3744 rtmsg
.hdr
.rtm_msglen
= len
;
3745 if (write(sockfd
, &rtmsg
, len
) == -1) {
3751 static const RouteListInfo IPv6RouteListInfo
= {
3752 IPv6RouteListComputeSize
,
3757 IPv6RouteSetGateway
,
3758 IPv6RouteDestination
,
3759 IPv6RouteSameSubnet
,
3761 IPv6RouteCopyDescription
,
3764 sizeof(struct in6_addr
),
3765 IPV6_ROUTE_ALL_BITS_SET
3768 #if TEST_IPV6_ROUTELIST
3769 static IPv6RouteListRef
3770 IPv6RouteListAddRouteList(IPv6RouteListRef routes
, int init_size
,
3771 IPv6RouteListRef service_routes
, Rank rank
)
3773 return ((IPv6RouteListRef
)
3774 RouteListAddRouteList(&IPv6RouteListInfo
,
3775 (RouteListRef
)routes
, init_size
,
3776 (RouteListRef
)service_routes
, rank
));
3778 #endif /* TEST_IPV6_ROUTELIST */
3780 #if !TARGET_OS_SIMULATOR
3781 static __inline__
void
3782 IPv6RouteListLog(int level
, IPv6RouteListRef routes
)
3784 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3786 my_log(level
, "%@", str
);
3792 IPv6RouteListFinalize(IPv6RouteListRef routes
)
3794 RouteListFinalize(&IPv6RouteListInfo
, (RouteListRef
)routes
);
3799 IPv6RouteListApply(IPv6RouteListRef old_routes
, IPv6RouteListRef new_routes
,
3802 RouteListApply(&IPv6RouteListInfo
,
3803 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
3807 #endif /* !TARGET_OS_SIMULATOR */
3810 * Function: parse_component
3812 * Given a string 'key' and a string prefix 'prefix',
3813 * return the next component in the slash '/' separated
3817 * 1. key = "a/b/c" prefix = "a/"
3819 * 2. key = "a/b/c" prefix = "a/b/"
3822 static CF_RETURNS_RETAINED CFStringRef
3823 parse_component(CFStringRef key
, CFStringRef prefix
)
3825 CFMutableStringRef comp
;
3828 if (!CFStringHasPrefix(key
, prefix
)) {
3831 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
3835 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
3836 range
= CFStringFind(comp
, CFSTR("/"), 0);
3837 if (range
.location
== kCFNotFound
) {
3840 range
.length
= CFStringGetLength(comp
) - range
.location
;
3841 CFStringDelete(comp
, range
);
3847 ipdict_is_routable(CFDictionaryRef entity_dict
)
3849 RouteListRef routes
;
3851 routes
= ipdict_get_routelist(entity_dict
);
3852 if (routes
== NULL
) {
3857 if ((routes
->flags
& kRouteListFlagsHasDefault
) == 0) {
3858 // if service has no default route
3862 if ((routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
3863 // if service should be excluded from NWI
3871 __private_extern__ boolean_t
3872 service_is_routable(CFDictionaryRef service_dict
, int af
)
3874 boolean_t contains_protocol
;
3876 CFDictionaryRef entity_dict
;
3878 entity
= (af
== AF_INET
) ? kSCEntNetIPv4
: kSCEntNetIPv6
;
3879 entity_dict
= CFDictionaryGetValue(service_dict
, entity
);
3880 if (entity_dict
== NULL
) {
3884 contains_protocol
= ipdict_is_routable(entity_dict
);
3885 return contains_protocol
;
3889 static CFMutableDictionaryRef
3890 service_dict_copy(CFStringRef serviceID
)
3892 CFDictionaryRef d
= NULL
;
3893 CFMutableDictionaryRef service_dict
;
3895 /* create a modifyable dictionary, a copy or a new one */
3896 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3899 = CFDictionaryCreateMutable(NULL
, 0,
3900 &kCFTypeDictionaryKeyCallBacks
,
3901 &kCFTypeDictionaryValueCallBacks
);
3904 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
3906 return (service_dict
);
3909 __private_extern__ boolean_t
3910 service_is_scoped_only(CFDictionaryRef service_dict
)
3912 nwi_ifstate_t alias
;
3913 CFDictionaryRef dict
;
3914 char ifname
[IFNAMSIZ
];
3915 nwi_ifstate_t ifstate
;
3916 CFStringRef interface
= NULL
;
3918 // get IPv4 (or IPv6) info
3919 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3921 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
3924 // if no connectivity
3929 interface
= ipdict_get_ifname(dict
);
3930 if ((interface
== NULL
) ||
3931 !CFStringGetCString(interface
, ifname
, sizeof(ifname
), kCFStringEncodingUTF8
)) {
3932 // if no interface / interface name
3937 if (S_nwi_state
== NULL
) {
3938 S_nwi_state
= nwi_state_copy();
3942 // get [nwi] interface state
3943 ifstate
= nwi_state_get_ifstate(S_nwi_state
, ifname
);
3944 if (ifstate
== NULL
) {
3947 } else if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3948 // if scoped (i.e. not in list)
3952 // check both both IPv4 and IPv6
3953 alias
= nwi_ifstate_get_alias(ifstate
, ifstate
->af
== AF_INET
? AF_INET6
: AF_INET
);
3954 if (alias
== NULL
) {
3955 // if only one address family
3957 } else if ((alias
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3958 // if scoped (i.e. not in list)
3966 log_service_entity(int level
, CFStringRef serviceID
, CFStringRef entity
,
3967 CFStringRef operation
, CFTypeRef val
)
3969 serviceIDNumber service_number
;
3970 CFMutableStringRef this_val
= NULL
;
3976 if ((is_ipv4
= CFEqual(entity
, kSCEntNetIPv4
))
3977 || (is_ipv6
= CFEqual(entity
, kSCEntNetIPv6
))) {
3978 RouteListUnion routes
;
3980 routes
.ptr
= ipdict_get_routelist(val
);
3981 if (routes
.ptr
!= NULL
) {
3982 CFDictionaryRef service_dict
= NULL
;
3985 this_val
= IPv4RouteListCopyDescription(routes
.v4
);
3988 this_val
= IPv6RouteListCopyDescription(routes
.v6
);
3990 service_dict
= ipdict_get_service(val
);
3991 if (service_dict
!= NULL
) {
3992 CFStringAppendFormat(this_val
, NULL
,
3993 CFSTR("\n<Service> = %@"),
4001 val
= CFSTR("<none>");
4003 if (serviceIDNumberGetIfPresent(serviceID
, &service_number
)) {
4004 my_log(level
, "serviceID %@ <SID %ld> %@ %@ value = %@",
4005 serviceID
, service_number
, operation
, entity
, val
);
4008 my_log(level
, "serviceID %@ %@ %@ value = %@",
4009 serviceID
, operation
, entity
, val
);
4011 my_CFRelease(&this_val
);
4016 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
4019 boolean_t changed
= FALSE
;
4021 CFMutableDictionaryRef service_dict
;
4023 service_dict
= service_dict_copy(serviceID
);
4024 old_val
= CFDictionaryGetValue(service_dict
, entity
);
4025 if (new_val
== NULL
) {
4026 if (old_val
!= NULL
) {
4027 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4028 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
4029 CFSTR("Removed:"), old_val
);
4031 CFDictionaryRemoveValue(service_dict
, entity
);
4036 if (old_val
== NULL
|| !CFEqual(new_val
, old_val
)) {
4037 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4038 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
4039 CFSTR("Changed: old"), old_val
);
4040 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
4041 CFSTR("Changed: new"), new_val
);
4043 CFDictionarySetValue(service_dict
, entity
, new_val
);
4047 if (CFDictionaryGetCount(service_dict
) == 0) {
4048 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
4049 serviceIDNumberRemove(serviceID
);
4052 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
4054 my_CFRelease(&service_dict
);
4058 static CFDictionaryRef
4059 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
4061 CFDictionaryRef service_dict
;
4063 if (S_service_state_dict
== NULL
) {
4066 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
4067 if (service_dict
== NULL
) {
4070 return (CFDictionaryGetValue(service_dict
, entity
));
4073 #if !TARGET_OS_SIMULATOR
4075 service_copy_interface(CFStringRef serviceID
, CFDictionaryRef new_service
)
4077 CFDictionaryRef dict
;
4078 CFStringRef interface
= NULL
;
4080 if (new_service
!= NULL
) {
4081 interface
= ipdict_get_ifname(new_service
);
4083 if (interface
== NULL
) {
4084 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4086 interface
= ipdict_get_ifname(dict
);
4089 if (interface
== NULL
) {
4090 dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4092 interface
= ipdict_get_ifname(dict
);
4095 if (interface
!= NULL
) {
4096 CFRetain(interface
);
4100 #endif /* !TARGET_OS_SIMULATOR */
4103 service_has_clat46_address(CFStringRef serviceID
)
4105 CFDictionaryRef ip_dict
;
4107 ip_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4108 if (ip_dict
!= NULL
) {
4109 CFBooleanRef clat46
= NULL
;
4110 CFDictionaryRef ipv4
;
4112 ipv4
= ipdict_get_service(ip_dict
);
4113 if (isA_CFDictionary(ipv4
) &&
4114 CFDictionaryGetValueIfPresent(ipv4
,
4115 kSCPropNetIPv4CLAT46
,
4116 (const void **)&clat46
) &&
4117 isA_CFBoolean(clat46
)) {
4118 return CFBooleanGetValue(clat46
);
4125 #ifndef kSCPropNetHostname
4126 #define kSCPropNetHostname CFSTR("Hostname")
4131 copy_dhcp_hostname(CFStringRef serviceID
)
4133 CFDictionaryRef dict
= NULL
;
4134 CFStringRef hostname
= NULL
;
4135 CFDictionaryRef service_dict
= NULL
;
4137 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4141 service_dict
= ipdict_get_service(dict
);
4142 if (service_dict
== NULL
) {
4145 hostname
= CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
4146 if (hostname
!= NULL
) {
4152 #if !TARGET_OS_SIMULATOR
4154 static struct in6_addr
*
4155 ipv6_service_get_router(CFDictionaryRef service
,
4156 IFIndex
* ifindex_p
, CFStringRef
* ifname_p
)
4158 IPv6RouteListRef routes
;
4159 struct in6_addr
* router
= NULL
;
4161 routes
= ipdict_get_routelist(service
);
4163 && (routes
->flags
& kRouteListFlagsExcludeNWI
) == 0
4164 && (routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
4165 router
= &routes
->list
[0].gateway
;
4166 if (*ifindex_p
== 0) {
4167 *ifindex_p
= routes
->list
[0].ifindex
;
4169 if (*ifname_p
== NULL
) {
4170 *ifname_p
= ipdict_get_ifname(service
);
4177 ipv6_service_update_router(CFStringRef serviceID
, CFDictionaryRef new_service
)
4179 IFIndex ifindex
= 0;
4180 CFStringRef ifname
= NULL
;
4181 char ntopbuf
[INET6_ADDRSTRLEN
];
4182 CFDictionaryRef old_service
;
4183 struct in6_addr
* old_router
;
4184 struct in6_addr
* new_router
;
4187 old_service
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4188 old_router
= ipv6_service_get_router(old_service
, &ifindex
, &ifname
);
4189 new_router
= ipv6_service_get_router(new_service
, &ifindex
, &ifname
);
4190 if (ifname
== NULL
|| ifindex
== 0) {
4193 s
= inet6_dgram_socket();
4197 /* remove the old router if it was defined */
4198 if (old_router
!= NULL
4199 && (new_router
== NULL
4200 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4201 if (siocdrdel_in6(s
, ifindex
, old_router
) < 0) {
4202 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4203 "siocdrdel_in6(%@, %s) failed: %s",
4205 inet_ntop(AF_INET6
, old_router
,
4206 ntopbuf
, sizeof(ntopbuf
)),
4211 "%@ removed default route %s",
4213 inet_ntop(AF_INET6
, old_router
, ntopbuf
, sizeof(ntopbuf
)));
4216 /* add the new router if it is defined */
4217 if (new_router
!= NULL
4218 && (old_router
== NULL
4219 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4220 if (siocdradd_in6(s
, ifindex
, new_router
, 0) < 0) {
4221 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4222 "siocdradd_in6(%@, %s) failed: %s",
4224 inet_ntop(AF_INET6
, new_router
,
4225 ntopbuf
, sizeof(ntopbuf
)),
4230 "%@ added default route %s",
4232 inet_ntop(AF_INET6
, new_router
, ntopbuf
, sizeof(ntopbuf
)));
4240 #endif /* !TARGET_OS_SIMULATOR */
4242 #define ALLOW_EMPTY_STRING 0x1
4244 static CF_RETURNS_RETAINED CFTypeRef
4245 sanitize_prop(CFTypeRef val
, uint32_t flags
)
4248 if (isA_CFString(val
)) {
4249 CFMutableStringRef str
;
4251 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
4252 CFStringTrimWhitespace(str
);
4253 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
4267 merge_array_prop(CFMutableDictionaryRef dict
,
4269 CFDictionaryRef state_dict
,
4270 CFDictionaryRef setup_dict
,
4274 CFMutableArrayRef merge_prop
;
4275 CFArrayRef setup_prop
= NULL
;
4276 CFArrayRef state_prop
= NULL
;
4278 if (setup_dict
!= NULL
) {
4279 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
4281 if (state_dict
!= NULL
) {
4282 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
4285 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
4289 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4290 if (setup_prop
!= NULL
) {
4294 n
= CFArrayGetCount(setup_prop
);
4295 for (i
= 0; i
< n
; i
++) {
4298 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
4299 val
= sanitize_prop(val
, flags
);
4301 CFArrayAppendValue(merge_prop
, val
);
4306 if (state_prop
!= NULL
4307 && (setup_prop
== NULL
|| S_append_state
)) {
4310 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
4312 n
= CFArrayGetCount(state_prop
);
4313 for (i
= 0; i
< n
; i
++) {
4316 val
= CFArrayGetValueAtIndex(state_prop
, i
);
4317 val
= sanitize_prop(val
, flags
);
4319 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
4320 CFArrayAppendValue(merge_prop
, val
);
4326 if (CFArrayGetCount(merge_prop
) > 0) {
4327 CFDictionarySetValue(dict
, key
, merge_prop
);
4329 CFRelease(merge_prop
);
4334 pick_prop(CFMutableDictionaryRef dict
,
4336 CFDictionaryRef state_dict
,
4337 CFDictionaryRef setup_dict
,
4340 CFTypeRef val
= NULL
;
4342 if (setup_dict
!= NULL
) {
4343 val
= CFDictionaryGetValue(setup_dict
, key
);
4344 val
= sanitize_prop(val
, flags
);
4346 if (val
== NULL
&& state_dict
!= NULL
) {
4347 val
= CFDictionaryGetValue(state_dict
, key
);
4348 val
= sanitize_prop(val
, flags
);
4351 CFDictionarySetValue(dict
, key
, val
);
4359 ** GetEntityChangesFunc functions
4361 #define IPV4_ROUTES_N_STATIC 5
4362 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4363 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4367 #define IPV4_ROUTES_BUF_DECL(routes) \
4368 IPv4RouteListRef routes; \
4369 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4371 routes = (IPv4RouteListRef)(void *)routes_buf; \
4372 routes->size = IPV4_ROUTES_N_STATIC; \
4373 routes->count = 0; \
4377 IPv4RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
,
4378 serviceIDNumber sidn
)
4381 CFDataRef routes_data
;
4382 IPV4_ROUTES_BUF_DECL(routes
);
4384 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
,
4416 serviceIDNumber sidn
)
4419 CFDataRef routes_data
;
4420 IPV6_ROUTES_BUF_DECL(routes
);
4422 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion
,
4425 routes_data
= CFDataCreate(NULL
,
4427 IPv6RouteListComputeSize(r
->count
));
4435 return (routes_data
);
4438 static CFDictionaryRef
4439 IPDictCreate(int af
, _Nonnull CFDictionaryRef state_dict
,
4440 CFDictionaryRef setup_dict
,
4441 CFNumberRef rank_assertion
, CFStringRef serviceID
)
4443 CFDictionaryRef aggregated_dict
= NULL
;
4444 CFDictionaryRef dict
;
4445 CFMutableDictionaryRef modified_dict
= NULL
;
4446 CFDataRef routes_data
;
4447 serviceIDNumber sidn
;
4449 sidn
= serviceIDNumberGet(serviceID
);
4451 if (setup_dict
!= NULL
) {
4452 /* look for keys in Setup: that override/merge with State: */
4453 CFArrayRef additional_routes
;
4454 CFStringRef route_list_prop
;
4457 CFStringRef router_prop
;
4462 router_prop
= kSCPropNetIPv4Router
;
4463 route_list_prop
= kSCPropNetIPv4AdditionalRoutes
;
4467 router_prop
= kSCPropNetIPv6Router
;
4468 route_list_prop
= kSCPropNetIPv6AdditionalRoutes
;
4471 router
= CFDictionaryGetValue(setup_dict
, router_prop
);
4473 && !cfstring_to_ipvx(af
, router
, &router_ip
, sizeof(router_ip
))) {
4477 /* AdditionalRoutes */
4479 = CFDictionaryGetValue(setup_dict
, route_list_prop
);
4480 additional_routes
= isA_CFArray(additional_routes
);
4482 if (router
!= NULL
|| additional_routes
!= NULL
) {
4483 modified_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
4484 if (router
!= NULL
) {
4485 CFDictionarySetValue(modified_dict
,
4489 if (additional_routes
!= NULL
) {
4490 CFArrayRef combined_routes
= NULL
;
4491 CFArrayRef state_routes
;
4494 = CFDictionaryGetValue(state_dict
,
4496 if (isA_CFArray(state_routes
) != NULL
) {
4498 = my_CFArrayCreateCombinedArray(additional_routes
,
4500 additional_routes
= combined_routes
;
4502 CFDictionarySetValue(modified_dict
,
4505 if (combined_routes
!= NULL
) {
4506 CFRelease(combined_routes
);
4509 dict
= modified_dict
;
4514 routes_data
= IPv4RouteListDataCreate(dict
, rank_assertion
, sidn
);
4518 routes_data
= IPv6RouteListDataCreate(dict
, rank_assertion
, sidn
);
4521 if (routes_data
!= NULL
) {
4522 aggregated_dict
= ipdict_create(dict
, routes_data
);
4523 CFRelease(routes_data
);
4525 if (modified_dict
!= NULL
) {
4526 CFRelease(modified_dict
);
4528 return (aggregated_dict
);
4532 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4533 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4535 #pragma unused(info)
4536 CFDictionaryRef dict
= NULL
;
4537 boolean_t changed
= FALSE
;
4538 CFNumberRef rank_assertion
= NULL
;
4539 CFDictionaryRef service_options
;
4541 if (state_dict
== NULL
) {
4544 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4545 if (service_options
!= NULL
) {
4547 = CFDictionaryGetValue(service_options
,
4548 kServiceOptionRankAssertion
);
4550 dict
= IPDictCreate(AF_INET
, state_dict
, setup_dict
, rank_assertion
,
4554 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, dict
);
4556 /* clean up the rank too */
4557 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
4559 my_CFRelease(&dict
);
4565 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4566 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4568 #pragma unused(info)
4569 CFDictionaryRef dict
= NULL
;
4570 boolean_t changed
= FALSE
;
4571 #if !TARGET_OS_SIMULATOR
4572 CFStringRef interface
;
4573 #endif /* !TARGET_OS_SIMULATOR */
4574 CFNumberRef rank_assertion
= NULL
;
4575 CFDictionaryRef service_options
;
4577 if (state_dict
== NULL
) {
4580 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4581 if (service_options
!= NULL
) {
4583 = CFDictionaryGetValue(service_options
,
4584 kServiceOptionRankAssertion
);
4587 dict
= IPDictCreate(AF_INET6
, state_dict
, setup_dict
, rank_assertion
,
4592 #if !TARGET_OS_SIMULATOR
4593 interface
= service_copy_interface(serviceID
, dict
);
4594 ipv6_service_update_router(serviceID
, dict
);
4595 #endif /* !TARGET_OS_SIMULATOR */
4597 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, dict
);
4599 #if !TARGET_OS_SIMULATOR
4600 if (interface
!= NULL
) {
4602 CFBooleanRef needs_plat
= NULL
;
4605 // if service is unpublished, cancel the request
4606 set_plat_discovery(kPLATDiscoveryOptionCancel
, interface
);
4607 } else if ((state_dict
!= NULL
) &&
4608 CFDictionaryGetValueIfPresent(state_dict
,
4609 kSCPropNetIPv6PerformPLATDiscovery
,
4610 (const void **)&needs_plat
) &&
4611 isA_CFBoolean(needs_plat
) &&
4612 CFBooleanGetValue(needs_plat
)) {
4613 // perform PLAT discovery
4614 set_plat_discovery(kPLATDiscoveryOptionStart
, interface
);
4616 // IPv6 configuration changed for this interface, poke NAT64
4617 set_plat_discovery(kPLATDiscoveryOptionUpdate
, interface
);
4620 CFRelease(interface
);
4622 #endif /* !TARGET_OS_SIMULATOR */
4625 /* service removed, clean up the rank too */
4626 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
4628 my_CFRelease(&dict
);
4634 __private_extern__ CFDictionaryRef
4635 ipv4_dict_create(CFDictionaryRef state_dict
)
4637 return (IPDictCreate(AF_INET
, state_dict
, NULL
, NULL
, NULL
));
4640 __private_extern__ CFDictionaryRef
4641 ipv6_dict_create(CFDictionaryRef state_dict
)
4643 return (IPDictCreate(AF_INET6
, state_dict
, NULL
, NULL
, NULL
));
4646 #endif /* TEST_DNS */
4649 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
4650 CFMutableArrayRef out_servers
, CFStringRef interface
)
4655 count
= CFArrayGetCount(in_servers
);
4656 for (i
= 0; i
< count
; i
++) {
4658 struct in6_addr ipv6_addr
;
4659 struct in_addr ip_addr
;
4661 addr
= CFArrayGetValueAtIndex(in_servers
, i
);
4662 assert(addr
!= NULL
);
4664 if (cfstring_to_ip(addr
, &ip_addr
)) {
4666 if ((active_protos
& kProtocolFlagsIPv4
) == 0
4667 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
4669 "no IPv4 connectivity, "
4670 "ignoring DNS server address " IP_FORMAT
,
4677 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
4679 if ((active_protos
& kProtocolFlagsIPv6
) == 0
4680 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
4681 char ntopbuf
[INET6_ADDRSTRLEN
];
4684 "no IPv6 connectivity, "
4685 "ignoring DNS server address %s",
4686 inet_ntop(AF_INET6
, &ipv6_addr
,
4687 ntopbuf
, sizeof(ntopbuf
)));
4691 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
4692 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
4693 && (interface
!= NULL
)
4694 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
4695 // append interface name to IPv6 link local address
4696 addr
= CFStringCreateWithFormat(NULL
, NULL
,
4705 /* bad IP address */
4706 my_log(LOG_NOTICE
, "ignoring bad DNS server address '%@'", addr
);
4710 /* DNS server is valid and one we want */
4711 CFArrayAppendValue(out_servers
, addr
);
4717 static CF_RETURNS_RETAINED CFArrayRef
4718 order_dns_servers(CFArrayRef servers
, ProtocolFlags active_protos
)
4720 Boolean favor_v4
= FALSE
;
4721 CFMutableArrayRef ordered_servers
;
4722 ProtocolFlags proto_last
= kProtocolFlagsIPv4
;
4723 struct sockaddr_in v4_dns1
= { .sin_family
= AF_INET
,
4724 .sin_len
= sizeof(struct sockaddr_in
) };
4726 struct sockaddr_in6 v6_dns1
= { .sin6_family
= AF_INET6
,
4727 .sin6_len
= sizeof(struct sockaddr_in6
),
4728 .sin6_scope_id
= 0 };
4731 if (((active_protos
& kProtocolFlagsIPv4
) == 0) ||
4732 ((active_protos
& kProtocolFlagsIPv6
) == 0)) {
4733 /* only one protocol */
4734 #ifdef TEST_DNS_ORDER
4735 printf("only one protocol\n");
4736 #endif // TEST_DNS_ORDER
4737 return CFRetain(servers
);
4740 ordered_servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4741 for (CFIndex i
= 0, n
= CFArrayGetCount(servers
); i
< n
; i
++) {
4743 struct in6_addr ia6
;
4744 ProtocolFlags proto
;
4747 server
= CFArrayGetValueAtIndex(servers
, i
);
4748 if (cfstring_to_ip(server
, &ia
)) {
4749 proto
= kProtocolFlagsIPv4
;
4751 v4_dns1
.sin_addr
= ia
;
4753 } else if (cfstring_to_ip6(server
, &ia6
)) {
4754 proto
= kProtocolFlagsIPv6
;
4756 memcpy(&v6_dns1
.sin6_addr
, &ia6
, sizeof(ia6
));
4759 CFRelease(ordered_servers
);
4760 return CFRetain(servers
);
4763 if ((i
> 0) && (proto
!= proto_last
)) {
4764 /* if the protocol of the server addresses changed */
4765 if (((proto
== kProtocolFlagsIPv4
) && (v4_n
== 1)) ||
4766 ((proto
== kProtocolFlagsIPv6
) && (v6_n
== 1))) {
4767 /* if we now have the 1st server address of another protocol */
4768 favor_v4
= (sa_dst_compare_no_dependencies((struct sockaddr
*)&v4_dns1
,
4769 (struct sockaddr
*)&v6_dns1
) >= 0);
4770 #ifdef TEST_DNS_ORDER
4771 char v4_buf
[INET_ADDRSTRLEN
];
4772 char v6_buf
[INET6_ADDRSTRLEN
];
4773 printf("comparing %s vs %s, favoring %s\n",
4774 inet_ntop(v4_dns1
.sin_family
, &v4_dns1
.sin_addr
, v4_buf
, sizeof(v4_buf
)),
4775 inet_ntop(v6_dns1
.sin6_family
, &v6_dns1
.sin6_addr
, v6_buf
, sizeof(v6_buf
)),
4776 favor_v4
? "v4" : "v6");
4777 #endif // TEST_DNS_ORDER
4779 /* if the server addresses array is randomly mixed */
4780 #ifdef TEST_DNS_ORDER
4781 printf("v4/v6 not ordered\n");
4782 #endif // TEST_DNS_ORDER
4783 CFRelease(ordered_servers
);
4784 return CFRetain(servers
);
4789 if ((proto
== kProtocolFlagsIPv4
) && favor_v4
) {
4790 CFArrayInsertValueAtIndex(ordered_servers
, v4_n
- 1, server
);
4791 } else if ((proto
== kProtocolFlagsIPv6
) && !favor_v4
) {
4792 CFArrayInsertValueAtIndex(ordered_servers
, v6_n
- 1, server
);
4794 CFArrayAppendValue(ordered_servers
, server
);
4798 return ordered_servers
;
4802 merge_dns_servers(CFMutableDictionaryRef new_dict
,
4803 CFArrayRef state_servers
,
4804 CFArrayRef setup_servers
,
4806 Boolean trust_state
,
4807 ProtocolFlags active_protos
,
4808 CFStringRef interface
)
4810 CFMutableArrayRef dns_servers
;
4811 Boolean have_dns_setup
= FALSE
;
4813 if (state_servers
== NULL
&& setup_servers
== NULL
) {
4814 /* no DNS servers */
4817 dns_servers
= CFArrayCreateMutable(NULL
, 0,
4818 &kCFTypeArrayCallBacks
);
4819 if (setup_servers
!= NULL
) {
4820 accumulate_dns_servers(setup_servers
, active_protos
,
4821 dns_servers
, interface
);
4822 if (CFArrayGetCount(dns_servers
) > 0) {
4823 have_dns_setup
= TRUE
;
4826 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
4827 && state_servers
!= NULL
) {
4828 CFArrayRef ordered_servers
;
4830 ordered_servers
= order_dns_servers(state_servers
, active_protos
);
4831 accumulate_dns_servers(ordered_servers
, active_protos
,
4833 CFRelease(ordered_servers
);
4837 * Here, we determine whether or not we want all queries for this DNS
4838 * configuration to be bound to the associated network interface.
4840 * For dynamically derived network configurations (i.e. from State:)
4841 * this would be the preferred option using the argument "Hey, the
4842 * server told us to use these servers on this network so let's not
4845 * But, when a DNS configuration has been provided by the user/admin
4846 * via the Network pref pane (i.e. from Setup:) we opt to not force
4847 * binding of the outbound queries. The simplest example why we take
4848 * this stance is with a multi-homing configuration. Consider a system
4849 * with one network service associated with "en0" and a second service
4850 * associated with "en1". The "en0" service has been set higher in
4851 * the network service order so it would be primary but the user/admin
4852 * wants the DNS queries to go to a server only accessible via "en1".
4853 * Without this exception we would take the DNS server addresses from
4854 * the Network pref pane (for "en0") and have the queries bound to
4855 * "en0" where they'd never reach their intended destination (via
4856 * "en1"). So, our exception to the rule is that we will not bind
4857 * user/admin configurations to any specific network interface.
4859 * We also add an exception to the "follow the dynamically derived
4860 * network configuration" path for on-the-fly (no Setup: content)
4863 * But, we add an exception to the exception to support our own
4864 * VPN code. Here, we look for a "ServiceID" property in the DNS
4865 * entity. If present, and if it matches, then we extend our
4866 * trust even when there is no Setup: content.
4868 if (CFArrayGetCount(dns_servers
) != 0) {
4869 CFDictionarySetValue(new_dict
,
4870 kSCPropNetDNSServerAddresses
, dns_servers
);
4871 if ((have_setup
&& !have_dns_setup
) || (!have_setup
&& trust_state
)) {
4872 // if this is a "setup"+"state" service with only "state" DNS content (i.e. no
4873 // setup override) or this is a TRUSTED "state"-only service
4874 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
4878 my_CFRelease(&dns_servers
);
4884 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4885 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4887 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4888 boolean_t changed
= FALSE
;
4890 Boolean have_setup
= FALSE
;
4891 CFStringRef interface
= NULL
;
4892 CFDictionaryRef ipv4
;
4893 CFDictionaryRef ipv6
;
4899 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
4900 { kSCPropNetDNSSortList
, 0, FALSE
},
4901 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4902 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
4904 CFMutableDictionaryRef new_dict
= NULL
;
4905 const CFStringRef pick_list
[] = {
4906 kSCPropNetDNSDomainName
,
4907 kSCPropNetDNSOptions
,
4908 kSCPropNetDNSSearchOrder
,
4909 kSCPropNetDNSServerPort
,
4910 kSCPropNetDNSServerTimeout
,
4911 kSCPropNetDNSServiceIdentifier
,
4912 kSCPropNetDNSSupplementalMatchDomainsNoSearch
,
4914 Boolean trust_state
= FALSE
;
4916 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4917 /* there is no DNS content */
4921 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4922 if (ipdict_is_routable(ipv4
)) {
4923 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
4926 active_protos
|= kProtocolFlagsIPv4
;
4927 interface
= ipdict_get_ifname(ipv4
);
4930 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4931 if (ipdict_is_routable(ipv6
)) {
4933 (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
) != NULL
)) {
4936 active_protos
|= kProtocolFlagsIPv6
;
4937 if (interface
== NULL
) {
4938 interface
= ipdict_get_ifname(ipv6
);
4943 if (active_protos
== kProtocolFlagsNone
) {
4944 /* there is no IPv4 nor IPv6 */
4945 if (state_dict
== NULL
) {
4946 /* ... and no DNS content that we care about */
4952 if (state_dict
!= NULL
) {
4953 CFStringRef state_serviceID
= NULL
;
4955 if (CFDictionaryGetValueIfPresent(state_dict
,
4956 kSCPropNetDNSConfirmedServiceID
,
4957 (const void **)&state_serviceID
) &&
4958 isA_CFString(state_serviceID
) &&
4959 CFEqual(serviceID
, state_serviceID
)) {
4964 /* merge DNS configuration */
4965 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
4966 &kCFTypeDictionaryKeyCallBacks
,
4967 &kCFTypeDictionaryValueCallBacks
);
4969 if (active_protos
== kProtocolFlagsNone
) {
4970 merge_dns_servers(new_dict
,
4971 my_CFDictionaryGetArray(state_dict
,
4972 kSCPropNetDNSServerAddresses
),
4976 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
4980 merge_dns_servers(new_dict
,
4981 my_CFDictionaryGetArray(state_dict
,
4982 kSCPropNetDNSServerAddresses
),
4983 my_CFDictionaryGetArray(setup_dict
,
4984 kSCPropNetDNSServerAddresses
),
4991 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
4992 merge_array_prop(new_dict
,
4996 merge_list
[i
].flags
,
4997 merge_list
[i
].append
);
5000 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5008 if (active_protos
== kProtocolFlagsNone
) {
5009 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
5010 if (CFDictionaryContainsKey(new_dict
,
5011 kSCPropNetDNSSupplementalMatchDomains
)) {
5012 /* only keep State: supplemental */
5013 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
5014 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
5015 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
5016 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
5018 if ((interface
== NULL
) && (setup_dict
== NULL
) && (state_dict
!= NULL
)) {
5020 * for supplemental-only configurations, add any scoped (or
5021 * wild-card "*") interface
5023 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
5025 } else if (CFDictionaryContainsKey(new_dict
, kSCPropNetDNSServiceIdentifier
) &&
5026 (interface
== NULL
) &&
5027 (state_dict
!= NULL
)) {
5028 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
5034 if (CFDictionaryGetCount(new_dict
) == 0) {
5035 my_CFRelease(&new_dict
);
5039 if (interface
!= NULL
) {
5040 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5043 if (S_append_state
) {
5045 * ensure any specified domain name (e.g. the domain returned by
5046 * a DHCP server) is in the search list.
5048 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
5049 if (isA_CFString(domain
)) {
5052 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
5053 if (isA_CFArray(search
) &&
5054 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
5055 CFMutableArrayRef new_search
;
5057 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
5058 CFArrayAppendValue(new_search
, domain
);
5059 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
5060 my_CFRelease(&new_search
);
5067 #if !TARGET_OS_SIMULATOR
5068 if (interface
!= NULL
) {
5069 CFRetain(interface
);
5071 #endif /* !TARGET_OS_SIMULATOR */
5073 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
5075 #if !TARGET_OS_SIMULATOR
5076 if (interface
!= NULL
) {
5078 // DNS configuration changed for this interface, poke NAT64
5079 if ((active_protos
& kProtocolFlagsIPv6
) != 0) {
5080 set_plat_discovery(kPLATDiscoveryOptionUpdate
, interface
);
5083 CFRelease(interface
);
5085 #endif /* !TARGET_OS_SIMULATOR */
5087 my_CFRelease(&new_dict
);
5092 merge_dict(const void *key
, const void *value
, void *context
)
5094 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
5096 CFDictionarySetValue(dict
, key
, value
);
5100 #define PROXY_AUTO_DISCOVERY_URL 252
5102 static CF_RETURNS_RETAINED CFStringRef
5103 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
5105 CFStringRef urlString
= NULL
;
5107 if (dhcp_options
!= NULL
) {
5110 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
5113 const UInt8
*urlBytes
;
5116 urlBytes
= CFDataGetBytePtr(data
);
5117 urlLen
= CFDataGetLength(data
);
5118 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
5119 // remove trailing NUL
5127 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
5129 urlString
= CFURLGetString(url
);
5130 if (urlString
!= NULL
) {
5131 CFRetain(urlString
);
5141 static CF_RETURNS_RETAINED CFStringRef
5145 CFStringRef urlString
= NULL
;
5147 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
5149 urlString
= CFURLGetString(url
);
5150 if (urlString
!= NULL
) {
5151 CFRetain(urlString
);
5160 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5161 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5163 ProtocolFlags active_protos
= kProtocolFlagsNone
;
5164 boolean_t changed
= FALSE
;
5165 CFStringRef interface
= NULL
;
5166 CFDictionaryRef ipv4
;
5167 CFDictionaryRef ipv6
;
5168 CFMutableDictionaryRef new_dict
= NULL
;
5174 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
5175 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
5178 CFStringRef key1
; /* an "enable" key */
5182 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
5183 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
5184 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
5185 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
5186 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
5187 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
5188 { kSCPropNetProxiesProxyAutoConfigEnable
,
5189 kSCPropNetProxiesProxyAutoConfigURLString
,
5190 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
5191 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5196 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
5197 /* there is no proxy content */
5200 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
5201 if (ipdict_is_routable(ipv4
)) {
5202 active_protos
|= kProtocolFlagsIPv4
;
5203 interface
= ipdict_get_ifname(ipv4
);
5205 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
5206 if (ipdict_is_routable(ipv6
)) {
5207 active_protos
|= kProtocolFlagsIPv6
;
5208 if (interface
== NULL
) {
5209 interface
= ipdict_get_ifname(ipv6
);
5212 if (active_protos
== kProtocolFlagsNone
) {
5213 /* there is no IPv4 nor IPv6 */
5214 if (state_dict
== NULL
) {
5215 /* ... and no proxy content that we care about */
5221 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
5222 CFMutableDictionaryRef setup_copy
;
5225 * Merge the per-service "Setup:" and "State:" proxy information with
5226 * the "Setup:" information always taking precedence. Additionally,
5227 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
5228 * Port) is defined than all of the values for that group will be
5229 * used. That is, we don't allow mixing some of the values from
5230 * the "Setup:" keys and others from the "State:" keys.
5232 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5233 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
5234 merge_array_prop(new_dict
,
5238 merge_list
[i
].flags
,
5239 merge_list
[i
].append
);
5242 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5243 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5244 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
5246 * if a "Setup:" enabled key has been provided than we want to
5247 * ignore all of the "State:" keys
5249 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
5250 if (pick_list
[i
].key2
!= NULL
) {
5251 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
5253 if (pick_list
[i
].key3
!= NULL
) {
5254 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
5256 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
5257 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
5258 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
5260 * if a "Setup:" enabled key has not been provided and we have
5261 * some" "State:" keys than we remove all of of "Setup:" keys
5263 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
5264 if (pick_list
[i
].key2
!= NULL
) {
5265 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
5267 if (pick_list
[i
].key3
!= NULL
) {
5268 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
5273 /* merge the "Setup:" keys */
5274 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
5275 CFRelease(setup_copy
);
5277 else if (setup_dict
!= NULL
) {
5278 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5280 else if (state_dict
!= NULL
) {
5281 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5284 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
5285 CFRelease(new_dict
);
5289 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
5290 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5294 if (new_dict
!= NULL
) {
5295 CFDictionaryRef dhcp_options
;
5297 CFNumberRef wpad
= NULL
;
5298 int wpadEnabled
= 0;
5299 CFStringRef wpadURL
= NULL
;
5301 if (CFDictionaryGetValueIfPresent(new_dict
,
5302 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5303 (const void **)&num
) &&
5304 isA_CFNumber(num
)) {
5305 /* if we have a WPAD key */
5307 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
5308 /* if we don't like the enabled key/value */
5316 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
5317 if (!isA_CFNumber(num
) ||
5318 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
5319 /* if we don't like the enabled key/value */
5326 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
5327 if (pacURL
!= NULL
) {
5328 if (!isA_CFString(pacURL
) || (CFStringGetLength(pacURL
) == 0)) {
5329 /* if we don't like the PAC URL */
5335 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
5336 if (!isA_CFString(pacJS
) || (CFStringGetLength(pacJS
) == 0)) {
5337 /* if we don't have (or like) the PAC JavaScript */
5345 * we already have a PAC URL so disable WPAD.
5352 * if WPAD is enabled and we don't already have a PAC URL then
5353 * we check for a DHCP provided URL. If not available, we use
5354 * a PAC URL pointing to a well-known file (wpad.dat) on a
5355 * well-known host (wpad.<domain>).
5357 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
5358 wpadURL
= wpadURL_dhcp(dhcp_options
);
5359 if (wpadURL
== NULL
) {
5360 wpadURL
= wpadURL_dns();
5362 if (wpadURL
== NULL
) {
5363 wpadEnabled
= 0; /* if we don't have a WPAD URL */
5368 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
5369 CFDictionarySetValue(new_dict
,
5370 kSCPropNetProxiesProxyAutoConfigEnable
,
5373 CFDictionarySetValue(new_dict
,
5374 kSCPropNetProxiesProxyAutoConfigURLString
,
5381 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
5382 CFDictionarySetValue(new_dict
,
5383 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5390 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
5391 my_CFRelease(&new_dict
);
5395 #if !TARGET_OS_IPHONE
5397 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5398 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5400 #pragma unused(info)
5401 boolean_t changed
= FALSE
;
5402 CFMutableDictionaryRef new_dict
= NULL
;
5403 const CFStringRef pick_list
[] = {
5404 kSCPropNetSMBNetBIOSName
,
5405 kSCPropNetSMBNetBIOSNodeType
,
5406 #ifdef ADD_NETBIOS_SCOPE
5407 kSCPropNetSMBNetBIOSScope
,
5408 #endif // ADD_NETBIOS_SCOPE
5409 kSCPropNetSMBWorkgroup
,
5412 if (state_dict
== NULL
&& setup_dict
== NULL
) {
5413 /* there is no SMB */
5416 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
5417 && service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
) {
5418 /* there is no IPv4 or IPv6 */
5422 /* merge SMB configuration */
5423 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5424 &kCFTypeDictionaryKeyCallBacks
,
5425 &kCFTypeDictionaryValueCallBacks
);
5426 merge_array_prop(new_dict
,
5427 kSCPropNetSMBWINSAddresses
,
5432 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5440 if (CFDictionaryGetCount(new_dict
) == 0) {
5441 my_CFRelease(&new_dict
);
5446 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
5447 my_CFRelease(&new_dict
);
5450 #endif /* !TARGET_OS_IPHONE */
5453 services_info_get_interface(CFDictionaryRef services_info
,
5454 CFStringRef serviceID
)
5456 CFStringRef interface
= NULL
;
5457 CFDictionaryRef ipv4_dict
;
5459 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
5461 if (ipv4_dict
!= NULL
) {
5462 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
5465 CFDictionaryRef ipv6_dict
;
5467 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
5469 if (ipv6_dict
!= NULL
) {
5470 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5477 static const struct {
5478 const CFStringRef
* entityName
;
5479 const CFStringRef
* statusKey
;
5480 } transientServiceInfo
[] = {
5481 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
5482 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
5483 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
5487 get_transient_status_changes(CFStringRef serviceID
,
5488 CFDictionaryRef services_info
)
5490 boolean_t changed
= FALSE
;
5492 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5493 CFDictionaryRef dict
;
5494 CFNumberRef status
= NULL
;
5495 CFMutableDictionaryRef ts_dict
= NULL
;
5497 dict
= get_service_state_entity(services_info
, serviceID
,
5498 *transientServiceInfo
[i
].entityName
);
5501 status
= CFDictionaryGetValue(dict
,
5502 *transientServiceInfo
[i
].statusKey
);
5505 if (isA_CFNumber(status
) != NULL
) {
5506 ts_dict
= CFDictionaryCreateMutable(NULL
,
5508 &kCFTypeDictionaryKeyCallBacks
,
5509 &kCFTypeDictionaryValueCallBacks
);
5510 CFDictionaryAddValue(ts_dict
,
5511 *transientServiceInfo
[i
].statusKey
,
5515 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
,
5520 if (ts_dict
!= NULL
) {
5528 if_dict_is_expensive(CFDictionaryRef if_dict
)
5530 boolean_t is_expensive
= FALSE
;
5532 if (isA_CFDictionary(if_dict
) != NULL
) {
5533 CFBooleanRef expensive
;
5534 expensive
= CFDictionaryGetValue(if_dict
, kSCPropNetLinkExpensive
);
5535 if (isA_CFBoolean(expensive
) != NULL
5536 && CFBooleanGetValue(expensive
)) {
5537 is_expensive
= TRUE
;
5540 return is_expensive
;
5544 service_is_expensive(CFStringRef serviceID
, CFDictionaryRef services_info
)
5547 boolean_t is_expensive
= FALSE
;
5549 ifname
= services_info_get_interface(services_info
, serviceID
);
5550 if (ifname
!= NULL
) {
5551 CFDictionaryRef if_dict
;
5554 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5555 if_dict
= CFDictionaryGetValue(services_info
, key
);
5557 is_expensive
= if_dict_is_expensive(if_dict
);
5559 return (is_expensive
);
5563 interface_is_expensive(CFStringRef ifname
)
5565 boolean_t is_expensive
= FALSE
;
5567 if (ifname
!= NULL
) {
5568 CFDictionaryRef if_dict
;
5571 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5572 if_dict
= SCDynamicStoreCopyValue(S_session
, key
);
5574 if (if_dict
!= NULL
) {
5575 is_expensive
= if_dict_is_expensive(if_dict
);
5579 return (is_expensive
);
5583 service_rank_entity_get_index(CFDictionaryRef dict
, CFStringRef serviceID
,
5584 CFStringRef which
, uint32_t * ret_val
)
5586 CFNumberRef service_index
= NULL
;
5589 service_index
= CFDictionaryGetValue(dict
,
5590 kSCPropNetServiceServiceIndex
);
5591 service_index
= isA_CFNumber(service_index
);
5593 if (service_index
!= NULL
) {
5596 if (!CFNumberGetValue(service_index
, kCFNumberSInt32Type
,
5598 || index_val
<= 0) {
5599 /* ServiceIndex must be >= 1 */
5601 "%@%@ ServiceIndex %@ is invalid, ignoring",
5602 which
, serviceID
, service_index
);
5603 service_index
= NULL
;
5605 else if (ret_val
!= NULL
) {
5606 *ret_val
= (uint32_t)index_val
;
5609 return (service_index
);
5613 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
5614 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
5616 boolean_t changed
= FALSE
;
5617 CFStringRef interface
;
5618 boolean_t ip_is_coupled
= FALSE
;
5619 CFMutableDictionaryRef new_dict
= NULL
;
5620 Rank rank_assertion
= kRankAssertionDefault
;
5621 Boolean rank_assertion_is_set
= FALSE
;
5622 CFStringRef setup_rank
= NULL
;
5623 CFStringRef state_rank
= NULL
;
5624 CFNumberRef service_index
= NULL
;
5625 boolean_t use_setup_rank
= TRUE
;
5628 if (setup_options
!= NULL
) {
5629 CFBooleanRef coupled
;
5632 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
5633 setup_rank
= isA_CFString(setup_rank
);
5634 if (setup_rank
!= NULL
&& !use_setup_rank
) {
5635 my_log(LOG_DEBUG
, "%@ ignoring Setup PrimaryRank = %@",
5636 serviceID
, setup_rank
);
5639 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
5640 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5641 ip_is_coupled
= TRUE
;
5644 = service_rank_entity_get_index(setup_options
,
5646 kSCDynamicStoreDomainSetup
,
5649 if (state_options
!= NULL
) {
5650 CFBooleanRef coupled
;
5653 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
5654 state_rank
= isA_CFString(state_rank
);
5655 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
5656 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5657 ip_is_coupled
= TRUE
;
5659 if (service_index
== NULL
) {
5661 = service_rank_entity_get_index(state_options
,
5663 kSCDynamicStoreDomainState
,
5668 if (!ip_is_coupled
) {
5669 ip_is_coupled
= service_is_expensive(serviceID
, services_info
);
5671 if (setup_rank
!= NULL
|| state_rank
!= NULL
) {
5672 /* rank assertion is set on the service */
5673 Rank setup_assertion
;
5674 Boolean setup_assertion_is_set
= FALSE
;
5675 Rank state_assertion
;
5676 Boolean state_assertion_is_set
= FALSE
;
5678 setup_assertion
= PrimaryRankGetRankAssertion(setup_rank
,
5679 &setup_assertion_is_set
);
5680 state_assertion
= PrimaryRankGetRankAssertion(state_rank
,
5681 &state_assertion_is_set
);
5682 if (setup_assertion_is_set
&& state_assertion_is_set
) {
5683 if (setup_assertion
> state_assertion
) {
5684 rank_assertion
= setup_assertion
;
5687 rank_assertion
= state_assertion
;
5689 rank_assertion_is_set
= TRUE
;
5691 else if (setup_assertion_is_set
) {
5692 rank_assertion
= setup_assertion
;
5693 rank_assertion_is_set
= TRUE
;
5695 else if (state_assertion_is_set
) {
5696 rank_assertion
= state_assertion
;
5697 rank_assertion_is_set
= TRUE
;
5701 interface
= services_info_get_interface(services_info
, serviceID
);
5702 if (interface
!= NULL
) {
5703 if (!rank_assertion_is_set
) {
5704 /* check for a rank assertion on the interface */
5705 CFNumberRef if_rank
= NULL
;
5707 if (S_if_rank_dict
!= NULL
) {
5708 if_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
5711 = InterfaceRankGetRankAssertion(if_rank
,
5712 &rank_assertion_is_set
);
5713 #define kNotSetString ((CFTypeRef)CFSTR("not set"))
5715 "serviceID %@ interface %@ rank = 0x%x (%@)%s",
5719 (if_rank
!= NULL
) ? (CFTypeRef
)if_rank
: kNotSetString
,
5720 ip_is_coupled
? " [coupled]" : "");
5724 "serviceID %@ interface %@ rank = 0x%x%s",
5725 serviceID
, interface
, rank_assertion
,
5726 ip_is_coupled
? " [coupled]" : "");
5731 if (service_index
!= NULL
|| rank_assertion_is_set
|| ip_is_coupled
) {
5732 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5733 &kCFTypeDictionaryKeyCallBacks
,
5734 &kCFTypeDictionaryValueCallBacks
);
5735 if (rank_assertion_is_set
) {
5736 CFNumberRef new_rank
;
5738 new_rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
,
5739 (const void *)&rank_assertion
);
5740 CFDictionarySetValue(new_dict
, kServiceOptionRankAssertion
,
5742 CFRelease(new_rank
);
5744 if (ip_is_coupled
) {
5745 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
5747 if (service_index
!= NULL
) {
5748 CFDictionarySetValue(new_dict
, kSCPropNetServiceServiceIndex
,
5752 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
5753 my_CFRelease(&new_dict
);
5758 add_service_keys(CFStringRef serviceID
,
5759 CFMutableArrayRef keys
,
5760 CFMutableArrayRef patterns
)
5765 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
5769 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5770 CFStringRef name
= *entityTypeNames
[i
];
5772 key
= setup_service_key(serviceID
, name
);
5773 my_CFArrayAppendUniqueValue(keys
, key
);
5775 key
= state_service_key(serviceID
, name
);
5776 my_CFArrayAppendUniqueValue(keys
, key
);
5780 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
5781 my_CFArrayAppendUniqueValue(keys
, key
);
5784 key
= setup_service_key(serviceID
, NULL
);
5785 my_CFArrayAppendUniqueValue(keys
, key
);
5787 key
= state_service_key(serviceID
, NULL
);
5788 my_CFArrayAppendUniqueValue(keys
, key
);
5795 add_transient_status_keys(CFStringRef serviceID
,
5796 CFMutableArrayRef keys
,
5797 CFMutableArrayRef patterns
)
5799 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
5803 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5806 key
= state_service_key(serviceID
,
5807 *transientServiceInfo
[i
].entityName
);
5808 my_CFArrayAppendUniqueValue(keys
, key
);
5815 static const CFStringRef
*reachabilitySetupKeys
[] = {
5817 &kSCEntNetInterface
,
5824 add_reachability_patterns(CFMutableArrayRef patterns
)
5826 for (size_t i
= 0; i
< countof(reachabilitySetupKeys
); i
++) {
5827 CFStringRef pattern
;
5828 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
5829 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5836 add_vpn_pattern(CFMutableArrayRef patterns
)
5838 CFStringRef pattern
;
5840 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
5841 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5846 add_interface_link_pattern(CFMutableArrayRef patterns
)
5848 CFStringRef pattern
;
5850 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetLink
);
5851 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5855 static CFDictionaryRef
5856 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
5859 CFMutableArrayRef keys
;
5860 CFDictionaryRef info
;
5861 CFMutableArrayRef patterns
;
5863 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5864 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5866 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
5867 CFArrayAppendValue(keys
, S_multicast_resolvers
);
5868 CFArrayAppendValue(keys
, S_private_resolvers
);
5870 count
= CFArrayGetCount(service_list
);
5871 for (CFIndex s
= 0; s
< count
; s
++) {
5872 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
5874 add_service_keys(serviceID
, keys
, patterns
);
5875 add_transient_status_keys(serviceID
, keys
, patterns
);
5878 add_reachability_patterns(patterns
);
5880 add_vpn_pattern(patterns
);
5882 add_interface_link_pattern(patterns
);
5884 info
= SCDynamicStoreCopyMultiple(session
, keys
, patterns
);
5885 my_CFRelease(&keys
);
5886 my_CFRelease(&patterns
);
5890 #if !TARGET_OS_SIMULATOR
5893 set_ipv6_default_interface(IFIndex ifindex
)
5895 struct in6_ndifreq ndifreq
;
5897 boolean_t success
= FALSE
;
5899 memset((char *)&ndifreq
, 0, sizeof(ndifreq
));
5900 strlcpy(ndifreq
.ifname
, kLoopbackInterface
, sizeof(ndifreq
.ifname
));
5902 ndifreq
.ifindex
= ifindex
;
5905 ndifreq
.ifindex
= lo0_ifindex();
5907 sock
= inet6_dgram_socket();
5911 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
5913 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5924 #endif /* !TARGET_OS_SIMULATOR */
5926 #if !TARGET_OS_IPHONE
5927 static __inline__
void
5930 (void)unlink(VAR_RUN_RESOLV_CONF
);
5934 set_dns(CFArrayRef val_search_domains
,
5935 CFStringRef val_domain_name
,
5936 CFArrayRef val_servers
,
5937 CFArrayRef val_sortlist
)
5939 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
5941 /* publish new resolv.conf */
5946 SCPrint(TRUE
, f
, CFSTR("#\n"));
5947 SCPrint(TRUE
, f
, CFSTR("# macOS Notice\n"));
5948 SCPrint(TRUE
, f
, CFSTR("#\n"));
5949 SCPrint(TRUE
, f
, CFSTR("# This file is not consulted for DNS hostname resolution, address\n"));
5950 SCPrint(TRUE
, f
, CFSTR("# resolution, or the DNS query routing mechanism used by most\n"));
5951 SCPrint(TRUE
, f
, CFSTR("# processes on this system.\n"));
5952 SCPrint(TRUE
, f
, CFSTR("#\n"));
5953 SCPrint(TRUE
, f
, CFSTR("# To view the DNS configuration used by this system, use:\n"));
5954 SCPrint(TRUE
, f
, CFSTR("# scutil --dns\n"));
5955 SCPrint(TRUE
, f
, CFSTR("#\n"));
5956 SCPrint(TRUE
, f
, CFSTR("# SEE ALSO\n"));
5957 SCPrint(TRUE
, f
, CFSTR("# dns-sd(1), scutil(8)\n"));
5958 SCPrint(TRUE
, f
, CFSTR("#\n"));
5959 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
5960 SCPrint(TRUE
, f
, CFSTR("#\n"));
5962 if (isA_CFArray(val_search_domains
)) {
5963 SCPrint(TRUE
, f
, CFSTR("search"));
5964 n
= CFArrayGetCount(val_search_domains
);
5965 for (i
= 0; i
< n
; i
++) {
5968 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
5969 if (isA_CFString(domain
)) {
5970 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
5973 SCPrint(TRUE
, f
, CFSTR("\n"));
5975 else if (isA_CFString(val_domain_name
)) {
5976 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
5979 if (isA_CFArray(val_servers
)) {
5980 n
= CFArrayGetCount(val_servers
);
5981 for (i
= 0; i
< n
; i
++) {
5982 CFStringRef nameserver
;
5984 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
5985 if (isA_CFString(nameserver
)) {
5986 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
5991 if (isA_CFArray(val_sortlist
)) {
5992 SCPrint(TRUE
, f
, CFSTR("sortlist"));
5993 n
= CFArrayGetCount(val_sortlist
);
5994 for (i
= 0; i
< n
; i
++) {
5995 CFStringRef address
;
5997 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
5998 if (isA_CFString(address
)) {
5999 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
6002 SCPrint(TRUE
, f
, CFSTR("\n"));
6006 (void)rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
6010 #endif /* !TARGET_OS_IPHONE */
6013 service_get_ip_is_coupled(CFStringRef serviceID
)
6015 CFDictionaryRef dict
;
6016 boolean_t ip_is_coupled
= FALSE
;
6018 dict
= service_dict_get(serviceID
, kSCEntNetService
);
6020 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
6021 ip_is_coupled
= TRUE
;
6024 return (ip_is_coupled
);
6028 my_CFStringCreateWithInAddr(struct in_addr ip
)
6032 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(IP_FORMAT
), IP_LIST(&ip
));
6037 my_CFStringCreateWithIn6Addr(const struct in6_addr
* ip
)
6039 char ntopbuf
[INET6_ADDRSTRLEN
];
6041 (void)inet_ntop(AF_INET6
, ip
, ntopbuf
, sizeof(ntopbuf
));
6042 return (CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), ntopbuf
));
6046 * Function: update_ipv4
6048 * Update the IPv4 configuration based on the latest information.
6049 * Publish the State:/Network/Global/IPv4 information, and update the
6050 * IPv4 routing table.
6053 update_ipv4(CFStringRef primary
,
6054 IPv4RouteListRef new_routelist
,
6055 keyChangeListRef keys
)
6057 #if !TARGET_OS_SIMULATOR
6059 #endif /* !TARGET_OS_SIMULATOR */
6062 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6063 const char * ifn_p
= NULL
;
6064 char ifname
[IFNAMSIZ
];
6066 CFMutableDictionaryRef dict
= NULL
;
6068 dict
= CFDictionaryCreateMutable(NULL
, 0,
6069 &kCFTypeDictionaryKeyCallBacks
,
6070 &kCFTypeDictionaryValueCallBacks
);
6071 /* the first entry is the default route */
6072 r
= new_routelist
->list
;
6073 if (r
->gateway
.s_addr
!= 0) {
6076 str
= my_CFStringCreateWithInAddr(r
->gateway
);
6077 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, str
);
6080 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6081 if (ifn_p
!= NULL
) {
6082 CFStringRef ifname_cf
;
6084 ifname_cf
= CFStringCreateWithCString(NULL
,
6086 kCFStringEncodingASCII
);
6087 if (ifname_cf
!= NULL
) {
6088 CFDictionarySetValue(dict
,
6089 kSCDynamicStorePropNetPrimaryInterface
,
6091 CFRelease(ifname_cf
);
6094 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6096 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
6100 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
6104 #if !TARGET_OS_SIMULATOR
6105 sockfd
= open_routing_socket();
6107 /* go through routelist and bind any unbound routes */
6108 if (new_routelist
!= NULL
) {
6109 IPv4RouteListFinalize(new_routelist
);
6112 /* provide a routelist with just loopback multicast */
6113 new_routelist
= IPv4RouteListCopyMulticastLoopback();
6115 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6116 if (S_ipv4_routelist
== NULL
) {
6117 my_log(LOG_DEBUG
, "Old Routes = <none>");
6120 my_log(LOG_DEBUG
, "Old Routes = ");
6121 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
6123 if (new_routelist
== NULL
) {
6124 my_log(LOG_DEBUG
, "New Routes = <none>");
6127 my_log(LOG_DEBUG
, "New Routes = ");
6128 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
6131 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
, sockfd
);
6134 if (S_ipv4_routelist
!= NULL
) {
6135 free(S_ipv4_routelist
);
6137 S_ipv4_routelist
= new_routelist
;
6138 #else /* !TARGET_OS_SIMULATOR */
6139 if (new_routelist
!= NULL
) {
6140 free(new_routelist
);
6142 #endif /* !TARGET_OS_SIMULATOR */
6148 * Function: update_ipv6
6150 * Update the IPv6 configuration based on the latest information.
6151 * Publish the State:/Network/Global/IPv6 information, and update the
6152 * IPv6 routing table.
6155 update_ipv6(CFStringRef primary
,
6156 IPv6RouteListRef new_routelist
,
6157 keyChangeListRef keys
)
6159 #if !TARGET_OS_SIMULATOR
6161 #endif /* !TARGET_OS_SIMULATOR */
6164 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6165 const char * ifn_p
= NULL
;
6166 char ifname
[IFNAMSIZ
];
6168 CFMutableDictionaryRef dict
= NULL
;
6170 dict
= CFDictionaryCreateMutable(NULL
, 0,
6171 &kCFTypeDictionaryKeyCallBacks
,
6172 &kCFTypeDictionaryValueCallBacks
);
6173 /* the first entry is the default route */
6174 r
= new_routelist
->list
;
6175 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
6178 router
= my_CFStringCreateWithIn6Addr(&r
->gateway
);
6179 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
, router
);
6182 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6183 if (ifn_p
!= NULL
) {
6184 CFStringRef ifname_cf
;
6186 ifname_cf
= CFStringCreateWithCString(NULL
,
6188 kCFStringEncodingASCII
);
6189 if (ifname_cf
!= NULL
) {
6190 CFDictionarySetValue(dict
,
6191 kSCDynamicStorePropNetPrimaryInterface
,
6193 CFRelease(ifname_cf
);
6196 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6198 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
6200 #if !TARGET_OS_SIMULATOR
6201 set_ipv6_default_interface(r
->ifindex
);
6202 #endif /* !TARGET_OS_SIMULATOR */
6205 #if !TARGET_OS_SIMULATOR
6206 set_ipv6_default_interface(0);
6207 #endif /* !TARGET_OS_SIMULATOR */
6208 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
6212 #if !TARGET_OS_SIMULATOR
6213 sockfd
= open_routing_socket();
6215 /* go through routelist and bind any unbound routes */
6216 IPv6RouteListFinalize(new_routelist
);
6217 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6218 if (S_ipv6_routelist
== NULL
) {
6219 my_log(LOG_DEBUG
, "Old Routes = <none>");
6222 my_log(LOG_DEBUG
, "Old Routes = ");
6223 IPv6RouteListLog(LOG_DEBUG
, S_ipv6_routelist
);
6225 if (new_routelist
== NULL
) {
6226 my_log(LOG_DEBUG
, "New Routes = <none>");
6229 my_log(LOG_DEBUG
, "New Routes = ");
6230 IPv6RouteListLog(LOG_DEBUG
, new_routelist
);
6233 IPv6RouteListApply(S_ipv6_routelist
, new_routelist
, sockfd
);
6236 if (S_ipv6_routelist
!= NULL
) {
6237 free(S_ipv6_routelist
);
6239 S_ipv6_routelist
= new_routelist
;
6240 #else /* !TARGET_OS_SIMULATOR */
6241 if (new_routelist
!= NULL
) {
6242 free(new_routelist
);
6244 #endif /* !TARGET_OS_SIMULATOR */
6250 update_dns(CFDictionaryRef services_info
,
6251 CFStringRef primary
,
6252 keyChangeListRef keys
)
6254 #pragma unused(services_info)
6255 Boolean changed
= FALSE
;
6256 CFDictionaryRef dict
= NULL
;
6258 if (primary
!= NULL
) {
6259 CFDictionaryRef service_dict
;
6261 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6262 if (service_dict
!= NULL
) {
6263 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6267 if (!_SC_CFEqual(S_dns_dict
, dict
)) {
6269 #if !TARGET_OS_IPHONE
6271 #endif /* !TARGET_OS_IPHONE */
6272 keyChangeListRemoveValue(keys
, S_state_global_dns
);
6274 CFMutableDictionaryRef new_dict
;
6276 #if !TARGET_OS_IPHONE
6277 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
6278 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
6279 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
6280 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
6281 #endif /* !TARGET_OS_IPHONE */
6282 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
6283 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
6284 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
6285 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
6286 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
6287 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
6288 CFRelease(new_dict
);
6293 if (dict
!= NULL
) CFRetain(dict
);
6294 if (S_dns_dict
!= NULL
) CFRelease(S_dns_dict
);
6301 update_dnsinfo(CFDictionaryRef services_info
,
6302 CFStringRef primary
,
6303 keyChangeListRef keys
,
6304 CFArrayRef service_order
)
6307 CFDictionaryRef dict
= NULL
;
6308 CFArrayRef multicastResolvers
;
6309 CFArrayRef privateResolvers
;
6311 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
6312 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
6314 if (primary
!= NULL
) {
6315 CFDictionaryRef service_dict
;
6317 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6318 if (service_dict
!= NULL
) {
6319 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6323 changed
= dns_configuration_set(dict
,
6324 S_service_state_dict
,
6329 keyChangeListNotifyKey(keys
, S_state_global_dns
);
6335 update_nwi(nwi_state_t state
)
6337 unsigned char signature
[CC_SHA256_DIGEST_LENGTH
];
6338 static unsigned char signature_last
[CC_SHA256_DIGEST_LENGTH
];
6340 _nwi_state_compute_sha256_hash(state
, signature
);
6341 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
6342 my_log(LOG_DEBUG
, "Not updating network information");
6346 // save [new] signature
6347 memcpy(signature_last
, signature
, sizeof(signature
));
6349 // save [new] configuration
6350 my_log(LOG_INFO
, "Updating network information");
6351 _nwi_state_log(state
, TRUE
, NULL
);
6353 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
6354 if (!_nwi_state_store(state
)) {
6355 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
6357 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
6363 update_proxies(CFDictionaryRef services_info
,
6364 CFStringRef primary
,
6365 keyChangeListRef keys
,
6366 CFArrayRef service_order
)
6368 Boolean changed
= FALSE
;
6369 CFDictionaryRef dict
= NULL
;
6370 CFDictionaryRef new_dict
;
6372 if (primary
!= NULL
) {
6373 CFDictionaryRef service_dict
;
6375 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6376 if (service_dict
!= NULL
) {
6377 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
6381 new_dict
= proxy_configuration_update(dict
,
6382 S_service_state_dict
,
6385 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
6386 if (new_dict
== NULL
) {
6387 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
6389 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
6394 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
6395 S_proxies_dict
= new_dict
;
6400 #if !TARGET_OS_IPHONE
6402 update_smb(CFDictionaryRef services_info
,
6403 CFStringRef primary
,
6404 keyChangeListRef keys
)
6406 #pragma unused(services_info)
6407 Boolean changed
= FALSE
;
6408 CFDictionaryRef dict
= NULL
;
6410 if (primary
!= NULL
) {
6411 CFDictionaryRef service_dict
;
6413 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6414 if (service_dict
!= NULL
) {
6415 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
6419 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
6421 keyChangeListRemoveValue(keys
, S_state_global_smb
);
6423 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
6428 if (dict
!= NULL
) CFRetain(dict
);
6429 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
6434 #endif /* !TARGET_OS_IPHONE */
6437 get_service_index(CFDictionaryRef rank_entity
,
6438 CFArrayRef order
, CFIndex n_order
, CFStringRef serviceID
)
6441 Rank rank
= kRankIndexMask
;
6442 CFNumberRef service_index
;
6445 = service_rank_entity_get_index(rank_entity
,
6449 if (service_index
!= NULL
) {
6450 /* ServiceIndex specified in service entity */
6453 "%@ specifies ServiceIndex %@, effective index is %d",
6454 serviceID
, service_index
, rank
);
6456 else if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
6457 for (i
= 0; i
< n_order
; i
++) {
6458 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
6463 if (CFEqual(serviceID
, s
)) {
6473 ** Service election:
6476 * Function: rank_dict_get_service_rank
6478 * Retrieve the service rank in the given dictionary.
6481 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
6484 Rank rank_val
= kRankAssertionDefault
;
6486 rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
6487 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
6489 if (!CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
)) {
6490 /* if we don't like the rank value */
6491 rank_val
= kRankAssertionDefault
;
6499 * Function: rank_dict_set_service_rank
6501 * Save the results of ranking the service so we can look it up later without
6502 * repeating all of the ranking code.
6505 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
6506 CFStringRef serviceID
, Rank rank_val
)
6510 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
6512 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
6518 static const CFStringRef
*transientInterfaceEntityNames
[] = {
6524 CollectTransientServices(const void * key
,
6528 #pragma unused(value)
6529 CFStringRef service
= key
;
6530 CFMutableArrayRef vif_setup_keys
= context
;
6532 /* This service is either a vpn type service or a comm center service */
6533 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
6537 for (size_t i
= 0; i
< countof(transientInterfaceEntityNames
); i
++) {
6538 if (CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
6539 my_CFArrayAppendUniqueValue(vif_setup_keys
, service
);
6548 static SCNetworkReachabilityFlags
6549 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
6550 CFStringRef service_id
,
6552 CFStringRef vpn_setup_key
)
6555 CFDictionaryRef dict
;
6556 SCNetworkReachabilityFlags flags
= 0;
6559 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6560 kSCDynamicStoreDomainSetup
,
6562 kSCEntNetInterface
);
6563 dict
= CFDictionaryGetValue(services_info
, key
);
6566 if (isA_CFDictionary(dict
)
6567 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
6569 flags
= (kSCNetworkReachabilityFlagsReachable
6570 | kSCNetworkReachabilityFlagsTransientConnection
6571 | kSCNetworkReachabilityFlagsConnectionRequired
);
6573 if (CFEqual(entity
, kSCEntNetPPP
)) {
6575 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
6577 if (!isA_CFDictionary(p_dict
)) {
6581 // get PPP dial-on-traffic status
6582 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
6583 if (isA_CFNumber(num
)) {
6586 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
6588 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6598 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
6600 Boolean ret
= def_value
;
6605 val
= CFDictionaryGetValue(dict
, key
);
6606 if (isA_CFBoolean(val
) != NULL
) {
6607 ret
= CFBooleanGetValue(val
);
6615 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
6616 SCNetworkReachabilityFlags
*reach_flags_v4
,
6617 SCNetworkReachabilityFlags
*reach_flags_v6
)
6621 CFMutableArrayRef vif_setup_keys
;
6623 vif_setup_keys
= CFArrayCreateMutable(NULL
,
6625 &kCFTypeArrayCallBacks
);
6626 CFDictionaryApplyFunction(services_info
, CollectTransientServices
,
6628 count
= CFArrayGetCount(vif_setup_keys
);
6629 for (i
= 0; i
< count
; i
++) {
6630 CFArrayRef components
= NULL
;
6632 CFStringRef service_id
;
6633 CFStringRef vif_setup_key
;
6635 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
6638 * setup key in the following format:
6639 * Setup:/Network/Service/<Service ID>/<Entity>
6641 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
6643 if (CFArrayGetCount(components
) != 5) {
6644 // invalid Setup key encountered
6648 /* service id is the 3rd element */
6649 service_id
= CFArrayGetValueAtIndex(components
, 3);
6651 /* entity id is the 4th element */
6652 entity
= CFArrayGetValueAtIndex(components
, 4);
6655 if (CFEqual(entity
, kSCEntNetPPP
)) {
6656 SCNetworkReachabilityFlags flags
;
6659 flags
= GetReachabilityFlagsFromVPN(services_info
,
6664 /* Check for the v4 reachability flags */
6665 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6666 kSCDynamicStoreDomainSetup
,
6670 if (CFDictionaryContainsKey(services_info
, key
)) {
6671 *reach_flags_v4
|= flags
;
6672 my_log(LOG_DEBUG
, "Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
6677 /* Check for the v6 reachability flags */
6678 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6679 kSCDynamicStoreDomainSetup
,
6683 if (CFDictionaryContainsKey(services_info
, key
)) {
6684 *reach_flags_v6
|= flags
;
6685 my_log(LOG_DEBUG
, "Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
6690 if (components
!= NULL
) {
6691 CFRelease(components
);
6697 if (components
!= NULL
) {
6698 CFRelease(components
);
6702 CFRelease(vif_setup_keys
);
6706 static SCNetworkReachabilityFlags
6707 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
6709 SCNetworkReachabilityFlags flags
= 0;
6711 if (CFEqual(entity
, kSCEntNetPPP
)) {
6714 /* if we're really UP and RUNNING */
6717 /* if we're effectively UP and RUNNING */
6720 /* if we're not connected at all */
6721 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6723 case PPP_STATERESERVED
:
6724 // if we're not connected at all
6725 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6728 /* if we're in the process of [dis]connecting */
6729 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6733 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
6735 case IPSEC_RUNNING
:
6736 /* if we're really UP and RUNNING */
6739 /* if we're not connected at all */
6740 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6743 /* if we're in the process of [dis]connecting */
6744 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6748 else if (CFEqual(entity
, kSCEntNetVPN
)) {
6751 /* if we're really UP and RUNNING */
6756 case VPN_UNLOADING
:
6757 /* if we're not connected at all */
6758 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6761 /* if we're in the process of [dis]connecting */
6762 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6770 VPNAttributesGet(CFStringRef service_id
,
6771 CFDictionaryRef services_info
,
6772 SCNetworkReachabilityFlags
*flags
,
6773 CFStringRef
*server_address
,
6776 CFDictionaryRef entity_dict
;
6778 CFDictionaryRef p_state
= NULL
;
6780 CFStringRef transient_entity
= NULL
;
6782 if (af
== AF_INET
) {
6783 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv4
);
6785 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv6
);
6787 entity_dict
= ipdict_get_service(entity_dict
);
6788 if (entity_dict
== NULL
) {
6792 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
6793 CFStringRef entity
= *transientServiceInfo
[i
].entityName
;
6795 p_state
= service_dict_get(service_id
, entity
);
6797 /* ensure that this is a VPN Type service */
6798 if (isA_CFDictionary(p_state
)) {
6799 transient_entity
= entity
;
6804 /* Did we find a vpn type service? If not, we are done.*/
6805 if (transient_entity
== NULL
) {
6809 *flags
|= (kSCNetworkReachabilityFlagsReachable
6810 | kSCNetworkReachabilityFlagsTransientConnection
);
6812 /* Get the Server Address */
6813 if (server_address
!= NULL
) {
6814 *server_address
= CFDictionaryGetValue(entity_dict
,
6815 CFSTR("ServerAddress"));
6816 *server_address
= isA_CFString(*server_address
);
6817 if (*server_address
!= NULL
) {
6818 CFRetain(*server_address
);
6823 if (!CFDictionaryGetValueIfPresent(p_state
,
6824 kSCPropNetVPNStatus
, // IPSecStatus, PPPStatus, VPNStatus
6825 (const void **)&num
) ||
6826 !isA_CFNumber(num
) ||
6827 !CFNumberGetValue(num
, kCFNumberIntType
, &status
)) {
6831 *flags
|= GetReachFlagsFromStatus(transient_entity
, status
);
6832 if (CFEqual(transient_entity
, kSCEntNetPPP
)) {
6834 CFDictionaryRef p_setup
;
6837 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6838 kSCDynamicStoreDomainSetup
,
6841 p_setup
= CFDictionaryGetValue(services_info
, key
);
6844 /* get dial-on-traffic status */
6845 if (isA_CFDictionary(p_setup
) &&
6846 CFDictionaryGetValueIfPresent(p_setup
,
6847 kSCPropNetPPPDialOnDemand
,
6848 (const void **)&num
) &&
6849 isA_CFNumber(num
) &&
6850 CFNumberGetValue(num
, kCFNumberIntType
, &ppp_demand
) &&
6851 (ppp_demand
!= 0)) {
6852 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6853 if (status
== PPP_IDLE
) {
6854 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
6862 typedef struct ElectionInfo
{
6868 ElectionResultsRef results
;
6869 CFMutableDictionaryRef rank_dict
;
6870 } ElectionInfo
, * ElectionInfoRef
;
6872 typedef CFDictionaryApplierFunction ElectionFuncRef
;
6875 CandidateRelease(CandidateRef candidate
)
6877 my_CFRelease(&candidate
->serviceID
);
6878 my_CFRelease(&candidate
->if_name
);
6879 my_CFRelease(&candidate
->signature
);
6884 CandidateCopy(CandidateRef dest
, CandidateRef src
)
6887 if (dest
->serviceID
) {
6888 CFRetain(dest
->serviceID
);
6890 if (dest
->if_name
) {
6891 CFRetain(dest
->if_name
);
6893 if(dest
->signature
) {
6894 CFRetain(dest
->signature
);
6899 static ElectionResultsRef
6900 ElectionResultsAlloc(int af
, int size
)
6902 ElectionResultsRef results
;
6904 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
6907 results
->size
= size
;
6912 ElectionResultsRelease(ElectionResultsRef results
)
6917 for (i
= 0, scan
= results
->candidates
;
6920 CandidateRelease(scan
);
6927 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
6932 if (results
== NULL
) {
6933 my_log(level
, "%s: no candidates", prefix
);
6936 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
6937 for (i
= 0, scan
= results
->candidates
;
6940 char ntopbuf
[INET6_ADDRSTRLEN
];
6942 (void)inet_ntop(results
->af
, &scan
->addr
, ntopbuf
, sizeof(ntopbuf
));
6943 my_log(level
, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6944 i
, scan
->if_name
, scan
->serviceID
, ntopbuf
, scan
->rank
,
6945 scan
->ineligible
? " [ineligible]" : "");
6951 * Function: ElectionResultsAddCandidate
6953 * Add the candidate into the election results. Find the insertion point
6954 * by comparing the rank of the candidate with existing entries.
6957 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
6962 if (results
->count
== results
->size
) {
6963 /* this should not happen */
6964 my_log(LOG_NOTICE
, "can't fit another candidate");
6968 /* find the insertion point */
6969 where
= kCFNotFound
;
6970 for (i
= 0; i
< results
->count
; i
++) {
6971 CandidateRef this_candidate
= results
->candidates
+ i
;
6973 if (candidate
->rank
< this_candidate
->rank
) {
6978 /* add it to the end */
6979 if (where
== kCFNotFound
) {
6980 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
6984 /* slide existing entries over */
6985 for (i
= results
->count
; i
> where
; i
--) {
6986 results
->candidates
[i
] = results
->candidates
[i
- 1];
6988 /* insert element */
6989 CandidateCopy(results
->candidates
+ where
, candidate
);
6995 elect_ip(const void * key
, const void * value
, void * context
);
6998 * Function: ElectionResultsCopy
7000 * Visit all of the services and invoke the protocol-specific election
7001 * function. Return the results of the election.
7003 static ElectionResultsRef
7004 ElectionResultsCopy(int af
, CFArrayRef order
)
7009 count
= (int)CFDictionaryGetCount(S_service_state_dict
);
7014 if (af
== AF_INET
) {
7015 info
.entity
= kSCEntNetIPv4
;
7016 info
.rank_dict
= S_ipv4_service_rank_dict
;
7019 info
.entity
= kSCEntNetIPv6
;
7020 info
.rank_dict
= S_ipv6_service_rank_dict
;
7022 info
.results
= ElectionResultsAlloc(af
, count
);
7023 info
.n_services
= count
;
7025 if (order
!= NULL
) {
7026 info
.n_order
= CFArrayGetCount(order
);
7031 CFDictionaryApplyFunction(S_service_state_dict
, elect_ip
, (void *)&info
);
7032 if (info
.results
->count
== 0) {
7033 ElectionResultsRelease(info
.results
);
7034 info
.results
= NULL
;
7036 return (info
.results
);
7040 * Function: ElectionResultsCandidateNeedsDemotion
7042 * Check whether the given candidate requires demotion. A candidate
7043 * might need to be demoted if its IPv4 and IPv6 services must be coupled
7044 * but a higher ranked service has IPv4 or IPv6.
7046 * The converse is also true: if the given candidate has lower rank than
7047 * the other candidate and the other candidate is coupled, this candidate
7048 * needs to be demoted.
7051 ElectionResultsCandidateNeedsDemotion(CandidateRef other_candidate
,
7052 CandidateRef candidate
)
7054 Boolean ret
= FALSE
;
7056 if (other_candidate
== NULL
) {
7057 /* no other candidate */
7060 if (other_candidate
->ineligible
) {
7061 /* other candidate can't become primary */
7064 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
7065 /* the other candidate can't become primary */
7068 if (!candidate
->ip_is_coupled
&& !other_candidate
->ip_is_coupled
) {
7069 /* neither candidate is coupled */
7072 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
7073 /* they are over the same interface, no need to demote */
7076 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
7077 /* avoid creating a feedback loop */
7080 if (candidate
->rank
< other_candidate
->rank
) {
7081 /* we're higher ranked than the other candidate, ignore */
7084 if (candidate
->ip_is_coupled
) {
7085 if (other_candidate
->ip_is_coupled
7086 && candidate
->rank
== other_candidate
->rank
) {
7087 /* same rank as another service that is coupled, ignore */
7091 else if (other_candidate
->ip_is_coupled
) { /* must be true */
7092 if (candidate
->rank
== other_candidate
->rank
) {
7093 /* other candidate will be demoted, so we don't need to */
7096 /* we're lower rank and need to be demoted */
7098 else { /* can't happen, we already tested for this above */
7099 /* neither candidate is coupled */
7111 get_signature_sha256(CFStringRef signature
,
7112 unsigned char * sha256
)
7115 CFDataRef signature_data
;
7117 signature_data
= CFStringCreateExternalRepresentation(NULL
,
7119 kCFStringEncodingUTF8
,
7122 CC_SHA256_Init(&ctx
);
7123 CC_SHA256_Update(&ctx
,
7124 CFDataGetBytePtr(signature_data
),
7125 (CC_LONG
)CFDataGetLength(signature_data
));
7126 CC_SHA256_Final(sha256
, &ctx
);
7128 CFRelease(signature_data
);
7135 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
7136 CandidateRef candidate
, Boolean not_in_list
,
7137 Boolean not_in_iflist
)
7140 char ifname
[IFNAMSIZ
];
7141 nwi_ifstate_t ifstate
;
7143 if (nwi_state
== NULL
) {
7148 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
7149 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
7151 if (not_in_iflist
) {
7152 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST
;
7154 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
7155 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
7157 if ((af
== AF_INET
) && service_has_clat46_address(candidate
->serviceID
)) {
7158 flags
|= NWI_IFSTATE_FLAGS_HAS_CLAT46
;
7160 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
7161 kCFStringEncodingASCII
);
7162 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
7163 char ntopbuf
[INET6_ADDRSTRLEN
];
7165 (void)inet_ntop(af
, &candidate
->addr
, ntopbuf
, sizeof(ntopbuf
));
7167 "Adding IPv%c [%s] %s "
7168 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
7169 ipvx_char(af
), ifname
, ntopbuf
,
7170 flags
, candidate
->rank
, candidate
->reachability_flags
);
7172 ifstate
= nwi_state_add_ifstate(nwi_state
, ifname
, af
, flags
,
7174 (void *)&candidate
->addr
,
7175 (void *)&candidate
->vpn_server_addr
,
7176 candidate
->reachability_flags
);
7177 if (ifstate
!= NULL
&& candidate
->signature
) {
7178 uint8_t hash
[CC_SHA256_DIGEST_LENGTH
];
7180 get_signature_sha256(candidate
->signature
, hash
);
7181 nwi_ifstate_set_signature(ifstate
, hash
);
7188 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
7190 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
7191 CFStringRef vpn_server_address
= NULL
;
7193 assert(candidate
!= NULL
);
7194 assert(services_info
!= NULL
);
7196 VPNAttributesGet(candidate
->serviceID
,
7199 &vpn_server_address
,
7202 candidate
->reachability_flags
= flags
;
7204 if (vpn_server_address
== NULL
) {
7205 memset(&candidate
->vpn_server_addr
, 0, sizeof(candidate
->vpn_server_addr
));
7209 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
),
7210 kCFStringEncodingASCII
);
7211 _SC_string_to_sockaddr(buf
,
7213 (void *)&candidate
->vpn_server_addr
,
7214 sizeof(candidate
->vpn_server_addr
));
7216 CFRelease(vpn_server_address
);
7221 * Function: ElectionResultsGetPrimary
7223 * Use the results of the current protocol and the other protocol to
7224 * determine which service should become primary.
7226 * At the same time, generate the IPv4/IPv6 routing table and
7227 * the nwi_state for the protocol.
7230 ElectionResultsGetPrimary(ElectionResultsRef results
,
7231 CandidateRef other_candidate
,
7232 nwi_state_t nwi_state
, int af
,
7233 RouteListRef
* ret_routes
,
7234 CFDictionaryRef services_info
)
7236 CandidateRef primary
= NULL
;
7237 Boolean primary_is_null
= FALSE
;
7238 RouteListRef routes
= NULL
;
7240 assert(services_info
!= NULL
);
7242 if (results
!= NULL
) {
7243 CandidateRef deferred
[results
->count
];
7245 CFStringRef entity_name
;
7248 RouteListInfoRef info
;
7253 entity_name
= kSCEntNetIPv4
;
7254 info
= &IPv4RouteListInfo
;
7255 initial_size
= results
->count
* IPV4_ROUTES_N_STATIC
;
7259 entity_name
= kSCEntNetIPv6
;
7260 info
= &IPv6RouteListInfo
;
7261 initial_size
= results
->count
* IPV6_ROUTES_N_STATIC
;
7265 for (i
= 0, scan
= results
->candidates
;
7268 Boolean is_primary
= FALSE
;
7269 CFDictionaryRef service_dict
;
7270 RouteListRef service_routes
;
7271 Boolean skip
= FALSE
;
7273 if (!scan
->ineligible
7275 && RANK_ASSERTION_MASK(scan
->rank
) != kRankAssertionNever
) {
7276 if (ElectionResultsCandidateNeedsDemotion(other_candidate
,
7278 /* demote the service */
7280 "IPv%c over %@ (rank 0x%x) demoted: "
7281 "primary IPv%c %@ (rank 0x%x)",
7282 ipvx_char(af
), scan
->if_name
, scan
->rank
,
7283 ipvx_other_char(af
), other_candidate
->if_name
,
7284 other_candidate
->rank
);
7285 deferred
[deferred_count
++] = scan
;
7293 /* contribute to the routing table */
7294 service_dict
= service_dict_get(scan
->serviceID
, entity_name
);
7295 service_routes
= ipdict_get_routelist(service_dict
);
7296 if (service_routes
!= NULL
) {
7297 Rank rank
= scan
->rank
;
7300 /* routes are RankNever to prevent becoming primary */
7301 rank
= RankMake(rank
, kRankAssertionNever
);
7303 routes
= RouteListAddRouteList(info
, routes
, initial_size
,
7304 service_routes
, rank
);
7305 if ((service_routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
7313 /* if we're skipping the primary, it's NULL */
7315 primary_is_null
= TRUE
;
7318 else if (!scan
->ineligible
) {
7319 Boolean not_in_iflist
;
7321 add_reachability_flags_to_candidate(scan
, services_info
, af
);
7323 = (service_routes
->flags
& kRouteListFlagsScopedOnly
) != 0;
7324 add_candidate_to_nwi_state(nwi_state
, af
, scan
,
7329 for (i
= 0; i
< deferred_count
; i
++) {
7330 CandidateRef candidate
= deferred
[i
];
7332 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
7333 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, TRUE
, FALSE
);
7336 if (ret_routes
!= NULL
) {
7337 *ret_routes
= routes
;
7339 else if (routes
!= NULL
) {
7342 if (primary_is_null
) {
7351 service_dict_get_signature(CFDictionaryRef service_dict
)
7355 ifname
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7356 if (isA_CFString(ifname
) == NULL
7357 || !confirm_interface_name(service_dict
, ifname
)) {
7360 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
7364 * Function: elect_ip
7366 * Evaluate the service and determine what rank the service should have.
7367 * If it's a suitable candidate, add it to the election results.
7370 elect_ip(const void * key
, const void * value
, void * context
)
7372 CFDictionaryRef all_entities_dict
= (CFDictionaryRef
)value
;
7373 Candidate candidate
;
7375 ElectionInfoRef elect_info
;
7376 CFStringRef if_name
;
7377 CFDictionaryRef ipdict
;
7379 CFDictionaryRef rank_entity
;
7380 RouteListUnion routelist
;
7381 CFDictionaryRef service_dict
;
7383 elect_info
= (ElectionInfoRef
)context
;
7384 ipdict
= CFDictionaryGetValue(all_entities_dict
, elect_info
->entity
);
7385 if (ipdict
!= NULL
) {
7386 routelist
.ptr
= ipdict_get_routelist(ipdict
);
7387 service_dict
= ipdict_get_service(ipdict
);
7390 routelist
.ptr
= NULL
;
7392 if (routelist
.ptr
== NULL
|| service_dict
== NULL
) {
7393 /* no connectivity */
7396 if_name
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7397 if (if_name
== NULL
) {
7398 /* need an interface name */
7401 if (CFEqual(if_name
, CFSTR(kLoopbackInterface
))) {
7402 /* don't process loopback */
7405 memset(&candidate
, 0, sizeof(candidate
));
7406 candidate
.serviceID
= (CFStringRef
)key
;
7407 if ((routelist
.common
->flags
& kRouteListFlagsHasDefault
) == 0) {
7408 /* no default route means it's ineligible to become primary */
7409 candidate
.ineligible
= TRUE
;
7411 rank_entity
= CFDictionaryGetValue(all_entities_dict
, kSCEntNetService
);
7412 candidate
.rank
= get_service_index(rank_entity
,
7413 elect_info
->order
, elect_info
->n_order
,
7414 candidate
.serviceID
);
7415 if (elect_info
->af
== AF_INET
) {
7416 default_rank
= routelist
.v4
->list
->rank
;
7417 candidate
.addr
.v4
= routelist
.v4
->list
->ifa
;
7420 default_rank
= routelist
.v6
->list
->rank
;
7421 candidate
.addr
.v6
= routelist
.v6
->list
->ifa
;
7423 primary_rank
= RANK_ASSERTION_MASK(default_rank
);
7424 if (S_ppp_override_primary
) {
7427 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
7428 kCFStringEncodingASCII
)
7429 && (strncmp(PPP_PREFIX
, ifn
, sizeof(PPP_PREFIX
) - 1) == 0)) {
7430 /* PPP override: make ppp* look the best */
7431 primary_rank
= kRankAssertionFirst
;
7434 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
7435 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
7436 candidate
.if_name
= if_name
;
7437 rank_dict_set_service_rank(elect_info
->rank_dict
,
7438 candidate
.serviceID
, candidate
.rank
);
7439 candidate
.signature
= service_dict_get_signature(service_dict
);
7440 ElectionResultsAddCandidate(elect_info
->results
, &candidate
);
7446 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
7448 uint32_t changed
= 0;
7451 /* update service options first (e.g. rank) */
7452 if (get_rank_changes(serviceID
,
7453 get_service_state_entity(services_info
, serviceID
,
7455 get_service_setup_entity(services_info
, serviceID
,
7458 changed
|= (1 << kEntityTypeServiceOptions
);
7461 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7462 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
7463 GetEntityChangesFuncRef func
;
7466 func
= entityChangeFunc
[i
];
7467 name
= *entityTypeNames
[i
];
7468 if ((*func
)(serviceID
,
7469 get_service_state_entity(services_info
, serviceID
, name
),
7470 get_service_setup_entity(services_info
, serviceID
, name
),
7472 changed
|= (1 << i
);
7476 /* update transient service status */
7477 if (get_transient_status_changes(serviceID
, services_info
)) {
7478 changed
|= (1 << kEntityTypeTransientStatus
);
7485 serviceID_get_ifname(CFStringRef serviceID
)
7487 CFDictionaryRef entity_dict
;
7488 CFStringRef ifname
= NULL
;
7490 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
7491 if (entity_dict
== NULL
) {
7492 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
7494 if (entity_dict
!= NULL
) {
7495 ifname
= ipdict_get_ifname(entity_dict
);
7500 __private_extern__ boolean_t
7501 check_if_service_expensive(CFStringRef serviceID
)
7504 ifname
= serviceID_get_ifname(serviceID
);
7506 return interface_is_expensive(ifname
);
7510 service_order_get(CFDictionaryRef services_info
)
7512 CFArrayRef order
= NULL
;
7513 CFDictionaryRef ipv4_dict
;
7515 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
7516 S_setup_global_ipv4
);
7517 if (ipv4_dict
!= NULL
) {
7518 CFNumberRef ppp_override
;
7521 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
7522 order
= isA_CFArray(order
);
7524 /* get ppp override primary */
7525 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
7526 kSCPropNetPPPOverridePrimary
);
7527 ppp_override
= isA_CFNumber(ppp_override
);
7528 if (ppp_override
!= NULL
) {
7529 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
7531 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
7534 S_ppp_override_primary
= FALSE
;
7540 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
7541 const char * entity
)
7543 boolean_t changed
= FALSE
;
7544 CFStringRef primary
= *primary_p
;
7546 if (new_primary
!= NULL
) {
7547 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
7548 my_log(LOG_INFO
, "%@ is still primary %s", new_primary
, entity
);
7551 my_CFRelease(primary_p
);
7552 *primary_p
= CFRetain(new_primary
);
7553 my_log(LOG_INFO
, "%@ is the new primary %s", new_primary
, entity
);
7557 else if (primary
!= NULL
) {
7558 my_log(LOG_INFO
, "%@ is no longer primary %s", primary
, entity
);
7559 my_CFRelease(primary_p
);
7566 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
7569 if (service_dict_get(serviceID
, entity
) == NULL
) {
7570 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
7572 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
7576 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
7582 #define N_KEYS_VALUES_STATIC 10
7583 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
7586 count
= CFDictionaryGetCount(S_service_state_dict
);
7587 if (count
<= N_KEYS_VALUES_STATIC
) {
7588 keys
= keys_values_buf
;
7590 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
7592 values
= keys
+ count
;
7593 CFDictionaryGetKeysAndValues(S_service_state_dict
,
7594 (const void * *)keys
,
7595 (const void * *)values
);
7597 for (i
= 0; i
< count
; i
++) {
7598 CFDictionaryRef ipdict
= NULL
;
7599 CFStringRef interface
= NULL
;
7600 CFStringRef serviceID
;
7601 CFDictionaryRef service_dict
;
7603 serviceID
= (CFStringRef
)keys
[i
];
7604 service_dict
= (CFDictionaryRef
)values
[i
];
7606 /* check whether service has IPv4 or IPv6 */
7607 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
7608 if (ipdict
== NULL
) {
7609 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
7610 if (ipdict
== NULL
) {
7614 interface
= ipdict_get_ifname(ipdict
);
7615 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
7617 "Found IP service %@ on interface %@",
7619 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
7622 if (keys
!= keys_values_buf
) {
7628 static __inline__
const char *
7629 get_changed_str(CFStringRef serviceID
, CFStringRef entity
,
7630 CFDictionaryRef old_dict
)
7632 CFDictionaryRef new_dict
= NULL
;
7634 if (serviceID
!= NULL
) {
7635 new_dict
= service_dict_get(serviceID
, entity
);
7638 if (old_dict
== NULL
) {
7639 if (new_dict
!= NULL
) {
7643 if (new_dict
== NULL
) {
7645 } else if (!CFEqual(old_dict
, new_dict
)) {
7652 #if !TARGET_OS_SIMULATOR
7655 #define MANAGE_IF_ORDER
7656 #define MANAGE_IF_IOCTL
7657 #endif /* SIOCSIFORDER */
7659 #ifdef SIOCSIFNETSIGNATURE
7660 #define MANAGE_IF_SIGNATURE
7661 #define MANAGE_IF_IOCTL
7662 #endif /* SIOCSIFNETSIGNATURE */
7664 #ifdef MANAGE_IF_IOCTL
7666 inet_dgram_socket(void)
7670 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
7672 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
7677 #endif /* MANAGE_IF_IOCTL */
7679 #ifdef MANAGE_IF_ORDER
7681 interface_order_changed(nwi_state_t old_state
, nwi_state_t new_state
)
7683 if (old_state
== NULL
&& new_state
== NULL
) {
7684 // Both are NULL, nothing changed
7688 if (old_state
== NULL
|| new_state
== NULL
) {
7689 // One is NULL, something changed
7693 if (old_state
->if_list_count
!= new_state
->if_list_count
) {
7694 // Count is different, something changed
7698 if (new_state
->if_list_count
== 0) {
7699 // Count is same and 0, nothing changed
7704 nwi_ifindex_t
*old_scan
;
7705 nwi_ifindex_t
*new_scan
;
7706 for (i
= 0, old_scan
= nwi_state_if_list(old_state
), new_scan
= nwi_state_if_list(new_state
);
7707 i
< new_state
->if_list_count
; i
++, old_scan
++, new_scan
++) {
7708 if (strcmp(old_state
->ifstate_list
[*old_scan
].ifname
, new_state
->ifstate_list
[*new_scan
].ifname
) != 0) {
7709 // Some interface in the list is different, something changed
7714 // Count and contents are the same, nothing changed
7719 update_interface_order(nwi_state_t state
, int sockfd
)
7721 Boolean success
= FALSE
;
7723 // Set interface order into the kernel
7724 struct if_order interface_order
;
7725 interface_order
.ifo_count
= (uint32_t)state
->if_list_count
;
7726 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)calloc((size_t)interface_order
.ifo_count
, sizeof(uint32_t));
7727 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7729 nwi_ifindex_t
*scan
;
7730 for (i
= 0, scan
= nwi_state_if_list(state
);
7731 i
< state
->if_list_count
; i
++, scan
++) {
7732 const char *ifname
= state
->ifstate_list
[*scan
].ifname
;
7733 ((uint32_t *)interface_order
.ifo_ordered_indices
)[i
] = my_if_nametoindex(ifname
);
7736 if (ioctl(sockfd
, SIOCSIFORDER
, &interface_order
) != 0) {
7737 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
));
7739 my_log(LOG_INFO
, "Set kernel interface order for %u interfaces", interface_order
.ifo_count
);
7742 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7743 free((void *)interface_order
.ifo_ordered_indices
);
7744 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)NULL
;
7749 #endif /* MANAGE_IF_ORDER */
7751 #ifdef MANAGE_IF_SIGNATURE
7753 siocsifnetsignature(int s
, const char * ifname
, int af
,
7754 const uint8_t * signature
, size_t signature_length
)
7756 struct if_nsreq nsreq
;
7758 memset(&nsreq
, 0, sizeof(nsreq
));
7759 strlcpy(nsreq
.ifnsr_name
, ifname
, sizeof(nsreq
.ifnsr_name
));
7760 nsreq
.ifnsr_family
= af
;
7761 if (signature_length
> 0) {
7762 if (signature_length
> sizeof(nsreq
.ifnsr_data
)) {
7763 signature_length
= sizeof(nsreq
.ifnsr_data
);
7765 nsreq
.ifnsr_len
= signature_length
;
7766 memcpy(nsreq
.ifnsr_data
, signature
, signature_length
);
7768 return (ioctl(s
, SIOCSIFNETSIGNATURE
, &nsreq
));
7772 process_ifstate_difference(nwi_ifstate_t ifstate
, int af
, int sockfd
)
7774 nwi_ifstate_difference_t diff
;
7775 boolean_t set_signature
= FALSE
;
7776 int signature_length
= 0;
7778 diff
= nwi_ifstate_get_difference(ifstate
);
7780 case knwi_ifstate_difference_changed
:
7781 /* set signature for this interface */
7782 set_signature
= TRUE
;
7783 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_SIGNATURE
) != 0) {
7784 signature_length
= sizeof(ifstate
->signature
);
7787 case knwi_ifstate_difference_removed
:
7788 /* remove signature for this interface */
7789 set_signature
= TRUE
;
7794 if (set_signature
) {
7795 if (siocsifnetsignature(sockfd
, ifstate
->ifname
, af
,
7797 signature_length
) < 0) {
7799 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7800 ifstate
->ifname
, ipvx_char(af
),
7805 my_log(LOG_DEBUG
, "IPv%c Network Signature %s %s",
7807 (signature_length
> 0) ? "Set" : "Cleared",
7809 if (signature_length
> 0
7810 && (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7812 char sig_buf
[signature_length
* 3 + 1];
7815 for (i
= 0; i
< signature_length
; i
++) {
7818 snprintf(byte_buf
, sizeof(byte_buf
),
7819 "%02x ", ifstate
->signature
[i
]);
7820 strlcat(sig_buf
, byte_buf
, sizeof(sig_buf
));
7822 my_log(LOG_DEBUG
, "Signature Bytes: %s", sig_buf
);
7830 process_state_differences(nwi_state_t state
, int af
, int sockfd
)
7836 if (af
== AF_INET
) {
7837 count
= state
->ipv4_count
;
7840 count
= state
->ipv6_count
;
7842 for (i
= 0, scan
= nwi_state_ifstate_list(state
, af
);
7843 i
< count
; i
++, scan
++) {
7844 process_ifstate_difference(scan
, af
, sockfd
);
7848 #endif /* MANAGE_IF_SIGNATURE */
7850 #endif /* !TARGET_OS_SIMULATOR */
7853 process_nwi_changes(CFMutableStringRef log_output
,
7854 nwi_state_t changes_state
,
7855 nwi_state_t new_state
,
7856 nwi_state_t old_state
,
7857 boolean_t dns_changed
,
7858 boolean_t dnsinfo_changed
,
7859 CFDictionaryRef old_primary_dns
,
7860 boolean_t proxy_changed
,
7861 CFDictionaryRef old_primary_proxy
,
7862 boolean_t smb_changed
,
7863 CFDictionaryRef old_primary_smb
)
7865 #ifndef MANAGE_IF_ORDER
7866 #pragma unused(new_state)
7867 #pragma unused(old_state)
7868 #endif // !MANAGE_IF_ORDER
7869 #if TARGET_OS_IPHONE
7870 #pragma unused(smb_changed)
7871 #pragma unused(old_primary_smb)
7872 #endif // TARGET_OS_IPHONE
7874 if (changes_state
!= NULL
) {
7875 const sa_family_t af_list
[] = {AF_INET
, AF_INET6
};
7877 #ifdef MANAGE_IF_IOCTL
7878 int sockfd
= inet_dgram_socket();
7879 #endif /* MANAGE_IF_IOCTL */
7881 #ifdef MANAGE_IF_ORDER
7882 if (interface_order_changed(new_state
, old_state
)) {
7883 update_interface_order(new_state
, sockfd
);
7885 #endif /* MANAGE_IF_ORDER */
7887 for (size_t idx
= 0; idx
< countof(af_list
); idx
++) {
7888 int af
= af_list
[idx
];
7889 CFMutableStringRef changes
= NULL
;
7890 CFMutableStringRef primary_str
= NULL
;
7892 #ifdef MANAGE_IF_SIGNATURE
7893 process_state_differences(changes_state
, af
, sockfd
);
7894 #endif /* MANAGE_IF_SIGNATURE */
7895 scan
= nwi_state_get_first_ifstate(changes_state
, af
);
7896 while (scan
!= NULL
) {
7897 const char * changed_str
;
7899 changed_str
= nwi_ifstate_get_diff_str(scan
);
7900 if (changed_str
!= NULL
) {
7902 const char * addr_str
;
7903 char ntopbuf
[INET6_ADDRSTRLEN
];
7905 address
= (void *)nwi_ifstate_get_address(scan
);
7906 addr_str
= inet_ntop(scan
->af
, address
, ntopbuf
,
7908 if (primary_str
== NULL
) {
7909 primary_str
= CFStringCreateMutable(NULL
, 0);
7910 CFStringAppendFormat(primary_str
, NULL
,
7912 nwi_ifstate_get_ifname(scan
),
7913 changed_str
, addr_str
);
7915 if (changes
== NULL
) {
7916 changes
= CFStringCreateMutable(NULL
, 0);
7918 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
7919 nwi_ifstate_get_ifname(scan
));
7920 if (strcmp(changed_str
, "") != 0) {
7921 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
7922 changed_str
, addr_str
);
7926 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
7929 if (primary_str
!= NULL
) {
7930 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
7931 af
== AF_INET
? "v4" : "v6",
7934 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
7935 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
7938 CFStringAppend(log_output
, CFSTR(")"));
7940 my_CFRelease(&primary_str
);
7941 my_CFRelease(&changes
);
7944 #ifdef MANAGE_IF_IOCTL
7948 #endif /* MANAGE_IF_IOCTL */
7951 if (dns_changed
|| dnsinfo_changed
) {
7954 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
7955 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
7956 str
= "*"; // dnsinfo change w/no change to primary
7958 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
7959 } else if (S_primary_dns
!= NULL
) {
7960 CFStringAppend(log_output
, CFSTR(" DNS"));
7963 if (proxy_changed
) {
7966 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
7967 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
7968 } else if (S_primary_proxies
!= NULL
) {
7969 CFStringAppend(log_output
, CFSTR(" Proxy"));
7972 #if !TARGET_OS_IPHONE
7976 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
7977 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
7978 } else if (S_primary_smb
!= NULL
) {
7979 CFStringAppend(log_output
, CFSTR(" SMB"));
7981 #endif // !TARGET_OS_IPHONE
7987 #pragma mark Network changed notification
7989 static dispatch_queue_t
7990 __network_change_queue()
7992 static dispatch_once_t once
;
7993 static dispatch_queue_t q
;
7995 dispatch_once(&once
, ^{
7996 q
= dispatch_queue_create("network change queue", NULL
);
8002 // Note: must run on __network_change_queue()
8004 post_network_change_when_ready()
8008 dispatch_assert_queue(__network_change_queue());
8010 if (S_network_change_needed
== 0) {
8014 if (!S_network_change_timeout
&&
8015 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
8016 // if we [still] need to wait for the DNS configuration
8017 // or network information changes to be ack'd
8019 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
8020 S_dnsinfo_synced
? "DNS" : "!DNS",
8021 S_nwi_synced
? "nwi" : "!nwi");
8025 // cancel any running timer
8026 if (S_network_change_timer
!= NULL
) {
8027 dispatch_source_cancel(S_network_change_timer
);
8028 dispatch_release(S_network_change_timer
);
8029 S_network_change_timer
= NULL
;
8030 S_network_change_timeout
= FALSE
;
8033 // set (and log?) the post time
8035 struct timeval elapsed
;
8038 (void) gettimeofday(&end
, NULL
);
8039 timersub(&end
, &S_network_change_start
, &elapsed
);
8041 #define QUERY_TIME__FMT "%ld.%6.6d"
8042 #define QUERY_TIME__DIV 1
8045 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
8046 S_network_change_timeout
? "timeout" : "delayed",
8048 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
8049 S_network_change_needed
);
8053 /* We are about to post a network change to everyone, get the agents up to date */
8054 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8055 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
8056 /* Setup or Update config agents */
8057 process_AgentMonitor_DNS();
8059 #endif //!TARGET_OS_SIMULATOR
8061 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
8062 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
8063 if (status
!= NOTIFY_STATUS_OK
) {
8065 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%d", status
);
8069 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
8070 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
8071 if (status
!= NOTIFY_STATUS_OK
) {
8073 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%d", status
);
8077 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
8078 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8079 /* Setup or Update config agents */
8080 process_AgentMonitor_Proxy();
8081 #endif //!TARGET_OS_SIMULATOR
8082 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8083 if (status
!= NOTIFY_STATUS_OK
) {
8085 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%d", status
);
8089 if ((S_network_change_needed
& NETWORK_CHANGE_NAT64
) != 0) {
8090 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8091 // process any NAT64 prefix update requests (and refresh existing prefixes on change)
8092 if (S_nat64_prefix_requests
!= NULL
|| S_nat64_prefix_updates
!= NULL
8093 || S_nat64_cancel_prefix_requests
!= NULL
) {
8094 nat64_configuration_update(S_nat64_prefix_requests
,
8095 S_nat64_prefix_updates
,
8096 S_nat64_cancel_prefix_requests
);
8097 my_CFRelease(&S_nat64_prefix_requests
);
8098 my_CFRelease(&S_nat64_prefix_updates
);
8099 my_CFRelease(&S_nat64_cancel_prefix_requests
);
8101 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8103 S_network_change_needed
&= ~(NETWORK_CHANGE_NAT64
);
8106 if (S_network_change_needed
!= 0) {
8107 // if more than just a NAT64 prefix change
8108 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
8109 if (status
!= NOTIFY_STATUS_OK
) {
8111 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%d", status
);
8115 S_network_change_needed
= 0;
8119 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
8121 // Note: must run on __network_change_queue()
8123 post_network_change(uint32_t change
)
8125 dispatch_assert_queue(__network_change_queue());
8127 if (S_network_change_needed
== 0) {
8128 // set the start time
8129 (void) gettimeofday(&S_network_change_start
, NULL
);
8132 // indicate that we need to post a change for ...
8133 S_network_change_needed
|= change
;
8135 // cancel any running timer
8136 if (S_network_change_timer
!= NULL
) {
8137 dispatch_source_cancel(S_network_change_timer
);
8138 dispatch_release(S_network_change_timer
);
8139 S_network_change_timer
= NULL
;
8140 S_network_change_timeout
= FALSE
;
8143 // if needed, start new timer
8144 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
8145 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
8148 __network_change_queue());
8149 dispatch_source_set_event_handler(S_network_change_timer
, ^{
8150 S_network_change_timeout
= TRUE
;
8151 post_network_change_when_ready();
8153 dispatch_source_set_timer(S_network_change_timer
,
8154 dispatch_time(DISPATCH_TIME_NOW
,
8155 TRAILING_EDGE_TIMEOUT_NSEC
), // start
8156 DISPATCH_TIME_FOREVER
, // interval
8157 10 * NSEC_PER_MSEC
); // leeway
8158 dispatch_resume(S_network_change_timer
);
8161 post_network_change_when_ready();
8167 #pragma mark Process network (SCDynamicStore) changes
8170 IPMonitorProcessChanges(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8171 CFArrayRef if_rank_changes
)
8174 uint32_t changes
= 0;
8175 nwi_state_t changes_state
= NULL
;
8176 boolean_t dns_changed
= FALSE
;
8177 boolean_t dnsinfo_changed
= FALSE
;
8178 boolean_t global_ipv4_changed
= FALSE
;
8179 boolean_t global_ipv6_changed
= FALSE
;
8182 boolean_t nat64_changed
= FALSE
;
8183 CFMutableStringRef network_change_msg
= NULL
;
8185 nwi_state_t old_nwi_state
= NULL
;
8186 CFDictionaryRef old_primary_dns
= NULL
;
8187 CFDictionaryRef old_primary_proxy
= NULL
;
8188 #if !TARGET_OS_IPHONE
8189 CFDictionaryRef old_primary_smb
= NULL
;
8190 #endif // !TARGET_OS_IPHONE
8191 boolean_t proxies_changed
= FALSE
;
8192 boolean_t reachability_changed
= FALSE
;
8193 CFArrayRef service_order
;
8194 CFMutableArrayRef service_changes
= NULL
;
8195 CFDictionaryRef services_info
= NULL
;
8196 #if !TARGET_OS_IPHONE
8197 boolean_t smb_changed
= FALSE
;
8198 #endif // !TARGET_OS_IPHONE
8200 /* populate name/index cache */
8203 if (changed_keys
!= NULL
) {
8204 count
= CFArrayGetCount(changed_keys
);
8205 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8207 "changed keys %@ (%ld)", changed_keys
, count
);
8210 if (if_rank_changes
== NULL
&& count
== 0) {
8214 if (S_primary_dns
!= NULL
) {
8215 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
8216 if (old_primary_dns
!= NULL
) {
8217 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
8221 if (S_primary_proxies
!= NULL
) {
8223 = service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
8224 if (old_primary_proxy
!= NULL
) {
8225 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
8229 #if !TARGET_OS_IPHONE
8230 if (S_primary_smb
!= NULL
) {
8231 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
8232 if (old_primary_smb
!= NULL
) {
8233 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
8236 #endif // !TARGET_OS_IPHONE
8238 keyChangeListInit(&keys
);
8239 service_changes
= CFArrayCreateMutable(NULL
, 0,
8240 &kCFTypeArrayCallBacks
);
8242 for (CFIndex i
= 0; i
< count
; i
++) {
8244 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8245 CFStringRef interface
= NULL
;
8246 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8248 change
= CFArrayGetValueAtIndex(changed_keys
, i
);
8249 if (CFEqual(change
, S_setup_global_ipv4
)) {
8250 global_ipv4_changed
= TRUE
;
8251 global_ipv6_changed
= TRUE
;
8253 else if (CFEqual(change
, S_multicast_resolvers
)) {
8254 dnsinfo_changed
= TRUE
;
8256 else if (CFEqual(change
, S_private_resolvers
)) {
8257 dnsinfo_changed
= TRUE
;
8259 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
8260 dnsinfo_changed
= TRUE
;
8262 else if (CFStringHasPrefix(change
, S_interface_delegation_prefix
) &&
8263 CFStringHasSuffix(change
, kSCEntNetInterfaceDelegation
)) {
8264 reachability_changed
= TRUE
;
8266 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
8267 CFStringRef serviceID
;
8269 serviceID
= parse_component(change
, S_state_service_prefix
);
8270 if (serviceID
!= NULL
) {
8271 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8272 CFRelease(serviceID
);
8275 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
8276 CFStringRef serviceID
;
8278 serviceID
= parse_component(change
, S_setup_service_prefix
);
8279 if (serviceID
!= NULL
) {
8280 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8281 CFRelease(serviceID
);
8284 for (size_t j
= 0; j
< countof(transientInterfaceEntityNames
); j
++) {
8285 if (CFStringHasSuffix(change
,
8286 *transientInterfaceEntityNames
[j
])) {
8287 reachability_changed
= TRUE
;
8292 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
8293 reachability_changed
= TRUE
;
8296 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8297 else if (is_nat64_prefix_request(change
, &interface
)) {
8298 set_plat_discovery(kPLATDiscoveryOptionStart
, interface
);
8299 nat64_changed
= TRUE
;
8301 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8304 /* determine which serviceIDs are impacted by the interface rank changes */
8305 if (if_rank_changes
!= NULL
) {
8306 n
= CFArrayGetCount(if_rank_changes
);
8307 for (CFIndex i
= 0; i
< n
; i
++) {
8308 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
8310 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8311 my_log(LOG_DEBUG
, "Interface rank changed %@", ifname
);
8313 append_serviceIDs_for_interface(service_changes
, ifname
);
8317 /* grab a snapshot of everything we need */
8318 services_info
= services_info_copy(session
, service_changes
);
8319 assert(services_info
!= NULL
);
8321 /* grab the service order */
8322 service_order
= service_order_get(services_info
);
8323 if (service_order
!= NULL
) {
8324 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8325 my_log(LOG_DEBUG
, "service_order %@ ", service_order
);
8329 /* process protocol (v4, v6, rank, ...) and configuration (dns, proxies, smb, ...) changes */
8330 n
= CFArrayGetCount(service_changes
);
8331 for (CFIndex i
= 0; i
< n
; i
++) {
8333 CFStringRef serviceID
;
8335 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
8336 changes
= service_changed(services_info
, serviceID
);
8337 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
8338 /* if __Service__ (e.g. PrimaryRank) changed */
8339 global_ipv4_changed
= TRUE
;
8340 global_ipv6_changed
= TRUE
;
8343 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
8344 global_ipv4_changed
= TRUE
;
8345 dnsinfo_changed
= TRUE
;
8346 proxies_changed
= TRUE
;
8348 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
8349 global_ipv6_changed
= TRUE
;
8350 dnsinfo_changed
= TRUE
;
8351 proxies_changed
= TRUE
;
8352 nat64_changed
= TRUE
;
8355 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
8356 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
8359 dnsinfo_changed
= TRUE
;
8360 nat64_changed
= TRUE
;
8362 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
8363 proxies_changed
= TRUE
;
8365 #if !TARGET_OS_IPHONE
8366 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
8367 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
8372 if ((changes
& (1 << kEntityTypeTransientStatus
)) != 0
8373 && (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
8374 || service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
)) {
8375 dnsinfo_changed
= TRUE
;
8379 /* ensure S_nwi_state can hold as many services as we have currently */
8380 n_services
= (int)CFDictionaryGetCount(S_service_state_dict
);
8381 old_nwi_state
= nwi_state_make_copy(S_nwi_state
);
8382 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
8384 if (global_ipv4_changed
) {
8385 if (S_ipv4_results
!= NULL
) {
8386 ElectionResultsRelease(S_ipv4_results
);
8389 = ElectionResultsCopy(AF_INET
, service_order
);
8390 ElectionResultsLog(LOG_INFO
, S_ipv4_results
, "IPv4");
8392 if (global_ipv6_changed
) {
8393 if (S_ipv6_results
!= NULL
) {
8394 ElectionResultsRelease(S_ipv6_results
);
8397 = ElectionResultsCopy(AF_INET6
, service_order
);
8398 ElectionResultsLog(LOG_INFO
, S_ipv6_results
, "IPv6");
8400 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
8401 CFStringRef new_primary
;
8402 CFStringRef new_primary_dns
= NULL
;
8403 CFStringRef new_primary_proxies
= NULL
;
8404 #if !TARGET_OS_IPHONE
8405 CFStringRef new_primary_smb
= NULL
;
8406 #endif /* !TARGET_OS_IPHONE */
8407 RouteListUnion new_routelist
;
8408 CandidateRef other_candidate
;
8409 CandidateRef primary_candidate
;
8411 if (S_nwi_state
!= NULL
) {
8412 nwi_state_clear(S_nwi_state
, AF_INET
);
8413 nwi_state_clear(S_nwi_state
, AF_INET6
);
8417 my_log(LOG_DEBUG
, "electing IPv4 primary");
8418 new_routelist
.ptr
= NULL
;
8419 other_candidate
= (S_ipv6_results
!= NULL
) /* get IPv6 primary */
8420 ? S_ipv6_results
->candidates
: NULL
;
8421 primary_candidate
= ElectionResultsGetPrimary(S_ipv4_results
,
8423 S_nwi_state
, AF_INET
,
8424 &new_routelist
.common
,
8426 new_primary
= (primary_candidate
!= NULL
)
8427 ? primary_candidate
->serviceID
: NULL
;
8428 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
8429 update_ipv4(S_primary_ipv4
, new_routelist
.v4
, &keys
);
8432 my_log(LOG_DEBUG
, "electing IPv6 primary");
8433 new_routelist
.ptr
= NULL
;
8434 other_candidate
= primary_candidate
; /* get IPv4 primary */
8435 primary_candidate
= ElectionResultsGetPrimary(S_ipv6_results
,
8437 S_nwi_state
, AF_INET6
,
8438 &new_routelist
.common
,
8440 new_primary
= (primary_candidate
!= NULL
)
8441 ? primary_candidate
->serviceID
: NULL
;
8442 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
8443 update_ipv6(S_primary_ipv6
, new_routelist
.v6
, &keys
);
8445 nwi_state_finalize(S_nwi_state
);
8447 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
8448 /* decide between IPv4 and IPv6 */
8449 if (rank_service_entity(S_ipv4_service_rank_dict
,
8450 S_primary_ipv4
, kSCEntNetDNS
)
8451 <= rank_service_entity(S_ipv6_service_rank_dict
,
8452 S_primary_ipv6
, kSCEntNetDNS
)) {
8453 new_primary_dns
= S_primary_ipv4
;
8456 new_primary_dns
= S_primary_ipv6
;
8458 if (rank_service_entity(S_ipv4_service_rank_dict
,
8459 S_primary_ipv4
, kSCEntNetProxies
)
8460 <= rank_service_entity(S_ipv6_service_rank_dict
,
8461 S_primary_ipv6
, kSCEntNetProxies
)) {
8462 new_primary_proxies
= S_primary_ipv4
;
8465 new_primary_proxies
= S_primary_ipv6
;
8467 #if !TARGET_OS_IPHONE
8468 if (rank_service_entity(S_ipv4_service_rank_dict
,
8469 S_primary_ipv4
, kSCEntNetSMB
)
8470 <= rank_service_entity(S_ipv6_service_rank_dict
,
8471 S_primary_ipv6
, kSCEntNetSMB
)) {
8472 new_primary_smb
= S_primary_ipv4
;
8475 new_primary_smb
= S_primary_ipv6
;
8477 #endif /* !TARGET_OS_IPHONE */
8480 else if (S_primary_ipv6
!= NULL
) {
8481 new_primary_dns
= S_primary_ipv6
;
8482 new_primary_proxies
= S_primary_ipv6
;
8483 #if !TARGET_OS_IPHONE
8484 new_primary_smb
= S_primary_ipv6
;
8485 #endif /* !TARGET_OS_IPHONE */
8487 else if (S_primary_ipv4
!= NULL
) {
8488 new_primary_dns
= S_primary_ipv4
;
8489 new_primary_proxies
= S_primary_ipv4
;
8490 #if !TARGET_OS_IPHONE
8491 new_primary_smb
= S_primary_ipv4
;
8492 #endif /* !TARGET_OS_IPHONE */
8495 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
8497 dnsinfo_changed
= TRUE
;
8499 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
,
8501 proxies_changed
= TRUE
;
8503 #if !TARGET_OS_IPHONE
8504 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
8507 #endif /* !TARGET_OS_IPHONE */
8510 if (!proxies_changed
&& dnsinfo_changed
8511 && ((G_supplemental_proxies_follow_dns
!= NULL
)
8512 && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
8513 proxies_changed
= TRUE
;
8516 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
8518 if (global_ipv4_changed
|| global_ipv6_changed
8519 || dnsinfo_changed
|| reachability_changed
) {
8520 if (S_nwi_state
!= NULL
) {
8521 S_nwi_state
->generation_count
= mach_absolute_time();
8522 if (global_ipv4_changed
|| global_ipv6_changed
8523 || reachability_changed
) {
8524 SCNetworkReachabilityFlags reach_flags_v4
= 0;
8525 SCNetworkReachabilityFlags reach_flags_v6
= 0;
8527 GetReachabilityFlagsFromTransientServices(services_info
,
8531 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
,
8535 /* Update the per-interface generation count */
8536 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
,
8540 if (update_nwi(S_nwi_state
)) {
8541 changes
|= NETWORK_CHANGE_NET
;
8544 * the DNS configuration includes per-resolver configuration
8545 * reachability flags that are based on the nwi state. Let's
8546 * make sure that we check for changes
8548 dnsinfo_changed
= TRUE
;
8552 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
8553 changes
|= NETWORK_CHANGE_DNS
;
8554 dnsinfo_changed
= TRUE
;
8556 dns_changed
= FALSE
;
8559 if (dnsinfo_changed
) {
8560 if (update_dnsinfo(services_info
, S_primary_dns
,
8561 &keys
, service_order
)) {
8562 changes
|= NETWORK_CHANGE_DNS
;
8564 dnsinfo_changed
= FALSE
;
8567 if (proxies_changed
) {
8568 // if proxy change OR supplemental Proxies follow supplemental DNS
8569 if (update_proxies(services_info
, S_primary_proxies
,
8570 &keys
, service_order
)) {
8571 changes
|= NETWORK_CHANGE_PROXY
;
8573 proxies_changed
= FALSE
;
8576 #if !TARGET_OS_IPHONE
8578 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
8579 changes
|= NETWORK_CHANGE_SMB
;
8581 smb_changed
= FALSE
;
8584 #endif /* !TARGET_OS_IPHONE */
8585 if (nat64_changed
) {
8586 changes
|= NETWORK_CHANGE_NAT64
;
8588 my_CFRelease(&service_changes
);
8589 my_CFRelease(&services_info
);
8592 network_change_msg
= CFStringCreateMutable(NULL
, 0);
8593 process_nwi_changes(network_change_msg
,
8602 #if !TARGET_OS_IPHONE
8605 #else // !TARGET_OS_IPHONE
8606 FALSE
, // smb_changed
8607 NULL
// old_primary_smb
8608 #endif // !TARGET_OS_IPHONE
8612 keyChangeListApplyToStore(&keys
, session
);
8613 my_CFRelease(&old_primary_dns
);
8614 my_CFRelease(&old_primary_proxy
);
8615 #if !TARGET_OS_IPHONE
8616 my_CFRelease(&old_primary_smb
);
8617 #endif // !TARGET_OS_IPHONE
8620 dispatch_async(__network_change_queue(), ^{
8621 post_network_change(changes
);
8625 if ((network_change_msg
!= NULL
)
8626 && (CFStringGetLength(network_change_msg
) != 0)) {
8627 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
8628 } else if (keyChangeListActive(&keys
)) {
8629 my_log(LOG_NOTICE
, "network changed");
8630 } else if (nat64_changed
) {
8631 my_log(LOG_NOTICE
, "nat64 update");
8633 my_log(LOG_INFO
, "network event w/no changes");
8636 my_CFRelease(&network_change_msg
);
8638 if (changes_state
!= NULL
) {
8639 nwi_state_free(changes_state
);
8641 if (old_nwi_state
!= NULL
) {
8642 nwi_state_free(old_nwi_state
);
8644 keyChangeListFree(&keys
);
8646 /* release the name/index cache */
8647 my_if_freenameindex();
8653 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8656 #pragma unused(info)
8657 IPMonitorProcessChanges(session
, changed_keys
, NULL
);
8661 #if !TARGET_OS_IPHONE
8662 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_mcx
8664 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_global
8670 static dispatch_queue_t proxy_cb_queue
;
8672 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
8673 _scprefs_observer_watch(PROXY_GLOBAL_OBSERVER_TYPE
,
8674 "com.apple.SystemConfiguration.plist",
8677 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
8678 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8679 /* Setup or Update config agents */
8680 process_AgentMonitor_Proxy();
8681 #endif //!TARGET_OS_SIMULATOR
8682 (void)notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8683 my_log(LOG_INFO
, "Notifying:\n%@",
8684 S_state_global_proxies
);
8689 #if TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST
8692 prefs_changed_callback_init(void)
8696 #else /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8698 #include "IPMonitorControlPrefs.h"
8701 prefs_changed(SCPreferencesRef prefs
)
8703 #pragma unused(prefs)
8704 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
8705 S_IPMonitor_debug
= kDebugFlagDefault
;
8706 S_IPMonitor_verbose
= TRUE
;
8707 my_log(LOG_DEBUG
, "Setting logging verbose mode on");
8709 my_log(LOG_DEBUG
, "Setting logging verbose mode off");
8710 S_IPMonitor_debug
= 0;
8711 S_IPMonitor_verbose
= FALSE
;
8717 prefs_changed_callback_init(void)
8719 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
8720 prefs_changed(NULL
);
8725 #endif /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8727 #if !TARGET_OS_SIMULATOR
8738 struct rt_msghdr
* rtm
;
8739 struct sockaddr_in
*sin
;
8745 mib
[4] = NET_RT_FLAGS
;
8746 mib
[5] = RTF_STATIC
| RTF_DYNAMIC
;
8747 for (i
= 0; i
< 3; i
++) {
8748 if (sysctl(mib
, N_MIB
, NULL
, &needed
, NULL
, 0) < 0) {
8751 if ((buf
= malloc(needed
)) == NULL
) {
8754 if (sysctl(mib
, N_MIB
, buf
, &needed
, NULL
, 0) >= 0) {
8764 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
8767 /* ALIGN: assume kernel provides necessary alignment */
8768 rtm
= (struct rt_msghdr
*)(void *)next
;
8769 sin
= (struct sockaddr_in
*)(rtm
+ 1);
8771 addr
= ntohl(sin
->sin_addr
.s_addr
);
8772 if (IN_LOOPBACK(addr
)) {
8774 "flush_routes: ignoring loopback route");
8777 if (IN_LOCAL_GROUP(addr
)) {
8779 "flush_routes: ignoring multicast route");
8782 rtm
->rtm_type
= RTM_DELETE
;
8783 rtm
->rtm_seq
= ++rtm_seq
;
8784 if (write(s
, rtm
, rtm
->rtm_msglen
) < 0) {
8786 "flush_routes: removing route for "
8787 IP_FORMAT
" failed: %s",
8788 IP_LIST(&sin
->sin_addr
),
8793 "flush_routes: removed route for " IP_FORMAT
,
8794 IP_LIST(&sin
->sin_addr
));
8802 flush_inet_routes(void)
8806 s
= open_routing_socket();
8813 #else /* !TARGET_OS_SIMULATOR */
8816 flush_inet_routes(void)
8820 #endif /* !TARGET_OS_SIMULATOR */
8827 CFMutableArrayRef keys
= NULL
;
8828 CFStringRef pattern
;
8829 CFMutableArrayRef patterns
= NULL
;
8830 CFRunLoopSourceRef rls
= NULL
;
8832 if (S_is_network_boot() != 0) {
8837 flush_inet_routes();
8840 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
8841 IPMonitorNotify
, NULL
);
8842 if (S_session
== NULL
) {
8844 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8845 SCErrorString(SCError()));
8849 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8850 kSCDynamicStoreDomainState
,
8853 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8854 kSCDynamicStoreDomainState
,
8857 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8858 kSCDynamicStoreDomainState
,
8860 S_state_global_proxies
8861 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8862 kSCDynamicStoreDomainState
,
8864 #if !TARGET_OS_IPHONE
8866 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8867 kSCDynamicStoreDomainState
,
8869 #endif /* !TARGET_OS_IPHONE */
8871 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8872 kSCDynamicStoreDomainSetup
,
8874 S_state_service_prefix
8875 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8876 kSCDynamicStoreDomainState
,
8879 S_setup_service_prefix
8880 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8881 kSCDynamicStoreDomainSetup
,
8884 S_interface_delegation_prefix
8885 = SCDynamicStoreKeyCreateNetworkInterface(NULL
,
8886 kSCDynamicStoreDomainState
);
8888 S_service_state_dict
8889 = CFDictionaryCreateMutable(NULL
, 0,
8890 &kCFTypeDictionaryKeyCallBacks
,
8891 &kCFTypeDictionaryValueCallBacks
);
8893 S_ipv4_service_rank_dict
8894 = CFDictionaryCreateMutable(NULL
, 0,
8895 &kCFTypeDictionaryKeyCallBacks
,
8896 &kCFTypeDictionaryValueCallBacks
);
8898 S_ipv6_service_rank_dict
8899 = CFDictionaryCreateMutable(NULL
, 0,
8900 &kCFTypeDictionaryKeyCallBacks
,
8901 &kCFTypeDictionaryValueCallBacks
);
8903 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8904 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8906 /* register for State: and Setup: per-service notifications */
8907 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
8909 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
8910 CFArrayAppendValue(patterns
, pattern
);
8913 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
8914 CFArrayAppendValue(patterns
, pattern
);
8917 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
8918 CFArrayAppendValue(patterns
, pattern
);
8921 /* register for State: per-service PPP/VPN/IPSec status notifications */
8922 add_transient_status_keys(kSCCompAnyRegex
, NULL
, patterns
);
8924 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
8925 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
8927 /* add notifier for multicast DNS configuration (Bonjour/.local) */
8928 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8929 kSCDynamicStoreDomainState
,
8931 CFSTR(kDNSServiceCompMulticastDNS
));
8932 CFArrayAppendValue(keys
, S_multicast_resolvers
);
8934 /* add notifier for private DNS configuration (Back to My Mac) */
8935 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8936 kSCDynamicStoreDomainState
,
8938 CFSTR(kDNSServiceCompPrivateDNS
));
8939 CFArrayAppendValue(keys
, S_private_resolvers
);
8941 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8942 /* add NAT64 prefix request pattern */
8943 nat64_prefix_request_add_pattern(patterns
);
8944 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8946 /* add interface delegation pattern */
8947 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetInterfaceDelegation
);
8948 CFArrayAppendValue(patterns
, pattern
);
8951 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
8953 "SCDynamicStoreSetNotificationKeys() failed: %s",
8954 SCErrorString(SCError()));
8958 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
8961 "SCDynamicStoreCreateRunLoopSource() failed: %s",
8962 SCErrorString(SCError()));
8966 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
8969 serviceIDNumberInit();
8971 /* initialize dns configuration */
8972 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
8973 #if !TARGET_OS_IPHONE
8975 #endif /* !TARGET_OS_IPHONE */
8976 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
8978 #if !TARGET_OS_IPHONE
8979 /* initialize SMB configuration */
8980 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
8981 #endif /* !TARGET_OS_IPHONE */
8986 my_CFRelease(&keys
);
8987 my_CFRelease(&patterns
);
8995 /* initialize multicast route */
8996 update_ipv4(NULL
, NULL
, NULL
);
8998 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8999 process_AgentMonitor();
9000 #endif // !TARGET_OS_SIMULATOR
9006 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
9010 boolean_t ret
= def
;
9012 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
9014 ret
= CFBooleanGetValue(b
);
9019 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9020 #include "IPMonitorControlServer.h"
9023 InterfaceRankChanged(void * info
)
9025 #pragma unused(info)
9026 CFDictionaryRef assertions
= NULL
;
9029 changes
= IPMonitorControlServerCopyInterfaceRankInformation(&assertions
);
9030 if (S_if_rank_dict
!= NULL
) {
9031 CFRelease(S_if_rank_dict
);
9033 S_if_rank_dict
= assertions
;
9034 if (changes
!= NULL
) {
9035 IPMonitorProcessChanges(S_session
, NULL
, changes
);
9043 StartIPMonitorControlServer(void)
9045 CFRunLoopSourceContext context
;
9046 CFRunLoopSourceRef rls
;
9048 memset(&context
, 0, sizeof(context
));
9049 context
.perform
= InterfaceRankChanged
;
9050 rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
9051 if (!IPMonitorControlServerStart(CFRunLoopGetCurrent(),
9053 &S_bundle_logging_verbose
)) {
9054 my_log(LOG_ERR
, "IPMonitorControlServerStart failed");
9057 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
9058 kCFRunLoopDefaultMode
);
9064 #endif /* !TARGET_OS_SIMULATOR */
9068 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
9070 CFDictionaryRef info_dict
;
9072 info_dict
= CFBundleGetInfoDictionary(bundle
);
9074 if (info_dict
!= NULL
) {
9076 = S_get_plist_boolean(info_dict
,
9077 CFSTR("AppendStateArrayToSetupArray"),
9080 if (bundleVerbose
) {
9081 S_IPMonitor_debug
= kDebugFlagDefault
;
9082 S_bundle_logging_verbose
= TRUE
;
9083 S_IPMonitor_verbose
= TRUE
;
9086 /* register to receive changes to the "verbose" flag and read the initial setting */
9087 prefs_changed_callback_init();
9089 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9090 /* start DNS configuration (dnsinfo) server */
9091 load_DNSConfiguration(bundle
, // bundle
9092 ^(Boolean inSync
) { // syncHandler
9093 dispatch_async(__network_change_queue(), ^{
9094 S_dnsinfo_synced
= inSync
;
9097 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
9098 // all of the DNS service ack's should result
9099 // in a [new] network change being posted
9100 post_network_change(NETWORK_CHANGE_DNS
);
9102 post_network_change_when_ready();
9107 /* start Network Information (nwi) server */
9108 load_NetworkInformation(bundle
, // bundle
9109 ^(Boolean inSync
) { // syncHandler
9110 dispatch_async(__network_change_queue(), ^{
9111 S_nwi_synced
= inSync
;
9112 post_network_change_when_ready();
9115 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
9117 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9118 /* start IPMonitor Control (InterfaceRank) server */
9119 StartIPMonitorControlServer();
9120 #endif /* !TARGET_OS_IPHONE */
9122 /* initialize DNS configuration */
9123 dns_configuration_init(bundle
);
9125 /* initialize proxy configuration */
9126 proxy_configuration_init(bundle
);
9130 if (S_session
!= NULL
) {
9131 dns_configuration_monitor(S_session
, IPMonitorNotify
);
9134 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9135 load_hostname(TRUE
);
9136 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
9138 #if !TARGET_OS_IPHONE
9139 load_smb_configuration(TRUE
);
9140 #endif /* !TARGET_OS_IPHONE */
9147 #pragma mark Standalone test code
9150 #ifdef TEST_IPMONITOR
9153 main(int argc
, char **argv
)
9157 S_IPMonitor_debug
= kDebugFlag1
;
9159 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9162 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
9164 S_IPMonitor_debug
= kDebugFlag1
;
9170 #endif /* TEST_IPMONITOR */
9172 #ifdef TEST_ROUTELIST
9177 const char * gateway
;
9178 const char * ifname
;
9183 #if TEST_IPV4_ROUTELIST
9189 const char * router
;
9190 const char * ifname
;
9192 const CFStringRef
* primary_rank
;
9193 struct route
* additional_routes
;
9194 int additional_routes_count
;
9195 struct route
* excluded_routes
;
9196 int excluded_routes_count
;
9197 } IPv4ServiceContents
;
9199 typedef const IPv4ServiceContents
* IPv4ServiceContentsRef
;
9201 struct route loop_routelist
[] = {
9202 { "1.1.1.1", 32, "1.1.1.2", NULL
},
9203 { "1.1.1.2", 32, "1.1.1.3", NULL
},
9204 { "1.1.1.3", 32, "1.1.1.4", NULL
},
9205 { "1.1.1.4", 32, "1.1.1.5", NULL
},
9206 { "1.1.1.5", 32, "1.1.1.6", NULL
},
9207 { "1.1.1.6", 32, "1.1.1.7", NULL
},
9208 { "1.1.1.7", 32, "1.1.1.8", NULL
},
9209 { "1.1.1.8", 32, "1.1.1.9", NULL
},
9210 { "1.1.1.9", 32, "1.1.1.10", NULL
},
9211 { "1.1.1.10", 32, "1.1.1.11", NULL
},
9212 { "1.1.1.11", 32, "1.1.1.1", NULL
},
9215 struct route vpn_routelist
[] = {
9216 { "10.1.3.0", 24, "17.153.46.24", NULL
},
9217 { "10.1.4.0", 24, "17.153.46.24", NULL
},
9218 { "10.1.5.0", 24, "17.153.46.24", NULL
},
9219 { "10.1.6.0", 24, "17.153.46.24", NULL
},
9220 { "10.1.7.0", 24, "17.153.46.24", NULL
},
9221 { "10.16.0.0", 12, "17.153.46.24", NULL
},
9222 { "10.45.0.0", 16, "17.153.46.24", NULL
},
9223 { "10.53.0.0", 16, "17.153.46.24", NULL
},
9224 { "10.70.0.0", 15, "17.153.46.24", NULL
},
9225 { "10.74.0.0", 15, "17.153.46.24", NULL
},
9226 { "10.90.0.0", 15, "17.153.46.24", NULL
},
9227 { "10.91.0.0", 16, "17.153.46.24", NULL
},
9228 { "10.100.0.0", 16, "17.153.46.24", NULL
},
9229 { "10.113.0.0", 16, "17.153.46.24", NULL
},
9230 { "10.128.0.0", 9, "17.153.46.24", NULL
},
9231 { "17.0.0.0", 9, "17.153.46.24", NULL
},
9232 { "17.34.0.0", 16, "17.153.46.24", NULL
},
9233 { "17.112.156.53", 32, "17.153.46.24", NULL
},
9234 { "17.128.0.0", 10, "17.153.46.24", NULL
},
9235 { "17.149.0.121", 32, "17.153.46.24", NULL
},
9236 { "17.149.7.200", 32, "17.153.46.24", NULL
},
9237 { "17.153.46.24", 32, "17.153.46.24", NULL
},
9238 { "17.192.0.0", 12, "17.153.46.24", NULL
},
9239 { "17.208.0.0", 15, "17.153.46.24", NULL
},
9240 { "17.211.0.0", 16, "17.153.46.24", NULL
},
9241 { "17.212.0.0", 14, "17.153.46.24", NULL
},
9242 { "17.216.0.0", 13, "17.153.46.24", NULL
},
9243 { "17.224.0.0", 12, "17.153.46.24", NULL
},
9244 { "17.240.0.0", 16, "17.153.46.24", NULL
},
9245 { "17.241.0.0", 16, "17.153.46.24", NULL
},
9246 { "17.248.0.0", 14, "17.153.46.24", NULL
},
9247 { "17.251.104.200", 32, "17.153.46.24", NULL
},
9248 { "17.252.0.0", 16, "17.153.46.24", NULL
},
9249 { "17.253.0.0", 16, "17.153.46.24", NULL
},
9250 { "17.254.0.0", 16, "17.153.46.24", NULL
},
9251 { "17.255.0.0", 16, "17.153.46.24", NULL
},
9252 { "151.193.141.0", 27, "17.153.46.24", NULL
},
9253 { "172.16.2.0", 24, "17.153.46.24", NULL
},
9254 { "192.35.50.0", 24, "17.153.46.24", NULL
},
9255 { "204.179.20.0", 24, "17.153.46.24", NULL
},
9256 { "206.112.116.0", 24, "17.153.46.24", NULL
},
9259 struct route vpn_routelist_ext
[] = {
9260 { "17.151.63.82", 32, "10.0.0.1", "en0" },
9261 { "17.151.63.81", 32, "17.151.63.81", "en0" },
9262 { "17.151.63.80", 32, NULL
, NULL
},
9263 { "17.1.0.0", 16, NULL
, NULL
},
9264 { "17.2.0.0", 24, NULL
, NULL
},
9265 { "10.0.0.0", 24, NULL
, NULL
},
9269 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
9271 const IPv4ServiceContents en0_10
= {
9272 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9275 const IPv4ServiceContents en0_15
= {
9276 "10.0.0.19", 24, NULL
, "10.0.0.1", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9279 const IPv4ServiceContents en0_30
= {
9280 "10.0.0.11", 24, NULL
, "10.0.0.1", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9283 const IPv4ServiceContents en0_40
= {
9284 "10.0.0.12", 24, NULL
, "10.0.0.1", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9287 const IPv4ServiceContents en0_50
= {
9288 "10.0.0.13", 24, NULL
, "10.0.0.1", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9291 const IPv4ServiceContents en0_110
= {
9292 "192.168.2.10", 24, NULL
, "192.168.2.1", "en0", 110, NULL
, NULL
, 0, NULL
, 0
9295 const IPv4ServiceContents en0_1
= {
9296 "17.202.40.191", 22, NULL
, "17.202.20.1", "en0", 1, NULL
, NULL
, 0, NULL
, 0
9299 const IPv4ServiceContents en1_20
= {
9300 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9303 const IPv4ServiceContents en1_2
= {
9304 "17.202.42.24", 22, NULL
, "17.202.20.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9307 const IPv4ServiceContents en1_125
= {
9308 "192.168.2.20", 24, NULL
, "192.168.2.1", "en1", 125, NULL
, NULL
, 0, NULL
, 0
9311 const IPv4ServiceContents fw0_25
= {
9312 "192.168.2.30", 24, NULL
, "192.168.2.1", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9315 const IPv4ServiceContents fw0_21
= {
9316 "192.168.3.30", 24, NULL
, "192.168.3.1", "fw0", 21, NULL
, NULL
, 0, NULL
, 0
9319 const IPv4ServiceContents ppp0_0_1
= {
9320 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
, NULL
, 0, NULL
, 0
9323 const IPv4ServiceContents utun0
= {
9324 "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
)
9327 const IPv4ServiceContents en0_test6
= {
9328 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 2, NULL
, NULL
, 0, NULL
, 0
9331 const IPv4ServiceContents en1_test6
= {
9332 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 3, NULL
, NULL
, 0, NULL
, 0
9335 const IPv4ServiceContents en2_test6
= {
9336 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9339 const IPv4ServiceContents en0_test7
= {
9340 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 3, NULL
, NULL
, 0, NULL
, 0
9343 const IPv4ServiceContents en1_test7
= {
9344 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9347 const IPv4ServiceContents en2_test7
= {
9348 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9351 const IPv4ServiceContents fw0_test6_and_7
= {
9352 "169.254.11.33", 16, NULL
, NULL
, "fw0", 0x0ffffff, NULL
, NULL
, 0, NULL
, 0
9355 const IPv4ServiceContents en0_10_last
= {
9356 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
, NULL
, 0, NULL
, 0
9359 const IPv4ServiceContents en0_10_never
= {
9360 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9363 const IPv4ServiceContents en1_20_first
= {
9364 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
, NULL
, 0, NULL
, 0
9367 const IPv4ServiceContents en1_20_never
= {
9368 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9371 const IPv4ServiceContents en1_20_other_never
= {
9372 "192.168.2.50", 24, NULL
, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9375 const IPv4ServiceContents en0_linklocal
= {
9376 "169.254.22.44", 16, NULL
, NULL
, "en0", 0xfffff, NULL
, NULL
, 0, NULL
, 0
9379 const IPv4ServiceContents en0_route_loop
= {
9380 "192.168.130.16", 24, NULL
, "192.168.130.1", "en0", 2, NULL
, loop_routelist
, countof(loop_routelist
), NULL
, 0
9385 IPv4ServiceContentsRef test
[];
9386 } IPv4RouteTest
, * IPv4RouteTestRef
;
9388 static IPv4RouteTest test1
= {
9402 static IPv4RouteTest test2
= {
9415 static IPv4RouteTest test3
= {
9432 static IPv4RouteTest test4
= {
9444 static IPv4RouteTest test5
= {
9457 static IPv4RouteTest test6
= {
9468 static IPv4RouteTest test7
= {
9479 static IPv4RouteTest test8
= {
9488 static IPv4RouteTest test9
= {
9498 static IPv4RouteTest test10
= {
9508 static IPv4RouteTest test11
= {
9518 static IPv4RouteTest test12
= {
9527 static IPv4RouteTest test13
= {
9536 static IPv4RouteTest test14
= {
9544 static IPv4RouteTest test15
= {
9552 static IPv4RouteTest test16
= {
9561 static IPv4RouteTest test17
= {
9565 &en1_20_other_never
,
9570 static IPv4RouteTest test18
= {
9578 static IPv4RouteTestRef ipv4_tests
[] = {
9601 ipv4_prefix_length_is_valid(int prefix_length
)
9603 if (prefix_length
< 0 || prefix_length
> IPV4_ROUTE_ALL_BITS_SET
) {
9610 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9613 CFStringRef prop_val
;
9618 prop_val
= CFStringCreateWithCString(NULL
,
9620 kCFStringEncodingASCII
);
9621 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9622 CFRelease(prop_val
);
9627 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9631 CFStringRef prop_val
;
9636 prop_val
= CFStringCreateWithCString(NULL
,
9638 kCFStringEncodingASCII
);
9639 array
= CFArrayCreate(NULL
,
9640 (const void **)&prop_val
, 1,
9641 &kCFTypeArrayCallBacks
);
9642 CFRelease(prop_val
);
9643 CFDictionarySetValue(dict
, prop_name
, array
);
9649 dict_add_ip(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9654 str
= my_CFStringCreateWithInAddr(ip
);
9655 CFDictionarySetValue(dict
, prop_name
, str
);
9661 dict_add_ip_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9667 str
= my_CFStringCreateWithInAddr(ip
);
9668 array
= CFArrayCreate(NULL
,
9669 (const void **)&str
, 1,
9670 &kCFTypeArrayCallBacks
);
9672 CFDictionarySetValue(dict
, prop_name
, array
);
9678 dict_insert_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9679 struct route
* routes
, int routes_count
)
9682 CFMutableArrayRef route_list
;
9683 struct route
* scan
;
9685 if (routes
== NULL
|| routes_count
== 0) {
9688 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9689 &kCFTypeArrayCallBacks
);
9690 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9691 struct in_addr mask
;
9692 CFMutableDictionaryRef route_dict
;
9695 = CFDictionaryCreateMutable(NULL
, 0,
9696 &kCFTypeDictionaryKeyCallBacks
,
9697 &kCFTypeDictionaryValueCallBacks
);
9698 dict_add_string(route_dict
, kSCPropNetIPv4RouteDestinationAddress
,
9700 if (ipv4_prefix_length_is_valid(scan
->prefix_length
)) {
9701 mask
.s_addr
= htonl(prefix_to_mask32(scan
->prefix_length
));
9702 dict_add_ip(route_dict
, kSCPropNetIPv4RouteSubnetMask
, mask
);
9704 dict_add_string(route_dict
, kSCPropNetIPv4RouteGatewayAddress
,
9706 dict_add_string(route_dict
, kSCPropNetIPv4RouteInterfaceName
,
9708 CFArrayAppendValue(route_list
, route_dict
);
9709 CFRelease(route_dict
);
9711 CFDictionarySetValue(dict
, prop_name
, route_list
);
9712 CFRelease(route_list
);
9716 static CFDictionaryRef
9717 make_IPv4_dict(IPv4ServiceContentsRef t
)
9719 CFMutableDictionaryRef dict
;
9721 dict
= CFDictionaryCreateMutable(NULL
, 0,
9722 &kCFTypeDictionaryKeyCallBacks
,
9723 &kCFTypeDictionaryValueCallBacks
);
9724 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
9725 if (ipv4_prefix_length_is_valid(t
->prefix_length
)) {
9726 struct in_addr mask
;
9728 mask
.s_addr
= htonl(prefix_to_mask32(t
->prefix_length
));
9729 dict_add_ip_as_array(dict
, kSCPropNetIPv4SubnetMasks
, mask
);
9731 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
9732 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
9733 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9734 dict_add_string(dict
, kSCPropConfirmedInterfaceName
, t
->ifname
);
9735 dict_insert_routes(dict
, kSCPropNetIPv4AdditionalRoutes
,
9736 t
->additional_routes
, t
->additional_routes_count
);
9737 dict_insert_routes(dict
, kSCPropNetIPv4ExcludedRoutes
,
9738 t
->excluded_routes
, t
->excluded_routes_count
);
9743 kDirectionForwards
= 0,
9744 kDirectionBackwards
= 1
9748 kLogRouteDisabled
= 0,
9749 kLogRouteEnabled
= 1
9752 static IPv4RouteListRef
9753 make_IPv4RouteList_for_test(IPv4RouteListRef list
,
9754 IPv4ServiceContentsRef test
,
9757 CFDictionaryRef dict
;
9760 Rank rank_assertion
= kRankAssertionDefault
;
9761 CFNumberRef rank_assertion_cf
= NULL
;
9762 Boolean rank_assertion_is_set
= FALSE
;
9763 IPv4RouteListRef ret
= NULL
;
9764 IPV4_ROUTES_BUF_DECL(routes
);
9766 dict
= make_IPv4_dict(test
);
9768 fprintf(stderr
, "make_IPv4_dict failed\n");
9771 if (test
->primary_rank
!= NULL
) {
9773 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9774 &rank_assertion_is_set
);
9775 if (rank_assertion_is_set
) {
9777 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9780 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion_cf
, 0);
9781 my_CFRelease(&rank_assertion_cf
);
9783 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
9787 if (rank_assertion
== kRankAssertionScoped
) {
9788 rank_assertion
= kRankAssertionNever
;
9790 rank
= RankMake(test
->rank
, rank_assertion
);
9791 if (log_it
== kLogRouteEnabled
9792 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9795 descr
= IPv4RouteListCopyDescription(r
);
9796 SCPrint(TRUE
, stdout
, CFSTR("Adding %@\n"), descr
);
9799 ret
= IPv4RouteListAddRouteList(list
, 1, r
, rank
);
9807 static IPv4RouteListRef
9808 make_IPv4RouteList(IPv4ServiceContentsRef
* test
, Direction direction
,
9811 IPv4RouteListRef ret
= NULL
;
9812 IPv4ServiceContentsRef
* scan
;
9814 switch (direction
) {
9815 case kDirectionBackwards
:
9816 for (scan
= test
; *scan
!= NULL
; scan
++) {
9817 /* find the end of the list */
9819 for (scan
--; scan
>= test
; scan
--) {
9820 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9824 case kDirectionForwards
:
9825 for (scan
= test
; *scan
!= NULL
; scan
++) {
9826 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9830 IPv4RouteListFinalize(ret
);
9834 #define EMPHASIS_CHARS "================="
9837 * Function: routelist_build_test
9839 * Runs through the given set of routes first in the forward direction,
9840 * then again backwards. We should end up with exactly the same set of
9841 * routes at the end.
9844 routelist_build_test(IPv4RouteTestRef test
)
9847 boolean_t ret
= FALSE
;
9848 IPv4RouteListRef routes1
;
9849 IPv4RouteListRef routes2
;
9851 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
9852 EMPHASIS_CHARS
"\n",
9855 routes1
= make_IPv4RouteList(test
->test
, kDirectionForwards
,
9857 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9858 if (routes1
!= NULL
) {
9859 descr
= IPv4RouteListCopyDescription(routes1
);
9860 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9864 routes2
= make_IPv4RouteList(test
->test
, kDirectionBackwards
,
9866 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9867 if (routes2
!= NULL
) {
9868 descr
= IPv4RouteListCopyDescription(routes2
);
9869 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9873 if ((routes1
!= NULL
&& routes2
== NULL
)
9874 || (routes1
== NULL
&& routes2
!= NULL
)) {
9875 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
9876 (routes1
!= NULL
) ? "not " : "",
9877 (routes2
!= NULL
) ? "not " : "");
9879 else if (routes1
!= NULL
&& routes2
!= NULL
) {
9880 /* check if they are different */
9881 if (routes1
->count
!= routes2
->count
) {
9882 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
9883 routes1
->count
, routes2
->count
);
9885 else if (bcmp(routes1
, routes2
,
9886 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
9887 fprintf(stderr
, "routes1 and routes2 are different\n");
9890 printf("routes1 and routes2 are the same\n");
9894 if (routes1
!= NULL
) {
9897 if (routes2
!= NULL
) {
9900 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
9901 EMPHASIS_CHARS
"\n",
9902 test
->name
, ret
? "PASSED" : "FAILED");
9907 apply_test(IPv4RouteTestRef old_test
, IPv4RouteTestRef new_test
)
9909 IPv4RouteListRef new_routes
;
9910 IPv4RouteListRef old_routes
;
9912 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
9913 EMPHASIS_CHARS
"\n",
9914 old_test
->name
, new_test
->name
);
9916 old_routes
= make_IPv4RouteList(old_test
->test
, kDirectionForwards
,
9918 new_routes
= make_IPv4RouteList(new_test
->test
, kDirectionForwards
,
9920 if (old_routes
== NULL
) {
9921 printf("No Old Routes\n");
9924 printf("Old routes ('%s') = ", old_test
->name
);
9925 IPv4RouteListPrint(old_routes
);
9928 /* apply the old routes */
9929 IPv4RouteListApply(NULL
, old_routes
, -1);
9931 if (new_routes
== NULL
) {
9932 printf("No New Routes\n");
9935 printf("New Routes ('%s') = ", new_test
->name
);
9936 IPv4RouteListPrint(new_routes
);
9939 /* apply the new routes */
9940 IPv4RouteListApply(old_routes
, new_routes
, -1);
9942 if (old_routes
!= NULL
) {
9945 if (new_routes
!= NULL
) {
9948 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
9949 EMPHASIS_CHARS
"\n",
9950 old_test
->name
, new_test
->name
);
9955 main(int argc
, char **argv
)
9957 IPv4RouteTestRef
* test
;
9960 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
9961 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
9963 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9965 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9966 if (!routelist_build_test(*test
)) {
9967 fprintf(stderr
, "%s failed\n", (*test
)->name
);
9971 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9972 IPv4RouteTestRef
* test2
;
9974 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
9975 apply_test(*test
, *test2
);
9976 apply_test(*test2
, *test
);
9983 printf("\nChecking for leaks\n");
9984 sprintf(cmd
, "leaks %d 2>&1", getpid());
9992 #endif /* TEST_IPV4_ROUTELIST */
9994 #if TEST_IPV6_ROUTELIST
10002 typedef const IPv6Address
* IPv6AddressRef
;
10005 IPv6AddressRef addr
;
10007 const char * router
;
10008 const char * ifname
;
10010 const CFStringRef
* primary_rank
;
10011 struct route
* additional_routes
;
10012 int additional_routes_count
;
10013 struct route
* excluded_routes
;
10014 int excluded_routes_count
;
10015 } IPv6ServiceContents
;
10017 typedef const IPv6ServiceContents
* IPv6ServiceContentsRef
;
10019 struct route loop_routelist
[] = {
10020 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
10021 "2620:149:4:f01:225:ff:fecc:89a2", NULL
},
10022 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
10023 "2620:149:4:f01:225:ff:fecc:89a3", NULL
},
10024 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
10025 "2620:149:4:f01:225:ff:fecc:89a4", NULL
},
10026 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
10027 "2620:149:4:f01:225:ff:fecc:89a5", NULL
},
10028 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
10029 "2620:149:4:f01:225:ff:fecc:89a6", NULL
},
10030 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
10031 "2620:149:4:f01:225:ff:fecc:89a7", NULL
},
10032 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
10033 "2620:149:4:f01:225:ff:fecc:89a8", NULL
},
10034 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
10035 "2620:149:4:f01:225:ff:fecc:89a9", NULL
},
10036 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
10037 "2620:149:4:f01:225:ff:fecc:89aa", NULL
},
10038 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
10039 "2620:149:4:f01:225:ff:fecc:89ab", NULL
},
10040 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
10041 "2620:149:4:f01:225:ff:fecc:89a1", NULL
},
10044 struct route vpn_routelist
[] = {
10045 { "2010:470:1f05:3cb::", 64,
10046 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10047 { "2010:222:3fa5:acb::", 48,
10048 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10049 { "2010:222:3fa5:1234::", 40,
10050 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10051 { "2010:222:3fa5:5678::", 40,
10055 struct route vpn_routelist_ext
[] = {
10056 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL
, NULL
},
10059 struct route en1_routelist_ext
[] = {
10060 { "2020:299:abcd:ef12::", 64, NULL
, NULL
},
10064 static const IPv6Address en0_addr1
[] = {
10065 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL
},
10066 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL
}
10069 static const IPv6Address en0_addr2
[] = {
10070 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL
},
10071 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL
}
10074 static const IPv6Address en0_addr3
[] = {
10075 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL
},
10076 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL
}
10079 static const IPv6Address en0_addr4
[] = {
10080 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL
},
10081 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL
}
10084 static const IPv6Address en0_addr5
[] = {
10085 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL
},
10086 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL
}
10089 static const IPv6Address en0_addr6
[] = {
10090 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL
},
10093 static const IPv6Address en0_lladdr
[] = {
10094 { "fe80::cabc:c8ff:fe96:96af", 64, NULL
}
10097 static const IPv6Address en1_addr
[] = {
10098 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL
},
10099 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL
}
10102 static const IPv6Address utun0_addr
[] = {
10103 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL
},
10106 static const IPv6Address fw0_addr1
[] = {
10107 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL
},
10108 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL
}
10112 * address+address-count
10113 * router ifname pri rank additional-routes+count excluded-routes+count
10116 static const IPv6ServiceContents en0_10
= {
10117 en0_addr1
, countof(en0_addr1
),
10118 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
10121 static const IPv6ServiceContents en0_15
= {
10122 en0_addr2
, countof(en0_addr2
),
10123 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL
, NULL
, 0, NULL
, 0
10126 static const IPv6ServiceContents en0_30
= {
10127 en0_addr3
, countof(en0_addr3
),
10128 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL
, NULL
, 0, NULL
, 0
10131 static const IPv6ServiceContents en0_40
= {
10132 en0_addr4
, countof(en0_addr4
),
10133 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL
, NULL
, 0, NULL
, 0
10136 static const IPv6ServiceContents en0_50
= {
10137 en0_addr5
, countof(en0_addr5
),
10138 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL
, NULL
, 0, NULL
, 0
10141 static const IPv6ServiceContents en0_10_a
= {
10142 en0_addr6
, countof(en0_addr6
),
10143 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
10146 static const IPv6ServiceContents fw0_25
= {
10147 fw0_addr1
, countof(fw0_addr1
),
10148 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
10151 static const IPv6ServiceContents en1_20
= {
10152 en1_addr
, countof(en1_addr
),
10153 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL
, NULL
, 0, NULL
, 0
10156 static const IPv6ServiceContents en1_10_ext
= {
10157 en1_addr
, countof(en1_addr
),
10158 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL
, NULL
, 0,
10159 en1_routelist_ext
, countof(en1_routelist_ext
)
10162 static const IPv6ServiceContents en0_0_lladdr
= {
10163 en0_lladdr
, countof(en0_lladdr
),
10164 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL
, NULL
, 0, NULL
, 0
10167 static const IPv6ServiceContents en0_loop
= {
10168 en0_addr1
, countof(en0_addr1
),
10169 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
,
10170 loop_routelist
, countof(loop_routelist
), NULL
, 0
10173 static const IPv6ServiceContents utun0
= {
10174 utun0_addr
, countof(utun0_addr
),
10175 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL
,
10176 vpn_routelist
, countof(vpn_routelist
),
10177 vpn_routelist_ext
, countof(vpn_routelist_ext
),
10182 IPv6ServiceContentsRef test
[];
10183 } IPv6RouteTest
, * IPv6RouteTestRef
;
10185 static IPv6RouteTest test1
= {
10199 static IPv6RouteTest test2
= {
10212 static IPv6RouteTest test3
= {
10221 static IPv6RouteTest test4
= {
10230 static IPv6RouteTest test5
= {
10242 static IPv6RouteTestRef ipv6_tests
[] = {
10253 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10256 CFStringRef prop_val
;
10261 prop_val
= CFStringCreateWithCString(NULL
,
10263 kCFStringEncodingASCII
);
10264 CFDictionarySetValue(dict
, prop_name
, prop_val
);
10265 CFRelease(prop_val
);
10270 dict_add_int(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10275 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10276 CFDictionarySetValue(dict
, prop_name
, num
);
10282 dict_insert_v6_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10283 struct route
* routes
, int routes_count
)
10286 CFMutableArrayRef route_list
;
10287 struct route
* scan
;
10289 if (routes
== NULL
|| routes_count
== 0) {
10292 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
10293 &kCFTypeArrayCallBacks
);
10294 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
10295 CFMutableDictionaryRef route_dict
;
10297 route_dict
= CFDictionaryCreateMutable(NULL
, 0,
10298 &kCFTypeDictionaryKeyCallBacks
,
10299 &kCFTypeDictionaryValueCallBacks
);
10300 dict_add_string(route_dict
, kSCPropNetIPv6RouteDestinationAddress
,
10302 dict_add_int(route_dict
, kSCPropNetIPv6PrefixLength
,
10303 scan
->prefix_length
);
10304 dict_add_string(route_dict
, kSCPropNetIPv6RouteGatewayAddress
,
10306 dict_add_string(route_dict
, kSCPropNetIPv6RouteInterfaceName
,
10308 CFArrayAppendValue(route_list
, route_dict
);
10309 CFRelease(route_dict
);
10311 CFDictionarySetValue(dict
, prop_name
, route_list
);
10312 CFRelease(route_list
);
10317 array_add_string(CFMutableArrayRef array
, const char * c_str
)
10321 str
= CFStringCreateWithCString(NULL
,
10323 kCFStringEncodingUTF8
);
10324 CFArrayAppendValue(array
, str
);
10330 array_add_int(CFMutableArrayRef array
, int int_val
)
10334 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10335 CFArrayAppendValue(array
, num
);
10341 dict_add_ipv6_addressing(CFMutableDictionaryRef dict
,
10342 IPv6AddressRef list
, int list_count
)
10344 CFMutableArrayRef addr
= NULL
;
10345 CFMutableArrayRef dest
= NULL
;
10347 CFMutableArrayRef prefix
= NULL
;
10348 IPv6AddressRef scan
;
10350 if (list
== NULL
|| list_count
== 0) {
10353 for (i
= 0, scan
= list
; i
< list_count
; i
++, scan
++) {
10354 if (scan
->addr
!= NULL
) {
10355 if (addr
== NULL
) {
10356 addr
= CFArrayCreateMutable(NULL
, list_count
,
10357 &kCFTypeArrayCallBacks
);
10359 array_add_string(addr
, scan
->addr
);
10361 if (scan
->prefix_length
>= 0) {
10362 if (prefix
== NULL
) {
10363 prefix
= CFArrayCreateMutable(NULL
, list_count
,
10364 &kCFTypeArrayCallBacks
);
10366 array_add_int(prefix
, scan
->prefix_length
);
10368 if (scan
->dest
!= NULL
) {
10369 if (dest
== NULL
) {
10370 dest
= CFArrayCreateMutable(NULL
, list_count
,
10371 &kCFTypeArrayCallBacks
);
10373 array_add_string(dest
, scan
->dest
);
10376 if (addr
!= NULL
) {
10377 CFDictionarySetValue(dict
, kSCPropNetIPv6Addresses
, addr
);
10380 if (dest
!= NULL
) {
10381 CFDictionarySetValue(dict
, kSCPropNetIPv6DestAddresses
, dest
);
10384 if (prefix
!= NULL
) {
10385 CFDictionarySetValue(dict
, kSCPropNetIPv6PrefixLength
, prefix
);
10391 static CFDictionaryRef
10392 make_IPv6_dict(IPv6ServiceContentsRef t
)
10394 CFMutableDictionaryRef dict
;
10396 dict
= CFDictionaryCreateMutable(NULL
, 0,
10397 &kCFTypeDictionaryKeyCallBacks
,
10398 &kCFTypeDictionaryValueCallBacks
);
10399 dict_add_ipv6_addressing(dict
, t
->addr
, t
->addr_count
);
10400 dict_add_string(dict
, kSCPropNetIPv6Router
, t
->router
);
10401 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
10402 dict_insert_v6_routes(dict
, kSCPropNetIPv6AdditionalRoutes
,
10403 t
->additional_routes
, t
->additional_routes_count
);
10404 dict_insert_v6_routes(dict
, kSCPropNetIPv6ExcludedRoutes
,
10405 t
->excluded_routes
, t
->excluded_routes_count
);
10410 kDirectionForwards
= 0,
10411 kDirectionBackwards
= 1
10415 kLogRouteDisabled
= 0,
10416 kLogRouteEnabled
= 1
10419 static IPv6RouteListRef
10420 make_IPv6RouteList_for_test(IPv6RouteListRef list
,
10421 IPv6ServiceContentsRef test
,
10424 CFDictionaryRef dict
;
10425 IPv6RouteListRef r
;
10427 Rank rank_assertion
= kRankAssertionDefault
;
10428 CFNumberRef rank_assertion_cf
= NULL
;
10429 Boolean rank_assertion_is_set
= FALSE
;
10430 IPv6RouteListRef ret
= NULL
;
10431 IPV6_ROUTES_BUF_DECL(routes
);
10433 dict
= make_IPv6_dict(test
);
10434 if (dict
== NULL
) {
10435 fprintf(stderr
, "make_IPv6_dict failed\n");
10438 if (test
->primary_rank
!= NULL
) {
10440 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
10441 &rank_assertion_is_set
);
10442 if (rank_assertion_is_set
) {
10444 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
10447 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion_cf
, 0);
10448 my_CFRelease(&rank_assertion_cf
);
10450 fprintf(stderr
, "IPv6RouteListCreateWithDictionary failed\n");
10454 if (rank_assertion
== kRankAssertionScoped
) {
10455 rank_assertion
= kRankAssertionNever
;
10457 rank
= RankMake(test
->rank
, rank_assertion
);
10458 if (log_it
== kLogRouteEnabled
10459 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10462 descr
= IPv6RouteListCopyDescription(r
);
10463 SCPrint(TRUE
, stdout
, CFSTR("Adding %@\n"), descr
);
10466 ret
= IPv6RouteListAddRouteList(list
, 1, r
, rank
);
10474 static IPv6RouteListRef
10475 make_IPv6RouteList(IPv6ServiceContentsRef
* test
, Direction direction
,
10478 IPv6RouteListRef ret
= NULL
;
10479 IPv6ServiceContentsRef
* scan
;
10481 switch (direction
) {
10482 case kDirectionBackwards
:
10483 for (scan
= test
; *scan
!= NULL
; scan
++) {
10484 /* find the end of the list */
10486 for (scan
--; scan
>= test
; scan
--) {
10487 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10491 case kDirectionForwards
:
10492 for (scan
= test
; *scan
!= NULL
; scan
++) {
10493 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10497 IPv6RouteListFinalize(ret
);
10501 #define EMPHASIS_CHARS "================="
10504 * Function: routelist_build_test
10506 * Runs through the given set of routes first in the forward direction,
10507 * then again backwards. We should end up with exactly the same set of
10508 * routes at the end.
10511 routelist_build_test(IPv6RouteTestRef test
)
10514 boolean_t ret
= FALSE
;
10515 IPv6RouteListRef routes1
;
10516 IPv6RouteListRef routes2
;
10518 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
10519 EMPHASIS_CHARS
"\n",
10522 routes1
= make_IPv6RouteList(test
->test
, kDirectionForwards
,
10524 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10525 if (routes1
!= NULL
) {
10526 descr
= IPv6RouteListCopyDescription(routes1
);
10527 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10531 routes2
= make_IPv6RouteList(test
->test
, kDirectionBackwards
,
10533 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10534 if (routes2
!= NULL
) {
10535 descr
= IPv6RouteListCopyDescription(routes2
);
10536 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10540 if ((routes1
!= NULL
&& routes2
== NULL
)
10541 || (routes1
== NULL
&& routes2
!= NULL
)) {
10542 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
10543 (routes1
!= NULL
) ? "not " : "",
10544 (routes2
!= NULL
) ? "not " : "");
10546 else if (routes1
!= NULL
&& routes2
!= NULL
) {
10547 /* check if they are different */
10548 if (routes1
->count
!= routes2
->count
) {
10549 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
10550 routes1
->count
, routes2
->count
);
10552 else if (bcmp(routes1
, routes2
,
10553 IPv6RouteListComputeSize(routes1
->count
)) != 0) {
10554 fprintf(stderr
, "routes1 and routes2 are different\n");
10557 printf("routes1 and routes2 are the same\n");
10561 if (routes1
!= NULL
) {
10564 if (routes2
!= NULL
) {
10567 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
10568 EMPHASIS_CHARS
"\n",
10569 test
->name
, ret
? "PASSED" : "FAILED");
10574 apply_test(IPv6RouteTestRef old_test
, IPv6RouteTestRef new_test
)
10576 IPv6RouteListRef new_routes
;
10577 IPv6RouteListRef old_routes
;
10579 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
10580 EMPHASIS_CHARS
"\n",
10581 old_test
->name
, new_test
->name
);
10583 old_routes
= make_IPv6RouteList(old_test
->test
, kDirectionForwards
,
10584 kLogRouteDisabled
);
10585 new_routes
= make_IPv6RouteList(new_test
->test
, kDirectionForwards
,
10586 kLogRouteDisabled
);
10587 if (old_routes
== NULL
) {
10588 printf("No Old Routes\n");
10591 printf("Old routes ('%s') = ", old_test
->name
);
10592 IPv6RouteListPrint(old_routes
);
10595 /* apply the old routes */
10596 IPv6RouteListApply(NULL
, old_routes
, -1);
10597 if (new_routes
== NULL
) {
10598 printf("No New Routes\n");
10601 printf("New Routes ('%s') = ", new_test
->name
);
10602 IPv6RouteListPrint(new_routes
);
10605 /* apply the new routes */
10606 IPv6RouteListApply(old_routes
, new_routes
, -1);
10607 if (old_routes
!= NULL
) {
10610 if (new_routes
!= NULL
) {
10613 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10614 EMPHASIS_CHARS
"\n",
10615 old_test
->name
, new_test
->name
);
10620 main(int argc
, char **argv
)
10622 IPv6RouteTestRef
* test
;
10625 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10626 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10628 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10630 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10631 if (!routelist_build_test(*test
)) {
10632 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10636 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10637 IPv6RouteTestRef
* test2
;
10639 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10640 apply_test(*test
, *test2
);
10641 apply_test(*test2
, *test
);
10648 printf("\nChecking for leaks\n");
10649 sprintf(cmd
, "leaks %d 2>&1", getpid());
10657 #endif /* TEST_IPV6_ROUTELIST */
10659 #ifdef TEST_DNS_ORDER
10661 #define kProtocolFlagsIPv4v6 (kProtocolFlagsIPv4 | kProtocolFlagsIPv6)
10663 #define V4_ADDR_LOOP CFSTR("127.0.0.1")
10664 #define V4_ADDR_1 CFSTR("192.168.1.1")
10665 #define V4_ADDR_2 CFSTR("192.168.1.2")
10666 #define V4_ADDR_3 CFSTR("8.8.8.8")
10667 #define V4_ADDR_4 CFSTR("8.8.4.4")
10669 #define V6_ADDR_LOOP CFSTR("::1")
10670 #define V6_ADDR_1 CFSTR("fe80::0123:4567:89ab:cdef%en0")
10671 #define V6_ADDR_2 CFSTR("fd00::2acf:e9ff:fe14:8c59")
10672 #define V6_ADDR_3 CFSTR("2001:4860:4860::8888")
10676 const ProtocolFlags flags
;
10677 const CFStringRef server_addrs
[];
10678 } DNSOrderTest
, * DNSOrderTestRef
;
10680 static DNSOrderTest test0a
= {
10682 kProtocolFlagsIPv4
,
10684 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, NULL
10688 static DNSOrderTest test0b
= {
10690 kProtocolFlagsIPv6
,
10692 V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10696 static DNSOrderTest test1a
= {
10698 kProtocolFlagsIPv4v6
,
10700 V4_ADDR_1
, V6_ADDR_1
, NULL
10704 static DNSOrderTest test2a
= {
10706 kProtocolFlagsIPv4v6
,
10708 V4_ADDR_1
, V6_ADDR_2
, NULL
10712 static DNSOrderTest test3a
= {
10714 kProtocolFlagsIPv4v6
,
10716 V4_ADDR_1
, V6_ADDR_3
, NULL
10720 static DNSOrderTest test1b
= {
10722 kProtocolFlagsIPv4v6
,
10724 V4_ADDR_3
, V6_ADDR_1
, NULL
10728 static DNSOrderTest test2b
= {
10730 kProtocolFlagsIPv4v6
,
10732 V4_ADDR_3
, V6_ADDR_2
, NULL
10736 static DNSOrderTest test3b
= {
10738 kProtocolFlagsIPv4v6
,
10740 V4_ADDR_3
, V6_ADDR_3
, NULL
10744 static DNSOrderTest test1c
= {
10746 kProtocolFlagsIPv4v6
,
10748 V6_ADDR_1
, V4_ADDR_1
, NULL
10752 static DNSOrderTest test2c
= {
10754 kProtocolFlagsIPv4v6
,
10756 V6_ADDR_2
, V4_ADDR_1
, NULL
10760 static DNSOrderTest test3c
= {
10762 kProtocolFlagsIPv4v6
,
10764 V6_ADDR_3
, V4_ADDR_1
, NULL
10768 static DNSOrderTest test1d
= {
10770 kProtocolFlagsIPv4v6
,
10772 V6_ADDR_1
, V4_ADDR_3
, NULL
10776 static DNSOrderTest test2d
= {
10778 kProtocolFlagsIPv4v6
,
10780 V6_ADDR_2
, V4_ADDR_3
, NULL
10784 static DNSOrderTest test3d
= {
10786 kProtocolFlagsIPv4v6
,
10788 V6_ADDR_3
, V4_ADDR_3
, NULL
10792 static DNSOrderTest test4
= {
10794 kProtocolFlagsIPv4v6
,
10796 V4_ADDR_LOOP
, V4_ADDR_3
, V6_ADDR_2
, NULL
10800 static DNSOrderTest test5
= {
10802 kProtocolFlagsIPv4v6
,
10804 V4_ADDR_3
, V6_ADDR_LOOP
, V6_ADDR_2
, NULL
10808 static DNSOrderTest test6
= {
10810 kProtocolFlagsIPv4v6
,
10812 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10816 static DNSOrderTest test7
= {
10818 kProtocolFlagsIPv4v6
,
10820 V4_ADDR_1
, V6_ADDR_1
, V4_ADDR_3
, V6_ADDR_2
, NULL
10824 static DNSOrderTestRef dns_order_tests
[] = {
10826 &test1a
, &test2a
, &test3a
,
10827 &test1b
, &test2b
, &test3b
,
10828 &test1c
, &test2c
, &test3c
,
10829 &test1d
, &test2d
, &test3d
,
10837 #define EMPHASIS_CHARS "================="
10840 apply_order(CFArrayRef servers
, ProtocolFlags flags
)
10842 CFArrayRef ordered_servers
;
10844 ordered_servers
= order_dns_servers(servers
, flags
);
10845 if (ordered_servers
!= NULL
) {
10846 SCPrint(TRUE
, stdout
, CFSTR("After :\n%@\n"), ordered_servers
);
10847 CFRelease(ordered_servers
);
10849 printf("FAIL: No ordered servers\n");
10856 apply_test(DNSOrderTestRef test
)
10858 CFMutableArrayRef servers
;
10860 printf("\n" EMPHASIS_CHARS
"> '%s' Begin <" EMPHASIS_CHARS
"\n\n", test
->name
);
10862 servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
10863 for (int i
= 0; test
->server_addrs
[i
] != NULL
; i
++) {
10864 CFStringRef server_addr
= test
->server_addrs
[i
];
10866 CFArrayAppendValue(servers
, server_addr
);
10869 SCPrint(TRUE
, stdout
, CFSTR("Before :\n%@\n"), servers
);
10871 apply_order(servers
, test
->flags
);
10873 CFRelease(servers
);
10879 main(int argc
, char **argv
)
10882 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10883 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10885 S_IPMonitor_debug
= (uint32
)strtoul(argv
[1], NULL
, 0);
10888 for (DNSOrderTestRef
* test
= dns_order_tests
; *test
!= NULL
; test
++) {
10895 printf("\nChecking for leaks\n");
10896 sprintf(cmd
, "leaks %d 2>&1", getpid());
10905 #endif /* TEST_DNS_ORDER */