2 * Copyright (c) 2000-2020 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 * - decides which interface will be made the "primary" interface,
27 * that is, the one with the default route assigned
31 * Modification History
33 * July 19, 2000 Dieter Siegmund (dieter@apple.com)
36 * November 15, 2000 Dieter Siegmund (dieter@apple.com)
37 * - changed to use new configuration model
39 * March 19, 2001 Dieter Siegmund (dieter@apple.com)
40 * - use service state instead of interface state
42 * July 16, 2001 Allan Nathanson (ajn@apple.com)
43 * - update to public SystemConfiguration.framework APIs
45 * August 28, 2001 Dieter Siegmund (dieter@apple.com)
46 * - specify the interface name when installing the default route
47 * - this ensures that default traffic goes to the highest priority
48 * service when multiple interfaces are configured to be on the same subnet
50 * September 16, 2002 Dieter Siegmund (dieter@apple.com)
51 * - don't elect a link-local service to be primary unless it's the only
52 * one that's available
54 * July 16, 2003 Dieter Siegmund (dieter@apple.com)
55 * - modifications to support IPv6
56 * - don't elect a service to be primary if it doesn't have a default route
58 * July 29, 2003 Dieter Siegmund (dieter@apple.com)
59 * - support installing a default route to a router that's not on our subnet
61 * March 22, 2004 Allan Nathanson (ajn@apple.com)
62 * - create expanded DNS configuration
64 * June 20, 2006 Allan Nathanson (ajn@apple.com)
65 * - add SMB configuration
67 * December 5, 2007 Dieter Siegmund (dieter@apple.com)
68 * - added support for multiple scoped routes
70 * November 13, 2013 Dieter Siegmund (dieter@apple.com)
71 * - added generic IPv4 routing support
78 #include <sys/fcntl.h>
79 #include <sys/ioctl.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <net/route.h>
84 #include <net/if_dl.h>
85 #include <netinet/in.h>
86 #include <netinet/icmp6.h>
87 #include <netinet6/in6_var.h>
88 #include <netinet6/nd6.h>
89 #include <nw/private.h>
90 #include <arpa/inet.h>
91 #include <sys/sysctl.h>
94 #include <mach/mach_time.h>
95 #include <dispatch/dispatch.h>
96 #include <CommonCrypto/CommonDigest.h>
98 #include "ip_plugin.h"
100 #include <SystemConfiguration/SystemConfiguration.h>
101 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
102 #include <SystemConfiguration/SCValidation.h>
103 #include <SystemConfiguration/scprefs_observer.h>
104 #include <SystemConfiguration/SCPrivate.h>
105 #include "SCNetworkReachabilityInternal.h"
106 #include "SCNetworkSignaturePrivate.h"
108 #include "dnsinfo_server.h"
110 #include <ppp/PPPControllerPriv.h>
113 #include <dns_sd_private.h>
115 #include <network_information.h>
116 #include "network_state_information_priv.h"
117 #include "network_state_information_logging.h"
118 #include "network_information_server.h"
119 #include <ppp/ppp_msg.h>
121 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
122 #include "set-hostname.h"
123 #include "nat64-configuration.h"
124 #include "agent-monitor.h"
125 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
127 #include "dns-configuration.h"
128 #include "proxy-configuration.h"
130 #if !TARGET_OS_IPHONE
131 #include "smb-configuration.h"
132 #endif /* !TARGET_OS_IPHONE */
134 #define kLoopbackInterface "lo0"
135 #define EROUTENOTAPPLIED 1001
137 typedef CF_ENUM(uint8_t, ProtocolFlags
) {
138 kProtocolFlagsNone
= 0x0,
139 kProtocolFlagsIPv4
= 0x1,
140 kProtocolFlagsIPv6
= 0x2
144 kDebugFlag1
= 0x00000001,
145 kDebugFlag2
= 0x00000002,
146 kDebugFlag4
= 0x00000004,
147 kDebugFlag8
= 0x00000008,
148 kDebugFlagDefault
= kDebugFlag1
,
149 kDebugFlagAll
= 0xffffffff
152 typedef unsigned int IFIndex
; /* interface index */
154 static dispatch_queue_t
__network_change_queue(void);
161 __private_extern__ os_log_t
162 __log_IPMonitor(void)
164 static os_log_t log
= NULL
;
167 log
= os_log_create("com.apple.SystemConfiguration", "IPMonitor");
174 #pragma mark interface index
177 #ifndef TEST_ROUTELIST
179 #define ROUTELIST_DEBUG(flag, fmt, ...)
181 static struct if_nameindex
* S_if_nameindex_cache
;
183 static dispatch_queue_t
184 __my_if_nametoindex_queue()
186 static dispatch_once_t once
;
187 static dispatch_queue_t q
;
189 dispatch_once(&once
, ^{
190 q
= dispatch_queue_create("my_if_nametoindex queue", NULL
);
196 __private_extern__ IFIndex
197 my_if_nametoindex(const char * ifname
)
199 __block IFIndex idx
= 0;
201 dispatch_sync(__my_if_nametoindex_queue(), ^{
202 struct if_nameindex
* scan
;
204 if (S_if_nameindex_cache
== NULL
) {
205 idx
= if_nametoindex(ifname
);
208 for (scan
= S_if_nameindex_cache
;
209 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
211 if (strcmp(scan
->if_name
, ifname
) == 0) {
212 idx
= scan
->if_index
;
221 __private_extern__
const char *
222 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
224 __block
const char * name
= NULL
;
226 dispatch_sync(__my_if_nametoindex_queue(), ^{
227 struct if_nameindex
* scan
;
229 if (S_if_nameindex_cache
== NULL
) {
230 name
= if_indextoname(idx
, if_name
);
233 for (scan
= S_if_nameindex_cache
;
234 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
236 if (scan
->if_index
== idx
) {
238 strlcpy(if_name
, scan
->if_name
, IFNAMSIZ
);
248 my_if_freenameindex(void)
250 dispatch_sync(__my_if_nametoindex_queue(), ^{
251 if (S_if_nameindex_cache
!= NULL
) {
252 if_freenameindex(S_if_nameindex_cache
);
253 S_if_nameindex_cache
= NULL
;
261 my_if_nameindex(void)
263 my_if_freenameindex();
264 dispatch_sync(__my_if_nametoindex_queue(), ^{
265 S_if_nameindex_cache
= if_nameindex();
272 #else /* TEST_ROUTELIST */
274 #define ROUTELIST_DEBUG(flags, format, ...) { if (((S_IPMonitor_debug & (flags)) != 0)) printf((format), ## __VA_ARGS__ ); }
277 static const char * * list
;
278 static int list_count
;
279 static int list_size
;
281 __private_extern__ IFIndex
282 my_if_nametoindex(const char * ifname
)
289 list
= (const char * *)malloc(sizeof(*list
) * list_size
);
290 list
[0] = strdup("");
291 list
[1] = strdup(kLoopbackInterface
);
296 for (i
= 1; i
< list_count
; i
++) {
297 if (strcmp(list
[i
], ifname
) == 0) {
303 if (list_count
== list_size
) {
305 list
= (const char * *)realloc(list
, sizeof(*list
) * list_size
);
307 list
[list_count
] = strdup(ifname
);
314 __private_extern__
const char *
315 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
317 const char * name
= NULL
;
319 if (idx
< list_count
) {
321 strlcpy(if_name
, list
[idx
], IFNAMSIZ
);
327 my_if_nameindex(void)
332 my_if_freenameindex(void)
336 #endif /* TEST_ROUTELIST */
339 my_if_indextoname2(IFIndex ifindex
, char ifname
[IFNAMSIZ
])
344 if (my_if_indextoname(ifindex
, ifname
) == NULL
) {
345 snprintf(ifname
, IFNAMSIZ
, "[%d]", ifindex
);
357 idx
= my_if_nametoindex(kLoopbackInterface
);
367 * Property: kServiceOptionRankAssertion
369 * Key used in the service options dictionary to hold the RankAssertion
370 * derived from the kSCPropNetServicePrimaryRank string.
372 #define kServiceOptionRankAssertion CFSTR("RankAssertion") /* number */
375 * Property: kIPIsCoupled
377 * Used to indicate that the IPv4 and IPv6 services are coupled.
378 * Neither the IPv4 part nor the IPv6 part of a coupled service
379 * may become primary if IPv4 or IPv6 is primary for another interface.
381 * For example, if the service over en3 is "coupled" and has IPv6,
382 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible
383 * to become primary for IPv6.
385 #define kIPIsCoupled CFSTR("IPIsCoupled")
387 #define PPP_PREFIX "ppp"
389 #define IP_FORMAT "%d.%d.%d.%d"
390 #define IP_CH(ip) ((u_char *)(ip))
391 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
393 static Boolean S_bundle_logging_verbose
;
396 * IPv4 Route management
399 typedef CF_ENUM(uint16_t, RouteFlags
) {
400 kRouteFlagsIsScoped
= 0x0001,
401 kRouteFlagsHasGateway
= 0x0002,
402 kRouteFlagsIsHost
= 0x0004,
403 kRouteFlagsIsNULL
= 0x0008,
404 kRouteFlagsKernelManaged
= 0x0010
407 typedef CF_ENUM(uint16_t, ControlFlags
) {
408 kControlFlagsProcessed
= 0x0001,
409 kControlFlagsAdded
= 0x0002,
410 kControlFlagsForce
= 0x0004,
413 #if !TARGET_OS_SIMULATOR
414 static inline ControlFlags
415 ControlFlagsPreserve(ControlFlags flags
)
417 /* only preserve the "processed" and "added" flags */
418 return (flags
& (kControlFlagsProcessed
| kControlFlagsAdded
));
420 #endif /* !TARGET_OS_SIMULATOR */
422 #define ROUTE_COMMON \
425 IFIndex exclude_ifindex; \
428 ControlFlags control_flags;
434 #define PREFIX_LENGTH_IN_CLASSC 24
435 #define PREFIX_LENGTH_IN_CLASSD 4
441 struct in_addr gateway
;
443 } IPv4Route
, * IPv4RouteRef
;
447 struct in6_addr dest
;
448 struct in6_addr gateway
;
450 } IPv6Route
, * IPv6RouteRef
;
452 typedef CF_ENUM(uint16_t, RouteListFlags
) {
453 kRouteListFlagsExcludeNWI
= 0x0001,
454 kRouteListFlagsHasDefault
= 0x0002,
455 kRouteListFlagsScopedOnly
= 0x0004
458 #define ROUTELIST_COMMON \
461 RouteListFlags flags;
465 } RouteListCommon
, * RouteListRef
;
469 IPv4Route list
[1]; /* variable length */
470 } IPv4RouteList
, * IPv4RouteListRef
;
474 IPv6Route list
[1]; /* variable length */
475 } IPv6RouteList
, * IPv6RouteListRef
;
490 * Election Information
491 * - information about the current best services
499 struct sockaddr_in v4
;
500 struct sockaddr_in6 v6
;
503 typedef struct Candidate
{
504 CFStringRef serviceID
;
507 boolean_t ip_is_coupled
;
508 boolean_t ineligible
;
509 SCNetworkReachabilityFlags reachability_flags
;
511 in_sockaddr vpn_server_addr
;
512 CFStringRef signature
;
513 } Candidate
, * CandidateRef
;
515 typedef struct ElectionResults
{
519 Candidate candidates
[1];
520 } ElectionResults
, * ElectionResultsRef
;
522 static __inline__
size_t
523 ElectionResultsComputeSize(unsigned int n
)
525 return (offsetof(ElectionResults
, candidates
[n
]));
532 static __inline__ Rank
533 RankMake(uint32_t service_index
, Rank primary_rank
)
535 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
539 InterfaceRankGetRankAssertion(CFNumberRef rank_cf
, Boolean
* ret_is_set
)
541 SCNetworkServicePrimaryRank if_rank
;
542 Boolean is_set
= FALSE
;
543 Rank rank
= kRankAssertionDefault
;
546 && CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &if_rank
)
547 && if_rank
!= kSCNetworkServicePrimaryRankDefault
) {
548 if (if_rank
== kSCNetworkServicePrimaryRankFirst
) {
549 rank
= kRankAssertionFirst
;
552 rank
= RANK_ASSERTION_MAKE(if_rank
);
556 if (ret_is_set
!= NULL
) {
557 *ret_is_set
= is_set
;
563 PrimaryRankGetRankAssertion(CFStringRef rank_str
, Boolean
* is_set
)
566 const CFStringRef
* name
;
569 { &kSCValNetServicePrimaryRankFirst
, kRankAssertionFirst
},
570 { &kSCValNetServicePrimaryRankLast
, kRankAssertionLast
},
571 { &kSCValNetServicePrimaryRankNever
, kRankAssertionNever
},
572 { &kSCValNetServicePrimaryRankScoped
, kRankAssertionScoped
}
575 if (rank_str
!= NULL
) {
576 for (size_t i
= 0; i
< countof(values
); i
++) {
577 if (CFEqual(rank_str
, *(values
[i
].name
))) {
578 if (is_set
!= NULL
) {
581 return (values
[i
].rank_assertion
);
585 if (is_set
!= NULL
) {
588 return (kRankAssertionDefault
);
591 /* SCDynamicStore session */
592 static SCDynamicStoreRef S_session
= NULL
;
594 /* debug output flags */
595 static uint32_t S_IPMonitor_debug
= 0;
596 static Boolean S_IPMonitor_verbose
= FALSE
;
598 /* are we netbooted? If so, don't touch the default route */
599 static boolean_t S_netboot
= FALSE
;
601 /* dictionary to hold per-service state: key is the serviceID */
602 static CFMutableDictionaryRef S_service_state_dict
;
604 /* dictionaries to hold per-service rank: key is the serviceID */
605 static CFMutableDictionaryRef S_ipv4_service_rank_dict
;
606 static CFMutableDictionaryRef S_ipv6_service_rank_dict
;
608 /* dictionary to hold per-interface rank information: key is the ifname */
609 static CFDictionaryRef S_if_rank_dict
;
611 /* if set, a PPP interface overrides the primary */
612 static boolean_t S_ppp_override_primary
= FALSE
;
614 /* the current primary serviceID's */
615 static CFStringRef S_primary_ipv4
= NULL
;
616 static CFStringRef S_primary_ipv6
= NULL
;
617 static CFStringRef S_primary_dns
= NULL
;
618 static CFStringRef S_primary_proxies
= NULL
;
620 /* the current election results */
621 static ElectionResultsRef S_ipv4_results
;
622 static ElectionResultsRef S_ipv6_results
;
624 static CFStringRef S_state_global_ipv4
= NULL
;
625 static CFStringRef S_state_global_ipv6
= NULL
;
626 static CFStringRef S_state_global_dns
= NULL
;
627 static CFStringRef S_state_global_proxies
= NULL
;
628 static CFStringRef S_state_service_prefix
= NULL
;
629 static CFStringRef S_setup_global_ipv4
= NULL
;
630 static CFStringRef S_setup_service_prefix
= NULL
;
632 static CFStringRef S_interface_delegation_prefix
= NULL
;
634 static CFStringRef S_multicast_resolvers
= NULL
;
635 static CFStringRef S_private_resolvers
= NULL
;
637 #if !TARGET_OS_SIMULATOR
638 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
639 static IPv6RouteListRef S_ipv6_routelist
= NULL
;
640 #endif /* !TARGET_OS_SIMULATOR */
642 static boolean_t S_append_state
= FALSE
;
644 static CFDictionaryRef S_dns_dict
= NULL
;
646 static Boolean S_dnsinfo_synced
= TRUE
;
648 static nwi_state_t S_nwi_state
= NULL
;
649 static Boolean S_nwi_synced
= TRUE
;
651 static CFDictionaryRef S_proxies_dict
= NULL
;
653 // Note: access should be gated with __network_change_queue()
654 static uint32_t S_network_change_needed
= 0;
655 #define NETWORK_CHANGE_NET 1<<0
656 #define NETWORK_CHANGE_DNS 1<<1
657 #define NETWORK_CHANGE_PROXY 1<<2
658 #if !TARGET_OS_IPHONE
659 #define NETWORK_CHANGE_SMB 1<<3
660 #endif /* !TARGET_OS_IPHONE */
661 #define NETWORK_CHANGE_NAT64 1<<4
662 static struct timeval S_network_change_start
;
663 static Boolean S_network_change_timeout
= FALSE
;
664 static dispatch_source_t S_network_change_timer
= NULL
;
666 #if !TARGET_OS_IPHONE
667 static CFStringRef S_primary_smb
= NULL
;
668 static CFStringRef S_state_global_smb
= NULL
;
669 static CFDictionaryRef S_smb_dict
= NULL
;
670 #endif /* !TARGET_OS_IPHONE */
672 #if !TARGET_OS_IPHONE
673 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
674 #endif /* !TARGET_OS_IPHONE */
677 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
678 #endif /* KERN_NETBOOT */
681 ** entityType*, GetEntityChanges*
682 ** - definitions for the entity types we handle
689 #if !TARGET_OS_IPHONE
691 #endif /* !TARGET_OS_IPHONE */
693 kEntityTypeTransientStatus
,
694 kEntityTypeServiceOptions
= 31
697 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
698 &kSCEntNetIPv4
, /* 0 */
699 &kSCEntNetIPv6
, /* 1 */
700 &kSCEntNetDNS
, /* 2 */
701 &kSCEntNetProxies
, /* 3 */
702 #if !TARGET_OS_IPHONE
703 &kSCEntNetSMB
, /* 4 */
704 #endif /* !TARGET_OS_IPHONE */
709 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
);
711 static __inline__
char
714 return ((af
== AF_INET
) ? '4' : '6');
717 static __inline__
char
718 ipvx_other_char(int af
)
720 return ((af
== AF_INET
) ? '6' : '4');
724 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
726 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
727 * Routes CFData containing IPv4RouteList/IPv6RouteList
728 * Service dictionary containing kSCEntNetIPv[46] service entity
730 #define kIPDictRoutes CFSTR("Routes") /* data */
731 #define kIPDictService CFSTR("Service") /* dict */
733 static CFDictionaryRef
734 ipdict_create(CFDictionaryRef dict
, CFDataRef routes_data
)
739 keys
[0] = kIPDictService
;
741 keys
[1] = kIPDictRoutes
;
742 values
[1] = routes_data
;
743 return (CFDictionaryCreate(NULL
,
744 (const void * *)keys
,
747 &kCFTypeDictionaryKeyCallBacks
,
748 &kCFTypeDictionaryValueCallBacks
));
752 ipdict_get_routelist(CFDictionaryRef dict
)
754 void * routes_list
= NULL
;
759 routes
= CFDictionaryGetValue(dict
, kIPDictRoutes
);
760 if (routes
!= NULL
) {
761 routes_list
= (void *)CFDataGetBytePtr(routes
);
764 return (routes_list
);
767 static CFDictionaryRef
768 ipdict_get_service(CFDictionaryRef dict
)
770 CFDictionaryRef ip_dict
= NULL
;
773 ip_dict
= CFDictionaryGetValue(dict
, kIPDictService
);
779 ipdict_get_ifname(CFDictionaryRef dict
)
781 CFStringRef ifname
= NULL
;
782 CFDictionaryRef ip_dict
;
784 ip_dict
= ipdict_get_service(dict
);
785 if (ip_dict
!= NULL
) {
786 ifname
= CFDictionaryGetValue(ip_dict
, kSCPropInterfaceName
);
791 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
792 CFDictionaryRef state_dict
,
793 CFDictionaryRef setup_dict
,
794 CFDictionaryRef info
);
795 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
797 static GetEntityChangesFunc get_ipv4_changes
;
798 static GetEntityChangesFunc get_ipv6_changes
;
799 static GetEntityChangesFunc get_dns_changes
;
800 static GetEntityChangesFunc get_proxies_changes
;
801 #if !TARGET_OS_IPHONE
802 static GetEntityChangesFunc get_smb_changes
;
803 #endif /* !TARGET_OS_IPHONE */
805 static __inline__
void
806 my_CFRelease(void * t
)
808 void * * obj
= (void * *)t
;
818 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
821 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
823 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
824 get_ipv4_changes
, /* 0 */
825 get_ipv6_changes
, /* 1 */
826 get_dns_changes
, /* 2 */
827 get_proxies_changes
,/* 3 */
828 #if !TARGET_OS_IPHONE
829 get_smb_changes
, /* 4 */
830 #endif /* !TARGET_OS_IPHONE */
835 ** - mechanism to do an atomic update of the SCDynamicStore
836 ** when the content needs to be changed across multiple functions
839 CFMutableArrayRef notify
;
840 CFMutableArrayRef remove
;
841 CFMutableDictionaryRef set
;
842 } keyChangeList
, * keyChangeListRef
;
845 keyChangeListInit(keyChangeListRef keys
)
847 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
848 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
849 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
850 &kCFTypeDictionaryKeyCallBacks
,
851 &kCFTypeDictionaryValueCallBacks
);
856 keyChangeListFree(keyChangeListRef keys
)
858 my_CFRelease(&keys
->notify
);
859 my_CFRelease(&keys
->remove
);
860 my_CFRelease(&keys
->set
);
865 keyChangeListActive(keyChangeListRef keys
)
867 return ((CFDictionaryGetCount(keys
->set
) > 0) ||
868 (CFArrayGetCount(keys
->remove
) > 0) ||
869 (CFArrayGetCount(keys
->notify
) > 0));
873 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
875 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
880 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
882 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
883 CFDictionaryRemoveValue(keys
->set
, key
);
888 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
890 my_CFArrayRemoveValue(keys
->remove
, key
);
891 CFDictionarySetValue(keys
->set
, key
, value
);
896 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
898 CFArrayRef notify
= keys
->notify
;
899 CFArrayRef remove
= keys
->remove
;
900 CFDictionaryRef set
= keys
->set
;
902 if (CFArrayGetCount(notify
) == 0) {
905 if (CFArrayGetCount(remove
) == 0) {
908 if (CFDictionaryGetCount(set
) == 0) {
911 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
914 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
916 my_log(LOG_DEBUG
, "Setting:\n%@", set
);
918 if (remove
!= NULL
) {
919 my_log(LOG_DEBUG
, "Removing:\n%@", remove
);
921 if (notify
!= NULL
) {
922 my_log(LOG_DEBUG
, "Notifying:\n%@", notify
);
925 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
938 mib
[1] = KERN_NETBOOT
;
939 len
= sizeof(netboot
);
940 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
944 static int rtm_seq
= 0;
946 #if !TARGET_OS_SIMULATOR
948 open_routing_socket(void)
952 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
953 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
958 static __inline__
int
959 inet6_dgram_socket(void)
963 sockfd
= socket(AF_INET6
, SOCK_DGRAM
, 0);
965 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
972 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
974 struct in6_defrouter dr
;
975 struct sockaddr_in6
* sin6
;
977 memset(&dr
, 0, sizeof(dr
));
979 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
980 sin6
->sin6_family
= AF_INET6
;
981 sin6
->sin6_addr
= *addr
;
983 dr
.if_index
= if_index
;
984 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
988 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
990 struct in6_defrouter dr
;
991 struct sockaddr_in6
* sin6
;
993 memset(&dr
, 0, sizeof(dr
));
995 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
996 sin6
->sin6_family
= AF_INET6
;
997 sin6
->sin6_addr
= *addr
;
998 dr
.if_index
= if_index
;
999 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
1002 #endif /* !TARGET_OS_SIMULATOR */
1005 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
1007 CFIndex n
= CFArrayGetCount(arr
);
1009 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
1012 CFArrayAppendValue(arr
, new);
1017 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
1021 i
= CFArrayGetFirstIndexOfValue(arr
,
1022 CFRangeMake(0, CFArrayGetCount(arr
)),
1024 if (i
!= kCFNotFound
) {
1025 CFArrayRemoveValueAtIndex(arr
, i
);
1031 my_CFArrayCreateCombinedArray(CFArrayRef array1
, CFArrayRef array2
)
1033 CFMutableArrayRef combined
;
1035 combined
= CFArrayCreateMutableCopy(NULL
, 0, array1
);
1036 CFArrayAppendArray(combined
,
1038 CFRangeMake(0, CFArrayGetCount(array2
)));
1042 static CFDictionaryRef
1043 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
1045 if (isA_CFDictionary(dict
) == NULL
) {
1048 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
1052 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
1054 if (isA_CFDictionary(dict
) == NULL
) {
1057 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
1061 my_CFSetAddValue(CFMutableSetRef
* set_p
, CFTypeRef value
)
1063 if (*set_p
== NULL
) {
1064 *set_p
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1066 CFSetAddValue(*set_p
, value
);
1070 my_CFSetContainsValue(CFSetRef set
, CFTypeRef value
)
1075 return (CFSetContainsValue(set
, value
));
1078 #if !TARGET_OS_SIMULATOR
1081 my_CFSetRemoveValue(CFMutableSetRef
* set_p
, CFTypeRef value
)
1083 if (*set_p
== NULL
) {
1086 CFSetRemoveValue(*set_p
, value
);
1087 if (CFSetGetCount(*set_p
) == 0) {
1088 my_CFRelease(set_p
);
1092 typedef CF_ENUM(uint16_t, PLATDiscoveryOption
) {
1093 kPLATDiscoveryOptionStart
,
1094 kPLATDiscoveryOptionUpdate
,
1095 kPLATDiscoveryOptionCancel
1098 // Note: must only accessed on __network_change_queue()
1099 static CFMutableSetRef S_nat64_cancel_prefix_requests
;
1100 static CFMutableSetRef S_nat64_prefix_updates
;
1101 static CFMutableSetRef S_nat64_prefix_requests
;
1104 set_plat_discovery_locked(PLATDiscoveryOption option
, CFStringRef interface
)
1107 case kPLATDiscoveryOptionStart
:
1108 my_log(LOG_DEBUG
, "NAT64 Start %@", interface
);
1109 my_CFSetAddValue(&S_nat64_prefix_requests
, interface
);
1110 my_CFSetRemoveValue(&S_nat64_prefix_updates
, interface
);
1111 my_CFSetRemoveValue(&S_nat64_cancel_prefix_requests
, interface
);
1113 case kPLATDiscoveryOptionUpdate
:
1114 my_log(LOG_DEBUG
, "NAT64 Update %@", interface
);
1115 if (!my_CFSetContainsValue(S_nat64_prefix_requests
, interface
)) {
1116 my_CFSetAddValue(&S_nat64_prefix_updates
, interface
);
1118 my_CFSetRemoveValue(&S_nat64_cancel_prefix_requests
, interface
);
1120 case kPLATDiscoveryOptionCancel
:
1121 my_log(LOG_DEBUG
, "NAT64 Cancel %@", interface
);
1122 my_CFSetRemoveValue(&S_nat64_prefix_requests
, interface
);
1123 my_CFSetRemoveValue(&S_nat64_prefix_updates
, interface
);
1124 my_CFSetAddValue(&S_nat64_cancel_prefix_requests
, interface
);
1132 set_plat_discovery(PLATDiscoveryOption option
, CFStringRef interface
)
1134 CFRetain(interface
);
1135 dispatch_async(__network_change_queue(), ^{
1136 set_plat_discovery_locked(option
, interface
);
1137 CFRelease(interface
);
1142 #endif /* !TARGET_OS_SIMULATOR */
1145 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, size_t addr_size
)
1149 if (isA_CFString(str
) == NULL
) {
1155 if (addr_size
< sizeof(struct in_addr
)) {
1160 if (addr_size
< sizeof(struct in6_addr
)) {
1167 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
1168 if (inet_pton(family
, buf
, addr
) == 1) {
1172 memset(addr
, 0, addr_size
);
1178 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
1180 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
1185 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
1187 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
1191 cfnumber_to_int(CFNumberRef num
, int * int_val
)
1193 if (isA_CFNumber(num
) == NULL
) {
1196 return (CFNumberGetValue(num
, kCFNumberIntType
, int_val
));
1199 static CF_RETURNS_RETAINED CFStringRef
1200 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
1202 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1203 kSCDynamicStoreDomainSetup
,
1208 static CF_RETURNS_RETAINED CFStringRef
1209 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
1211 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1212 kSCDynamicStoreDomainState
,
1218 interface_entity_key_copy(CFStringRef ifname
, CFStringRef entity
)
1220 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1221 kSCDynamicStoreDomainState
,
1226 static CFDictionaryRef
1227 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1230 CFStringRef setup_key
;
1231 CFDictionaryRef setup_dict
;
1233 setup_key
= setup_service_key(serviceID
, entity
);
1234 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
1235 my_CFRelease(&setup_key
);
1236 return (setup_dict
);
1239 static CFDictionaryRef
1240 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1243 CFStringRef state_key
;
1244 CFDictionaryRef state_dict
;
1246 state_key
= state_service_key(serviceID
, entity
);
1247 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
1248 my_CFRelease(&state_key
);
1249 return (state_dict
);
1253 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1257 ip_list
= CFDictionaryGetValue(dict
, prop
);
1258 if (isA_CFArray(ip_list
) != NULL
1259 && CFArrayGetCount(ip_list
) > 0
1260 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1267 dict_get_first_ipv6(CFDictionaryRef dict
, CFStringRef prop
,
1268 struct in6_addr
* ip_p
)
1272 ip_list
= CFDictionaryGetValue(dict
, prop
);
1273 if (isA_CFArray(ip_list
) != NULL
1274 && CFArrayGetCount(ip_list
) > 0
1275 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1282 dict_get_first_int(CFDictionaryRef dict
, CFStringRef prop
,
1287 list
= CFDictionaryGetValue(dict
, prop
);
1288 if (isA_CFArray(list
) != NULL
1289 && CFArrayGetCount(list
) > 0
1290 && cfnumber_to_int(CFArrayGetValueAtIndex(list
, 0), val
)) {
1297 dict_get_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1301 val
= CFDictionaryGetValue(dict
, prop
);
1302 return (cfstring_to_ip(val
, ip_p
));
1306 dict_get_ipv6(CFDictionaryRef dict
, CFStringRef prop
, struct in6_addr
* ip_p
)
1310 val
= CFDictionaryGetValue(dict
, prop
);
1311 return (cfstring_to_ip6(val
, ip_p
));
1315 dict_get_int(CFDictionaryRef dict
, CFStringRef prop
, int * intval
)
1319 val
= CFDictionaryGetValue(dict
, prop
);
1320 return (cfnumber_to_int(val
, intval
));
1324 get_override_primary(CFDictionaryRef dict
)
1328 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
1329 if (isA_CFNumber(override
) != NULL
) {
1332 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
1337 else if (isA_CFBoolean(override
) != NULL
) {
1338 if (CFBooleanGetValue(override
)) {
1350 (*RouteListComputeSize
)(CFIndex n
);
1353 (*RouteIsEqual
)(RouteRef a
, RouteRef b
);
1356 (*RouteApply
)(RouteRef route
, int cmd
, int sockfd
);
1358 typedef const void *
1359 (*RouteGateway
)(RouteRef route
);
1362 (*RouteSetGateway
)(RouteRef route
, const void * address
);
1364 typedef const void *
1365 (*RouteDestination
)(RouteRef route
);
1368 (*RouteSameSubnet
)(RouteRef route
, const void * address
);
1371 (*RouteCopyDescription
)(RouteRef route
);
1374 (*RouteLog
)(int priority
, RouteRef route
, const char * msg
);
1377 RouteListComputeSize list_compute_size
;
1379 RouteIsEqual route_equal
;
1380 RouteApply route_apply
;
1381 RouteGateway route_gateway
;
1382 RouteSetGateway route_set_gateway
;
1383 RouteDestination route_destination
;
1384 RouteSameSubnet route_same_subnet
;
1386 RouteCopyDescription route_copy_description
;
1393 typedef const RouteListInfo
* RouteListInfoRef
;
1396 RouteListInfoRef info
;
1397 RouteListRef old_routes
;
1398 RouteListRef new_routes
;
1401 } RouteListApplyContext
, * RouteListApplyContextRef
;
1405 RouteAddressCompare(RouteListInfoRef info
,
1409 return (memcmp(addr1
, addr2
, info
->address_size
));
1413 RouteCompare(RouteListInfoRef info
,
1414 RouteRef a
, Rank a_rank
,
1415 RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
1418 RouteDestination route_destination
;
1419 RouteCopyDescription route_copy_description
;
1422 route_destination
= info
->route_destination
;
1423 route_copy_description
= info
->route_copy_description
;
1424 cmp
= RouteAddressCompare(info
,
1425 (*route_destination
)(a
),
1426 (*route_destination
)(b
));
1428 cmp
= a
->prefix_length
- b
->prefix_length
;
1430 int index_cmp
= a
->ifindex
- b
->ifindex
;
1432 if (index_cmp
== 0) {
1435 else if ((a
->ifindex
== 0 || b
->ifindex
== 0)
1436 && (a
->flags
& kRouteFlagsIsScoped
) == 0
1437 && (b
->flags
& kRouteFlagsIsScoped
) == 0) {
1439 * Either of the routes specifies no interface and neither
1440 * route is scoped. Claim they are equal to eliminate the
1447 cmp
= RankCompare(a_rank
, b_rank
);
1454 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
1462 else if (cmp
== 0) {
1468 a_str
= (*route_copy_description
)(a
);
1469 b_str
= (*route_copy_description
)(b
);
1470 my_log(LOG_DEBUG
, "%@ rank 0x%x %c %@ rank 0x%x",
1471 a_str
, a_rank
, ch
, b_str
, b_rank
);
1479 RouteListGetRouteAtIndexSimple(RouteListInfoRef info
, RouteListRef routes
,
1482 return ((void *)routes
+ (*info
->list_compute_size
)(where
));
1486 RouteListGetRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1489 if (routes
->count
== 0
1490 || where
>= routes
->count
) {
1493 return (RouteListGetRouteAtIndexSimple(info
, routes
, where
));
1497 RouteListGetFirstRoute(RouteListInfoRef info
, RouteListRef routes
)
1499 return (RouteListGetRouteAtIndexSimple(info
, routes
, 0));
1502 #if !TARGET_OS_SIMULATOR
1504 RouteListRouteIndex(RouteListInfoRef info
, RouteListRef routes
,
1507 return (((void *)route
1508 - (void *)RouteListGetFirstRoute(info
, routes
))
1509 / info
->element_size
);
1511 #endif /* !TARGET_OS_SIMULATOR */
1514 RouteGetNextRoute(RouteListInfoRef info
, RouteRef route
)
1516 return ((RouteRef
)(((void *)route
) + info
->element_size
));
1520 RouteListAddRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1521 RouteRef this_route
, CFIndex where
)
1523 RouteRef insert_route
;
1525 if (where
== kCFNotFound
) {
1526 /* add it to the end */
1528 = RouteListGetRouteAtIndexSimple(info
, routes
, routes
->count
);
1531 /* make space at [where] */
1532 insert_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1533 memcpy((void *)insert_route
+ info
->element_size
,
1535 info
->element_size
* (routes
->count
- where
));
1537 /* copy the route */
1538 memcpy(insert_route
, this_route
, info
->element_size
);
1540 return (insert_route
);
1544 RouteListRemoveRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1547 if (routes
->count
== 0
1548 || where
>= routes
->count
) {
1552 if (where
== routes
->count
) {
1553 /* last slot, decrementing gets rid of it */
1556 RouteRef remove_route
;
1558 remove_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1559 memcpy(remove_route
,
1560 (void *)remove_route
+ info
->element_size
,
1561 info
->element_size
* (routes
->count
- where
));
1567 * Function: RouteListAddRoute
1570 * Add the given route to the list of routes, eliminating lower-ranked
1571 * duplicates on the same interface, and marking any lower ranked duplicates
1572 * on other interfaces with kRouteFlagsIsScoped.
1574 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1577 * Route list updated with the given route, possibly a different pointer,
1578 * due to using realloc'd memory.
1588 RouteListAddRoute(RouteListInfoRef info
,
1589 RouteListRef routes
, int init_size
,
1590 RouteRef this_route
, Rank this_rank
,
1594 RouteRef first_scan
= NULL
;
1597 Scope scope_which
= kScopeNone
;
1598 CFIndex where
= kCFNotFound
;
1600 if (routes
== NULL
) {
1601 size_t alloc_size
= (*info
->list_compute_size
)(init_size
);
1603 routes
= (RouteListRef
)malloc(alloc_size
);
1604 memset(routes
, 0, alloc_size
);
1605 routes
->size
= init_size
;
1607 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1609 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1611 boolean_t same_dest
;
1613 cmp
= RouteCompare(info
, this_route
, this_rank
, scan
, scan
->rank
,
1615 if (same_dest
&& (first_scan
== NULL
)) {
1619 if (where
== kCFNotFound
) {
1621 && (first_scan
!= NULL
)
1622 && (first_scan
->flags
& kRouteFlagsIsScoped
) == 0) {
1623 if ((scan
->flags
& kRouteFlagsIsScoped
) != 0) {
1624 ROUTELIST_DEBUG(kDebugFlag8
,
1625 "Hit 1: set scope on self\n");
1626 scope_which
= kScopeThis
;
1629 ROUTELIST_DEBUG(kDebugFlag8
,
1630 "Hit 2: set scope on next\n");
1631 scope_which
= kScopeNext
;
1634 /* remember our insertion point, but keep going to find a dup */
1638 else if (cmp
== 0) {
1640 if (where
!= kCFNotFound
1641 && scan
->ifindex
== this_route
->ifindex
1642 && scan
->exclude_ifindex
== 0
1643 && this_route
->exclude_ifindex
== 0) {
1644 /* this route is a duplicate */
1645 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 3: removing [%ld]\n", i
);
1646 RouteListRemoveRouteAtIndex(info
, routes
, i
);
1650 * this_route is "better" than scan if this_route is not excluded
1651 * and scan is excluded or this_route sorts ahead of scan
1653 if (this_route
->exclude_ifindex
== 0
1654 && (scan
->exclude_ifindex
!= 0 || this_rank
< scan
->rank
)) {
1655 IFIndex ifindex
= 0;
1656 boolean_t is_scoped
= FALSE
;
1658 if (scan
->flags
& kRouteFlagsIsScoped
) {
1661 if (this_rank
< scan
->rank
) {
1662 ROUTELIST_DEBUG(kDebugFlag8
,
1663 "Hit 4a: replacing [%ld]"
1664 " rank 0x%x < 0x%x\n",
1665 i
, this_rank
, scan
->rank
);
1668 ROUTELIST_DEBUG(kDebugFlag8
,
1669 "Hit 4b: replacing [%ld] excluded route\n",
1672 if (scan
->ifindex
!= 0) {
1673 ifindex
= scan
->ifindex
;
1675 else if (this_route
->ifindex
!= 0) {
1676 ifindex
= this_route
->ifindex
;
1678 memcpy(scan
, this_route
, info
->element_size
);
1679 scan
->rank
= this_rank
;
1680 scan
->ifindex
= ifindex
;
1681 scan
->exclude_ifindex
= 0;
1683 /* preserve whether route was scoped */
1684 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 5: preserved scope\n");
1685 scan
->flags
|= kRouteFlagsIsScoped
;
1688 scan
->control_flags
|= kControlFlagsForce
;
1696 if (scope_which
== kScopeNone
) {
1697 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 6: set scope on self\n");
1698 scope_which
= kScopeThis
;
1701 #ifdef TEST_ROUTELIST
1702 else if (where
!= kCFNotFound
) {
1703 /* not possible because we maintain a sorted list */
1705 "Hit 7: moved past routes - can't happen\n");
1709 #endif /* TEST_ROUTELIST */
1713 if (routes
->size
== routes
->count
) {
1715 RouteListRef new_routes
;
1718 /* double the size */
1719 old_size
= routes
->size
;
1720 how_many
= old_size
* 2;
1721 new_routes
= (RouteListRef
)
1722 reallocf(routes
, (*info
->list_compute_size
)(how_many
));
1723 if (new_routes
== NULL
) {
1728 ROUTELIST_DEBUG(kDebugFlag8
, "increasing size from %d to %d\n",
1729 old_size
, how_many
);
1730 new_routes
->size
= how_many
;
1731 routes
= new_routes
;
1734 /* add/insert the new route */
1735 this_route
= RouteListAddRouteAtIndex(info
, routes
, this_route
, where
);
1736 this_route
->rank
= this_rank
;
1738 this_route
->control_flags
|= kControlFlagsForce
;
1741 if (RANK_ASSERTION_MASK(this_rank
) == kRankAssertionNever
) {
1742 flags
|= kRouteFlagsIsScoped
;
1744 switch (scope_which
) {
1746 flags
|= kRouteFlagsIsScoped
;
1749 this_route
= RouteListGetRouteAtIndex(info
, routes
, where
+ 1);
1750 flags
|= kRouteFlagsIsScoped
;
1756 if (this_route
!= NULL
&& flags
!= 0) {
1757 this_route
->flags
|= flags
;
1765 * Function: RouteListAddRouteList
1767 * Invoke RouteListAddRoute for each route in the given list
1768 * 'service_routes' combining them into a combined list 'routes'.
1771 * See RouteListAddRoute for more information.
1774 RouteListAddRouteList(RouteListInfoRef info
,
1775 RouteListRef routes
, int init_size
,
1776 RouteListRef service_routes
, Rank rank
,
1782 for (i
= 0, scan
= RouteListGetFirstRoute(info
, service_routes
);
1783 i
< service_routes
->count
;
1784 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1788 && (service_routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
1789 /* only apply rank to first element of the list (default route) */
1793 this_rank
= RANK_INDEX_MASK(rank
) | RANK_ASSERTION_MASK(scan
->rank
);
1795 routes
= RouteListAddRoute(info
, routes
, init_size
, scan
, this_rank
,
1802 RouteAddInterfaceToDescription(RouteRef r
, CFMutableStringRef str
)
1804 char if_name
[IFNAMSIZ
];
1806 if (my_if_indextoname2(r
->ifindex
, if_name
) != NULL
) {
1807 CFStringAppendFormat(str
, NULL
,
1811 if (my_if_indextoname2(r
->exclude_ifindex
, if_name
) != NULL
) {
1812 CFStringAppendFormat(str
, NULL
,
1820 RouteAddFlagsToDescription(RouteRef r
, CFMutableStringRef str
)
1822 if ((r
->flags
& kRouteFlagsIsNULL
) != 0) {
1823 CFStringAppend(str
, CFSTR(" [null]"));
1826 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
1828 switch (rank_assertion
) {
1829 case kRankAssertionFirst
:
1830 CFStringAppend(str
, CFSTR(" [first]"));
1832 case kRankAssertionLast
:
1833 CFStringAppend(str
, CFSTR(" [last]"));
1835 case kRankAssertionNever
:
1836 CFStringAppend(str
, CFSTR(" [never]"));
1841 if ((r
->flags
& kRouteFlagsKernelManaged
) != 0) {
1842 CFStringAppend(str
, CFSTR(" [kern]"));
1844 if ((r
->flags
& kRouteFlagsIsScoped
) != 0) {
1845 CFStringAppend(str
, CFSTR(" [SCOPED]"));
1847 #ifndef TEST_ROUTELIST
1848 if ((r
->control_flags
& kControlFlagsForce
) != 0) {
1849 CFStringAppend(str
, CFSTR(" [force]"));
1856 #if !TARGET_OS_SIMULATOR
1858 RouteListFindRoute(RouteListInfoRef info
, RouteListRef routes
, RouteRef route
)
1861 RouteRef match
= NULL
;
1864 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1866 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1867 if ((*info
->route_equal
)(scan
, route
)) {
1877 kRouteLookupFlagsNone
= 0x0,
1878 kRouteLookupFlagsExcludeInterface
= 0x1
1882 RouteListLookup(RouteListInfoRef info
,
1883 RouteListRef routes
,
1884 const void * address
,
1887 RouteLookupFlags lookup_flags
)
1889 RouteRef best_match
= NULL
;
1893 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1895 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1896 if (scan
->ifindex
== 0 || scan
->exclude_ifindex
!= 0) {
1897 /* ignore exclude routes */
1900 if ((lookup_flags
& kRouteLookupFlagsExcludeInterface
) != 0) {
1901 /* exclude interfaces with the same interface index */
1902 if (ifindex
== scan
->ifindex
) {
1906 else if (ifindex
!= scan
->ifindex
) {
1909 if ((scan
->flags
& kRouteFlagsHasGateway
) != 0
1910 && RouteAddressCompare(info
,
1911 (*info
->route_gateway
)(scan
),
1913 /* skip route whose gateway is the address we're looking for */
1916 if ((scan
->flags
& kRouteFlagsIsHost
) != 0) {
1917 /* if host route and we're looking for an exact match */
1918 if (n_bits
== info
->all_bits_set
1919 && RouteAddressCompare(info
,
1920 (*info
->route_destination
)(scan
),
1922 /* found exact match */
1929 /* verify that address is on the same subnet */
1930 if ((*info
->route_same_subnet
)(scan
, address
) == FALSE
) {
1931 /* different subnet */
1935 if (scan
->prefix_length
== n_bits
) {
1940 if (scan
->prefix_length
> n_bits
) {
1941 /* matched too many bits */
1944 if (best_match
== NULL
1945 || scan
->prefix_length
> best_match
->prefix_length
) {
1949 return (best_match
);
1954 * Function: RouteProcess
1956 * Function to process adding or removing the specified route.
1957 * In the case of adding, that may involve first processing the gateway
1958 * route (recursively).
1961 RouteProcess(RouteRef route
,
1963 RouteListApplyContextRef context
)
1965 RouteLog route_log
= context
->info
->route_log
;
1966 RouteApply route_apply
= context
->info
->route_apply
;
1967 RouteGateway route_gateway
= context
->info
->route_gateway
;
1971 case kRouteCommandAdd
:
1972 if ((route
->control_flags
& kControlFlagsProcessed
) != 0) {
1973 return ((route
->control_flags
& kControlFlagsAdded
) != 0);
1975 route
->control_flags
|= kControlFlagsProcessed
;
1976 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
1978 RouteRef gateway_route
;
1981 = RouteListLookup(context
->info
,
1982 context
->new_routes
,
1983 (*route_gateway
)(route
),
1984 context
->info
->all_bits_set
,
1986 kRouteLookupFlagsNone
);
1987 if (gateway_route
== NULL
) {
1988 (*route_log
)(LOG_NOTICE
, route
, "no gateway route");
1991 #define MAX_RECURSE_DEPTH 10
1992 /* avoid infinite recursion */
1993 if (context
->depth
== MAX_RECURSE_DEPTH
) {
1994 (*route_log
)(LOG_NOTICE
, route
, "routing loop detected, not adding");
1997 /* recurse to add gateway route */
1999 added
= RouteProcess(gateway_route
,
2004 (*route_log
)(LOG_NOTICE
, route
, "failed to add");
2009 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
2010 if (retval
== EEXIST
) {
2011 /* delete and add again */
2012 (void)(*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
2013 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
2018 "failed to add route, %s:",
2020 (*route_log
)(LOG_NOTICE
, route
, NULL
);
2023 case EROUTENOTAPPLIED
:
2024 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2028 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
2029 snprintf(buf
, sizeof(buf
), "%sAdd new[%ld]",
2031 RouteListRouteIndex(context
->info
,
2032 context
->new_routes
,
2034 (*route_log
)(LOG_DEBUG
, route
, buf
);
2036 route
->control_flags
|= kControlFlagsAdded
;
2040 case kRouteCommandRemove
:
2041 retval
= (*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
2045 case EROUTENOTAPPLIED
:
2046 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2050 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
2051 snprintf(buf
, sizeof(buf
), "%sRemove old[%ld]%s",
2053 RouteListRouteIndex(context
->info
,
2054 context
->old_routes
,
2056 (retval
== ESRCH
) ? "(ESRCH)" : "");
2057 (*route_log
)(LOG_DEBUG
, route
, buf
);
2062 "failed to remove route, %s",
2064 (*route_log
)(LOG_NOTICE
, route
, NULL
);
2075 RouteListApply(RouteListInfoRef info
,
2076 RouteListRef old_routes
, RouteListRef new_routes
,
2079 RouteListApplyContext context
;
2083 if (old_routes
== new_routes
&& old_routes
== NULL
) {
2084 /* both old and new are NULL, so there's nothing to do */
2087 memset(&context
, 0, sizeof(context
));
2088 context
.old_routes
= old_routes
;
2089 context
.new_routes
= new_routes
;
2090 context
.sockfd
= sockfd
;
2091 context
.info
= info
;
2092 if (old_routes
!= NULL
) {
2093 for (i
= 0, scan
= RouteListGetFirstRoute(info
, old_routes
);
2094 i
< old_routes
->count
;
2095 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2096 RouteRef new_route
= NULL
;
2098 if (new_routes
!= NULL
) {
2099 new_route
= RouteListFindRoute(info
, new_routes
, scan
);
2101 if (new_route
== NULL
) {
2102 if ((scan
->control_flags
& kControlFlagsAdded
) != 0) {
2103 RouteProcess(scan
, kRouteCommandRemove
, &context
);
2108 if (new_routes
!= NULL
) {
2109 if (old_routes
!= NULL
) {
2110 /* preserve the control flags from any old routes */
2111 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2112 i
< new_routes
->count
;
2113 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2114 RouteRef old_route
= NULL
;
2116 old_route
= RouteListFindRoute(info
, old_routes
, scan
);
2117 if (old_route
!= NULL
) {
2118 if ((scan
->control_flags
& kControlFlagsForce
) == 0) {
2119 /* preserve the control state in the new route */
2121 = ControlFlagsPreserve(old_route
->control_flags
);
2124 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
,
2125 "Re-applying route");
2130 /* add any routes that need to be added */
2131 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2132 i
< new_routes
->count
;
2133 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2134 if ((scan
->control_flags
& kControlFlagsProcessed
) != 0) {
2137 RouteProcess(scan
, kRouteCommandAdd
, &context
);
2143 * Function: RouteListFinalize
2145 * Look for excluded routes. If the excluded route does not have an assigned
2146 * interface, search for a route that *does not* go over the excluded
2149 * If the excluded route does have an assigned interface, search for a route
2150 * that *does* go over the assigned interface.
2152 * Set the gateway on the excluded route to match the gateway of the found
2156 RouteListFinalize(RouteListInfoRef info
, RouteListRef routes
)
2161 if (routes
== NULL
) {
2164 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
2166 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2169 RouteLookupFlags flags
;
2171 if (scan
->exclude_ifindex
== 0) {
2174 if (scan
->ifindex
== 0) {
2175 ifindex
= scan
->exclude_ifindex
;
2176 flags
= kRouteLookupFlagsExcludeInterface
;
2179 ifindex
= scan
->ifindex
;
2180 flags
= kRouteLookupFlagsNone
;
2182 route
= RouteListLookup(info
, routes
,
2183 (*info
->route_destination
)(scan
),
2184 scan
->prefix_length
, ifindex
, flags
);
2185 if (route
== NULL
) {
2186 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
,
2187 "can't resolve excluded route");
2190 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
2191 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)scan
, "Excluded route");
2192 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)route
, "Resolved to");
2194 scan
->ifindex
= route
->ifindex
;
2195 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2196 (*info
->route_set_gateway
)(scan
, (*info
->route_gateway
)(route
));
2197 scan
->flags
|= kRouteFlagsHasGateway
;
2198 if (scan
->prefix_length
== info
->all_bits_set
) {
2199 scan
->flags
|= kRouteFlagsIsHost
;
2203 /* routes directly to interface */
2204 scan
->flags
&= ~(kRouteFlagsHasGateway
| kRouteFlagsIsHost
);
2210 #endif /* !TARGET_OS_SIMULATOR */
2216 #define IPV4_ROUTE_ALL_BITS_SET 32
2218 static __inline__
struct in_addr
2219 subnet_addr(struct in_addr addr
, struct in_addr mask
)
2223 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
2228 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
2230 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
2231 CFStringAppendFormat(str
, NULL
,
2232 CFSTR("Host " IP_FORMAT
),
2236 CFStringAppendFormat(str
, NULL
,
2237 CFSTR("Net " IP_FORMAT
),
2239 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
2242 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
2243 CFStringAppendFormat(str
, NULL
,
2244 CFSTR(" Gate " IP_FORMAT
),
2245 IP_LIST(&r
->gateway
));
2247 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
2248 if (r
->ifa
.s_addr
!= 0) {
2249 CFStringAppendFormat(str
, NULL
,
2250 CFSTR(" Ifa " IP_FORMAT
),
2253 RouteAddFlagsToDescription((RouteRef
)r
, str
);
2258 IPv4RouteCopyDescription(RouteRef r
)
2260 CFMutableStringRef str
;
2262 str
= CFStringCreateMutable(NULL
, 0);
2263 IPv4RouteCopyDescriptionWithString((IPv4RouteRef
)r
, str
);
2267 #ifdef TEST_IPV4_ROUTELIST
2268 static CFMutableStringRef
2269 IPv4RouteListCopyDescription(IPv4RouteListRef routes
);
2272 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2274 CFStringRef str
= IPv4RouteCopyDescription(route
);
2277 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2280 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
2286 static __inline__
void
2287 IPv4RouteListPrint(IPv4RouteListRef routes
)
2289 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2291 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2296 #else /* TEST_IPV4_ROUTELIST */
2298 static __inline__
void
2299 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2301 CFStringRef str
= IPv4RouteCopyDescription(route
);
2304 my_log(level
, "%@", str
);
2307 my_log(level
, "%s: %@", msg
, str
);
2313 #endif /* TEST_IPV4_ROUTELIST */
2316 IPv4RouteIsEqual(RouteRef r_scan
, RouteRef r_route
)
2318 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2319 IPv4RouteRef scan
= (IPv4RouteRef
)r_scan
;
2321 return ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
2322 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
2323 && (scan
->ifindex
== route
->ifindex
)
2324 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
2325 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
2326 && (scan
->flags
== route
->flags
));
2329 static CFMutableStringRef
2330 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
2334 CFMutableStringRef str
;
2336 str
= CFStringCreateMutable(NULL
, 0);
2337 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
2339 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
2340 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
2341 IPv4RouteCopyDescriptionWithString(r
, str
);
2343 CFStringAppend(str
, CFSTR("\n}"));
2348 IPv4RouteListComputeSize(CFIndex n
)
2350 return (offsetof(IPv4RouteList
, list
[n
]));
2354 count_prefix_bits_set(uint32_t n
)
2357 const static int8_t bits
[16] = {
2376 for (count
= 0; n
!= 0; n
>>= 4) {
2377 int nbits
= bits
[n
& 0x0f];
2388 prefix_to_mask32(unsigned int prefix_length
)
2390 if (prefix_length
> 32 || prefix_length
== 0) {
2393 return (0xffffffff << (32 - prefix_length
));
2397 mask_get_prefix_length(struct in_addr mask
)
2401 count
= count_prefix_bits_set(mask
.s_addr
);
2405 val
= prefix_to_mask32(count
);
2406 if (ntohl(mask
.s_addr
) != val
) {
2407 /* expected mask based on prefix length doesn't match */
2415 IPv4RouteSetPrefixLength(IPv4RouteRef route
)
2419 length
= mask_get_prefix_length(route
->mask
);
2423 route
->prefix_length
= length
;
2428 IPv4RouteGateway(RouteRef r_route
)
2430 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2431 return (&route
->gateway
);
2435 IPv4RouteSetGateway(RouteRef r_route
, const void * address
)
2437 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2439 route
->gateway
= *((struct in_addr
*)address
);
2444 IPv4RouteDestination(RouteRef r_route
)
2446 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2447 return (&route
->dest
);
2451 IPv4RouteSameSubnet(RouteRef r_route
, const void * addr
)
2453 const struct in_addr
* address
;
2454 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2456 address
= (const struct in_addr
*)addr
;
2457 return ((address
->s_addr
& route
->mask
.s_addr
) == route
->dest
.s_addr
);
2461 * Define: ROUTE_MSG_ADDRS_SPACE
2463 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2464 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2465 * someone changes the code and doesn't think to modify this.
2467 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2468 + 2 * sizeof(struct sockaddr_dl) \
2471 struct rt_msghdr hdr
;
2472 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2476 * Function: IPv4RouteApply
2478 * Add or remove the specified route to/from the kernel routing table.
2481 IPv4RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
2485 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2488 struct sockaddr_in
* in_p
;
2489 struct sockaddr_dl
* dl_p
;
2493 if (S_netboot
&& route
->dest
.s_addr
== 0) {
2494 /* don't touch the default route */
2495 return (EROUTENOTAPPLIED
);
2497 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
2498 return (EROUTENOTAPPLIED
);
2500 if (route
->ifindex
== 0) {
2502 IP_FORMAT
" no interface specified, ignoring",
2503 IP_LIST(&route
->dest
));
2507 #ifdef TEST_IPV4_ROUTELIST
2509 #else /* TEST_IPV4_ROUTELIST */
2511 #endif /* TEST_IPV4_ROUTELIST */
2513 memset(&rtmsg
, 0, sizeof(rtmsg
));
2514 rtmsg
.hdr
.rtm_type
= cmd
;
2515 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2516 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2517 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
2518 if (route
->ifa
.s_addr
!= 0) {
2519 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
2521 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2522 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
2523 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
2526 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
2527 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
2528 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
2531 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2532 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
2534 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
2535 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
2536 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2539 rtaddr
.ptr
= rtmsg
.addrs
;
2542 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2543 rtaddr
.in_p
->sin_family
= AF_INET
;
2544 rtaddr
.in_p
->sin_addr
= route
->dest
;
2545 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2548 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2549 /* gateway is an IP address */
2550 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2551 rtaddr
.in_p
->sin_family
= AF_INET
;
2552 rtaddr
.in_p
->sin_addr
= route
->gateway
;
2553 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2556 /* gateway is the interface itself */
2557 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2558 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2559 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2560 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2564 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
2565 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2566 rtaddr
.in_p
->sin_family
= AF_INET
;
2567 rtaddr
.in_p
->sin_addr
= route
->mask
;
2568 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2572 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
2573 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2574 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2575 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2576 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2578 /* interface address */
2579 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
2580 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2581 rtaddr
.in_p
->sin_family
= AF_INET
;
2582 rtaddr
.in_p
->sin_addr
= route
->ifa
;
2583 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2586 /* apply the route */
2587 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
2588 rtmsg
.hdr
.rtm_msglen
= len
;
2589 if (write(sockfd
, &rtmsg
, len
) == -1) {
2595 static const RouteListInfo IPv4RouteListInfo
= {
2596 IPv4RouteListComputeSize
,
2601 IPv4RouteSetGateway
,
2602 IPv4RouteDestination
,
2603 IPv4RouteSameSubnet
,
2605 IPv4RouteCopyDescription
,
2608 sizeof(struct in_addr
),
2609 IPV4_ROUTE_ALL_BITS_SET
2612 #if !TARGET_OS_SIMULATOR
2613 static __inline__
void
2614 IPv4RouteListLog(int level
, IPv4RouteListRef routes
)
2616 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2618 my_log(level
, "%@", str
);
2624 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
2627 RouteListApply(&IPv4RouteListInfo
,
2628 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
2634 IPv4RouteListFinalize(IPv4RouteListRef routes
)
2636 RouteListFinalize(&IPv4RouteListInfo
, (RouteListRef
)routes
);
2639 #endif /* !TARGET_OS_SIMULATOR */
2641 #if TEST_IPV4_ROUTELIST
2642 static IPv4RouteListRef
2643 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
2644 IPv4RouteListRef service_routes
, Rank rank
)
2646 return ((IPv4RouteListRef
)
2647 RouteListAddRouteList(&IPv4RouteListInfo
,
2648 (RouteListRef
)routes
, init_size
,
2649 (RouteListRef
)service_routes
, rank
,
2652 #endif /* TEST_IPV4_ROUTELIST */
2655 plist_get_string(CFDictionaryRef dict
, CFStringRef prop_name
,
2656 char * buf
, int buf_size
)
2660 val
= CFDictionaryGetValue(dict
, prop_name
);
2661 if (isA_CFString(val
) == NULL
) {
2664 if (!CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingUTF8
)) {
2671 struct in_addr addr
;
2674 IFIndex exclude_ifindex
;
2675 IPv4RouteRef
* route_p
;
2678 } AddIPv4RouteContext
, * AddIPv4RouteContextRef
;
2681 AddIPv4Route(const void * value
, void * context
)
2683 AddIPv4RouteContextRef ctx
= (AddIPv4RouteContextRef
)context
;
2684 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
2685 IPv4RouteRef r
= *ctx
->route_p
;
2687 dict
= isA_CFDictionary(dict
);
2689 || !dict_get_ip(dict
, kSCPropNetIPv4RouteDestinationAddress
, &r
->dest
)
2690 || !dict_get_ip(dict
, kSCPropNetIPv4RouteSubnetMask
, &r
->mask
)) {
2691 /* one less route than we expected */
2693 my_log(LOG_NOTICE
, "%s route is not a dictionary",
2697 my_log(LOG_NOTICE
, "%s route is invalid, %@",
2702 if (!IPv4RouteSetPrefixLength(r
)) {
2703 my_log(LOG_NOTICE
, "%s route has invalid subnet mask, %@",
2707 r
->rank
= ctx
->rank
;
2708 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
2709 if (ctx
->ifindex
!= 0) {
2710 r
->ifindex
= ctx
->ifindex
;
2712 if (ctx
->exclude_ifindex
== 0
2713 && dict_get_ip(dict
,
2714 kSCPropNetIPv4RouteGatewayAddress
,
2716 r
->flags
|= kRouteFlagsHasGateway
;
2717 if (r
->prefix_length
== IPV4_ROUTE_ALL_BITS_SET
) {
2718 r
->flags
|= kRouteFlagsIsHost
;
2723 char ifname
[IFNAMSIZ
];
2725 if (plist_get_string(dict
, kSCPropNetIPv4RouteInterfaceName
,
2726 ifname
, sizeof(ifname
)) != NULL
) {
2729 ifindex
= my_if_nametoindex(ifname
);
2732 "%s: interface %s does not exist, %@",
2733 ctx
->descr
, ifname
, dict
);
2736 else if (ifindex
== ctx
->ifindex
) {
2738 "%s: interface %s unexpected, %@",
2739 ctx
->descr
, ifname
, dict
);
2742 r
->ifindex
= ifindex
;
2755 confirm_interface_name(CFDictionaryRef dict
, CFStringRef ifname
)
2757 CFStringRef confirmed_ifname
;
2758 boolean_t confirmed
;
2761 = CFDictionaryGetValue(dict
, kSCPropConfirmedInterfaceName
);
2762 if (isA_CFString(confirmed_ifname
) != NULL
) {
2763 confirmed
= CFEqual(confirmed_ifname
, ifname
);
2772 * Function: IPv4RouteListCreateWithDictionary
2775 * Given the service ipv4 entity dictionary, generate the list of routes.
2776 * Currently, this includes just the default route and subnet route,
2777 * if the service has a subnet mask.
2780 * If the passed in route_list is NULL or too small, this routine
2781 * allocates malloc'd memory to hold the routes.
2783 static IPv4RouteListRef
2784 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
2785 CFDictionaryRef dict
,
2786 CFNumberRef rank_assertion
)
2788 boolean_t add_broadcast_multicast
= FALSE
;
2789 boolean_t add_default
= FALSE
;
2790 boolean_t add_router_subnet
= FALSE
;
2791 boolean_t add_subnet
= FALSE
;
2792 struct in_addr addr
= { 0 };
2793 CFArrayRef additional_routes
= NULL
;
2794 CFIndex additional_routes_count
;
2795 boolean_t allow_additional_routes
= FALSE
;
2796 boolean_t exclude_from_nwi
= FALSE
;
2797 CFArrayRef excluded_routes
= NULL
;
2798 CFIndex excluded_routes_count
;
2799 RouteFlags flags
= 0;
2801 char ifname
[IFNAMSIZ
];
2802 CFStringRef ifname_cf
;
2803 struct in_addr mask
= { 0 };
2805 int prefix_length
= 0;
2806 Rank primary_rank
= kRankAssertionDefault
;
2808 Rank rank
= kRankAssertionDefault
;
2809 struct in_addr router
= { 0 };
2810 boolean_t scoped_only
= FALSE
;
2811 struct in_addr subnet
= { 0 };
2816 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
2817 ifname
, sizeof(ifname
));
2818 if (ifname_cf
== NULL
) {
2821 ifindex
= my_if_nametoindex(ifname
);
2823 /* interface doesn't exist */
2826 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
2827 if (!dict_get_ip(dict
, kSCPropNetIPv4Router
, &router
)) {
2828 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
2830 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
2831 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
2833 subnet
= subnet_addr(addr
, mask
);
2834 prefix_length
= mask_get_prefix_length(mask
);
2835 if (prefix_length
< 0) {
2837 "ignoring bad subnet mask "
2839 IP_LIST(&mask
), ifname
);
2846 if (addr
.s_addr
== 0) {
2847 /* invalid/non-existent address */
2850 if (rank_assertion
!= NULL
) {
2851 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
2854 if (router
.s_addr
== 0) {
2855 /* if no router is configured, demote the rank if necessary */
2856 switch (primary_rank
) {
2857 case kRankAssertionLast
:
2858 case kRankAssertionNever
:
2859 case kRankAssertionScoped
:
2860 /* rank is already demoted */
2863 /* demote to RankLast */
2864 primary_rank
= kRankAssertionLast
;
2870 * If the router address is our address and the subnet mask is
2871 * not 255.255.255.255, assume all routes are local to the interface.
2873 if (addr
.s_addr
== router
.s_addr
2874 && mask
.s_addr
!= INADDR_BROADCAST
) {
2875 ; /* all routes local */
2878 flags
|= kRouteFlagsHasGateway
;
2880 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
2881 primary_rank
= kRankAssertionFirst
;
2885 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
2886 exclude_from_nwi
= TRUE
;
2887 flags
|= kRouteFlagsIsNULL
;
2890 switch (primary_rank
) {
2891 case kRankAssertionScoped
:
2892 /* Scoped means all routes for the service get scoped */
2893 primary_rank
= rank
= kRankAssertionNever
;
2894 flags
|= kRouteFlagsIsScoped
;
2897 case kRankAssertionNever
:
2898 /* Never means just the default route gets scoped */
2899 rank
= kRankAssertionLast
;
2900 flags
|= kRouteFlagsIsScoped
;
2903 rank
= primary_rank
;
2907 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2908 add_router_subnet
= TRUE
;
2912 if (ifindex
!= lo0_ifindex()) {
2913 if (router
.s_addr
!= 0) {
2917 add_broadcast_multicast
= TRUE
;
2920 if (allow_additional_routes
) {
2922 = CFDictionaryGetValue(dict
, kSCPropNetIPv4AdditionalRoutes
);
2923 additional_routes
= isA_CFArray(additional_routes
);
2924 if (additional_routes
!= NULL
) {
2925 additional_routes_count
= CFArrayGetCount(additional_routes
);
2926 n
+= additional_routes_count
;
2929 = CFDictionaryGetValue(dict
, kSCPropNetIPv4ExcludedRoutes
);
2930 excluded_routes
= isA_CFArray(excluded_routes
);
2931 if (excluded_routes
!= NULL
) {
2932 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
2933 n
+= excluded_routes_count
;
2936 if (routes
== NULL
|| routes
->size
< n
) {
2937 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
2938 memset(routes
, 0, IPv4RouteListComputeSize(n
));
2942 memset(routes
->list
, 0, sizeof(routes
->list
[0]) * n
);
2945 if (exclude_from_nwi
) {
2946 routes
->flags
|= kRouteListFlagsExcludeNWI
;
2948 else if (scoped_only
) {
2949 routes
->flags
|= kRouteListFlagsScopedOnly
;
2952 /* start at the beginning */
2956 /* add the default route */
2957 routes
->flags
|= kRouteListFlagsHasDefault
;
2958 r
->ifindex
= ifindex
;
2961 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2962 r
->gateway
= router
;
2967 r
->rank
= primary_rank
;
2970 if (add_broadcast_multicast
) {
2971 /* add the broadcast route (rdar://problem/22149738) */
2972 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2973 r
->flags
|= kRouteFlagsIsNULL
;
2975 r
->dest
.s_addr
= INADDR_BROADCAST
;
2976 r
->mask
.s_addr
= INADDR_BROADCAST
;
2977 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2978 r
->ifindex
= ifindex
;
2983 /* add multicast route (rdar://problem/26457121) */
2984 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2985 r
->flags
|= kRouteFlagsIsNULL
;
2987 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
2988 r
->mask
.s_addr
= htonl(IN_CLASSD_NET
);
2989 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSD
;
2990 r
->ifindex
= ifindex
;
2997 /* add the subnet route */
2999 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3000 r
->flags
|= kRouteFlagsIsNULL
;
3002 r
->ifindex
= ifindex
;
3006 r
->prefix_length
= prefix_length
;
3012 /* add the router subnet route */
3013 if (add_router_subnet
) {
3014 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3015 r
->flags
|= kRouteFlagsIsNULL
;
3017 r
->ifindex
= ifindex
;
3020 r
->mask
.s_addr
= INADDR_BROADCAST
;
3021 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
3027 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3028 AddIPv4RouteContext context
;
3030 memset(&context
, 0, sizeof(context
));
3031 context
.count_p
= &routes
->count
;
3032 context
.route_p
= &r
;
3033 context
.rank
= rank
;
3035 /* additional routes */
3036 if (additional_routes
!= NULL
) {
3037 context
.ifindex
= ifindex
;
3038 context
.addr
= addr
;
3039 context
.descr
= "AdditionalRoutes";
3040 CFArrayApplyFunction(additional_routes
,
3041 CFRangeMake(0, additional_routes_count
),
3042 AddIPv4Route
, &context
);
3044 /* excluded routes */
3045 if (excluded_routes
!= NULL
) {
3046 context
.descr
= "ExcludedRoutes";
3047 /* exclude this interface */
3048 context
.ifindex
= 0;
3049 context
.exclude_ifindex
= ifindex
;
3050 CFArrayApplyFunction(excluded_routes
,
3051 CFRangeMake(0, excluded_routes_count
),
3052 AddIPv4Route
, &context
);
3058 #if !TARGET_OS_SIMULATOR
3059 static IPv4RouteListRef
3060 IPv4RouteListCopyMulticastLoopback(void)
3063 IPv4RouteListRef routes
;
3065 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(1));
3066 memset(routes
, 0, IPv4RouteListComputeSize(1));
3067 routes
->count
= routes
->size
= 1;
3070 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
3071 r
->mask
.s_addr
= htonl(IN_CLASSC_NET
);
3072 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSC
;
3073 r
->ifindex
= lo0_ifindex();
3076 #endif /* !TARGET_OS_SIMULATOR */
3081 #define IPV6_ROUTE_ALL_BITS_SET 128
3084 ipv6_prefix_length_is_valid(int prefix_length
)
3086 if (prefix_length
< 0 || prefix_length
> IPV6_ROUTE_ALL_BITS_SET
) {
3093 * from netinet6/in6.c
3096 in6_len2mask(struct in6_addr
* mask
, int len
)
3100 memset(mask
, 0, sizeof(*mask
));
3101 for (i
= 0; i
< len
/ 8; i
++)
3102 mask
->s6_addr
[i
] = 0xff;
3104 mask
->s6_addr
[i
] = (0xff00 >> (len
% 8)) & 0xff;
3108 in6_maskaddr(struct in6_addr
* addr
, const struct in6_addr
* mask
)
3110 for (size_t i
= 0; i
< sizeof(addr
->s6_addr
); i
++) {
3111 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3117 in6_netaddr(struct in6_addr
* addr
, int len
)
3119 struct in6_addr mask
;
3121 in6_len2mask(&mask
, len
);
3122 in6_maskaddr(addr
, &mask
);
3127 in6_addr_scope_linklocal(struct in6_addr
* addr
, IFIndex ifindex
)
3129 if (IN6_IS_ADDR_LINKLOCAL(addr
)) {
3130 addr
->__u6_addr
.__u6_addr16
[1] = htons(ifindex
);
3136 string_append_in6_addr(CFMutableStringRef str
, const struct in6_addr
* addr
)
3138 char ntopbuf
[INET6_ADDRSTRLEN
];
3140 CFStringAppendCString(str
,
3141 inet_ntop(AF_INET6
, addr
, ntopbuf
, sizeof(ntopbuf
)),
3142 kCFStringEncodingASCII
);
3147 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r
, CFMutableStringRef str
)
3149 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
3150 CFStringAppend(str
, CFSTR("Host "));
3151 string_append_in6_addr(str
, &r
->dest
);
3154 CFStringAppend(str
, CFSTR("Net "));
3155 string_append_in6_addr(str
, &r
->dest
);
3156 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
3159 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
3160 CFStringAppend(str
, CFSTR(" Gate "));
3161 string_append_in6_addr(str
, &r
->gateway
);
3163 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
3164 if (!IN6_ARE_ADDR_EQUAL(&r
->ifa
, &in6addr_any
)) {
3165 CFStringAppend(str
, CFSTR(" Ifa "));
3166 string_append_in6_addr(str
, &r
->ifa
);
3168 RouteAddFlagsToDescription((RouteRef
)r
, str
);
3173 IPv6RouteCopyDescription(RouteRef r
)
3175 CFMutableStringRef str
;
3177 str
= CFStringCreateMutable(NULL
, 0);
3178 IPv6RouteCopyDescriptionWithString((IPv6RouteRef
)r
, str
);
3182 static CFMutableStringRef
3183 IPv6RouteListCopyDescription(IPv6RouteListRef routes
)
3187 CFMutableStringRef str
;
3189 str
= CFStringCreateMutable(NULL
, 0);
3190 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv6RouteList[%d]> = {"),
3192 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
3193 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
3194 IPv6RouteCopyDescriptionWithString(r
, str
);
3196 CFStringAppend(str
, CFSTR("\n}"));
3200 #if TEST_IPV6_ROUTELIST
3203 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3205 CFStringRef str
= IPv6RouteCopyDescription(route
);
3208 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3211 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
3217 static __inline__
void
3218 IPv6RouteListPrint(IPv6RouteListRef routes
)
3220 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3222 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3227 #else /* TEST_IPV6_ROUTELIST */
3229 static __inline__
void
3230 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3232 CFStringRef str
= IPv6RouteCopyDescription(route
);
3235 my_log(level
, "%@", str
);
3238 my_log(level
, "%s: %@", msg
, str
);
3244 #endif /* TEST_IPV6_ROUTELIST */
3247 IPv6RouteListComputeSize(CFIndex n
)
3249 return (offsetof(IPv6RouteList
, list
[n
]));
3254 struct in6_addr
* addr
;
3257 IFIndex exclude_ifindex
;
3258 IPv6RouteRef
* route_p
;
3261 } AddIPv6RouteContext
, * AddIPv6RouteContextRef
;
3264 AddIPv6Route(const void * value
, void * context
)
3266 AddIPv6RouteContextRef ctx
= (AddIPv6RouteContextRef
)context
;
3267 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
3268 IPv6RouteRef r
= *ctx
->route_p
;
3270 dict
= isA_CFDictionary(dict
);
3272 || !dict_get_ipv6(dict
, kSCPropNetIPv6RouteDestinationAddress
, &r
->dest
)
3273 || !dict_get_int(dict
, kSCPropNetIPv6RoutePrefixLength
,
3275 || !ipv6_prefix_length_is_valid(r
->prefix_length
)) {
3276 /* one less route than we expected */
3278 my_log(LOG_NOTICE
, "%s route is not a dictionary",
3282 my_log(LOG_NOTICE
, "%s route is invalid, %@",
3287 r
->rank
= ctx
->rank
;
3288 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
3289 if (ctx
->ifindex
!= 0) {
3290 r
->ifindex
= ctx
->ifindex
;
3291 r
->ifa
= *ctx
->addr
;
3292 if (ctx
->exclude_ifindex
== 0
3293 && dict_get_ipv6(dict
,
3294 kSCPropNetIPv6RouteGatewayAddress
,
3296 r
->flags
|= kRouteFlagsHasGateway
;
3297 if (r
->prefix_length
== IPV6_ROUTE_ALL_BITS_SET
) {
3298 r
->flags
|= kRouteFlagsIsHost
;
3303 char ifname
[IFNAMSIZ
];
3305 if (plist_get_string(dict
, kSCPropNetIPv6RouteInterfaceName
,
3306 ifname
, sizeof(ifname
)) != NULL
) {
3309 ifindex
= my_if_nametoindex(ifname
);
3312 "%s: interface %s does not exist, %@",
3313 ctx
->descr
, ifname
, dict
);
3316 else if (ifindex
== ctx
->ifindex
) {
3318 "%s: interface %s unexpected, %@",
3319 ctx
->descr
, ifname
, dict
);
3322 r
->ifindex
= ifindex
;
3335 * Function: IPv6RouteListCreateWithDictionary
3338 * Given the service IPv6 entity dictionary, generate the list of routes.
3341 * If the passed in route_list is NULL or too small, this routine
3342 * allocates malloc'd memory to hold the routes.
3344 static IPv6RouteListRef
3345 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes
,
3346 CFDictionaryRef dict
,
3347 CFNumberRef rank_assertion
)
3349 boolean_t add_default
= FALSE
;
3350 boolean_t add_prefix
= FALSE
;
3351 struct in6_addr addr
;
3352 CFArrayRef additional_routes
= NULL
;
3353 CFIndex additional_routes_count
;
3354 boolean_t allow_additional_routes
= FALSE
;
3355 boolean_t exclude_from_nwi
= FALSE
;
3356 CFArrayRef excluded_routes
= NULL
;
3357 CFIndex excluded_routes_count
;
3358 RouteFlags flags
= 0;
3360 char ifname
[IFNAMSIZ
];
3361 CFStringRef ifname_cf
;
3363 int prefix_length
= 0;
3364 Rank primary_rank
= kRankAssertionDefault
;
3366 Rank rank
= kRankAssertionDefault
;
3367 struct in6_addr router
= in6addr_any
;
3368 boolean_t scoped_only
= FALSE
;
3373 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
3374 ifname
, sizeof(ifname
));
3375 if (ifname_cf
== NULL
) {
3378 ifindex
= my_if_nametoindex(ifname
);
3380 /* interface doesn't exist */
3383 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
3384 if (!dict_get_ipv6(dict
, kSCPropNetIPv6Router
, &router
)) {
3385 (void)dict_get_first_ipv6(dict
, kSCPropNetIPv6DestAddresses
, &router
);
3387 if (dict_get_first_ipv6(dict
, kSCPropNetIPv6Addresses
, &addr
)) {
3388 if (IN6_IS_ADDR_UNSPECIFIED(&addr
)) {
3391 if (dict_get_first_int(dict
, kSCPropNetIPv6PrefixLength
,
3393 && !IN6_IS_ADDR_LINKLOCAL(&addr
)
3394 && ipv6_prefix_length_is_valid(prefix_length
)) {
3406 if (rank_assertion
!= NULL
) {
3407 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
3410 if (!IN6_IS_ADDR_UNSPECIFIED(&router
)) {
3411 if (ifindex
!= lo0_ifindex()) {
3416 * If the router address is our address and the prefix length is
3417 * not 128, assume all routes are local to the interface.
3419 if (IN6_ARE_ADDR_EQUAL(&router
, &addr
)
3420 && prefix_length
!= IPV6_ROUTE_ALL_BITS_SET
) {
3421 ; /* all routes local */
3424 flags
|= kRouteFlagsHasGateway
;
3426 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
3427 primary_rank
= kRankAssertionFirst
;
3430 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
3431 exclude_from_nwi
= TRUE
;
3432 flags
|= kRouteFlagsIsNULL
;
3435 switch (primary_rank
) {
3436 case kRankAssertionScoped
:
3437 /* Scoped means all routes for the service get scoped */
3438 primary_rank
= rank
= kRankAssertionNever
;
3439 flags
|= kRouteFlagsIsScoped
;
3442 case kRankAssertionNever
:
3443 /* Never means just the default route gets scoped */
3444 rank
= kRankAssertionLast
;
3445 flags
|= kRouteFlagsIsScoped
;
3448 rank
= primary_rank
;
3452 if (allow_additional_routes
) {
3454 = CFDictionaryGetValue(dict
, kSCPropNetIPv6AdditionalRoutes
);
3455 additional_routes
= isA_CFArray(additional_routes
);
3456 if (additional_routes
!= NULL
) {
3457 additional_routes_count
= CFArrayGetCount(additional_routes
);
3458 n
+= additional_routes_count
;
3460 excluded_routes
= CFDictionaryGetValue(dict
,
3461 kSCPropNetIPv6ExcludedRoutes
);
3462 excluded_routes
= isA_CFArray(excluded_routes
);
3463 if (excluded_routes
!= NULL
) {
3464 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
3465 n
+= excluded_routes_count
;
3472 /* need IPv6LL subnet route */
3475 if (routes
== NULL
|| routes
->size
< n
) {
3476 routes
= (IPv6RouteListRef
)malloc(IPv6RouteListComputeSize(n
));
3477 memset(routes
, 0, IPv6RouteListComputeSize(n
));
3481 memset(routes
->list
, 0, sizeof(routes
->list
[0]) * n
);
3484 if (exclude_from_nwi
) {
3485 routes
->flags
|= kRouteListFlagsExcludeNWI
;
3487 else if (scoped_only
) {
3488 routes
->flags
|= kRouteListFlagsScopedOnly
;
3491 /* start at the beginning */
3494 /* add the default route */
3495 routes
->flags
|= kRouteListFlagsHasDefault
;
3496 r
->ifindex
= ifindex
;
3499 if ((flags
& kRouteFlagsHasGateway
) != 0) {
3500 r
->gateway
= router
;
3505 r
->rank
= primary_rank
;
3506 r
->flags
|= kRouteFlagsKernelManaged
;
3511 /* add IPv6LL route */
3512 r
->ifindex
= ifindex
;
3513 r
->dest
.s6_addr
[0] = 0xfe;
3514 r
->dest
.s6_addr
[1] = 0x80;
3515 r
->prefix_length
= 64;
3517 r
->flags
|= kRouteFlagsKernelManaged
;
3521 /* add the prefix route(s) */
3523 r
->flags
|= kRouteFlagsKernelManaged
;
3524 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3525 r
->flags
|= kRouteFlagsIsNULL
;
3527 r
->ifindex
= ifindex
;
3530 in6_netaddr(&r
->dest
, prefix_length
);
3531 r
->prefix_length
= prefix_length
;
3537 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3538 AddIPv6RouteContext context
;
3540 memset(&context
, 0, sizeof(context
));
3541 context
.count_p
= &routes
->count
;
3542 context
.route_p
= &r
;
3543 context
.rank
= rank
;
3545 /* additional routes */
3546 if (additional_routes
!= NULL
) {
3547 context
.ifindex
= ifindex
;
3548 context
.addr
= &addr
;
3549 context
.descr
= "AdditionalRoutes";
3550 CFArrayApplyFunction(additional_routes
,
3551 CFRangeMake(0, additional_routes_count
),
3552 AddIPv6Route
, &context
);
3554 /* excluded routes */
3555 if (excluded_routes
!= NULL
) {
3556 context
.descr
= "ExcludedRoutes";
3557 /* exclude this interface */
3558 context
.ifindex
= 0;
3559 context
.exclude_ifindex
= ifindex
;
3560 context
.addr
= NULL
;
3561 CFArrayApplyFunction(excluded_routes
,
3562 CFRangeMake(0, excluded_routes_count
),
3563 AddIPv6Route
, &context
);
3570 IPv6RouteGateway(RouteRef r_route
)
3572 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3573 return (&route
->gateway
);
3577 IPv6RouteSetGateway(RouteRef r_route
, const void * address
)
3579 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3581 route
->gateway
= *((struct in6_addr
*)address
);
3586 IPv6RouteDestination(RouteRef r_route
)
3588 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3589 return (&route
->dest
);
3592 static __inline__
int
3593 in6_addr_cmp(const struct in6_addr
* a
, const struct in6_addr
* b
)
3595 return (memcmp(a
->s6_addr
, b
->s6_addr
, sizeof(struct in6_addr
)));
3599 IPv6RouteIsEqual(RouteRef r_route1
, RouteRef r_route2
)
3601 IPv6RouteRef route1
= (IPv6RouteRef
)r_route1
;
3602 IPv6RouteRef route2
= (IPv6RouteRef
)r_route2
;
3604 return (route1
->prefix_length
== route2
->prefix_length
3605 && route1
->ifindex
== route2
->ifindex
3606 && route1
->flags
== route2
->flags
3607 && in6_addr_cmp(&route1
->dest
, &route2
->dest
) == 0
3608 && in6_addr_cmp(&route1
->ifa
, &route2
->ifa
) == 0
3609 && in6_addr_cmp(&route1
->gateway
, &route2
->gateway
) == 0);
3613 IPv6RouteSameSubnet(RouteRef r_route
, const void * addr
)
3615 const struct in6_addr
* address
= (const struct in6_addr
*)addr
;
3616 struct in6_addr netaddr
;
3617 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3620 in6_netaddr(&netaddr
, route
->prefix_length
);
3621 return (in6_addr_cmp(&netaddr
, &route
->dest
) == 0);
3625 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3628 struct rt_msghdr hdr
;
3629 char addrs
[V6_ROUTE_MSG_ADDRS_SPACE
];
3633 * Function: IPv6RouteApply
3635 * Add or remove the specified route to/from the kernel routing table.
3638 IPv6RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
3642 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3645 struct sockaddr_in6
* in_p
;
3646 struct sockaddr_dl
* dl_p
;
3650 if ((route
->flags
& kRouteFlagsKernelManaged
) != 0) {
3651 /* the kernel manages this route, don't touch it */
3652 return (EROUTENOTAPPLIED
);
3654 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
3655 return (EROUTENOTAPPLIED
);
3657 if (route
->ifindex
== 0) {
3658 IPv6RouteLog(LOG_NOTICE
, (RouteRef
)route
,
3659 "no interface specified");
3663 #if TEST_IPV6_ROUTELIST
3665 #else /* TEST_IPV6_ROUTELIST */
3667 #endif /* TEST_IPV6_ROUTELIST */
3669 memset(&rtmsg
, 0, sizeof(rtmsg
));
3670 rtmsg
.hdr
.rtm_type
= cmd
;
3671 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3672 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3673 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
3674 if (!IN6_IS_ADDR_UNSPECIFIED(&route
->ifa
)) {
3675 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
3677 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3678 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
3679 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
3682 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
3683 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
3684 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
3687 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
3688 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
3690 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
3691 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
3692 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
3695 rtaddr
.ptr
= rtmsg
.addrs
;
3698 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3699 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3700 rtaddr
.in_p
->sin6_addr
= route
->dest
;
3701 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3702 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3705 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
3706 /* gateway is an IP address */
3707 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3708 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3709 rtaddr
.in_p
->sin6_addr
= route
->gateway
;
3710 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3711 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3714 /* gateway is the interface itself */
3715 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3716 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3717 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3718 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3722 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
3723 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3724 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3725 in6_len2mask(&rtaddr
.in_p
->sin6_addr
, route
->prefix_length
);
3726 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3730 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
3731 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3732 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3733 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3734 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3736 /* interface address */
3737 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
3738 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3739 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3740 rtaddr
.in_p
->sin6_addr
= route
->ifa
;
3741 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3744 /* apply the route */
3745 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
3746 rtmsg
.hdr
.rtm_msglen
= len
;
3747 if (write(sockfd
, &rtmsg
, len
) == -1) {
3753 static const RouteListInfo IPv6RouteListInfo
= {
3754 IPv6RouteListComputeSize
,
3759 IPv6RouteSetGateway
,
3760 IPv6RouteDestination
,
3761 IPv6RouteSameSubnet
,
3763 IPv6RouteCopyDescription
,
3766 sizeof(struct in6_addr
),
3767 IPV6_ROUTE_ALL_BITS_SET
3770 #if TEST_IPV6_ROUTELIST
3771 static IPv6RouteListRef
3772 IPv6RouteListAddRouteList(IPv6RouteListRef routes
, int init_size
,
3773 IPv6RouteListRef service_routes
, Rank rank
)
3775 return ((IPv6RouteListRef
)
3776 RouteListAddRouteList(&IPv6RouteListInfo
,
3777 (RouteListRef
)routes
, init_size
,
3778 (RouteListRef
)service_routes
, rank
,
3781 #endif /* TEST_IPV6_ROUTELIST */
3783 #if !TARGET_OS_SIMULATOR
3784 static __inline__
void
3785 IPv6RouteListLog(int level
, IPv6RouteListRef routes
)
3787 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3789 my_log(level
, "%@", str
);
3795 IPv6RouteListFinalize(IPv6RouteListRef routes
)
3797 RouteListFinalize(&IPv6RouteListInfo
, (RouteListRef
)routes
);
3802 IPv6RouteListApply(IPv6RouteListRef old_routes
, IPv6RouteListRef new_routes
,
3805 RouteListApply(&IPv6RouteListInfo
,
3806 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
3810 #endif /* !TARGET_OS_SIMULATOR */
3813 * Function: parseNetworkServiceString
3815 * Parse either of the following two formats:
3816 * <domain>:/Network/Service/<serviceID>
3817 * <domain>:/Network/Service/<serviceID>/<protocol>
3818 * returning <serviceID>, and if available and required, <protocol>.
3820 static CF_RETURNS_RETAINED CFStringRef
3821 parseNetworkServiceString(CFStringRef str
, CFStringRef
*ret_protocol
)
3823 CFArrayRef components
;
3825 CFStringRef protocol
= NULL
;
3826 CFStringRef serviceID
= NULL
;
3829 * str = "<domain>:/Network/Service/<serviceID>"
3831 * str = "<domain>:/Network/Service/<serviceID>/<protocol>"
3833 components
= CFStringCreateArrayBySeparatingStrings(NULL
, str
, CFSTR("/"));
3834 count
= CFArrayGetCount(components
);
3836 /* we have a serviceID */
3837 serviceID
= CFArrayGetValueAtIndex(components
, 3);
3838 CFRetain(serviceID
);
3839 if (count
>= 5 && ret_protocol
!= NULL
) {
3840 /* we have and want a protocol */
3841 protocol
= CFArrayGetValueAtIndex(components
, 4);
3845 if (ret_protocol
!= NULL
) {
3846 *ret_protocol
= protocol
;
3848 my_CFRelease(&components
);
3853 ipdict_is_routable(CFDictionaryRef entity_dict
)
3855 RouteListRef routes
;
3857 routes
= ipdict_get_routelist(entity_dict
);
3858 if (routes
== NULL
) {
3863 if ((routes
->flags
& kRouteListFlagsHasDefault
) == 0) {
3864 // if service has no default route
3868 if ((routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
3869 // if service should be excluded from NWI
3877 __private_extern__ boolean_t
3878 service_is_routable(CFDictionaryRef service_dict
, int af
)
3880 boolean_t contains_protocol
;
3882 CFDictionaryRef entity_dict
;
3884 entity
= (af
== AF_INET
) ? kSCEntNetIPv4
: kSCEntNetIPv6
;
3885 entity_dict
= CFDictionaryGetValue(service_dict
, entity
);
3886 if (entity_dict
== NULL
) {
3890 contains_protocol
= ipdict_is_routable(entity_dict
);
3891 return contains_protocol
;
3895 static CFMutableDictionaryRef
3896 service_dict_copy(CFStringRef serviceID
)
3898 CFDictionaryRef d
= NULL
;
3899 CFMutableDictionaryRef service_dict
;
3901 /* create a modifyable dictionary, a copy or a new one */
3902 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3905 = CFDictionaryCreateMutable(NULL
, 0,
3906 &kCFTypeDictionaryKeyCallBacks
,
3907 &kCFTypeDictionaryValueCallBacks
);
3910 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
3912 return (service_dict
);
3915 __private_extern__ boolean_t
3916 service_is_scoped_only(CFDictionaryRef service_dict
)
3918 nwi_ifstate_t alias
;
3919 CFDictionaryRef dict
;
3920 char ifname
[IFNAMSIZ
];
3921 nwi_ifstate_t ifstate
;
3922 CFStringRef interface
= NULL
;
3924 // get IPv4 (or IPv6) info
3925 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3927 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
3930 // if no connectivity
3935 interface
= ipdict_get_ifname(dict
);
3936 if ((interface
== NULL
) ||
3937 !CFStringGetCString(interface
, ifname
, sizeof(ifname
), kCFStringEncodingUTF8
)) {
3938 // if no interface / interface name
3943 if (S_nwi_state
== NULL
) {
3944 S_nwi_state
= nwi_state_copy();
3948 // get [nwi] interface state
3949 ifstate
= nwi_state_get_ifstate(S_nwi_state
, ifname
);
3950 if (ifstate
== NULL
) {
3953 } else if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3954 // if scoped (i.e. not in list)
3958 // check both both IPv4 and IPv6
3959 alias
= nwi_ifstate_get_alias(ifstate
, ifstate
->af
== AF_INET
? AF_INET6
: AF_INET
);
3960 if (alias
== NULL
) {
3961 // if only one address family
3963 } else if ((alias
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3964 // if scoped (i.e. not in list)
3972 log_service_entity(int level
, CFStringRef serviceID
, CFStringRef entity
,
3973 CFStringRef operation
, CFTypeRef val
)
3975 CFMutableStringRef this_val
= NULL
;
3981 if ((is_ipv4
= CFEqual(entity
, kSCEntNetIPv4
))
3982 || (is_ipv6
= CFEqual(entity
, kSCEntNetIPv6
))) {
3983 RouteListUnion routes
;
3985 routes
.ptr
= ipdict_get_routelist(val
);
3986 if (routes
.ptr
!= NULL
) {
3987 CFDictionaryRef service_dict
= NULL
;
3990 this_val
= IPv4RouteListCopyDescription(routes
.v4
);
3993 this_val
= IPv6RouteListCopyDescription(routes
.v6
);
3995 service_dict
= ipdict_get_service(val
);
3996 if (service_dict
!= NULL
) {
3997 CFStringAppendFormat(this_val
, NULL
,
3998 CFSTR("\n<Service> = %@"),
4006 val
= CFSTR("<none>");
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
);
4051 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
4053 my_CFRelease(&service_dict
);
4057 static CFDictionaryRef
4058 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
4060 CFDictionaryRef service_dict
;
4062 if (S_service_state_dict
== NULL
) {
4065 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
4066 if (service_dict
== NULL
) {
4069 return (CFDictionaryGetValue(service_dict
, entity
));
4072 #if !TARGET_OS_SIMULATOR
4074 service_copy_interface(CFStringRef serviceID
, CFDictionaryRef new_service
)
4076 CFDictionaryRef dict
;
4077 CFStringRef interface
= NULL
;
4079 if (new_service
!= NULL
) {
4080 interface
= ipdict_get_ifname(new_service
);
4082 if (interface
== NULL
) {
4083 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4085 interface
= ipdict_get_ifname(dict
);
4088 if (interface
== NULL
) {
4089 dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4091 interface
= ipdict_get_ifname(dict
);
4094 if (interface
!= NULL
) {
4095 CFRetain(interface
);
4099 #endif /* !TARGET_OS_SIMULATOR */
4102 service_has_clat46_address(CFStringRef serviceID
)
4104 CFDictionaryRef ip_dict
;
4106 ip_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4107 if (ip_dict
!= NULL
) {
4108 CFBooleanRef clat46
= NULL
;
4109 CFDictionaryRef ipv4
;
4111 ipv4
= ipdict_get_service(ip_dict
);
4112 if (isA_CFDictionary(ipv4
) &&
4113 CFDictionaryGetValueIfPresent(ipv4
,
4114 kSCPropNetIPv4CLAT46
,
4115 (const void **)&clat46
) &&
4116 isA_CFBoolean(clat46
)) {
4117 return CFBooleanGetValue(clat46
);
4124 #ifndef kSCPropNetHostname
4125 #define kSCPropNetHostname CFSTR("Hostname")
4130 copy_dhcp_hostname(CFStringRef serviceID
)
4132 CFDictionaryRef dict
= NULL
;
4133 CFStringRef hostname
= NULL
;
4134 CFDictionaryRef service_dict
= NULL
;
4136 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4140 service_dict
= ipdict_get_service(dict
);
4141 if (service_dict
== NULL
) {
4144 hostname
= CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
4145 if (hostname
!= NULL
) {
4151 #if !TARGET_OS_SIMULATOR
4153 static struct in6_addr
*
4154 ipv6_service_get_router(CFDictionaryRef service
,
4155 IFIndex
* ifindex_p
, CFStringRef
* ifname_p
)
4157 IPv6RouteListRef routes
;
4158 struct in6_addr
* router
= NULL
;
4160 routes
= ipdict_get_routelist(service
);
4162 && (routes
->flags
& kRouteListFlagsExcludeNWI
) == 0
4163 && (routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
4164 router
= &routes
->list
[0].gateway
;
4165 if (*ifindex_p
== 0) {
4166 *ifindex_p
= routes
->list
[0].ifindex
;
4168 if (*ifname_p
== NULL
) {
4169 *ifname_p
= ipdict_get_ifname(service
);
4176 ipv6_service_update_router(CFStringRef serviceID
, CFDictionaryRef new_service
)
4178 IFIndex ifindex
= 0;
4179 CFStringRef ifname
= NULL
;
4180 char ntopbuf
[INET6_ADDRSTRLEN
];
4181 CFDictionaryRef old_service
;
4182 struct in6_addr
* old_router
;
4183 struct in6_addr
* new_router
;
4186 old_service
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4187 old_router
= ipv6_service_get_router(old_service
, &ifindex
, &ifname
);
4188 new_router
= ipv6_service_get_router(new_service
, &ifindex
, &ifname
);
4189 if (ifname
== NULL
|| ifindex
== 0) {
4192 s
= inet6_dgram_socket();
4196 /* remove the old router if it was defined */
4197 if (old_router
!= NULL
4198 && (new_router
== NULL
4199 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4200 if (siocdrdel_in6(s
, ifindex
, old_router
) < 0) {
4201 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4202 "siocdrdel_in6(%@, %s) failed: %s",
4204 inet_ntop(AF_INET6
, old_router
,
4205 ntopbuf
, sizeof(ntopbuf
)),
4210 "%@ removed default route %s",
4212 inet_ntop(AF_INET6
, old_router
, ntopbuf
, sizeof(ntopbuf
)));
4215 /* add the new router if it is defined */
4216 if (new_router
!= NULL
4217 && (old_router
== NULL
4218 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4219 if (siocdradd_in6(s
, ifindex
, new_router
, 0) < 0) {
4220 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4221 "siocdradd_in6(%@, %s) failed: %s",
4223 inet_ntop(AF_INET6
, new_router
,
4224 ntopbuf
, sizeof(ntopbuf
)),
4229 "%@ added default route %s",
4231 inet_ntop(AF_INET6
, new_router
, ntopbuf
, sizeof(ntopbuf
)));
4239 #endif /* !TARGET_OS_SIMULATOR */
4241 #define ALLOW_EMPTY_STRING 0x1
4243 static CF_RETURNS_RETAINED CFTypeRef
4244 sanitize_prop(CFTypeRef val
, uint32_t flags
)
4247 if (isA_CFString(val
)) {
4248 CFMutableStringRef str
;
4250 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
4251 CFStringTrimWhitespace(str
);
4252 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
4266 merge_array_prop(CFMutableDictionaryRef dict
,
4268 CFDictionaryRef state_dict
,
4269 CFDictionaryRef setup_dict
,
4273 CFMutableArrayRef merge_prop
;
4274 CFArrayRef setup_prop
= NULL
;
4275 CFArrayRef state_prop
= NULL
;
4277 if (setup_dict
!= NULL
) {
4278 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
4280 if (state_dict
!= NULL
) {
4281 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
4284 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
4288 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4289 if (setup_prop
!= NULL
) {
4293 n
= CFArrayGetCount(setup_prop
);
4294 for (i
= 0; i
< n
; i
++) {
4297 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
4298 val
= sanitize_prop(val
, flags
);
4300 CFArrayAppendValue(merge_prop
, val
);
4305 if (state_prop
!= NULL
4306 && (setup_prop
== NULL
|| S_append_state
)) {
4309 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
4311 n
= CFArrayGetCount(state_prop
);
4312 for (i
= 0; i
< n
; i
++) {
4315 val
= CFArrayGetValueAtIndex(state_prop
, i
);
4316 val
= sanitize_prop(val
, flags
);
4318 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
4319 CFArrayAppendValue(merge_prop
, val
);
4325 if (CFArrayGetCount(merge_prop
) > 0) {
4326 CFDictionarySetValue(dict
, key
, merge_prop
);
4328 CFRelease(merge_prop
);
4333 pick_prop(CFMutableDictionaryRef dict
,
4335 CFDictionaryRef state_dict
,
4336 CFDictionaryRef setup_dict
,
4339 CFTypeRef val
= NULL
;
4341 if (setup_dict
!= NULL
) {
4342 val
= CFDictionaryGetValue(setup_dict
, key
);
4343 val
= sanitize_prop(val
, flags
);
4345 if (val
== NULL
&& state_dict
!= NULL
) {
4346 val
= CFDictionaryGetValue(state_dict
, key
);
4347 val
= sanitize_prop(val
, flags
);
4350 CFDictionarySetValue(dict
, key
, val
);
4358 ** GetEntityChangesFunc functions
4360 #define IPV4_ROUTES_N_STATIC 5
4361 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4362 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4366 #define IPV4_ROUTES_BUF_DECL(routes) \
4367 IPv4RouteListRef routes; \
4368 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4370 routes = (IPv4RouteListRef)(void *)routes_buf; \
4371 routes->size = IPV4_ROUTES_N_STATIC; \
4372 routes->count = 0; \
4376 IPv4RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4379 CFDataRef routes_data
;
4380 IPV4_ROUTES_BUF_DECL(routes
);
4382 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4384 routes_data
= CFDataCreate(NULL
,
4386 IPv4RouteListComputeSize(r
->count
));
4394 return (routes_data
);
4396 #define IPV6_ROUTES_N_STATIC 3
4397 #define IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4398 (roundup(IPv6RouteListComputeSize(IPV6_ROUTES_N_STATIC), \
4402 #define IPV6_ROUTES_BUF_DECL(routes) \
4403 IPv6RouteListRef routes; \
4404 uint32_t routes_buf[IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4406 routes = (IPv6RouteListRef)(void *)routes_buf; \
4407 routes->size = IPV6_ROUTES_N_STATIC; \
4408 routes->count = 0; \
4412 IPv6RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4415 CFDataRef routes_data
;
4416 IPV6_ROUTES_BUF_DECL(routes
);
4418 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4420 routes_data
= CFDataCreate(NULL
,
4422 IPv6RouteListComputeSize(r
->count
));
4430 return (routes_data
);
4433 static CFDictionaryRef
4434 IPDictCreate(int af
, _Nonnull CFDictionaryRef state_dict
,
4435 CFDictionaryRef setup_dict
,
4436 CFNumberRef rank_assertion
)
4438 CFDictionaryRef aggregated_dict
= NULL
;
4439 CFDictionaryRef dict
;
4440 CFMutableDictionaryRef modified_dict
= NULL
;
4441 CFDataRef routes_data
;
4444 if (setup_dict
!= NULL
) {
4445 /* look for keys in Setup: that override/merge with State: */
4446 CFArrayRef additional_routes
;
4447 CFStringRef route_list_prop
;
4450 CFStringRef router_prop
;
4455 router_prop
= kSCPropNetIPv4Router
;
4456 route_list_prop
= kSCPropNetIPv4AdditionalRoutes
;
4460 router_prop
= kSCPropNetIPv6Router
;
4461 route_list_prop
= kSCPropNetIPv6AdditionalRoutes
;
4464 router
= CFDictionaryGetValue(setup_dict
, router_prop
);
4466 && !cfstring_to_ipvx(af
, router
, &router_ip
, sizeof(router_ip
))) {
4470 /* AdditionalRoutes */
4472 = CFDictionaryGetValue(setup_dict
, route_list_prop
);
4473 additional_routes
= isA_CFArray(additional_routes
);
4475 if (router
!= NULL
|| additional_routes
!= NULL
) {
4476 modified_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
4477 if (router
!= NULL
) {
4478 CFDictionarySetValue(modified_dict
,
4482 if (additional_routes
!= NULL
) {
4483 CFArrayRef combined_routes
= NULL
;
4484 CFArrayRef state_routes
;
4487 = CFDictionaryGetValue(state_dict
,
4489 if (isA_CFArray(state_routes
) != NULL
) {
4491 = my_CFArrayCreateCombinedArray(additional_routes
,
4493 additional_routes
= combined_routes
;
4495 CFDictionarySetValue(modified_dict
,
4498 if (combined_routes
!= NULL
) {
4499 CFRelease(combined_routes
);
4502 dict
= modified_dict
;
4507 routes_data
= IPv4RouteListDataCreate(dict
, rank_assertion
);
4511 routes_data
= IPv6RouteListDataCreate(dict
, rank_assertion
);
4514 if (routes_data
!= NULL
) {
4515 aggregated_dict
= ipdict_create(dict
, routes_data
);
4516 CFRelease(routes_data
);
4518 if (modified_dict
!= NULL
) {
4519 CFRelease(modified_dict
);
4521 return (aggregated_dict
);
4525 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4526 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4528 #pragma unused(info)
4529 CFDictionaryRef dict
= NULL
;
4530 boolean_t changed
= FALSE
;
4531 CFNumberRef rank_assertion
= NULL
;
4532 CFDictionaryRef service_options
;
4534 if (state_dict
== NULL
) {
4537 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4538 if (service_options
!= NULL
) {
4540 = CFDictionaryGetValue(service_options
,
4541 kServiceOptionRankAssertion
);
4543 dict
= IPDictCreate(AF_INET
, state_dict
, setup_dict
, rank_assertion
);
4546 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, dict
);
4548 /* clean up the rank too */
4549 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
4551 my_CFRelease(&dict
);
4557 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4558 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4560 #pragma unused(info)
4561 CFDictionaryRef dict
= NULL
;
4562 boolean_t changed
= FALSE
;
4563 #if !TARGET_OS_SIMULATOR
4564 CFStringRef interface
;
4565 #endif /* !TARGET_OS_SIMULATOR */
4566 CFNumberRef rank_assertion
= NULL
;
4567 CFDictionaryRef service_options
;
4569 if (state_dict
== NULL
) {
4572 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4573 if (service_options
!= NULL
) {
4575 = CFDictionaryGetValue(service_options
,
4576 kServiceOptionRankAssertion
);
4579 dict
= IPDictCreate(AF_INET6
, state_dict
, setup_dict
, rank_assertion
);
4583 #if !TARGET_OS_SIMULATOR
4584 interface
= service_copy_interface(serviceID
, dict
);
4585 ipv6_service_update_router(serviceID
, dict
);
4586 #endif /* !TARGET_OS_SIMULATOR */
4588 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, dict
);
4590 #if !TARGET_OS_SIMULATOR
4591 if (interface
!= NULL
) {
4593 CFBooleanRef needs_plat
= NULL
;
4596 // if service is unpublished, cancel the request
4597 set_plat_discovery(kPLATDiscoveryOptionCancel
, interface
);
4598 } else if ((state_dict
!= NULL
) &&
4599 CFDictionaryGetValueIfPresent(state_dict
,
4600 kSCPropNetIPv6PerformPLATDiscovery
,
4601 (const void **)&needs_plat
) &&
4602 isA_CFBoolean(needs_plat
) &&
4603 CFBooleanGetValue(needs_plat
)) {
4604 // perform PLAT discovery
4605 set_plat_discovery(kPLATDiscoveryOptionStart
, interface
);
4607 // IPv6 configuration changed for this interface, poke NAT64
4608 set_plat_discovery(kPLATDiscoveryOptionUpdate
, interface
);
4611 CFRelease(interface
);
4613 #endif /* !TARGET_OS_SIMULATOR */
4616 /* service removed, clean up the rank too */
4617 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
4619 my_CFRelease(&dict
);
4625 __private_extern__ CFDictionaryRef
4626 ipv4_dict_create(CFDictionaryRef state_dict
)
4628 return (IPDictCreate(AF_INET
, state_dict
, NULL
, NULL
, NULL
));
4631 __private_extern__ CFDictionaryRef
4632 ipv6_dict_create(CFDictionaryRef state_dict
)
4634 return (IPDictCreate(AF_INET6
, state_dict
, NULL
, NULL
, NULL
));
4637 #endif /* TEST_DNS */
4640 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
4641 CFMutableArrayRef out_servers
, CFStringRef interface
)
4646 count
= CFArrayGetCount(in_servers
);
4647 for (i
= 0; i
< count
; i
++) {
4649 struct in6_addr ipv6_addr
;
4650 struct in_addr ip_addr
;
4652 addr
= CFArrayGetValueAtIndex(in_servers
, i
);
4653 assert(addr
!= NULL
);
4655 if (cfstring_to_ip(addr
, &ip_addr
)) {
4657 if ((active_protos
& kProtocolFlagsIPv4
) == 0
4658 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
4660 "no IPv4 connectivity, "
4661 "ignoring DNS server address " IP_FORMAT
,
4668 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
4670 if ((active_protos
& kProtocolFlagsIPv6
) == 0
4671 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
4672 char ntopbuf
[INET6_ADDRSTRLEN
];
4675 "no IPv6 connectivity, "
4676 "ignoring DNS server address %s",
4677 inet_ntop(AF_INET6
, &ipv6_addr
,
4678 ntopbuf
, sizeof(ntopbuf
)));
4682 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
4683 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
4684 && (interface
!= NULL
)
4685 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
4686 // append interface name to IPv6 link local address
4687 addr
= CFStringCreateWithFormat(NULL
, NULL
,
4696 /* bad IP address */
4697 my_log(LOG_NOTICE
, "ignoring bad DNS server address '%@'", addr
);
4701 /* DNS server is valid and one we want */
4702 CFArrayAppendValue(out_servers
, addr
);
4708 static CF_RETURNS_RETAINED CFArrayRef
4709 order_dns_servers(CFArrayRef servers
, ProtocolFlags active_protos
)
4711 Boolean favor_v4
= FALSE
;
4712 CFMutableArrayRef ordered_servers
;
4713 ProtocolFlags proto_last
= kProtocolFlagsIPv4
;
4714 struct sockaddr_in v4_dns1
= { .sin_family
= AF_INET
,
4715 .sin_len
= sizeof(struct sockaddr_in
) };
4717 struct sockaddr_in6 v6_dns1
= { .sin6_family
= AF_INET6
,
4718 .sin6_len
= sizeof(struct sockaddr_in6
),
4719 .sin6_scope_id
= 0 };
4722 if (((active_protos
& kProtocolFlagsIPv4
) == 0) ||
4723 ((active_protos
& kProtocolFlagsIPv6
) == 0)) {
4724 /* only one protocol */
4725 #ifdef TEST_DNS_ORDER
4726 printf("only one protocol\n");
4727 #endif // TEST_DNS_ORDER
4728 return CFRetain(servers
);
4731 ordered_servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4732 for (CFIndex i
= 0, n
= CFArrayGetCount(servers
); i
< n
; i
++) {
4734 struct in6_addr ia6
;
4735 ProtocolFlags proto
;
4738 server
= CFArrayGetValueAtIndex(servers
, i
);
4739 if (cfstring_to_ip(server
, &ia
)) {
4740 proto
= kProtocolFlagsIPv4
;
4742 v4_dns1
.sin_addr
= ia
;
4744 } else if (cfstring_to_ip6(server
, &ia6
)) {
4745 proto
= kProtocolFlagsIPv6
;
4747 memcpy(&v6_dns1
.sin6_addr
, &ia6
, sizeof(ia6
));
4750 CFRelease(ordered_servers
);
4751 return CFRetain(servers
);
4754 if ((i
> 0) && (proto
!= proto_last
)) {
4755 /* if the protocol of the server addresses changed */
4756 if (((proto
== kProtocolFlagsIPv4
) && (v4_n
== 1)) ||
4757 ((proto
== kProtocolFlagsIPv6
) && (v6_n
== 1))) {
4758 /* if we now have the 1st server address of another protocol */
4759 favor_v4
= (sa_dst_compare_no_dependencies((struct sockaddr
*)&v4_dns1
,
4760 (struct sockaddr
*)&v6_dns1
) >= 0);
4761 #ifdef TEST_DNS_ORDER
4762 char v4_buf
[INET_ADDRSTRLEN
];
4763 char v6_buf
[INET6_ADDRSTRLEN
];
4764 printf("comparing %s vs %s, favoring %s\n",
4765 inet_ntop(v4_dns1
.sin_family
, &v4_dns1
.sin_addr
, v4_buf
, sizeof(v4_buf
)),
4766 inet_ntop(v6_dns1
.sin6_family
, &v6_dns1
.sin6_addr
, v6_buf
, sizeof(v6_buf
)),
4767 favor_v4
? "v4" : "v6");
4768 #endif // TEST_DNS_ORDER
4770 /* if the server addresses array is randomly mixed */
4771 #ifdef TEST_DNS_ORDER
4772 printf("v4/v6 not ordered\n");
4773 #endif // TEST_DNS_ORDER
4774 CFRelease(ordered_servers
);
4775 return CFRetain(servers
);
4780 if ((proto
== kProtocolFlagsIPv4
) && favor_v4
) {
4781 CFArrayInsertValueAtIndex(ordered_servers
, v4_n
- 1, server
);
4782 } else if ((proto
== kProtocolFlagsIPv6
) && !favor_v4
) {
4783 CFArrayInsertValueAtIndex(ordered_servers
, v6_n
- 1, server
);
4785 CFArrayAppendValue(ordered_servers
, server
);
4789 return ordered_servers
;
4793 merge_dns_servers(CFMutableDictionaryRef new_dict
,
4794 CFArrayRef state_servers
,
4795 CFArrayRef setup_servers
,
4797 Boolean trust_state
,
4798 ProtocolFlags active_protos
,
4799 CFStringRef interface
)
4801 CFMutableArrayRef dns_servers
;
4802 Boolean have_dns_setup
= FALSE
;
4804 if (state_servers
== NULL
&& setup_servers
== NULL
) {
4805 /* no DNS servers */
4808 dns_servers
= CFArrayCreateMutable(NULL
, 0,
4809 &kCFTypeArrayCallBacks
);
4810 if (setup_servers
!= NULL
) {
4811 accumulate_dns_servers(setup_servers
, active_protos
,
4812 dns_servers
, interface
);
4813 if (CFArrayGetCount(dns_servers
) > 0) {
4814 have_dns_setup
= TRUE
;
4817 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
4818 && state_servers
!= NULL
) {
4819 CFArrayRef ordered_servers
;
4821 ordered_servers
= order_dns_servers(state_servers
, active_protos
);
4822 accumulate_dns_servers(ordered_servers
, active_protos
,
4824 CFRelease(ordered_servers
);
4828 * Here, we determine whether or not we want all queries for this DNS
4829 * configuration to be bound to the associated network interface.
4831 * For dynamically derived network configurations (i.e. from State:)
4832 * this would be the preferred option using the argument "Hey, the
4833 * server told us to use these servers on this network so let's not
4836 * But, when a DNS configuration has been provided by the user/admin
4837 * via the Network pref pane (i.e. from Setup:) we opt to not force
4838 * binding of the outbound queries. The simplest example why we take
4839 * this stance is with a multi-homing configuration. Consider a system
4840 * with one network service associated with "en0" and a second service
4841 * associated with "en1". The "en0" service has been set higher in
4842 * the network service order so it would be primary but the user/admin
4843 * wants the DNS queries to go to a server only accessible via "en1".
4844 * Without this exception we would take the DNS server addresses from
4845 * the Network pref pane (for "en0") and have the queries bound to
4846 * "en0" where they'd never reach their intended destination (via
4847 * "en1"). So, our exception to the rule is that we will not bind
4848 * user/admin configurations to any specific network interface.
4850 * We also add an exception to the "follow the dynamically derived
4851 * network configuration" path for on-the-fly (no Setup: content)
4854 * But, we add an exception to the exception to support our own
4855 * VPN code. Here, we look for a "ServiceID" property in the DNS
4856 * entity. If present, and if it matches, then we extend our
4857 * trust even when there is no Setup: content.
4859 if (CFArrayGetCount(dns_servers
) != 0) {
4860 CFDictionarySetValue(new_dict
,
4861 kSCPropNetDNSServerAddresses
, dns_servers
);
4862 if ((have_setup
&& !have_dns_setup
) || (!have_setup
&& trust_state
)) {
4863 // if this is a "setup"+"state" service with only "state" DNS content (i.e. no
4864 // setup override) or this is a TRUSTED "state"-only service
4865 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
4869 my_CFRelease(&dns_servers
);
4875 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4876 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4878 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4879 boolean_t changed
= FALSE
;
4881 Boolean have_setup
= FALSE
;
4882 CFStringRef interface
= NULL
;
4883 CFDictionaryRef ipv4
;
4884 CFDictionaryRef ipv6
;
4890 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
4891 { kSCPropNetDNSSortList
, 0, FALSE
},
4892 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4893 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
4895 CFMutableDictionaryRef new_dict
= NULL
;
4896 const CFStringRef pick_list
[] = {
4897 kSCPropNetDNSDomainName
,
4898 kSCPropNetDNSOptions
,
4899 kSCPropNetDNSSearchOrder
,
4900 kSCPropNetDNSServerPort
,
4901 kSCPropNetDNSServerTimeout
,
4902 kSCPropNetDNSServiceIdentifier
,
4903 kSCPropNetDNSSupplementalMatchDomainsNoSearch
,
4905 Boolean trust_state
= FALSE
;
4907 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4908 /* there is no DNS content */
4912 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4913 if (ipdict_is_routable(ipv4
)) {
4914 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
4917 active_protos
|= kProtocolFlagsIPv4
;
4918 interface
= ipdict_get_ifname(ipv4
);
4921 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4922 if (ipdict_is_routable(ipv6
)) {
4924 (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
) != NULL
)) {
4927 active_protos
|= kProtocolFlagsIPv6
;
4928 if (interface
== NULL
) {
4929 interface
= ipdict_get_ifname(ipv6
);
4934 if (active_protos
== kProtocolFlagsNone
) {
4935 /* there is no IPv4 nor IPv6 */
4936 if (state_dict
== NULL
) {
4937 /* ... and no DNS content that we care about */
4943 if (state_dict
!= NULL
) {
4944 CFStringRef state_serviceID
= NULL
;
4946 if (CFDictionaryGetValueIfPresent(state_dict
,
4947 kSCPropNetDNSConfirmedServiceID
,
4948 (const void **)&state_serviceID
) &&
4949 isA_CFString(state_serviceID
) &&
4950 CFEqual(serviceID
, state_serviceID
)) {
4955 /* merge DNS configuration */
4956 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
4957 &kCFTypeDictionaryKeyCallBacks
,
4958 &kCFTypeDictionaryValueCallBacks
);
4960 if (active_protos
== kProtocolFlagsNone
) {
4961 merge_dns_servers(new_dict
,
4962 my_CFDictionaryGetArray(state_dict
,
4963 kSCPropNetDNSServerAddresses
),
4967 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
4971 merge_dns_servers(new_dict
,
4972 my_CFDictionaryGetArray(state_dict
,
4973 kSCPropNetDNSServerAddresses
),
4974 my_CFDictionaryGetArray(setup_dict
,
4975 kSCPropNetDNSServerAddresses
),
4982 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
4983 merge_array_prop(new_dict
,
4987 merge_list
[i
].flags
,
4988 merge_list
[i
].append
);
4991 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
4999 if (active_protos
== kProtocolFlagsNone
) {
5000 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
5001 if (CFDictionaryContainsKey(new_dict
,
5002 kSCPropNetDNSSupplementalMatchDomains
)) {
5003 /* only keep State: supplemental */
5004 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
5005 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
5006 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
5007 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
5009 if ((interface
== NULL
) && (setup_dict
== NULL
) && (state_dict
!= NULL
)) {
5011 * for supplemental-only configurations, add any scoped (or
5012 * wild-card "*") interface
5014 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
5016 } else if (CFDictionaryContainsKey(new_dict
, kSCPropNetDNSServiceIdentifier
) &&
5017 (interface
== NULL
) &&
5018 (state_dict
!= NULL
)) {
5019 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
5025 if (CFDictionaryGetCount(new_dict
) == 0) {
5026 my_CFRelease(&new_dict
);
5030 if (interface
!= NULL
) {
5031 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5034 if (S_append_state
) {
5036 * ensure any specified domain name (e.g. the domain returned by
5037 * a DHCP server) is in the search list.
5039 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
5040 if (isA_CFString(domain
)) {
5043 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
5044 if (isA_CFArray(search
) &&
5045 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
5046 CFMutableArrayRef new_search
;
5048 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
5049 CFArrayAppendValue(new_search
, domain
);
5050 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
5051 my_CFRelease(&new_search
);
5058 #if !TARGET_OS_SIMULATOR
5059 if (interface
!= NULL
) {
5060 CFRetain(interface
);
5062 #endif /* !TARGET_OS_SIMULATOR */
5064 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
5066 #if !TARGET_OS_SIMULATOR
5067 if (interface
!= NULL
) {
5069 // DNS configuration changed for this interface, poke NAT64
5070 if ((active_protos
& kProtocolFlagsIPv6
) != 0) {
5071 set_plat_discovery(kPLATDiscoveryOptionUpdate
, interface
);
5074 CFRelease(interface
);
5076 #endif /* !TARGET_OS_SIMULATOR */
5078 my_CFRelease(&new_dict
);
5083 merge_dict(const void *key
, const void *value
, void *context
)
5085 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
5087 CFDictionarySetValue(dict
, key
, value
);
5091 #define PROXY_AUTO_DISCOVERY_URL 252
5093 static CF_RETURNS_RETAINED CFStringRef
5094 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
5096 CFStringRef urlString
= NULL
;
5098 if (dhcp_options
!= NULL
) {
5101 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
5104 const UInt8
*urlBytes
;
5107 urlBytes
= CFDataGetBytePtr(data
);
5108 urlLen
= CFDataGetLength(data
);
5109 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
5110 // remove trailing NUL
5118 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
5120 urlString
= CFURLGetString(url
);
5121 if (urlString
!= NULL
) {
5122 CFRetain(urlString
);
5132 static CF_RETURNS_RETAINED CFStringRef
5136 CFStringRef urlString
= NULL
;
5138 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
5140 urlString
= CFURLGetString(url
);
5141 if (urlString
!= NULL
) {
5142 CFRetain(urlString
);
5151 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5152 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5154 ProtocolFlags active_protos
= kProtocolFlagsNone
;
5155 boolean_t changed
= FALSE
;
5156 CFStringRef interface
= NULL
;
5157 CFDictionaryRef ipv4
;
5158 CFDictionaryRef ipv6
;
5159 CFMutableDictionaryRef new_dict
= NULL
;
5165 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
5166 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
5169 CFStringRef key1
; /* an "enable" key */
5173 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
5174 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
5175 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
5176 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
5177 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
5178 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
5179 { kSCPropNetProxiesProxyAutoConfigEnable
,
5180 kSCPropNetProxiesProxyAutoConfigURLString
,
5181 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
5182 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5187 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
5188 /* there is no proxy content */
5191 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
5192 if (ipdict_is_routable(ipv4
)) {
5193 active_protos
|= kProtocolFlagsIPv4
;
5194 interface
= ipdict_get_ifname(ipv4
);
5196 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
5197 if (ipdict_is_routable(ipv6
)) {
5198 active_protos
|= kProtocolFlagsIPv6
;
5199 if (interface
== NULL
) {
5200 interface
= ipdict_get_ifname(ipv6
);
5203 if (active_protos
== kProtocolFlagsNone
) {
5204 /* there is no IPv4 nor IPv6 */
5205 if (state_dict
== NULL
) {
5206 /* ... and no proxy content that we care about */
5212 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
5213 CFMutableDictionaryRef setup_copy
;
5216 * Merge the per-service "Setup:" and "State:" proxy information with
5217 * the "Setup:" information always taking precedence. Additionally,
5218 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
5219 * Port) is defined than all of the values for that group will be
5220 * used. That is, we don't allow mixing some of the values from
5221 * the "Setup:" keys and others from the "State:" keys.
5223 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5224 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
5225 merge_array_prop(new_dict
,
5229 merge_list
[i
].flags
,
5230 merge_list
[i
].append
);
5233 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5234 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5235 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
5237 * if a "Setup:" enabled key has been provided than we want to
5238 * ignore all of the "State:" keys
5240 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
5241 if (pick_list
[i
].key2
!= NULL
) {
5242 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
5244 if (pick_list
[i
].key3
!= NULL
) {
5245 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
5247 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
5248 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
5249 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
5251 * if a "Setup:" enabled key has not been provided and we have
5252 * some" "State:" keys than we remove all of of "Setup:" keys
5254 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
5255 if (pick_list
[i
].key2
!= NULL
) {
5256 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
5258 if (pick_list
[i
].key3
!= NULL
) {
5259 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
5264 /* merge the "Setup:" keys */
5265 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
5266 CFRelease(setup_copy
);
5268 else if (setup_dict
!= NULL
) {
5269 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5271 else if (state_dict
!= NULL
) {
5272 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5275 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
5276 CFRelease(new_dict
);
5280 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
5281 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5285 if (new_dict
!= NULL
) {
5286 CFDictionaryRef dhcp_options
;
5288 CFNumberRef wpad
= NULL
;
5289 int wpadEnabled
= 0;
5290 CFStringRef wpadURL
= NULL
;
5292 if (CFDictionaryGetValueIfPresent(new_dict
,
5293 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5294 (const void **)&num
) &&
5295 isA_CFNumber(num
)) {
5296 /* if we have a WPAD key */
5298 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
5299 /* if we don't like the enabled key/value */
5307 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
5308 if (!isA_CFNumber(num
) ||
5309 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
5310 /* if we don't like the enabled key/value */
5317 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
5318 if (pacURL
!= NULL
) {
5319 if (!isA_CFString(pacURL
) || (CFStringGetLength(pacURL
) == 0)) {
5320 /* if we don't like the PAC URL */
5326 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
5327 if (!isA_CFString(pacJS
) || (CFStringGetLength(pacJS
) == 0)) {
5328 /* if we don't have (or like) the PAC JavaScript */
5336 * we already have a PAC URL so disable WPAD.
5343 * if WPAD is enabled and we don't already have a PAC URL then
5344 * we check for a DHCP provided URL. If not available, we use
5345 * a PAC URL pointing to a well-known file (wpad.dat) on a
5346 * well-known host (wpad.<domain>).
5348 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
5349 wpadURL
= wpadURL_dhcp(dhcp_options
);
5350 if (wpadURL
== NULL
) {
5351 wpadURL
= wpadURL_dns();
5353 if (wpadURL
== NULL
) {
5354 wpadEnabled
= 0; /* if we don't have a WPAD URL */
5359 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
5360 CFDictionarySetValue(new_dict
,
5361 kSCPropNetProxiesProxyAutoConfigEnable
,
5364 CFDictionarySetValue(new_dict
,
5365 kSCPropNetProxiesProxyAutoConfigURLString
,
5372 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
5373 CFDictionarySetValue(new_dict
,
5374 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5381 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
5382 my_CFRelease(&new_dict
);
5386 #if !TARGET_OS_IPHONE
5388 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5389 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5391 #pragma unused(info)
5392 boolean_t changed
= FALSE
;
5393 CFMutableDictionaryRef new_dict
= NULL
;
5394 const CFStringRef pick_list
[] = {
5395 kSCPropNetSMBNetBIOSName
,
5396 kSCPropNetSMBNetBIOSNodeType
,
5397 #ifdef ADD_NETBIOS_SCOPE
5398 kSCPropNetSMBNetBIOSScope
,
5399 #endif // ADD_NETBIOS_SCOPE
5400 kSCPropNetSMBWorkgroup
,
5403 if (state_dict
== NULL
&& setup_dict
== NULL
) {
5404 /* there is no SMB */
5407 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
5408 && service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
) {
5409 /* there is no IPv4 or IPv6 */
5413 /* merge SMB configuration */
5414 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5415 &kCFTypeDictionaryKeyCallBacks
,
5416 &kCFTypeDictionaryValueCallBacks
);
5417 merge_array_prop(new_dict
,
5418 kSCPropNetSMBWINSAddresses
,
5423 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5431 if (CFDictionaryGetCount(new_dict
) == 0) {
5432 my_CFRelease(&new_dict
);
5437 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
5438 my_CFRelease(&new_dict
);
5441 #endif /* !TARGET_OS_IPHONE */
5444 services_info_get_interface(CFDictionaryRef services_info
,
5445 CFStringRef serviceID
)
5447 CFStringRef interface
= NULL
;
5448 CFDictionaryRef ipv4_dict
;
5450 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
5452 if (ipv4_dict
!= NULL
) {
5453 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
5456 CFDictionaryRef ipv6_dict
;
5458 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
5460 if (ipv6_dict
!= NULL
) {
5461 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5468 static const struct {
5469 const CFStringRef
* entityName
;
5470 const CFStringRef
* statusKey
;
5471 } transientServiceInfo
[] = {
5472 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
5473 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
5474 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
5478 get_transient_status_changes(CFStringRef serviceID
,
5479 CFDictionaryRef services_info
)
5481 boolean_t changed
= FALSE
;
5483 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5484 CFDictionaryRef dict
;
5485 CFNumberRef status
= NULL
;
5486 CFMutableDictionaryRef ts_dict
= NULL
;
5488 dict
= get_service_state_entity(services_info
, serviceID
,
5489 *transientServiceInfo
[i
].entityName
);
5492 status
= CFDictionaryGetValue(dict
,
5493 *transientServiceInfo
[i
].statusKey
);
5496 if (isA_CFNumber(status
) != NULL
) {
5497 ts_dict
= CFDictionaryCreateMutable(NULL
,
5499 &kCFTypeDictionaryKeyCallBacks
,
5500 &kCFTypeDictionaryValueCallBacks
);
5501 CFDictionaryAddValue(ts_dict
,
5502 *transientServiceInfo
[i
].statusKey
,
5506 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
,
5511 if (ts_dict
!= NULL
) {
5519 if_dict_is_expensive(CFDictionaryRef if_dict
)
5521 boolean_t is_expensive
= FALSE
;
5523 if (isA_CFDictionary(if_dict
) != NULL
) {
5524 CFBooleanRef expensive
;
5525 expensive
= CFDictionaryGetValue(if_dict
, kSCPropNetLinkExpensive
);
5526 if (isA_CFBoolean(expensive
) != NULL
5527 && CFBooleanGetValue(expensive
)) {
5528 is_expensive
= TRUE
;
5531 return is_expensive
;
5535 service_is_expensive(CFStringRef serviceID
, CFDictionaryRef services_info
)
5538 boolean_t is_expensive
= FALSE
;
5540 ifname
= services_info_get_interface(services_info
, serviceID
);
5541 if (ifname
!= NULL
) {
5542 CFDictionaryRef if_dict
;
5545 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5546 if_dict
= CFDictionaryGetValue(services_info
, key
);
5548 is_expensive
= if_dict_is_expensive(if_dict
);
5550 return (is_expensive
);
5554 interface_is_expensive(CFStringRef ifname
)
5556 boolean_t is_expensive
= FALSE
;
5558 if (ifname
!= NULL
) {
5559 CFDictionaryRef if_dict
;
5562 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5563 if_dict
= SCDynamicStoreCopyValue(S_session
, key
);
5565 if (if_dict
!= NULL
) {
5566 is_expensive
= if_dict_is_expensive(if_dict
);
5570 return (is_expensive
);
5574 service_rank_entity_get_index(CFDictionaryRef dict
, CFStringRef serviceID
,
5575 CFStringRef which
, uint32_t * ret_val
)
5577 CFNumberRef service_index
= NULL
;
5580 service_index
= CFDictionaryGetValue(dict
,
5581 kSCPropNetServiceServiceIndex
);
5582 service_index
= isA_CFNumber(service_index
);
5584 if (service_index
!= NULL
) {
5587 if (!CFNumberGetValue(service_index
, kCFNumberSInt32Type
,
5589 || index_val
<= 0) {
5590 /* ServiceIndex must be >= 1 */
5592 "%@%@ ServiceIndex %@ is invalid, ignoring",
5593 which
, serviceID
, service_index
);
5594 service_index
= NULL
;
5596 else if (ret_val
!= NULL
) {
5597 *ret_val
= (uint32_t)index_val
;
5600 return (service_index
);
5604 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
5605 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
5607 boolean_t changed
= FALSE
;
5608 CFStringRef interface
;
5609 boolean_t ip_is_coupled
= FALSE
;
5610 CFMutableDictionaryRef new_dict
= NULL
;
5611 Rank rank_assertion
= kRankAssertionDefault
;
5612 Boolean rank_assertion_is_set
= FALSE
;
5613 CFStringRef setup_rank
= NULL
;
5614 CFStringRef state_rank
= NULL
;
5615 CFNumberRef service_index
= NULL
;
5616 boolean_t use_setup_rank
= TRUE
;
5619 if (setup_options
!= NULL
) {
5620 CFBooleanRef coupled
;
5623 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
5624 setup_rank
= isA_CFString(setup_rank
);
5625 if (setup_rank
!= NULL
&& !use_setup_rank
) {
5626 my_log(LOG_DEBUG
, "%@ ignoring Setup PrimaryRank = %@",
5627 serviceID
, setup_rank
);
5630 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
5631 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5632 ip_is_coupled
= TRUE
;
5635 = service_rank_entity_get_index(setup_options
,
5637 kSCDynamicStoreDomainSetup
,
5640 if (state_options
!= NULL
) {
5641 CFBooleanRef coupled
;
5644 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
5645 state_rank
= isA_CFString(state_rank
);
5646 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
5647 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5648 ip_is_coupled
= TRUE
;
5650 if (service_index
== NULL
) {
5652 = service_rank_entity_get_index(state_options
,
5654 kSCDynamicStoreDomainState
,
5659 if (!ip_is_coupled
) {
5660 ip_is_coupled
= service_is_expensive(serviceID
, services_info
);
5662 if (setup_rank
!= NULL
|| state_rank
!= NULL
) {
5663 /* rank assertion is set on the service */
5664 Rank setup_assertion
;
5665 Boolean setup_assertion_is_set
= FALSE
;
5666 Rank state_assertion
;
5667 Boolean state_assertion_is_set
= FALSE
;
5669 setup_assertion
= PrimaryRankGetRankAssertion(setup_rank
,
5670 &setup_assertion_is_set
);
5671 state_assertion
= PrimaryRankGetRankAssertion(state_rank
,
5672 &state_assertion_is_set
);
5673 if (setup_assertion_is_set
&& state_assertion_is_set
) {
5674 if (setup_assertion
> state_assertion
) {
5675 rank_assertion
= setup_assertion
;
5678 rank_assertion
= state_assertion
;
5680 rank_assertion_is_set
= TRUE
;
5682 else if (setup_assertion_is_set
) {
5683 rank_assertion
= setup_assertion
;
5684 rank_assertion_is_set
= TRUE
;
5686 else if (state_assertion_is_set
) {
5687 rank_assertion
= state_assertion
;
5688 rank_assertion_is_set
= TRUE
;
5692 interface
= services_info_get_interface(services_info
, serviceID
);
5693 if (interface
!= NULL
) {
5694 if (!rank_assertion_is_set
) {
5695 /* check for a rank assertion on the interface */
5696 CFNumberRef if_rank
= NULL
;
5698 if (S_if_rank_dict
!= NULL
) {
5699 if_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
5702 = InterfaceRankGetRankAssertion(if_rank
,
5703 &rank_assertion_is_set
);
5704 #define kNotSetString ((CFTypeRef)CFSTR("not set"))
5706 "serviceID %@ interface %@ rank = 0x%x (%@)%s",
5710 (if_rank
!= NULL
) ? (CFTypeRef
)if_rank
: kNotSetString
,
5711 ip_is_coupled
? " [coupled]" : "");
5715 "serviceID %@ interface %@ rank = 0x%x%s",
5716 serviceID
, interface
, rank_assertion
,
5717 ip_is_coupled
? " [coupled]" : "");
5722 if (service_index
!= NULL
|| rank_assertion_is_set
|| ip_is_coupled
) {
5723 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5724 &kCFTypeDictionaryKeyCallBacks
,
5725 &kCFTypeDictionaryValueCallBacks
);
5726 if (rank_assertion_is_set
) {
5727 CFNumberRef new_rank
;
5729 new_rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
,
5730 (const void *)&rank_assertion
);
5731 CFDictionarySetValue(new_dict
, kServiceOptionRankAssertion
,
5733 CFRelease(new_rank
);
5735 if (ip_is_coupled
) {
5736 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
5738 if (service_index
!= NULL
) {
5739 CFDictionarySetValue(new_dict
, kSCPropNetServiceServiceIndex
,
5743 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
5744 my_CFRelease(&new_dict
);
5749 add_service_keys(CFStringRef serviceID
,
5750 CFMutableArrayRef keys
,
5751 CFMutableArrayRef patterns
)
5756 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
5760 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5761 CFStringRef name
= *entityTypeNames
[i
];
5763 key
= setup_service_key(serviceID
, name
);
5764 my_CFArrayAppendUniqueValue(keys
, key
);
5766 key
= state_service_key(serviceID
, name
);
5767 my_CFArrayAppendUniqueValue(keys
, key
);
5771 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
5772 my_CFArrayAppendUniqueValue(keys
, key
);
5775 key
= setup_service_key(serviceID
, NULL
);
5776 my_CFArrayAppendUniqueValue(keys
, key
);
5778 key
= state_service_key(serviceID
, NULL
);
5779 my_CFArrayAppendUniqueValue(keys
, key
);
5786 add_transient_status_keys(CFStringRef serviceID
,
5787 CFMutableArrayRef keys
,
5788 CFMutableArrayRef patterns
)
5790 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
5794 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5797 key
= state_service_key(serviceID
,
5798 *transientServiceInfo
[i
].entityName
);
5799 my_CFArrayAppendUniqueValue(keys
, key
);
5806 static const CFStringRef
*reachabilitySetupKeys
[] = {
5808 &kSCEntNetInterface
,
5815 add_reachability_patterns(CFMutableArrayRef patterns
)
5817 for (size_t i
= 0; i
< countof(reachabilitySetupKeys
); i
++) {
5818 CFStringRef pattern
;
5819 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
5820 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5827 add_vpn_pattern(CFMutableArrayRef patterns
)
5829 CFStringRef pattern
;
5831 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
5832 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5837 add_interface_link_pattern(CFMutableArrayRef patterns
)
5839 CFStringRef pattern
;
5841 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetLink
);
5842 my_CFArrayAppendUniqueValue(patterns
, pattern
);
5846 static CFDictionaryRef
5847 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
5850 CFMutableArrayRef keys
;
5851 CFDictionaryRef info
;
5852 CFMutableArrayRef patterns
;
5854 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5855 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5857 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
5858 CFArrayAppendValue(keys
, S_multicast_resolvers
);
5859 CFArrayAppendValue(keys
, S_private_resolvers
);
5861 count
= CFArrayGetCount(service_list
);
5862 for (CFIndex s
= 0; s
< count
; s
++) {
5863 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
5865 add_service_keys(serviceID
, keys
, patterns
);
5866 add_transient_status_keys(serviceID
, keys
, patterns
);
5869 add_reachability_patterns(patterns
);
5871 add_vpn_pattern(patterns
);
5873 add_interface_link_pattern(patterns
);
5875 info
= SCDynamicStoreCopyMultiple(session
, keys
, patterns
);
5876 my_CFRelease(&keys
);
5877 my_CFRelease(&patterns
);
5881 #if !TARGET_OS_SIMULATOR
5884 set_ipv6_default_interface(IFIndex ifindex
)
5886 struct in6_ndifreq ndifreq
;
5888 boolean_t success
= FALSE
;
5890 memset((char *)&ndifreq
, 0, sizeof(ndifreq
));
5891 strlcpy(ndifreq
.ifname
, kLoopbackInterface
, sizeof(ndifreq
.ifname
));
5893 ndifreq
.ifindex
= ifindex
;
5896 ndifreq
.ifindex
= lo0_ifindex();
5898 sock
= inet6_dgram_socket();
5902 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
5904 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5915 #endif /* !TARGET_OS_SIMULATOR */
5917 #if !TARGET_OS_IPHONE
5918 static __inline__
void
5921 (void)unlink(VAR_RUN_RESOLV_CONF
);
5925 set_dns(CFArrayRef val_search_domains
,
5926 CFStringRef val_domain_name
,
5927 CFArrayRef val_servers
,
5928 CFArrayRef val_sortlist
)
5930 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
5932 /* publish new resolv.conf */
5937 SCPrint(TRUE
, f
, CFSTR("#\n"));
5938 SCPrint(TRUE
, f
, CFSTR("# macOS Notice\n"));
5939 SCPrint(TRUE
, f
, CFSTR("#\n"));
5940 SCPrint(TRUE
, f
, CFSTR("# This file is not consulted for DNS hostname resolution, address\n"));
5941 SCPrint(TRUE
, f
, CFSTR("# resolution, or the DNS query routing mechanism used by most\n"));
5942 SCPrint(TRUE
, f
, CFSTR("# processes on this system.\n"));
5943 SCPrint(TRUE
, f
, CFSTR("#\n"));
5944 SCPrint(TRUE
, f
, CFSTR("# To view the DNS configuration used by this system, use:\n"));
5945 SCPrint(TRUE
, f
, CFSTR("# scutil --dns\n"));
5946 SCPrint(TRUE
, f
, CFSTR("#\n"));
5947 SCPrint(TRUE
, f
, CFSTR("# SEE ALSO\n"));
5948 SCPrint(TRUE
, f
, CFSTR("# dns-sd(1), scutil(8)\n"));
5949 SCPrint(TRUE
, f
, CFSTR("#\n"));
5950 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
5951 SCPrint(TRUE
, f
, CFSTR("#\n"));
5953 if (isA_CFArray(val_search_domains
)) {
5954 SCPrint(TRUE
, f
, CFSTR("search"));
5955 n
= CFArrayGetCount(val_search_domains
);
5956 for (i
= 0; i
< n
; i
++) {
5959 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
5960 if (isA_CFString(domain
)) {
5961 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
5964 SCPrint(TRUE
, f
, CFSTR("\n"));
5966 else if (isA_CFString(val_domain_name
)) {
5967 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
5970 if (isA_CFArray(val_servers
)) {
5971 n
= CFArrayGetCount(val_servers
);
5972 for (i
= 0; i
< n
; i
++) {
5973 CFStringRef nameserver
;
5975 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
5976 if (isA_CFString(nameserver
)) {
5977 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
5982 if (isA_CFArray(val_sortlist
)) {
5983 SCPrint(TRUE
, f
, CFSTR("sortlist"));
5984 n
= CFArrayGetCount(val_sortlist
);
5985 for (i
= 0; i
< n
; i
++) {
5986 CFStringRef address
;
5988 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
5989 if (isA_CFString(address
)) {
5990 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
5993 SCPrint(TRUE
, f
, CFSTR("\n"));
5997 (void)rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
6001 #endif /* !TARGET_OS_IPHONE */
6004 service_get_ip_is_coupled(CFStringRef serviceID
)
6006 CFDictionaryRef dict
;
6007 boolean_t ip_is_coupled
= FALSE
;
6009 dict
= service_dict_get(serviceID
, kSCEntNetService
);
6011 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
6012 ip_is_coupled
= TRUE
;
6015 return (ip_is_coupled
);
6019 my_CFStringCreateWithInAddr(struct in_addr ip
)
6023 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(IP_FORMAT
), IP_LIST(&ip
));
6028 my_CFStringCreateWithIn6Addr(const struct in6_addr
* ip
)
6030 char ntopbuf
[INET6_ADDRSTRLEN
];
6032 (void)inet_ntop(AF_INET6
, ip
, ntopbuf
, sizeof(ntopbuf
));
6033 return (CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), ntopbuf
));
6037 * Function: update_ipv4
6039 * Update the IPv4 configuration based on the latest information.
6040 * Publish the State:/Network/Global/IPv4 information, and update the
6041 * IPv4 routing table.
6044 update_ipv4(CFStringRef primary
,
6045 IPv4RouteListRef new_routelist
,
6046 keyChangeListRef keys
)
6048 #if !TARGET_OS_SIMULATOR
6050 #endif /* !TARGET_OS_SIMULATOR */
6053 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6054 const char * ifn_p
= NULL
;
6055 char ifname
[IFNAMSIZ
];
6057 CFMutableDictionaryRef dict
= NULL
;
6059 dict
= CFDictionaryCreateMutable(NULL
, 0,
6060 &kCFTypeDictionaryKeyCallBacks
,
6061 &kCFTypeDictionaryValueCallBacks
);
6062 /* the first entry is the default route */
6063 r
= new_routelist
->list
;
6064 if (r
->gateway
.s_addr
!= 0) {
6067 str
= my_CFStringCreateWithInAddr(r
->gateway
);
6068 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, str
);
6071 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6072 if (ifn_p
!= NULL
) {
6073 CFStringRef ifname_cf
;
6075 ifname_cf
= CFStringCreateWithCString(NULL
,
6077 kCFStringEncodingASCII
);
6078 if (ifname_cf
!= NULL
) {
6079 CFDictionarySetValue(dict
,
6080 kSCDynamicStorePropNetPrimaryInterface
,
6082 CFRelease(ifname_cf
);
6085 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6087 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
6091 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
6095 #if !TARGET_OS_SIMULATOR
6096 sockfd
= open_routing_socket();
6098 /* go through routelist and bind any unbound routes */
6099 if (new_routelist
!= NULL
) {
6100 IPv4RouteListFinalize(new_routelist
);
6103 /* provide a routelist with just loopback multicast */
6104 new_routelist
= IPv4RouteListCopyMulticastLoopback();
6106 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6107 if (S_ipv4_routelist
== NULL
) {
6108 my_log(LOG_DEBUG
, "Old Routes = <none>");
6111 my_log(LOG_DEBUG
, "Old Routes = ");
6112 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
6114 if (new_routelist
== NULL
) {
6115 my_log(LOG_DEBUG
, "New Routes = <none>");
6118 my_log(LOG_DEBUG
, "New Routes = ");
6119 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
6122 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
, sockfd
);
6125 if (S_ipv4_routelist
!= NULL
) {
6126 free(S_ipv4_routelist
);
6128 S_ipv4_routelist
= new_routelist
;
6129 #else /* !TARGET_OS_SIMULATOR */
6130 if (new_routelist
!= NULL
) {
6131 free(new_routelist
);
6133 #endif /* !TARGET_OS_SIMULATOR */
6139 * Function: update_ipv6
6141 * Update the IPv6 configuration based on the latest information.
6142 * Publish the State:/Network/Global/IPv6 information, and update the
6143 * IPv6 routing table.
6146 update_ipv6(CFStringRef primary
,
6147 IPv6RouteListRef new_routelist
,
6148 keyChangeListRef keys
)
6150 #if !TARGET_OS_SIMULATOR
6152 #endif /* !TARGET_OS_SIMULATOR */
6155 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6156 const char * ifn_p
= NULL
;
6157 char ifname
[IFNAMSIZ
];
6159 CFMutableDictionaryRef dict
= NULL
;
6161 dict
= CFDictionaryCreateMutable(NULL
, 0,
6162 &kCFTypeDictionaryKeyCallBacks
,
6163 &kCFTypeDictionaryValueCallBacks
);
6164 /* the first entry is the default route */
6165 r
= new_routelist
->list
;
6166 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
6169 router
= my_CFStringCreateWithIn6Addr(&r
->gateway
);
6170 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
, router
);
6173 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6174 if (ifn_p
!= NULL
) {
6175 CFStringRef ifname_cf
;
6177 ifname_cf
= CFStringCreateWithCString(NULL
,
6179 kCFStringEncodingASCII
);
6180 if (ifname_cf
!= NULL
) {
6181 CFDictionarySetValue(dict
,
6182 kSCDynamicStorePropNetPrimaryInterface
,
6184 CFRelease(ifname_cf
);
6187 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6189 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
6191 #if !TARGET_OS_SIMULATOR
6192 set_ipv6_default_interface(r
->ifindex
);
6193 #endif /* !TARGET_OS_SIMULATOR */
6196 #if !TARGET_OS_SIMULATOR
6197 set_ipv6_default_interface(0);
6198 #endif /* !TARGET_OS_SIMULATOR */
6199 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
6203 #if !TARGET_OS_SIMULATOR
6204 sockfd
= open_routing_socket();
6206 /* go through routelist and bind any unbound routes */
6207 IPv6RouteListFinalize(new_routelist
);
6208 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6209 if (S_ipv6_routelist
== NULL
) {
6210 my_log(LOG_DEBUG
, "Old Routes = <none>");
6213 my_log(LOG_DEBUG
, "Old Routes = ");
6214 IPv6RouteListLog(LOG_DEBUG
, S_ipv6_routelist
);
6216 if (new_routelist
== NULL
) {
6217 my_log(LOG_DEBUG
, "New Routes = <none>");
6220 my_log(LOG_DEBUG
, "New Routes = ");
6221 IPv6RouteListLog(LOG_DEBUG
, new_routelist
);
6224 IPv6RouteListApply(S_ipv6_routelist
, new_routelist
, sockfd
);
6227 if (S_ipv6_routelist
!= NULL
) {
6228 free(S_ipv6_routelist
);
6230 S_ipv6_routelist
= new_routelist
;
6231 #else /* !TARGET_OS_SIMULATOR */
6232 if (new_routelist
!= NULL
) {
6233 free(new_routelist
);
6235 #endif /* !TARGET_OS_SIMULATOR */
6241 update_dns(CFDictionaryRef services_info
,
6242 CFStringRef primary
,
6243 keyChangeListRef keys
)
6245 #pragma unused(services_info)
6246 Boolean changed
= FALSE
;
6247 CFDictionaryRef dict
= NULL
;
6249 if (primary
!= NULL
) {
6250 CFDictionaryRef service_dict
;
6252 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6253 if (service_dict
!= NULL
) {
6254 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6258 if (!_SC_CFEqual(S_dns_dict
, dict
)) {
6260 #if !TARGET_OS_IPHONE
6262 #endif /* !TARGET_OS_IPHONE */
6263 keyChangeListRemoveValue(keys
, S_state_global_dns
);
6265 CFMutableDictionaryRef new_dict
;
6267 #if !TARGET_OS_IPHONE
6268 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
6269 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
6270 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
6271 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
6272 #endif /* !TARGET_OS_IPHONE */
6273 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
6274 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
6275 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
6276 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
6277 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
6278 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
6279 CFRelease(new_dict
);
6284 if (dict
!= NULL
) CFRetain(dict
);
6285 if (S_dns_dict
!= NULL
) CFRelease(S_dns_dict
);
6292 update_dnsinfo(CFDictionaryRef services_info
,
6293 CFStringRef primary
,
6294 keyChangeListRef keys
,
6295 CFArrayRef service_order
)
6298 CFDictionaryRef dict
= NULL
;
6299 CFArrayRef multicastResolvers
;
6300 CFArrayRef privateResolvers
;
6302 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
6303 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
6305 if (primary
!= NULL
) {
6306 CFDictionaryRef service_dict
;
6308 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6309 if (service_dict
!= NULL
) {
6310 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6314 changed
= dns_configuration_set(dict
,
6315 S_service_state_dict
,
6320 keyChangeListNotifyKey(keys
, S_state_global_dns
);
6326 update_nwi(nwi_state_t state
)
6328 unsigned char signature
[CC_SHA256_DIGEST_LENGTH
];
6329 static unsigned char signature_last
[CC_SHA256_DIGEST_LENGTH
];
6331 _nwi_state_compute_sha256_hash(state
, signature
);
6332 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
6333 my_log(LOG_DEBUG
, "Not updating network information");
6337 // save [new] signature
6338 memcpy(signature_last
, signature
, sizeof(signature
));
6340 // save [new] configuration
6341 my_log(LOG_INFO
, "Updating network information");
6342 _nwi_state_log(state
, TRUE
, NULL
);
6344 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
6345 if (!_nwi_state_store(state
)) {
6346 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
6348 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
6354 update_proxies(CFDictionaryRef services_info
,
6355 CFStringRef primary
,
6356 keyChangeListRef keys
,
6357 CFArrayRef service_order
)
6359 Boolean changed
= FALSE
;
6360 CFDictionaryRef dict
= NULL
;
6361 CFDictionaryRef new_dict
;
6363 if (primary
!= NULL
) {
6364 CFDictionaryRef service_dict
;
6366 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6367 if (service_dict
!= NULL
) {
6368 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
6372 new_dict
= proxy_configuration_update(dict
,
6373 S_service_state_dict
,
6376 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
6377 if (new_dict
== NULL
) {
6378 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
6380 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
6385 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
6386 S_proxies_dict
= new_dict
;
6391 #if !TARGET_OS_IPHONE
6393 update_smb(CFDictionaryRef services_info
,
6394 CFStringRef primary
,
6395 keyChangeListRef keys
)
6397 #pragma unused(services_info)
6398 Boolean changed
= FALSE
;
6399 CFDictionaryRef dict
= NULL
;
6401 if (primary
!= NULL
) {
6402 CFDictionaryRef service_dict
;
6404 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6405 if (service_dict
!= NULL
) {
6406 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
6410 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
6412 keyChangeListRemoveValue(keys
, S_state_global_smb
);
6414 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
6419 if (dict
!= NULL
) CFRetain(dict
);
6420 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
6425 #endif /* !TARGET_OS_IPHONE */
6428 get_service_index(CFDictionaryRef rank_entity
,
6429 CFArrayRef order
, CFIndex n_order
, CFStringRef serviceID
)
6432 Rank rank
= kRankIndexMask
;
6433 CFNumberRef service_index
;
6436 = service_rank_entity_get_index(rank_entity
,
6440 if (service_index
!= NULL
) {
6441 /* ServiceIndex specified in service entity */
6444 "%@ specifies ServiceIndex %@, effective index is %d",
6445 serviceID
, service_index
, rank
);
6447 else if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
6448 for (i
= 0; i
< n_order
; i
++) {
6449 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
6454 if (CFEqual(serviceID
, s
)) {
6464 ** Service election:
6467 * Function: rank_dict_get_service_rank
6469 * Retrieve the service rank in the given dictionary.
6472 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
6475 Rank rank_val
= kRankAssertionDefault
;
6477 rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
6478 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
6480 if (!CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
)) {
6481 /* if we don't like the rank value */
6482 rank_val
= kRankAssertionDefault
;
6490 * Function: rank_dict_set_service_rank
6492 * Save the results of ranking the service so we can look it up later without
6493 * repeating all of the ranking code.
6496 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
6497 CFStringRef serviceID
, Rank rank_val
)
6501 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
6503 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
6509 static const CFStringRef
*transientInterfaceEntityNames
[] = {
6515 CollectTransientServices(const void * key
,
6519 #pragma unused(value)
6520 CFStringRef service
= key
;
6521 CFMutableArrayRef vif_setup_keys
= context
;
6523 /* This service is either a vpn type service or a comm center service */
6524 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
6528 for (size_t i
= 0; i
< countof(transientInterfaceEntityNames
); i
++) {
6529 if (CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
6530 my_CFArrayAppendUniqueValue(vif_setup_keys
, service
);
6539 static SCNetworkReachabilityFlags
6540 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
6541 CFStringRef service_id
,
6543 CFStringRef vpn_setup_key
)
6546 CFDictionaryRef dict
;
6547 SCNetworkReachabilityFlags flags
= 0;
6550 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6551 kSCDynamicStoreDomainSetup
,
6553 kSCEntNetInterface
);
6554 dict
= CFDictionaryGetValue(services_info
, key
);
6557 if (isA_CFDictionary(dict
)
6558 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
6560 flags
= (kSCNetworkReachabilityFlagsReachable
6561 | kSCNetworkReachabilityFlagsTransientConnection
6562 | kSCNetworkReachabilityFlagsConnectionRequired
);
6564 if (CFEqual(entity
, kSCEntNetPPP
)) {
6566 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
6568 if (!isA_CFDictionary(p_dict
)) {
6572 // get PPP dial-on-traffic status
6573 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
6574 if (isA_CFNumber(num
)) {
6577 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
6579 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6589 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
6591 Boolean ret
= def_value
;
6596 val
= CFDictionaryGetValue(dict
, key
);
6597 if (isA_CFBoolean(val
) != NULL
) {
6598 ret
= CFBooleanGetValue(val
);
6606 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
6607 SCNetworkReachabilityFlags
*reach_flags_v4
,
6608 SCNetworkReachabilityFlags
*reach_flags_v6
)
6612 CFMutableArrayRef vif_setup_keys
;
6614 vif_setup_keys
= CFArrayCreateMutable(NULL
,
6616 &kCFTypeArrayCallBacks
);
6617 CFDictionaryApplyFunction(services_info
, CollectTransientServices
,
6619 count
= CFArrayGetCount(vif_setup_keys
);
6620 for (i
= 0; i
< count
; i
++) {
6621 CFArrayRef components
= NULL
;
6623 CFStringRef service_id
;
6624 CFStringRef vif_setup_key
;
6626 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
6629 * setup key in the following format:
6630 * Setup:/Network/Service/<Service ID>/<Entity>
6632 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
6634 if (CFArrayGetCount(components
) != 5) {
6635 // invalid Setup key encountered
6639 /* service id is the 3rd element */
6640 service_id
= CFArrayGetValueAtIndex(components
, 3);
6642 /* entity id is the 4th element */
6643 entity
= CFArrayGetValueAtIndex(components
, 4);
6646 if (CFEqual(entity
, kSCEntNetPPP
)) {
6647 SCNetworkReachabilityFlags flags
;
6650 flags
= GetReachabilityFlagsFromVPN(services_info
,
6655 /* Check for the v4 reachability flags */
6656 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6657 kSCDynamicStoreDomainSetup
,
6661 if (CFDictionaryContainsKey(services_info
, key
)) {
6662 *reach_flags_v4
|= flags
;
6663 my_log(LOG_DEBUG
, "Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
6668 /* Check for the v6 reachability flags */
6669 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6670 kSCDynamicStoreDomainSetup
,
6674 if (CFDictionaryContainsKey(services_info
, key
)) {
6675 *reach_flags_v6
|= flags
;
6676 my_log(LOG_DEBUG
, "Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
6681 if (components
!= NULL
) {
6682 CFRelease(components
);
6688 if (components
!= NULL
) {
6689 CFRelease(components
);
6693 CFRelease(vif_setup_keys
);
6697 static SCNetworkReachabilityFlags
6698 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
6700 SCNetworkReachabilityFlags flags
= 0;
6702 if (CFEqual(entity
, kSCEntNetPPP
)) {
6705 /* if we're really UP and RUNNING */
6708 /* if we're effectively UP and RUNNING */
6711 /* if we're not connected at all */
6712 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6714 case PPP_STATERESERVED
:
6715 // if we're not connected at all
6716 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6719 /* if we're in the process of [dis]connecting */
6720 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6724 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
6726 case IPSEC_RUNNING
:
6727 /* if we're really UP and RUNNING */
6730 /* if we're not connected at all */
6731 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6734 /* if we're in the process of [dis]connecting */
6735 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6739 else if (CFEqual(entity
, kSCEntNetVPN
)) {
6742 /* if we're really UP and RUNNING */
6747 case VPN_UNLOADING
:
6748 /* if we're not connected at all */
6749 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6752 /* if we're in the process of [dis]connecting */
6753 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6761 VPNAttributesGet(CFStringRef service_id
,
6762 CFDictionaryRef services_info
,
6763 SCNetworkReachabilityFlags
*flags
,
6764 CFStringRef
*server_address
,
6767 CFDictionaryRef entity_dict
;
6769 CFDictionaryRef p_state
= NULL
;
6771 CFStringRef transient_entity
= NULL
;
6773 if (af
== AF_INET
) {
6774 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv4
);
6776 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv6
);
6778 entity_dict
= ipdict_get_service(entity_dict
);
6779 if (entity_dict
== NULL
) {
6783 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
6784 CFStringRef entity
= *transientServiceInfo
[i
].entityName
;
6786 p_state
= service_dict_get(service_id
, entity
);
6788 /* ensure that this is a VPN Type service */
6789 if (isA_CFDictionary(p_state
)) {
6790 transient_entity
= entity
;
6795 /* Did we find a vpn type service? If not, we are done.*/
6796 if (transient_entity
== NULL
) {
6800 *flags
|= (kSCNetworkReachabilityFlagsReachable
6801 | kSCNetworkReachabilityFlagsTransientConnection
);
6803 /* Get the Server Address */
6804 if (server_address
!= NULL
) {
6805 *server_address
= CFDictionaryGetValue(entity_dict
,
6806 CFSTR("ServerAddress"));
6807 *server_address
= isA_CFString(*server_address
);
6808 if (*server_address
!= NULL
) {
6809 CFRetain(*server_address
);
6814 if (!CFDictionaryGetValueIfPresent(p_state
,
6815 kSCPropNetVPNStatus
, // IPSecStatus, PPPStatus, VPNStatus
6816 (const void **)&num
) ||
6817 !isA_CFNumber(num
) ||
6818 !CFNumberGetValue(num
, kCFNumberIntType
, &status
)) {
6822 *flags
|= GetReachFlagsFromStatus(transient_entity
, status
);
6823 if (CFEqual(transient_entity
, kSCEntNetPPP
)) {
6825 CFDictionaryRef p_setup
;
6828 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6829 kSCDynamicStoreDomainSetup
,
6832 p_setup
= CFDictionaryGetValue(services_info
, key
);
6835 /* get dial-on-traffic status */
6836 if (isA_CFDictionary(p_setup
) &&
6837 CFDictionaryGetValueIfPresent(p_setup
,
6838 kSCPropNetPPPDialOnDemand
,
6839 (const void **)&num
) &&
6840 isA_CFNumber(num
) &&
6841 CFNumberGetValue(num
, kCFNumberIntType
, &ppp_demand
) &&
6842 (ppp_demand
!= 0)) {
6843 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6844 if (status
== PPP_IDLE
) {
6845 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
6853 typedef struct ElectionInfo
{
6859 ElectionResultsRef results
;
6860 CFMutableDictionaryRef rank_dict
;
6861 } ElectionInfo
, * ElectionInfoRef
;
6863 typedef CFDictionaryApplierFunction ElectionFuncRef
;
6866 CandidateRelease(CandidateRef candidate
)
6868 my_CFRelease(&candidate
->serviceID
);
6869 my_CFRelease(&candidate
->if_name
);
6870 my_CFRelease(&candidate
->signature
);
6875 CandidateCopy(CandidateRef dest
, CandidateRef src
)
6878 if (dest
->serviceID
) {
6879 CFRetain(dest
->serviceID
);
6881 if (dest
->if_name
) {
6882 CFRetain(dest
->if_name
);
6884 if(dest
->signature
) {
6885 CFRetain(dest
->signature
);
6890 static ElectionResultsRef
6891 ElectionResultsAlloc(int af
, int size
)
6893 ElectionResultsRef results
;
6895 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
6898 results
->size
= size
;
6903 ElectionResultsRelease(ElectionResultsRef results
)
6908 for (i
= 0, scan
= results
->candidates
;
6911 CandidateRelease(scan
);
6918 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
6923 if (results
== NULL
) {
6924 my_log(level
, "%s: no candidates", prefix
);
6927 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
6928 for (i
= 0, scan
= results
->candidates
;
6931 char ntopbuf
[INET6_ADDRSTRLEN
];
6933 (void)inet_ntop(results
->af
, &scan
->addr
, ntopbuf
, sizeof(ntopbuf
));
6934 my_log(level
, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6935 i
, scan
->if_name
, scan
->serviceID
, ntopbuf
, scan
->rank
,
6936 scan
->ineligible
? " [ineligible]" : "");
6942 * Function: ElectionResultsAddCandidate
6944 * Add the candidate into the election results. Find the insertion point
6945 * by comparing the rank of the candidate with existing entries.
6948 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
6953 if (results
->count
== results
->size
) {
6954 /* this should not happen */
6955 my_log(LOG_NOTICE
, "can't fit another candidate");
6959 /* find the insertion point */
6960 where
= kCFNotFound
;
6961 for (i
= 0; i
< results
->count
; i
++) {
6962 CandidateRef this_candidate
= results
->candidates
+ i
;
6964 if (candidate
->rank
< this_candidate
->rank
) {
6969 /* add it to the end */
6970 if (where
== kCFNotFound
) {
6971 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
6975 /* slide existing entries over */
6976 for (i
= results
->count
; i
> where
; i
--) {
6977 results
->candidates
[i
] = results
->candidates
[i
- 1];
6979 /* insert element */
6980 CandidateCopy(results
->candidates
+ where
, candidate
);
6986 elect_ip(const void * key
, const void * value
, void * context
);
6989 * Function: ElectionResultsCopy
6991 * Visit all of the services and invoke the protocol-specific election
6992 * function. Return the results of the election.
6994 static ElectionResultsRef
6995 ElectionResultsCopy(int af
, CFArrayRef order
)
7000 count
= (int)CFDictionaryGetCount(S_service_state_dict
);
7005 if (af
== AF_INET
) {
7006 info
.entity
= kSCEntNetIPv4
;
7007 info
.rank_dict
= S_ipv4_service_rank_dict
;
7010 info
.entity
= kSCEntNetIPv6
;
7011 info
.rank_dict
= S_ipv6_service_rank_dict
;
7013 info
.results
= ElectionResultsAlloc(af
, count
);
7014 info
.n_services
= count
;
7016 if (order
!= NULL
) {
7017 info
.n_order
= CFArrayGetCount(order
);
7022 CFDictionaryApplyFunction(S_service_state_dict
, elect_ip
, (void *)&info
);
7023 if (info
.results
->count
== 0) {
7024 ElectionResultsRelease(info
.results
);
7025 info
.results
= NULL
;
7027 return (info
.results
);
7031 * Function: ElectionResultsCandidateNeedsDemotion
7033 * Check whether the given candidate requires demotion. A candidate
7034 * might need to be demoted if its IPv4 and IPv6 services must be coupled
7035 * but a higher ranked service has IPv4 or IPv6.
7037 * The converse is also true: if the given candidate has lower rank than
7038 * the other candidate and the other candidate is coupled, this candidate
7039 * needs to be demoted.
7042 ElectionResultsCandidateNeedsDemotion(CandidateRef other_candidate
,
7043 CandidateRef candidate
)
7045 Boolean ret
= FALSE
;
7047 if (other_candidate
== NULL
) {
7048 /* no other candidate */
7051 if (other_candidate
->ineligible
) {
7052 /* other candidate can't become primary */
7055 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
7056 /* the other candidate can't become primary */
7059 if (!candidate
->ip_is_coupled
&& !other_candidate
->ip_is_coupled
) {
7060 /* neither candidate is coupled */
7063 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
7064 /* they are over the same interface, no need to demote */
7067 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
7068 /* avoid creating a feedback loop */
7071 if (candidate
->rank
< other_candidate
->rank
) {
7072 /* we're higher ranked than the other candidate, ignore */
7075 if (candidate
->ip_is_coupled
) {
7076 if (other_candidate
->ip_is_coupled
7077 && candidate
->rank
== other_candidate
->rank
) {
7078 /* same rank as another service that is coupled, ignore */
7082 else if (other_candidate
->ip_is_coupled
) { /* must be true */
7083 if (candidate
->rank
== other_candidate
->rank
) {
7084 /* other candidate will be demoted, so we don't need to */
7087 /* we're lower rank and need to be demoted */
7089 else { /* can't happen, we already tested for this above */
7090 /* neither candidate is coupled */
7102 get_signature_sha256(CFStringRef signature
,
7103 unsigned char * sha256
)
7106 CFDataRef signature_data
;
7108 signature_data
= CFStringCreateExternalRepresentation(NULL
,
7110 kCFStringEncodingUTF8
,
7113 CC_SHA256_Init(&ctx
);
7114 CC_SHA256_Update(&ctx
,
7115 CFDataGetBytePtr(signature_data
),
7116 (CC_LONG
)CFDataGetLength(signature_data
));
7117 CC_SHA256_Final(sha256
, &ctx
);
7119 CFRelease(signature_data
);
7126 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
7127 CandidateRef candidate
, Boolean not_in_list
,
7128 Boolean not_in_iflist
)
7131 char ifname
[IFNAMSIZ
];
7132 nwi_ifstate_t ifstate
;
7134 if (nwi_state
== NULL
) {
7139 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
7140 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
7142 if (not_in_iflist
) {
7143 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST
;
7145 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
7146 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
7148 if ((af
== AF_INET
) && service_has_clat46_address(candidate
->serviceID
)) {
7149 flags
|= NWI_IFSTATE_FLAGS_HAS_CLAT46
;
7151 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
7152 kCFStringEncodingASCII
);
7153 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
7154 char ntopbuf
[INET6_ADDRSTRLEN
];
7156 (void)inet_ntop(af
, &candidate
->addr
, ntopbuf
, sizeof(ntopbuf
));
7158 "Adding IPv%c [%s] %s "
7159 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
7160 ipvx_char(af
), ifname
, ntopbuf
,
7161 flags
, candidate
->rank
, candidate
->reachability_flags
);
7163 ifstate
= nwi_state_add_ifstate(nwi_state
, ifname
, af
, flags
,
7165 (void *)&candidate
->addr
,
7166 (void *)&candidate
->vpn_server_addr
,
7167 candidate
->reachability_flags
);
7168 if (ifstate
!= NULL
&& candidate
->signature
) {
7169 uint8_t hash
[CC_SHA256_DIGEST_LENGTH
];
7171 get_signature_sha256(candidate
->signature
, hash
);
7172 nwi_ifstate_set_signature(ifstate
, hash
);
7179 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
7181 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
7182 CFStringRef vpn_server_address
= NULL
;
7184 assert(candidate
!= NULL
);
7185 assert(services_info
!= NULL
);
7187 VPNAttributesGet(candidate
->serviceID
,
7190 &vpn_server_address
,
7193 candidate
->reachability_flags
= flags
;
7195 if (vpn_server_address
== NULL
) {
7196 memset(&candidate
->vpn_server_addr
, 0, sizeof(candidate
->vpn_server_addr
));
7200 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
),
7201 kCFStringEncodingASCII
);
7202 _SC_string_to_sockaddr(buf
,
7204 (void *)&candidate
->vpn_server_addr
,
7205 sizeof(candidate
->vpn_server_addr
));
7207 CFRelease(vpn_server_address
);
7212 * Function: ElectionResultsGetPrimary
7214 * Use the results of the current protocol and the other protocol to
7215 * determine which service should become primary.
7217 * At the same time, generate the IPv4/IPv6 routing table and
7218 * the nwi_state for the protocol.
7221 ElectionResultsGetPrimary(ElectionResultsRef results
,
7222 CandidateRef other_candidate
,
7223 nwi_state_t nwi_state
, int af
,
7224 RouteListRef
* ret_routes
,
7225 CFDictionaryRef services_info
,
7226 CFSetRef ip_service_changes
)
7228 CandidateRef primary
= NULL
;
7229 Boolean primary_is_null
= FALSE
;
7230 RouteListRef routes
= NULL
;
7232 assert(services_info
!= NULL
);
7234 if (results
!= NULL
) {
7235 CandidateRef deferred
[results
->count
];
7237 CFStringRef entity_name
;
7240 RouteListInfoRef info
;
7245 entity_name
= kSCEntNetIPv4
;
7246 info
= &IPv4RouteListInfo
;
7247 initial_size
= results
->count
* IPV4_ROUTES_N_STATIC
;
7251 entity_name
= kSCEntNetIPv6
;
7252 info
= &IPv6RouteListInfo
;
7253 initial_size
= results
->count
* IPV6_ROUTES_N_STATIC
;
7257 for (i
= 0, scan
= results
->candidates
;
7260 Boolean is_primary
= FALSE
;
7261 CFDictionaryRef service_dict
;
7262 RouteListRef service_routes
;
7263 Boolean skip
= FALSE
;
7265 if (!scan
->ineligible
7267 && RANK_ASSERTION_MASK(scan
->rank
) != kRankAssertionNever
) {
7268 if (ElectionResultsCandidateNeedsDemotion(other_candidate
,
7270 /* demote the service */
7272 "IPv%c over %@ (rank 0x%x) demoted: "
7273 "primary IPv%c %@ (rank 0x%x)",
7274 ipvx_char(af
), scan
->if_name
, scan
->rank
,
7275 ipvx_other_char(af
), other_candidate
->if_name
,
7276 other_candidate
->rank
);
7277 deferred
[deferred_count
++] = scan
;
7285 /* contribute to the routing table */
7286 service_dict
= service_dict_get(scan
->serviceID
, entity_name
);
7287 service_routes
= ipdict_get_routelist(service_dict
);
7288 if (service_routes
!= NULL
) {
7290 Rank rank
= scan
->rank
;
7293 /* routes are RankNever to prevent becoming primary */
7294 rank
= RankMake(rank
, kRankAssertionNever
);
7296 force
= my_CFSetContainsValue(ip_service_changes
,
7298 routes
= RouteListAddRouteList(info
, routes
, initial_size
,
7299 service_routes
, rank
, force
);
7300 if ((service_routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
7308 /* if we're skipping the primary, it's NULL */
7310 primary_is_null
= TRUE
;
7313 else if (!scan
->ineligible
) {
7314 Boolean not_in_iflist
;
7316 add_reachability_flags_to_candidate(scan
, services_info
, af
);
7318 = (service_routes
->flags
& kRouteListFlagsScopedOnly
) != 0;
7319 add_candidate_to_nwi_state(nwi_state
, af
, scan
,
7324 for (i
= 0; i
< deferred_count
; i
++) {
7325 CandidateRef candidate
= deferred
[i
];
7327 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
7328 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, TRUE
, FALSE
);
7331 if (ret_routes
!= NULL
) {
7332 *ret_routes
= routes
;
7334 else if (routes
!= NULL
) {
7337 if (primary_is_null
) {
7346 service_dict_get_signature(CFDictionaryRef service_dict
)
7350 ifname
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7351 if (isA_CFString(ifname
) == NULL
7352 || !confirm_interface_name(service_dict
, ifname
)) {
7355 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
7359 * Function: elect_ip
7361 * Evaluate the service and determine what rank the service should have.
7362 * If it's a suitable candidate, add it to the election results.
7365 elect_ip(const void * key
, const void * value
, void * context
)
7367 CFDictionaryRef all_entities_dict
= (CFDictionaryRef
)value
;
7368 Candidate candidate
;
7370 ElectionInfoRef elect_info
;
7371 CFStringRef if_name
;
7372 CFDictionaryRef ipdict
;
7374 CFDictionaryRef rank_entity
;
7375 RouteListUnion routelist
;
7376 CFDictionaryRef service_dict
;
7378 elect_info
= (ElectionInfoRef
)context
;
7379 ipdict
= CFDictionaryGetValue(all_entities_dict
, elect_info
->entity
);
7380 if (ipdict
!= NULL
) {
7381 routelist
.ptr
= ipdict_get_routelist(ipdict
);
7382 service_dict
= ipdict_get_service(ipdict
);
7385 routelist
.ptr
= NULL
;
7387 if (routelist
.ptr
== NULL
|| service_dict
== NULL
) {
7388 /* no connectivity */
7391 if_name
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7392 if (if_name
== NULL
) {
7393 /* need an interface name */
7396 if (CFEqual(if_name
, CFSTR(kLoopbackInterface
))) {
7397 /* don't process loopback */
7400 memset(&candidate
, 0, sizeof(candidate
));
7401 candidate
.serviceID
= (CFStringRef
)key
;
7402 if ((routelist
.common
->flags
& kRouteListFlagsHasDefault
) == 0) {
7403 /* no default route means it's ineligible to become primary */
7404 candidate
.ineligible
= TRUE
;
7406 rank_entity
= CFDictionaryGetValue(all_entities_dict
, kSCEntNetService
);
7407 candidate
.rank
= get_service_index(rank_entity
,
7408 elect_info
->order
, elect_info
->n_order
,
7409 candidate
.serviceID
);
7410 if (elect_info
->af
== AF_INET
) {
7411 default_rank
= routelist
.v4
->list
->rank
;
7412 candidate
.addr
.v4
= routelist
.v4
->list
->ifa
;
7415 default_rank
= routelist
.v6
->list
->rank
;
7416 candidate
.addr
.v6
= routelist
.v6
->list
->ifa
;
7418 primary_rank
= RANK_ASSERTION_MASK(default_rank
);
7419 if (S_ppp_override_primary
) {
7422 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
7423 kCFStringEncodingASCII
)
7424 && (strncmp(PPP_PREFIX
, ifn
, sizeof(PPP_PREFIX
) - 1) == 0)) {
7425 /* PPP override: make ppp* look the best */
7426 primary_rank
= kRankAssertionFirst
;
7429 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
7430 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
7431 candidate
.if_name
= if_name
;
7432 rank_dict_set_service_rank(elect_info
->rank_dict
,
7433 candidate
.serviceID
, candidate
.rank
);
7434 candidate
.signature
= service_dict_get_signature(service_dict
);
7435 ElectionResultsAddCandidate(elect_info
->results
, &candidate
);
7441 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
7443 uint32_t changed
= 0;
7446 /* update service options first (e.g. rank) */
7447 if (get_rank_changes(serviceID
,
7448 get_service_state_entity(services_info
, serviceID
,
7450 get_service_setup_entity(services_info
, serviceID
,
7453 changed
|= (1 << kEntityTypeServiceOptions
);
7456 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7457 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
7458 GetEntityChangesFuncRef func
;
7461 func
= entityChangeFunc
[i
];
7462 name
= *entityTypeNames
[i
];
7463 if ((*func
)(serviceID
,
7464 get_service_state_entity(services_info
, serviceID
, name
),
7465 get_service_setup_entity(services_info
, serviceID
, name
),
7467 changed
|= (1 << i
);
7471 /* update transient service status */
7472 if (get_transient_status_changes(serviceID
, services_info
)) {
7473 changed
|= (1 << kEntityTypeTransientStatus
);
7480 serviceID_get_ifname(CFStringRef serviceID
)
7482 CFDictionaryRef entity_dict
;
7483 CFStringRef ifname
= NULL
;
7485 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
7486 if (entity_dict
== NULL
) {
7487 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
7489 if (entity_dict
!= NULL
) {
7490 ifname
= ipdict_get_ifname(entity_dict
);
7495 __private_extern__ boolean_t
7496 check_if_service_expensive(CFStringRef serviceID
)
7499 ifname
= serviceID_get_ifname(serviceID
);
7501 return interface_is_expensive(ifname
);
7505 service_order_get(CFDictionaryRef services_info
)
7507 CFArrayRef order
= NULL
;
7508 CFDictionaryRef ipv4_dict
;
7510 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
7511 S_setup_global_ipv4
);
7512 if (ipv4_dict
!= NULL
) {
7513 CFNumberRef ppp_override
;
7516 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
7517 order
= isA_CFArray(order
);
7519 /* get ppp override primary */
7520 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
7521 kSCPropNetPPPOverridePrimary
);
7522 ppp_override
= isA_CFNumber(ppp_override
);
7523 if (ppp_override
!= NULL
) {
7524 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
7526 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
7529 S_ppp_override_primary
= FALSE
;
7535 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
7536 const char * entity
)
7538 boolean_t changed
= FALSE
;
7539 CFStringRef primary
= *primary_p
;
7541 if (new_primary
!= NULL
) {
7542 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
7543 my_log(LOG_INFO
, "%@ is still primary %s", new_primary
, entity
);
7546 my_CFRelease(primary_p
);
7547 *primary_p
= CFRetain(new_primary
);
7548 my_log(LOG_INFO
, "%@ is the new primary %s", new_primary
, entity
);
7552 else if (primary
!= NULL
) {
7553 my_log(LOG_INFO
, "%@ is no longer primary %s", primary
, entity
);
7554 my_CFRelease(primary_p
);
7561 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
7564 if (service_dict_get(serviceID
, entity
) == NULL
) {
7565 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
7567 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
7571 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
7577 #define N_KEYS_VALUES_STATIC 10
7578 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
7581 count
= CFDictionaryGetCount(S_service_state_dict
);
7582 if (count
<= N_KEYS_VALUES_STATIC
) {
7583 keys
= keys_values_buf
;
7585 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
7587 values
= keys
+ count
;
7588 CFDictionaryGetKeysAndValues(S_service_state_dict
,
7589 (const void * *)keys
,
7590 (const void * *)values
);
7592 for (i
= 0; i
< count
; i
++) {
7593 CFDictionaryRef ipdict
= NULL
;
7594 CFStringRef interface
= NULL
;
7595 CFStringRef serviceID
;
7596 CFDictionaryRef service_dict
;
7598 serviceID
= (CFStringRef
)keys
[i
];
7599 service_dict
= (CFDictionaryRef
)values
[i
];
7601 /* check whether service has IPv4 or IPv6 */
7602 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
7603 if (ipdict
== NULL
) {
7604 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
7605 if (ipdict
== NULL
) {
7609 interface
= ipdict_get_ifname(ipdict
);
7610 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
7612 "Found IP service %@ on interface %@",
7614 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
7617 if (keys
!= keys_values_buf
) {
7623 static __inline__
const char *
7624 get_changed_str(CFStringRef serviceID
, CFStringRef entity
,
7625 CFDictionaryRef old_dict
)
7627 CFDictionaryRef new_dict
= NULL
;
7629 if (serviceID
!= NULL
) {
7630 new_dict
= service_dict_get(serviceID
, entity
);
7633 if (old_dict
== NULL
) {
7634 if (new_dict
!= NULL
) {
7638 if (new_dict
== NULL
) {
7640 } else if (!CFEqual(old_dict
, new_dict
)) {
7647 #if !TARGET_OS_SIMULATOR
7650 #define MANAGE_IF_ORDER
7651 #define MANAGE_IF_IOCTL
7652 #endif /* SIOCSIFORDER */
7654 #ifdef SIOCSIFNETSIGNATURE
7655 #define MANAGE_IF_SIGNATURE
7656 #define MANAGE_IF_IOCTL
7657 #endif /* SIOCSIFNETSIGNATURE */
7659 #ifdef MANAGE_IF_IOCTL
7661 inet_dgram_socket(void)
7665 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
7667 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
7672 #endif /* MANAGE_IF_IOCTL */
7674 #ifdef MANAGE_IF_ORDER
7676 interface_order_changed(nwi_state_t old_state
, nwi_state_t new_state
)
7678 if (old_state
== NULL
&& new_state
== NULL
) {
7679 // Both are NULL, nothing changed
7683 if (old_state
== NULL
|| new_state
== NULL
) {
7684 // One is NULL, something changed
7688 if (old_state
->if_list_count
!= new_state
->if_list_count
) {
7689 // Count is different, something changed
7693 if (new_state
->if_list_count
== 0) {
7694 // Count is same and 0, nothing changed
7699 nwi_ifindex_t
*old_scan
;
7700 nwi_ifindex_t
*new_scan
;
7701 for (i
= 0, old_scan
= nwi_state_if_list(old_state
), new_scan
= nwi_state_if_list(new_state
);
7702 i
< new_state
->if_list_count
; i
++, old_scan
++, new_scan
++) {
7703 if (strcmp(old_state
->ifstate_list
[*old_scan
].ifname
, new_state
->ifstate_list
[*new_scan
].ifname
) != 0) {
7704 // Some interface in the list is different, something changed
7709 // Count and contents are the same, nothing changed
7714 update_interface_order(nwi_state_t state
, int sockfd
)
7716 Boolean success
= FALSE
;
7718 // Set interface order into the kernel
7719 struct if_order interface_order
;
7720 interface_order
.ifo_count
= (uint32_t)state
->if_list_count
;
7721 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)calloc((size_t)interface_order
.ifo_count
, sizeof(uint32_t));
7722 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7724 nwi_ifindex_t
*scan
;
7725 for (i
= 0, scan
= nwi_state_if_list(state
);
7726 i
< state
->if_list_count
; i
++, scan
++) {
7727 const char *ifname
= state
->ifstate_list
[*scan
].ifname
;
7728 ((uint32_t *)interface_order
.ifo_ordered_indices
)[i
] = my_if_nametoindex(ifname
);
7731 if (ioctl(sockfd
, SIOCSIFORDER
, &interface_order
) != 0) {
7732 my_log(LOG_ERR
, "SIOCSIFORDER for %u(%p) failed on %d: %s", interface_order
.ifo_count
, (void *)interface_order
.ifo_ordered_indices
, sockfd
, strerror(errno
));
7734 my_log(LOG_INFO
, "Set kernel interface order for %u interfaces", interface_order
.ifo_count
);
7737 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7738 free((void *)interface_order
.ifo_ordered_indices
);
7739 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)NULL
;
7744 #endif /* MANAGE_IF_ORDER */
7746 #ifdef MANAGE_IF_SIGNATURE
7748 siocsifnetsignature(int s
, const char * ifname
, int af
,
7749 const uint8_t * signature
, size_t signature_length
)
7751 struct if_nsreq nsreq
;
7753 memset(&nsreq
, 0, sizeof(nsreq
));
7754 strlcpy(nsreq
.ifnsr_name
, ifname
, sizeof(nsreq
.ifnsr_name
));
7755 nsreq
.ifnsr_family
= af
;
7756 if (signature_length
> 0) {
7757 if (signature_length
> sizeof(nsreq
.ifnsr_data
)) {
7758 signature_length
= sizeof(nsreq
.ifnsr_data
);
7760 nsreq
.ifnsr_len
= signature_length
;
7761 memcpy(nsreq
.ifnsr_data
, signature
, signature_length
);
7763 return (ioctl(s
, SIOCSIFNETSIGNATURE
, &nsreq
));
7767 process_ifstate_difference(nwi_ifstate_t ifstate
, int af
, int sockfd
)
7769 nwi_ifstate_difference_t diff
;
7770 boolean_t set_signature
= FALSE
;
7771 int signature_length
= 0;
7773 diff
= nwi_ifstate_get_difference(ifstate
);
7775 case knwi_ifstate_difference_changed
:
7776 /* set signature for this interface */
7777 set_signature
= TRUE
;
7778 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_SIGNATURE
) != 0) {
7779 signature_length
= sizeof(ifstate
->signature
);
7782 case knwi_ifstate_difference_removed
:
7783 /* remove signature for this interface */
7784 set_signature
= TRUE
;
7789 if (set_signature
) {
7790 if (siocsifnetsignature(sockfd
, ifstate
->ifname
, af
,
7792 signature_length
) < 0) {
7794 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7795 ifstate
->ifname
, ipvx_char(af
),
7800 my_log(LOG_DEBUG
, "IPv%c Network Signature %s %s",
7802 (signature_length
> 0) ? "Set" : "Cleared",
7804 if (signature_length
> 0
7805 && (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7807 char sig_buf
[signature_length
* 3 + 1];
7810 for (i
= 0; i
< signature_length
; i
++) {
7813 snprintf(byte_buf
, sizeof(byte_buf
),
7814 "%02x ", ifstate
->signature
[i
]);
7815 strlcat(sig_buf
, byte_buf
, sizeof(sig_buf
));
7817 my_log(LOG_DEBUG
, "Signature Bytes: %s", sig_buf
);
7825 process_state_differences(nwi_state_t state
, int af
, int sockfd
)
7831 if (af
== AF_INET
) {
7832 count
= state
->ipv4_count
;
7835 count
= state
->ipv6_count
;
7837 for (i
= 0, scan
= nwi_state_ifstate_list(state
, af
);
7838 i
< count
; i
++, scan
++) {
7839 process_ifstate_difference(scan
, af
, sockfd
);
7843 #endif /* MANAGE_IF_SIGNATURE */
7845 #endif /* !TARGET_OS_SIMULATOR */
7848 process_nwi_changes(CFMutableStringRef log_output
,
7849 nwi_state_t changes_state
,
7850 nwi_state_t new_state
,
7851 nwi_state_t old_state
,
7852 boolean_t dns_changed
,
7853 boolean_t dnsinfo_changed
,
7854 CFDictionaryRef old_primary_dns
,
7855 boolean_t proxy_changed
,
7856 CFDictionaryRef old_primary_proxy
,
7857 boolean_t smb_changed
,
7858 CFDictionaryRef old_primary_smb
)
7860 #ifndef MANAGE_IF_ORDER
7861 #pragma unused(new_state)
7862 #pragma unused(old_state)
7863 #endif // !MANAGE_IF_ORDER
7864 #if TARGET_OS_IPHONE
7865 #pragma unused(smb_changed)
7866 #pragma unused(old_primary_smb)
7867 #endif // TARGET_OS_IPHONE
7869 if (changes_state
!= NULL
) {
7870 const sa_family_t af_list
[] = {AF_INET
, AF_INET6
};
7872 #ifdef MANAGE_IF_IOCTL
7873 int sockfd
= inet_dgram_socket();
7874 #endif /* MANAGE_IF_IOCTL */
7876 #ifdef MANAGE_IF_ORDER
7877 if (interface_order_changed(new_state
, old_state
)) {
7878 update_interface_order(new_state
, sockfd
);
7880 #endif /* MANAGE_IF_ORDER */
7882 for (size_t idx
= 0; idx
< countof(af_list
); idx
++) {
7883 int af
= af_list
[idx
];
7884 CFMutableStringRef changes
= NULL
;
7885 CFMutableStringRef primary_str
= NULL
;
7887 #ifdef MANAGE_IF_SIGNATURE
7888 process_state_differences(changes_state
, af
, sockfd
);
7889 #endif /* MANAGE_IF_SIGNATURE */
7890 scan
= nwi_state_get_first_ifstate(changes_state
, af
);
7891 while (scan
!= NULL
) {
7892 const char * changed_str
;
7894 changed_str
= nwi_ifstate_get_diff_str(scan
);
7895 if (changed_str
!= NULL
) {
7897 const char * addr_str
;
7898 char ntopbuf
[INET6_ADDRSTRLEN
];
7900 address
= (void *)nwi_ifstate_get_address(scan
);
7901 addr_str
= inet_ntop(scan
->af
, address
, ntopbuf
,
7903 if (primary_str
== NULL
) {
7904 primary_str
= CFStringCreateMutable(NULL
, 0);
7905 CFStringAppendFormat(primary_str
, NULL
,
7907 nwi_ifstate_get_ifname(scan
),
7908 changed_str
, addr_str
);
7910 if (changes
== NULL
) {
7911 changes
= CFStringCreateMutable(NULL
, 0);
7913 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
7914 nwi_ifstate_get_ifname(scan
));
7915 if (strcmp(changed_str
, "") != 0) {
7916 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
7917 changed_str
, addr_str
);
7921 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
7924 if (primary_str
!= NULL
) {
7925 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
7926 af
== AF_INET
? "v4" : "v6",
7929 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
7930 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
7933 CFStringAppend(log_output
, CFSTR(")"));
7935 my_CFRelease(&primary_str
);
7936 my_CFRelease(&changes
);
7939 #ifdef MANAGE_IF_IOCTL
7943 #endif /* MANAGE_IF_IOCTL */
7946 if (dns_changed
|| dnsinfo_changed
) {
7949 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
7950 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
7951 str
= "*"; // dnsinfo change w/no change to primary
7953 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
7954 } else if (S_primary_dns
!= NULL
) {
7955 CFStringAppend(log_output
, CFSTR(" DNS"));
7958 if (proxy_changed
) {
7961 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
7962 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
7963 } else if (S_primary_proxies
!= NULL
) {
7964 CFStringAppend(log_output
, CFSTR(" Proxy"));
7967 #if !TARGET_OS_IPHONE
7971 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
7972 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
7973 } else if (S_primary_smb
!= NULL
) {
7974 CFStringAppend(log_output
, CFSTR(" SMB"));
7976 #endif // !TARGET_OS_IPHONE
7982 #pragma mark Network changed notification
7984 static dispatch_queue_t
7985 __network_change_queue()
7987 static dispatch_once_t once
;
7988 static dispatch_queue_t q
;
7990 dispatch_once(&once
, ^{
7991 q
= dispatch_queue_create("network change queue", NULL
);
7997 // Note: must run on __network_change_queue()
7999 post_network_change_when_ready()
8003 dispatch_assert_queue(__network_change_queue());
8005 if (S_network_change_needed
== 0) {
8009 if (!S_network_change_timeout
&&
8010 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
8011 // if we [still] need to wait for the DNS configuration
8012 // or network information changes to be ack'd
8014 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
8015 S_dnsinfo_synced
? "DNS" : "!DNS",
8016 S_nwi_synced
? "nwi" : "!nwi");
8020 // cancel any running timer
8021 if (S_network_change_timer
!= NULL
) {
8022 dispatch_source_cancel(S_network_change_timer
);
8023 dispatch_release(S_network_change_timer
);
8024 S_network_change_timer
= NULL
;
8025 S_network_change_timeout
= FALSE
;
8028 // set (and log?) the post time
8030 struct timeval elapsed
;
8033 (void) gettimeofday(&end
, NULL
);
8034 timersub(&end
, &S_network_change_start
, &elapsed
);
8036 #define QUERY_TIME__FMT "%ld.%6.6d"
8037 #define QUERY_TIME__DIV 1
8040 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
8041 S_network_change_timeout
? "timeout" : "delayed",
8043 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
8044 S_network_change_needed
);
8048 /* We are about to post a network change to everyone, get the agents up to date */
8049 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8050 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
8051 /* Setup or Update config agents */
8052 process_AgentMonitor_DNS();
8054 #endif //!TARGET_OS_SIMULATOR
8056 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
8057 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
8058 if (status
!= NOTIFY_STATUS_OK
) {
8060 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%d", status
);
8064 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
8065 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
8066 if (status
!= NOTIFY_STATUS_OK
) {
8068 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%d", status
);
8072 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
8073 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8074 /* Setup or Update config agents */
8075 process_AgentMonitor_Proxy();
8076 #endif //!TARGET_OS_SIMULATOR
8077 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8078 if (status
!= NOTIFY_STATUS_OK
) {
8080 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%d", status
);
8084 if ((S_network_change_needed
& NETWORK_CHANGE_NAT64
) != 0) {
8085 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8086 // process any NAT64 prefix update requests (and refresh existing prefixes on change)
8087 if (S_nat64_prefix_requests
!= NULL
|| S_nat64_prefix_updates
!= NULL
8088 || S_nat64_cancel_prefix_requests
!= NULL
) {
8089 nat64_configuration_update(S_nat64_prefix_requests
,
8090 S_nat64_prefix_updates
,
8091 S_nat64_cancel_prefix_requests
);
8092 my_CFRelease(&S_nat64_prefix_requests
);
8093 my_CFRelease(&S_nat64_prefix_updates
);
8094 my_CFRelease(&S_nat64_cancel_prefix_requests
);
8096 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8098 S_network_change_needed
&= ~(NETWORK_CHANGE_NAT64
);
8101 if (S_network_change_needed
!= 0) {
8102 // if more than just a NAT64 prefix change
8103 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
8104 if (status
!= NOTIFY_STATUS_OK
) {
8106 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%d", status
);
8110 S_network_change_needed
= 0;
8114 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
8116 // Note: must run on __network_change_queue()
8118 post_network_change(uint32_t change
)
8120 dispatch_assert_queue(__network_change_queue());
8122 if (S_network_change_needed
== 0) {
8123 // set the start time
8124 (void) gettimeofday(&S_network_change_start
, NULL
);
8127 // indicate that we need to post a change for ...
8128 S_network_change_needed
|= change
;
8130 // cancel any running timer
8131 if (S_network_change_timer
!= NULL
) {
8132 dispatch_source_cancel(S_network_change_timer
);
8133 dispatch_release(S_network_change_timer
);
8134 S_network_change_timer
= NULL
;
8135 S_network_change_timeout
= FALSE
;
8138 // if needed, start new timer
8139 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
8140 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
8143 __network_change_queue());
8144 dispatch_source_set_event_handler(S_network_change_timer
, ^{
8145 S_network_change_timeout
= TRUE
;
8146 post_network_change_when_ready();
8148 dispatch_source_set_timer(S_network_change_timer
,
8149 dispatch_time(DISPATCH_TIME_NOW
,
8150 TRAILING_EDGE_TIMEOUT_NSEC
), // start
8151 DISPATCH_TIME_FOREVER
, // interval
8152 10 * NSEC_PER_MSEC
); // leeway
8153 dispatch_resume(S_network_change_timer
);
8156 post_network_change_when_ready();
8162 #pragma mark Process network (SCDynamicStore) changes
8165 IPMonitorProcessChanges(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8166 CFArrayRef if_rank_changes
)
8169 uint32_t changes
= 0;
8170 nwi_state_t changes_state
= NULL
;
8171 boolean_t dns_changed
= FALSE
;
8172 boolean_t dnsinfo_changed
= FALSE
;
8173 boolean_t global_ipv4_changed
= FALSE
;
8174 boolean_t global_ipv6_changed
= FALSE
;
8175 CFMutableSetRef ipv4_service_changes
= NULL
;
8176 CFMutableSetRef ipv6_service_changes
= NULL
;
8179 boolean_t nat64_changed
= FALSE
;
8180 CFMutableStringRef network_change_msg
= NULL
;
8182 nwi_state_t old_nwi_state
= NULL
;
8183 CFDictionaryRef old_primary_dns
= NULL
;
8184 CFDictionaryRef old_primary_proxy
= NULL
;
8185 #if !TARGET_OS_IPHONE
8186 CFDictionaryRef old_primary_smb
= NULL
;
8187 #endif // !TARGET_OS_IPHONE
8188 boolean_t proxies_changed
= FALSE
;
8189 boolean_t reachability_changed
= FALSE
;
8190 CFArrayRef service_order
;
8191 CFMutableArrayRef service_changes
= NULL
;
8192 CFDictionaryRef services_info
= NULL
;
8193 #if !TARGET_OS_IPHONE
8194 boolean_t smb_changed
= FALSE
;
8195 #endif // !TARGET_OS_IPHONE
8197 /* populate name/index cache */
8200 if (changed_keys
!= NULL
) {
8201 count
= CFArrayGetCount(changed_keys
);
8202 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8204 "changed keys %@ (%ld)", changed_keys
, count
);
8207 if (if_rank_changes
== NULL
&& count
== 0) {
8211 if (S_primary_dns
!= NULL
) {
8212 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
8213 if (old_primary_dns
!= NULL
) {
8214 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
8218 if (S_primary_proxies
!= NULL
) {
8220 = service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
8221 if (old_primary_proxy
!= NULL
) {
8222 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
8226 #if !TARGET_OS_IPHONE
8227 if (S_primary_smb
!= NULL
) {
8228 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
8229 if (old_primary_smb
!= NULL
) {
8230 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
8233 #endif // !TARGET_OS_IPHONE
8235 keyChangeListInit(&keys
);
8236 service_changes
= CFArrayCreateMutable(NULL
, 0,
8237 &kCFTypeArrayCallBacks
);
8238 for (CFIndex i
= 0; i
< count
; i
++) {
8240 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8241 CFStringRef interface
= NULL
;
8242 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8244 change
= CFArrayGetValueAtIndex(changed_keys
, i
);
8245 if (CFEqual(change
, S_setup_global_ipv4
)) {
8246 global_ipv4_changed
= TRUE
;
8247 global_ipv6_changed
= TRUE
;
8249 else if (CFEqual(change
, S_multicast_resolvers
)) {
8250 dnsinfo_changed
= TRUE
;
8252 else if (CFEqual(change
, S_private_resolvers
)) {
8253 dnsinfo_changed
= TRUE
;
8255 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
8256 dnsinfo_changed
= TRUE
;
8258 else if (CFStringHasPrefix(change
, S_interface_delegation_prefix
) &&
8259 CFStringHasSuffix(change
, kSCEntNetInterfaceDelegation
)) {
8260 // ensure that we update the reachability flags in the NWI
8261 // state (including the non-interface-specific flags)
8262 reachability_changed
= TRUE
;
8263 // ensure that we update the reachability flags in the DNS
8265 dnsinfo_changed
= TRUE
;
8267 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
8268 CFStringRef protocol
= NULL
;
8269 CFStringRef serviceID
;
8271 serviceID
= parseNetworkServiceString(change
, &protocol
);
8272 if (serviceID
!= NULL
) {
8273 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8274 if (protocol
!= NULL
) {
8275 if (CFEqual(protocol
, kSCEntNetIPv4
)) {
8276 /* IPv4 service changed, remember that */
8277 my_CFSetAddValue(&ipv4_service_changes
, serviceID
);
8279 else if (CFEqual(protocol
, kSCEntNetIPv6
)) {
8280 /* IPv6 service changed, remember that */
8281 my_CFSetAddValue(&ipv6_service_changes
, serviceID
);
8284 CFRelease(serviceID
);
8286 my_CFRelease(&protocol
);
8288 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
8289 CFStringRef serviceID
;
8291 serviceID
= parseNetworkServiceString(change
, NULL
);
8292 if (serviceID
!= NULL
) {
8293 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8294 CFRelease(serviceID
);
8297 for (size_t j
= 0; j
< countof(transientInterfaceEntityNames
); j
++) {
8298 if (CFStringHasSuffix(change
,
8299 *transientInterfaceEntityNames
[j
])) {
8300 reachability_changed
= TRUE
;
8305 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
8306 reachability_changed
= TRUE
;
8309 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8310 else if (is_nat64_prefix_request(change
, &interface
)) {
8311 set_plat_discovery(kPLATDiscoveryOptionStart
, interface
);
8312 nat64_changed
= TRUE
;
8314 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8317 /* determine which serviceIDs are impacted by the interface rank changes */
8318 if (if_rank_changes
!= NULL
) {
8319 n
= CFArrayGetCount(if_rank_changes
);
8320 for (CFIndex i
= 0; i
< n
; i
++) {
8321 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
8323 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8324 my_log(LOG_DEBUG
, "Interface rank changed %@", ifname
);
8326 append_serviceIDs_for_interface(service_changes
, ifname
);
8330 /* grab a snapshot of everything we need */
8331 services_info
= services_info_copy(session
, service_changes
);
8332 assert(services_info
!= NULL
);
8334 /* grab the service order */
8335 service_order
= service_order_get(services_info
);
8336 if (service_order
!= NULL
) {
8337 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8338 my_log(LOG_DEBUG
, "service_order %@ ", service_order
);
8343 * process protocol (v4, v6, rank, ...) and
8344 * configuration (dns, proxies, smb, ...) changes
8346 n
= CFArrayGetCount(service_changes
);
8347 for (CFIndex i
= 0; i
< n
; i
++) {
8349 CFStringRef serviceID
;
8351 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
8352 changes
= service_changed(services_info
, serviceID
);
8353 if (my_CFSetContainsValue(ipv4_service_changes
, serviceID
)) {
8354 changes
|= (1 << kEntityTypeIPv4
);
8356 if (my_CFSetContainsValue(ipv6_service_changes
, serviceID
)) {
8357 changes
|= (1 << kEntityTypeIPv6
);
8359 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
8360 /* if __Service__ (e.g. PrimaryRank) changed */
8361 global_ipv4_changed
= TRUE
;
8362 global_ipv6_changed
= TRUE
;
8365 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
8366 global_ipv4_changed
= TRUE
;
8367 dnsinfo_changed
= TRUE
;
8368 proxies_changed
= TRUE
;
8370 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
8371 global_ipv6_changed
= TRUE
;
8372 dnsinfo_changed
= TRUE
;
8373 proxies_changed
= TRUE
;
8374 nat64_changed
= TRUE
;
8377 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
8378 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
8381 dnsinfo_changed
= TRUE
;
8382 nat64_changed
= TRUE
;
8384 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
8385 proxies_changed
= TRUE
;
8387 #if !TARGET_OS_IPHONE
8388 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
8389 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
8394 if ((changes
& (1 << kEntityTypeTransientStatus
)) != 0
8395 && (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
8396 || service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
)) {
8397 dnsinfo_changed
= TRUE
;
8401 /* ensure S_nwi_state can hold as many services as we have currently */
8402 n_services
= (int)CFDictionaryGetCount(S_service_state_dict
);
8403 old_nwi_state
= nwi_state_make_copy(S_nwi_state
);
8404 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
8406 if (global_ipv4_changed
) {
8407 if (S_ipv4_results
!= NULL
) {
8408 ElectionResultsRelease(S_ipv4_results
);
8411 = ElectionResultsCopy(AF_INET
, service_order
);
8412 ElectionResultsLog(LOG_INFO
, S_ipv4_results
, "IPv4");
8414 if (global_ipv6_changed
) {
8415 if (S_ipv6_results
!= NULL
) {
8416 ElectionResultsRelease(S_ipv6_results
);
8419 = ElectionResultsCopy(AF_INET6
, service_order
);
8420 ElectionResultsLog(LOG_INFO
, S_ipv6_results
, "IPv6");
8422 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
8423 CFStringRef new_primary
;
8424 CFStringRef new_primary_dns
= NULL
;
8425 CFStringRef new_primary_proxies
= NULL
;
8426 #if !TARGET_OS_IPHONE
8427 CFStringRef new_primary_smb
= NULL
;
8428 #endif /* !TARGET_OS_IPHONE */
8429 RouteListUnion new_routelist
;
8430 CandidateRef other_candidate
;
8431 CandidateRef primary_candidate
;
8433 if (S_nwi_state
!= NULL
) {
8434 nwi_state_clear(S_nwi_state
, AF_INET
);
8435 nwi_state_clear(S_nwi_state
, AF_INET6
);
8439 my_log(LOG_DEBUG
, "electing IPv4 primary");
8440 new_routelist
.ptr
= NULL
;
8441 other_candidate
= (S_ipv6_results
!= NULL
) /* get IPv6 primary */
8442 ? S_ipv6_results
->candidates
: NULL
;
8443 primary_candidate
= ElectionResultsGetPrimary(S_ipv4_results
,
8445 S_nwi_state
, AF_INET
,
8446 &new_routelist
.common
,
8448 ipv4_service_changes
);
8449 new_primary
= (primary_candidate
!= NULL
)
8450 ? primary_candidate
->serviceID
: NULL
;
8451 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
8452 update_ipv4(S_primary_ipv4
, new_routelist
.v4
, &keys
);
8455 my_log(LOG_DEBUG
, "electing IPv6 primary");
8456 new_routelist
.ptr
= NULL
;
8457 other_candidate
= primary_candidate
; /* get IPv4 primary */
8458 primary_candidate
= ElectionResultsGetPrimary(S_ipv6_results
,
8460 S_nwi_state
, AF_INET6
,
8461 &new_routelist
.common
,
8463 ipv6_service_changes
);
8464 new_primary
= (primary_candidate
!= NULL
)
8465 ? primary_candidate
->serviceID
: NULL
;
8466 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
8467 update_ipv6(S_primary_ipv6
, new_routelist
.v6
, &keys
);
8469 nwi_state_finalize(S_nwi_state
);
8471 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
8472 /* decide between IPv4 and IPv6 */
8473 if (rank_service_entity(S_ipv4_service_rank_dict
,
8474 S_primary_ipv4
, kSCEntNetDNS
)
8475 <= rank_service_entity(S_ipv6_service_rank_dict
,
8476 S_primary_ipv6
, kSCEntNetDNS
)) {
8477 new_primary_dns
= S_primary_ipv4
;
8480 new_primary_dns
= S_primary_ipv6
;
8482 if (rank_service_entity(S_ipv4_service_rank_dict
,
8483 S_primary_ipv4
, kSCEntNetProxies
)
8484 <= rank_service_entity(S_ipv6_service_rank_dict
,
8485 S_primary_ipv6
, kSCEntNetProxies
)) {
8486 new_primary_proxies
= S_primary_ipv4
;
8489 new_primary_proxies
= S_primary_ipv6
;
8491 #if !TARGET_OS_IPHONE
8492 if (rank_service_entity(S_ipv4_service_rank_dict
,
8493 S_primary_ipv4
, kSCEntNetSMB
)
8494 <= rank_service_entity(S_ipv6_service_rank_dict
,
8495 S_primary_ipv6
, kSCEntNetSMB
)) {
8496 new_primary_smb
= S_primary_ipv4
;
8499 new_primary_smb
= S_primary_ipv6
;
8501 #endif /* !TARGET_OS_IPHONE */
8504 else if (S_primary_ipv6
!= NULL
) {
8505 new_primary_dns
= S_primary_ipv6
;
8506 new_primary_proxies
= S_primary_ipv6
;
8507 #if !TARGET_OS_IPHONE
8508 new_primary_smb
= S_primary_ipv6
;
8509 #endif /* !TARGET_OS_IPHONE */
8511 else if (S_primary_ipv4
!= NULL
) {
8512 new_primary_dns
= S_primary_ipv4
;
8513 new_primary_proxies
= S_primary_ipv4
;
8514 #if !TARGET_OS_IPHONE
8515 new_primary_smb
= S_primary_ipv4
;
8516 #endif /* !TARGET_OS_IPHONE */
8519 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
8521 dnsinfo_changed
= TRUE
;
8523 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
,
8525 proxies_changed
= TRUE
;
8527 #if !TARGET_OS_IPHONE
8528 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
8531 #endif /* !TARGET_OS_IPHONE */
8534 if (!proxies_changed
&& dnsinfo_changed
8535 && ((G_supplemental_proxies_follow_dns
!= NULL
)
8536 && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
8537 proxies_changed
= TRUE
;
8540 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
8542 if (global_ipv4_changed
|| global_ipv6_changed
8543 || dnsinfo_changed
|| reachability_changed
) {
8544 if (S_nwi_state
!= NULL
) {
8545 S_nwi_state
->generation_count
= mach_absolute_time();
8546 if (global_ipv4_changed
|| global_ipv6_changed
8547 || reachability_changed
) {
8548 SCNetworkReachabilityFlags reach_flags_v4
= 0;
8549 SCNetworkReachabilityFlags reach_flags_v6
= 0;
8551 GetReachabilityFlagsFromTransientServices(services_info
,
8555 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
,
8559 /* Update the per-interface generation count */
8560 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
,
8564 if (update_nwi(S_nwi_state
)) {
8565 changes
|= NETWORK_CHANGE_NET
;
8568 * the DNS configuration includes per-resolver configuration
8569 * reachability flags that are based on the nwi state. Let's
8570 * make sure that we check for changes
8572 dnsinfo_changed
= TRUE
;
8576 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
8577 changes
|= NETWORK_CHANGE_DNS
;
8578 dnsinfo_changed
= TRUE
;
8580 dns_changed
= FALSE
;
8583 if (dnsinfo_changed
) {
8584 if (update_dnsinfo(services_info
, S_primary_dns
,
8585 &keys
, service_order
)) {
8586 changes
|= NETWORK_CHANGE_DNS
;
8588 dnsinfo_changed
= FALSE
;
8591 if (proxies_changed
) {
8592 // if proxy change OR supplemental Proxies follow supplemental DNS
8593 if (update_proxies(services_info
, S_primary_proxies
,
8594 &keys
, service_order
)) {
8595 changes
|= NETWORK_CHANGE_PROXY
;
8597 proxies_changed
= FALSE
;
8600 #if !TARGET_OS_IPHONE
8602 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
8603 changes
|= NETWORK_CHANGE_SMB
;
8605 smb_changed
= FALSE
;
8608 #endif /* !TARGET_OS_IPHONE */
8609 if (nat64_changed
) {
8610 changes
|= NETWORK_CHANGE_NAT64
;
8612 my_CFRelease(&service_changes
);
8613 my_CFRelease(&services_info
);
8614 my_CFRelease(&ipv4_service_changes
);
8615 my_CFRelease(&ipv6_service_changes
);
8618 network_change_msg
= CFStringCreateMutable(NULL
, 0);
8619 process_nwi_changes(network_change_msg
,
8628 #if !TARGET_OS_IPHONE
8631 #else // !TARGET_OS_IPHONE
8632 FALSE
, // smb_changed
8633 NULL
// old_primary_smb
8634 #endif // !TARGET_OS_IPHONE
8638 keyChangeListApplyToStore(&keys
, session
);
8639 my_CFRelease(&old_primary_dns
);
8640 my_CFRelease(&old_primary_proxy
);
8641 #if !TARGET_OS_IPHONE
8642 my_CFRelease(&old_primary_smb
);
8643 #endif // !TARGET_OS_IPHONE
8646 dispatch_async(__network_change_queue(), ^{
8647 post_network_change(changes
);
8651 if ((network_change_msg
!= NULL
)
8652 && (CFStringGetLength(network_change_msg
) != 0)) {
8653 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
8654 } else if (keyChangeListActive(&keys
)) {
8655 my_log(LOG_NOTICE
, "network changed");
8656 } else if (nat64_changed
) {
8657 my_log(LOG_NOTICE
, "nat64 update");
8659 my_log(LOG_INFO
, "network event w/no changes");
8662 my_CFRelease(&network_change_msg
);
8664 if (changes_state
!= NULL
) {
8665 nwi_state_free(changes_state
);
8667 if (old_nwi_state
!= NULL
) {
8668 nwi_state_free(old_nwi_state
);
8670 keyChangeListFree(&keys
);
8672 /* release the name/index cache */
8673 my_if_freenameindex();
8679 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8682 #pragma unused(info)
8683 IPMonitorProcessChanges(session
, changed_keys
, NULL
);
8687 #if !TARGET_OS_IPHONE
8688 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_mcx
8690 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_global
8696 static dispatch_queue_t proxy_cb_queue
;
8698 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
8699 _scprefs_observer_watch(PROXY_GLOBAL_OBSERVER_TYPE
,
8700 "com.apple.SystemConfiguration.plist",
8703 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
8704 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8705 /* Setup or Update config agents */
8706 process_AgentMonitor_Proxy();
8707 #endif //!TARGET_OS_SIMULATOR
8708 (void)notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8709 my_log(LOG_INFO
, "Notifying:\n%@",
8710 S_state_global_proxies
);
8715 #if TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST
8718 prefs_changed_callback_init(void)
8722 #else /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8724 #include "IPMonitorControlPrefs.h"
8727 prefs_changed(SCPreferencesRef prefs
)
8729 #pragma unused(prefs)
8730 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
8731 S_IPMonitor_debug
= kDebugFlagDefault
;
8732 S_IPMonitor_verbose
= TRUE
;
8733 my_log(LOG_DEBUG
, "Setting logging verbose mode on");
8735 my_log(LOG_DEBUG
, "Setting logging verbose mode off");
8736 S_IPMonitor_debug
= 0;
8737 S_IPMonitor_verbose
= FALSE
;
8743 prefs_changed_callback_init(void)
8745 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
8746 prefs_changed(NULL
);
8751 #endif /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8753 #if !TARGET_OS_SIMULATOR
8764 struct rt_msghdr
* rtm
;
8765 struct sockaddr_in
*sin
;
8771 mib
[4] = NET_RT_FLAGS
;
8772 mib
[5] = RTF_STATIC
| RTF_DYNAMIC
;
8773 for (i
= 0; i
< 3; i
++) {
8774 if (sysctl(mib
, N_MIB
, NULL
, &needed
, NULL
, 0) < 0) {
8777 if ((buf
= malloc(needed
)) == NULL
) {
8780 if (sysctl(mib
, N_MIB
, buf
, &needed
, NULL
, 0) >= 0) {
8790 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
8793 /* ALIGN: assume kernel provides necessary alignment */
8794 rtm
= (struct rt_msghdr
*)(void *)next
;
8795 sin
= (struct sockaddr_in
*)(rtm
+ 1);
8797 addr
= ntohl(sin
->sin_addr
.s_addr
);
8798 if (IN_LOOPBACK(addr
)) {
8800 "flush_routes: ignoring loopback route");
8803 if (IN_LOCAL_GROUP(addr
)) {
8805 "flush_routes: ignoring multicast route");
8808 rtm
->rtm_type
= RTM_DELETE
;
8809 rtm
->rtm_seq
= ++rtm_seq
;
8810 if (write(s
, rtm
, rtm
->rtm_msglen
) < 0) {
8812 "flush_routes: removing route for "
8813 IP_FORMAT
" failed: %s",
8814 IP_LIST(&sin
->sin_addr
),
8819 "flush_routes: removed route for " IP_FORMAT
,
8820 IP_LIST(&sin
->sin_addr
));
8828 flush_inet_routes(void)
8832 s
= open_routing_socket();
8839 #else /* !TARGET_OS_SIMULATOR */
8842 flush_inet_routes(void)
8846 #endif /* !TARGET_OS_SIMULATOR */
8853 CFMutableArrayRef keys
= NULL
;
8854 CFStringRef pattern
;
8855 CFMutableArrayRef patterns
= NULL
;
8856 CFRunLoopSourceRef rls
= NULL
;
8858 if (S_is_network_boot() != 0) {
8863 flush_inet_routes();
8866 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
8867 IPMonitorNotify
, NULL
);
8868 if (S_session
== NULL
) {
8870 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8871 SCErrorString(SCError()));
8875 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8876 kSCDynamicStoreDomainState
,
8879 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8880 kSCDynamicStoreDomainState
,
8883 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8884 kSCDynamicStoreDomainState
,
8886 S_state_global_proxies
8887 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8888 kSCDynamicStoreDomainState
,
8890 #if !TARGET_OS_IPHONE
8892 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8893 kSCDynamicStoreDomainState
,
8895 #endif /* !TARGET_OS_IPHONE */
8897 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8898 kSCDynamicStoreDomainSetup
,
8900 S_state_service_prefix
8901 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8902 kSCDynamicStoreDomainState
,
8905 S_setup_service_prefix
8906 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8907 kSCDynamicStoreDomainSetup
,
8910 S_interface_delegation_prefix
8911 = SCDynamicStoreKeyCreateNetworkInterface(NULL
,
8912 kSCDynamicStoreDomainState
);
8914 S_service_state_dict
8915 = CFDictionaryCreateMutable(NULL
, 0,
8916 &kCFTypeDictionaryKeyCallBacks
,
8917 &kCFTypeDictionaryValueCallBacks
);
8919 S_ipv4_service_rank_dict
8920 = CFDictionaryCreateMutable(NULL
, 0,
8921 &kCFTypeDictionaryKeyCallBacks
,
8922 &kCFTypeDictionaryValueCallBacks
);
8924 S_ipv6_service_rank_dict
8925 = CFDictionaryCreateMutable(NULL
, 0,
8926 &kCFTypeDictionaryKeyCallBacks
,
8927 &kCFTypeDictionaryValueCallBacks
);
8929 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8930 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8932 /* register for State: and Setup: per-service notifications */
8933 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
8935 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
8936 CFArrayAppendValue(patterns
, pattern
);
8939 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
8940 CFArrayAppendValue(patterns
, pattern
);
8943 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
8944 CFArrayAppendValue(patterns
, pattern
);
8947 /* register for State: per-service PPP/VPN/IPSec status notifications */
8948 add_transient_status_keys(kSCCompAnyRegex
, NULL
, patterns
);
8950 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
8951 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
8953 /* add notifier for multicast DNS configuration (Bonjour/.local) */
8954 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8955 kSCDynamicStoreDomainState
,
8957 CFSTR(kDNSServiceCompMulticastDNS
));
8958 CFArrayAppendValue(keys
, S_multicast_resolvers
);
8960 /* add notifier for private DNS configuration (Back to My Mac) */
8961 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8962 kSCDynamicStoreDomainState
,
8964 CFSTR(kDNSServiceCompPrivateDNS
));
8965 CFArrayAppendValue(keys
, S_private_resolvers
);
8967 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8968 /* add NAT64 prefix request pattern */
8969 nat64_prefix_request_add_pattern(patterns
);
8970 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8972 /* add interface delegation pattern */
8973 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetInterfaceDelegation
);
8974 CFArrayAppendValue(patterns
, pattern
);
8977 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
8979 "SCDynamicStoreSetNotificationKeys() failed: %s",
8980 SCErrorString(SCError()));
8984 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
8987 "SCDynamicStoreCreateRunLoopSource() failed: %s",
8988 SCErrorString(SCError()));
8992 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
8995 /* initialize dns configuration */
8996 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
8997 #if !TARGET_OS_IPHONE
8999 #endif /* !TARGET_OS_IPHONE */
9000 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
9002 #if !TARGET_OS_IPHONE
9003 /* initialize SMB configuration */
9004 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
9005 #endif /* !TARGET_OS_IPHONE */
9010 my_CFRelease(&keys
);
9011 my_CFRelease(&patterns
);
9019 /* initialize multicast route */
9020 update_ipv4(NULL
, NULL
, NULL
);
9022 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9023 process_AgentMonitor();
9024 #endif // !TARGET_OS_SIMULATOR
9030 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
9034 boolean_t ret
= def
;
9036 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
9038 ret
= CFBooleanGetValue(b
);
9043 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9044 #include "IPMonitorControlServer.h"
9047 InterfaceRankChanged(void * info
)
9049 #pragma unused(info)
9050 CFDictionaryRef assertions
= NULL
;
9053 changes
= IPMonitorControlServerCopyInterfaceRankInformation(&assertions
);
9054 if (S_if_rank_dict
!= NULL
) {
9055 CFRelease(S_if_rank_dict
);
9057 S_if_rank_dict
= assertions
;
9058 if (changes
!= NULL
) {
9059 IPMonitorProcessChanges(S_session
, NULL
, changes
);
9067 StartIPMonitorControlServer(void)
9069 CFRunLoopSourceContext context
;
9070 CFRunLoopSourceRef rls
;
9072 memset(&context
, 0, sizeof(context
));
9073 context
.perform
= InterfaceRankChanged
;
9074 rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
9075 if (!IPMonitorControlServerStart(CFRunLoopGetCurrent(),
9077 &S_bundle_logging_verbose
)) {
9078 my_log(LOG_ERR
, "IPMonitorControlServerStart failed");
9081 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
9082 kCFRunLoopDefaultMode
);
9088 #endif /* !TARGET_OS_SIMULATOR */
9092 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
9094 CFDictionaryRef info_dict
;
9096 info_dict
= CFBundleGetInfoDictionary(bundle
);
9098 if (info_dict
!= NULL
) {
9100 = S_get_plist_boolean(info_dict
,
9101 CFSTR("AppendStateArrayToSetupArray"),
9104 if (bundleVerbose
) {
9105 S_IPMonitor_debug
= kDebugFlagDefault
;
9106 S_bundle_logging_verbose
= TRUE
;
9107 S_IPMonitor_verbose
= TRUE
;
9110 /* register to receive changes to the "verbose" flag and read the initial setting */
9111 prefs_changed_callback_init();
9113 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9114 /* start DNS configuration (dnsinfo) server */
9115 load_DNSConfiguration(bundle
, // bundle
9116 ^(Boolean inSync
) { // syncHandler
9117 dispatch_async(__network_change_queue(), ^{
9118 S_dnsinfo_synced
= inSync
;
9121 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
9122 // all of the DNS service ack's should result
9123 // in a [new] network change being posted
9124 post_network_change(NETWORK_CHANGE_DNS
);
9126 post_network_change_when_ready();
9131 /* start Network Information (nwi) server */
9132 load_NetworkInformation(bundle
, // bundle
9133 ^(Boolean inSync
) { // syncHandler
9134 dispatch_async(__network_change_queue(), ^{
9135 S_nwi_synced
= inSync
;
9136 post_network_change_when_ready();
9139 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
9141 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9142 /* start IPMonitor Control (InterfaceRank) server */
9143 StartIPMonitorControlServer();
9144 #endif /* !TARGET_OS_IPHONE */
9146 /* initialize DNS configuration */
9147 dns_configuration_init(bundle
);
9149 /* initialize proxy configuration */
9150 proxy_configuration_init(bundle
);
9154 if (S_session
!= NULL
) {
9155 dns_configuration_monitor(S_session
, IPMonitorNotify
);
9158 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9159 load_hostname(TRUE
);
9160 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
9162 #if !TARGET_OS_IPHONE
9163 load_smb_configuration(TRUE
);
9164 #endif /* !TARGET_OS_IPHONE */
9171 #pragma mark Standalone test code
9174 #ifdef TEST_IPMONITOR
9177 main(int argc
, char **argv
)
9179 _sc_log
= kSCLogDestinationFile
;
9181 S_IPMonitor_debug
= kDebugFlag1
;
9183 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9186 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
9188 S_IPMonitor_debug
= kDebugFlag1
;
9194 #endif /* TEST_IPMONITOR */
9196 #ifdef TEST_ROUTELIST
9201 const char * gateway
;
9202 const char * ifname
;
9207 #if TEST_IPV4_ROUTELIST
9213 const char * router
;
9214 const char * ifname
;
9216 const CFStringRef
* primary_rank
;
9217 struct route
* additional_routes
;
9218 int additional_routes_count
;
9219 struct route
* excluded_routes
;
9220 int excluded_routes_count
;
9221 } IPv4ServiceContents
;
9223 typedef const IPv4ServiceContents
* IPv4ServiceContentsRef
;
9225 struct route loop_routelist
[] = {
9226 { "1.1.1.1", 32, "1.1.1.2", NULL
},
9227 { "1.1.1.2", 32, "1.1.1.3", NULL
},
9228 { "1.1.1.3", 32, "1.1.1.4", NULL
},
9229 { "1.1.1.4", 32, "1.1.1.5", NULL
},
9230 { "1.1.1.5", 32, "1.1.1.6", NULL
},
9231 { "1.1.1.6", 32, "1.1.1.7", NULL
},
9232 { "1.1.1.7", 32, "1.1.1.8", NULL
},
9233 { "1.1.1.8", 32, "1.1.1.9", NULL
},
9234 { "1.1.1.9", 32, "1.1.1.10", NULL
},
9235 { "1.1.1.10", 32, "1.1.1.11", NULL
},
9236 { "1.1.1.11", 32, "1.1.1.1", NULL
},
9239 struct route vpn_routelist
[] = {
9240 { "10.1.3.0", 24, "17.153.46.24", NULL
},
9241 { "10.1.4.0", 24, "17.153.46.24", NULL
},
9242 { "10.1.5.0", 24, "17.153.46.24", NULL
},
9243 { "10.1.6.0", 24, "17.153.46.24", NULL
},
9244 { "10.1.7.0", 24, "17.153.46.24", NULL
},
9245 { "10.16.0.0", 12, "17.153.46.24", NULL
},
9246 { "10.45.0.0", 16, "17.153.46.24", NULL
},
9247 { "10.53.0.0", 16, "17.153.46.24", NULL
},
9248 { "10.70.0.0", 15, "17.153.46.24", NULL
},
9249 { "10.74.0.0", 15, "17.153.46.24", NULL
},
9250 { "10.90.0.0", 15, "17.153.46.24", NULL
},
9251 { "10.91.0.0", 16, "17.153.46.24", NULL
},
9252 { "10.100.0.0", 16, "17.153.46.24", NULL
},
9253 { "10.113.0.0", 16, "17.153.46.24", NULL
},
9254 { "10.128.0.0", 9, "17.153.46.24", NULL
},
9255 { "17.0.0.0", 9, "17.153.46.24", NULL
},
9256 { "17.34.0.0", 16, "17.153.46.24", NULL
},
9257 { "17.112.156.53", 32, "17.153.46.24", NULL
},
9258 { "17.128.0.0", 10, "17.153.46.24", NULL
},
9259 { "17.149.0.121", 32, "17.153.46.24", NULL
},
9260 { "17.149.7.200", 32, "17.153.46.24", NULL
},
9261 { "17.153.46.24", 32, "17.153.46.24", NULL
},
9262 { "17.192.0.0", 12, "17.153.46.24", NULL
},
9263 { "17.208.0.0", 15, "17.153.46.24", NULL
},
9264 { "17.211.0.0", 16, "17.153.46.24", NULL
},
9265 { "17.212.0.0", 14, "17.153.46.24", NULL
},
9266 { "17.216.0.0", 13, "17.153.46.24", NULL
},
9267 { "17.224.0.0", 12, "17.153.46.24", NULL
},
9268 { "17.240.0.0", 16, "17.153.46.24", NULL
},
9269 { "17.241.0.0", 16, "17.153.46.24", NULL
},
9270 { "17.248.0.0", 14, "17.153.46.24", NULL
},
9271 { "17.251.104.200", 32, "17.153.46.24", NULL
},
9272 { "17.252.0.0", 16, "17.153.46.24", NULL
},
9273 { "17.253.0.0", 16, "17.153.46.24", NULL
},
9274 { "17.254.0.0", 16, "17.153.46.24", NULL
},
9275 { "17.255.0.0", 16, "17.153.46.24", NULL
},
9276 { "151.193.141.0", 27, "17.153.46.24", NULL
},
9277 { "172.16.2.0", 24, "17.153.46.24", NULL
},
9278 { "192.35.50.0", 24, "17.153.46.24", NULL
},
9279 { "204.179.20.0", 24, "17.153.46.24", NULL
},
9280 { "206.112.116.0", 24, "17.153.46.24", NULL
},
9283 struct route vpn_routelist_ext
[] = {
9284 { "17.151.63.82", 32, "10.0.0.1", "en0" },
9285 { "17.151.63.81", 32, "17.151.63.81", "en0" },
9286 { "17.151.63.80", 32, NULL
, NULL
},
9287 { "17.1.0.0", 16, NULL
, NULL
},
9288 { "17.2.0.0", 24, NULL
, NULL
},
9289 { "10.0.0.0", 24, NULL
, NULL
},
9293 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
9295 const IPv4ServiceContents en0_10
= {
9296 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9299 const IPv4ServiceContents en0_15
= {
9300 "10.0.0.19", 24, NULL
, "10.0.0.1", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9303 const IPv4ServiceContents en0_30
= {
9304 "10.0.0.11", 24, NULL
, "10.0.0.1", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9307 const IPv4ServiceContents en0_40
= {
9308 "10.0.0.12", 24, NULL
, "10.0.0.1", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9311 const IPv4ServiceContents en0_50
= {
9312 "10.0.0.13", 24, NULL
, "10.0.0.1", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9315 const IPv4ServiceContents en0_110
= {
9316 "192.168.2.10", 24, NULL
, "192.168.2.1", "en0", 110, NULL
, NULL
, 0, NULL
, 0
9319 const IPv4ServiceContents en0_1
= {
9320 "17.202.40.191", 22, NULL
, "17.202.20.1", "en0", 1, NULL
, NULL
, 0, NULL
, 0
9323 const IPv4ServiceContents en1_20
= {
9324 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9327 const IPv4ServiceContents en1_2
= {
9328 "17.202.42.24", 22, NULL
, "17.202.20.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9331 const IPv4ServiceContents en1_125
= {
9332 "192.168.2.20", 24, NULL
, "192.168.2.1", "en1", 125, NULL
, NULL
, 0, NULL
, 0
9335 const IPv4ServiceContents fw0_25
= {
9336 "192.168.2.30", 24, NULL
, "192.168.2.1", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9339 const IPv4ServiceContents fw0_21
= {
9340 "192.168.3.30", 24, NULL
, "192.168.3.1", "fw0", 21, NULL
, NULL
, 0, NULL
, 0
9343 const IPv4ServiceContents ppp0_0_1
= {
9344 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
, NULL
, 0, NULL
, 0
9347 const IPv4ServiceContents utun0
= {
9348 "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
)
9351 const IPv4ServiceContents en0_test6
= {
9352 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 2, NULL
, NULL
, 0, NULL
, 0
9355 const IPv4ServiceContents en1_test6
= {
9356 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 3, NULL
, NULL
, 0, NULL
, 0
9359 const IPv4ServiceContents en2_test6
= {
9360 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9363 const IPv4ServiceContents en0_test7
= {
9364 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 3, NULL
, NULL
, 0, NULL
, 0
9367 const IPv4ServiceContents en1_test7
= {
9368 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9371 const IPv4ServiceContents en2_test7
= {
9372 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9375 const IPv4ServiceContents fw0_test6_and_7
= {
9376 "169.254.11.33", 16, NULL
, NULL
, "fw0", 0x0ffffff, NULL
, NULL
, 0, NULL
, 0
9379 const IPv4ServiceContents en0_10_last
= {
9380 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
, NULL
, 0, NULL
, 0
9383 const IPv4ServiceContents en0_10_never
= {
9384 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9387 const IPv4ServiceContents en1_20_first
= {
9388 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
, NULL
, 0, NULL
, 0
9391 const IPv4ServiceContents en1_20_never
= {
9392 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9395 const IPv4ServiceContents en1_20_other_never
= {
9396 "192.168.2.50", 24, NULL
, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9399 const IPv4ServiceContents en0_linklocal
= {
9400 "169.254.22.44", 16, NULL
, NULL
, "en0", 0xfffff, NULL
, NULL
, 0, NULL
, 0
9403 const IPv4ServiceContents en0_route_loop
= {
9404 "192.168.130.16", 24, NULL
, "192.168.130.1", "en0", 2, NULL
, loop_routelist
, countof(loop_routelist
), NULL
, 0
9409 IPv4ServiceContentsRef test
[];
9410 } IPv4RouteTest
, * IPv4RouteTestRef
;
9412 static IPv4RouteTest test1
= {
9426 static IPv4RouteTest test2
= {
9439 static IPv4RouteTest test3
= {
9456 static IPv4RouteTest test4
= {
9468 static IPv4RouteTest test5
= {
9481 static IPv4RouteTest test6
= {
9492 static IPv4RouteTest test7
= {
9503 static IPv4RouteTest test8
= {
9512 static IPv4RouteTest test9
= {
9522 static IPv4RouteTest test10
= {
9532 static IPv4RouteTest test11
= {
9542 static IPv4RouteTest test12
= {
9551 static IPv4RouteTest test13
= {
9560 static IPv4RouteTest test14
= {
9568 static IPv4RouteTest test15
= {
9576 static IPv4RouteTest test16
= {
9585 static IPv4RouteTest test17
= {
9589 &en1_20_other_never
,
9594 static IPv4RouteTest test18
= {
9602 static IPv4RouteTestRef ipv4_tests
[] = {
9625 ipv4_prefix_length_is_valid(int prefix_length
)
9627 if (prefix_length
< 0 || prefix_length
> IPV4_ROUTE_ALL_BITS_SET
) {
9634 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9637 CFStringRef prop_val
;
9642 prop_val
= CFStringCreateWithCString(NULL
,
9644 kCFStringEncodingASCII
);
9645 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9646 CFRelease(prop_val
);
9651 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9655 CFStringRef prop_val
;
9660 prop_val
= CFStringCreateWithCString(NULL
,
9662 kCFStringEncodingASCII
);
9663 array
= CFArrayCreate(NULL
,
9664 (const void **)&prop_val
, 1,
9665 &kCFTypeArrayCallBacks
);
9666 CFRelease(prop_val
);
9667 CFDictionarySetValue(dict
, prop_name
, array
);
9673 dict_add_ip(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9678 str
= my_CFStringCreateWithInAddr(ip
);
9679 CFDictionarySetValue(dict
, prop_name
, str
);
9685 dict_add_ip_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9691 str
= my_CFStringCreateWithInAddr(ip
);
9692 array
= CFArrayCreate(NULL
,
9693 (const void **)&str
, 1,
9694 &kCFTypeArrayCallBacks
);
9696 CFDictionarySetValue(dict
, prop_name
, array
);
9702 dict_insert_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9703 struct route
* routes
, int routes_count
)
9706 CFMutableArrayRef route_list
;
9707 struct route
* scan
;
9709 if (routes
== NULL
|| routes_count
== 0) {
9712 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9713 &kCFTypeArrayCallBacks
);
9714 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9715 struct in_addr mask
;
9716 CFMutableDictionaryRef route_dict
;
9719 = CFDictionaryCreateMutable(NULL
, 0,
9720 &kCFTypeDictionaryKeyCallBacks
,
9721 &kCFTypeDictionaryValueCallBacks
);
9722 dict_add_string(route_dict
, kSCPropNetIPv4RouteDestinationAddress
,
9724 if (ipv4_prefix_length_is_valid(scan
->prefix_length
)) {
9725 mask
.s_addr
= htonl(prefix_to_mask32(scan
->prefix_length
));
9726 dict_add_ip(route_dict
, kSCPropNetIPv4RouteSubnetMask
, mask
);
9728 dict_add_string(route_dict
, kSCPropNetIPv4RouteGatewayAddress
,
9730 dict_add_string(route_dict
, kSCPropNetIPv4RouteInterfaceName
,
9732 CFArrayAppendValue(route_list
, route_dict
);
9733 CFRelease(route_dict
);
9735 CFDictionarySetValue(dict
, prop_name
, route_list
);
9736 CFRelease(route_list
);
9740 static CFDictionaryRef
9741 make_IPv4_dict(IPv4ServiceContentsRef t
)
9743 CFMutableDictionaryRef dict
;
9745 dict
= CFDictionaryCreateMutable(NULL
, 0,
9746 &kCFTypeDictionaryKeyCallBacks
,
9747 &kCFTypeDictionaryValueCallBacks
);
9748 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
9749 if (ipv4_prefix_length_is_valid(t
->prefix_length
)) {
9750 struct in_addr mask
;
9752 mask
.s_addr
= htonl(prefix_to_mask32(t
->prefix_length
));
9753 dict_add_ip_as_array(dict
, kSCPropNetIPv4SubnetMasks
, mask
);
9755 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
9756 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
9757 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9758 dict_add_string(dict
, kSCPropConfirmedInterfaceName
, t
->ifname
);
9759 dict_insert_routes(dict
, kSCPropNetIPv4AdditionalRoutes
,
9760 t
->additional_routes
, t
->additional_routes_count
);
9761 dict_insert_routes(dict
, kSCPropNetIPv4ExcludedRoutes
,
9762 t
->excluded_routes
, t
->excluded_routes_count
);
9767 kDirectionForwards
= 0,
9768 kDirectionBackwards
= 1
9772 kLogRouteDisabled
= 0,
9773 kLogRouteEnabled
= 1
9776 static IPv4RouteListRef
9777 make_IPv4RouteList_for_test(IPv4RouteListRef list
,
9778 IPv4ServiceContentsRef test
,
9781 CFDictionaryRef dict
;
9784 Rank rank_assertion
= kRankAssertionDefault
;
9785 CFNumberRef rank_assertion_cf
= NULL
;
9786 Boolean rank_assertion_is_set
= FALSE
;
9787 IPv4RouteListRef ret
= NULL
;
9788 IPV4_ROUTES_BUF_DECL(routes
);
9790 dict
= make_IPv4_dict(test
);
9792 fprintf(stderr
, "make_IPv4_dict failed\n");
9795 if (test
->primary_rank
!= NULL
) {
9797 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9798 &rank_assertion_is_set
);
9799 if (rank_assertion_is_set
) {
9801 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9804 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion_cf
);
9805 my_CFRelease(&rank_assertion_cf
);
9807 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
9811 if (rank_assertion
== kRankAssertionScoped
) {
9812 rank_assertion
= kRankAssertionNever
;
9814 rank
= RankMake(test
->rank
, rank_assertion
);
9815 if (log_it
== kLogRouteEnabled
9816 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9819 descr
= IPv4RouteListCopyDescription(r
);
9820 SCPrint(TRUE
, stdout
, CFSTR("Adding %@\n"), descr
);
9823 ret
= IPv4RouteListAddRouteList(list
, 1, r
, rank
);
9831 static IPv4RouteListRef
9832 make_IPv4RouteList(IPv4ServiceContentsRef
* test
, Direction direction
,
9835 IPv4RouteListRef ret
= NULL
;
9836 IPv4ServiceContentsRef
* scan
;
9838 switch (direction
) {
9839 case kDirectionBackwards
:
9840 for (scan
= test
; *scan
!= NULL
; scan
++) {
9841 /* find the end of the list */
9843 for (scan
--; scan
>= test
; scan
--) {
9844 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9848 case kDirectionForwards
:
9849 for (scan
= test
; *scan
!= NULL
; scan
++) {
9850 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9854 IPv4RouteListFinalize(ret
);
9858 #define EMPHASIS_CHARS "================="
9861 * Function: routelist_build_test
9863 * Runs through the given set of routes first in the forward direction,
9864 * then again backwards. We should end up with exactly the same set of
9865 * routes at the end.
9868 routelist_build_test(IPv4RouteTestRef test
)
9871 boolean_t ret
= FALSE
;
9872 IPv4RouteListRef routes1
;
9873 IPv4RouteListRef routes2
;
9875 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
9876 EMPHASIS_CHARS
"\n",
9879 routes1
= make_IPv4RouteList(test
->test
, kDirectionForwards
,
9881 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9882 if (routes1
!= NULL
) {
9883 descr
= IPv4RouteListCopyDescription(routes1
);
9884 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9888 routes2
= make_IPv4RouteList(test
->test
, kDirectionBackwards
,
9890 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9891 if (routes2
!= NULL
) {
9892 descr
= IPv4RouteListCopyDescription(routes2
);
9893 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9897 if ((routes1
!= NULL
&& routes2
== NULL
)
9898 || (routes1
== NULL
&& routes2
!= NULL
)) {
9899 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
9900 (routes1
!= NULL
) ? "not " : "",
9901 (routes2
!= NULL
) ? "not " : "");
9903 else if (routes1
!= NULL
&& routes2
!= NULL
) {
9904 /* check if they are different */
9905 if (routes1
->count
!= routes2
->count
) {
9906 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
9907 routes1
->count
, routes2
->count
);
9909 else if (bcmp(routes1
, routes2
,
9910 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
9911 fprintf(stderr
, "routes1 and routes2 are different\n");
9914 printf("routes1 and routes2 are the same\n");
9918 if (routes1
!= NULL
) {
9921 if (routes2
!= NULL
) {
9924 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
9925 EMPHASIS_CHARS
"\n",
9926 test
->name
, ret
? "PASSED" : "FAILED");
9931 apply_test(IPv4RouteTestRef old_test
, IPv4RouteTestRef new_test
)
9933 IPv4RouteListRef new_routes
;
9934 IPv4RouteListRef old_routes
;
9936 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
9937 EMPHASIS_CHARS
"\n",
9938 old_test
->name
, new_test
->name
);
9940 old_routes
= make_IPv4RouteList(old_test
->test
, kDirectionForwards
,
9942 new_routes
= make_IPv4RouteList(new_test
->test
, kDirectionForwards
,
9944 if (old_routes
== NULL
) {
9945 printf("No Old Routes\n");
9948 printf("Old routes ('%s') = ", old_test
->name
);
9949 IPv4RouteListPrint(old_routes
);
9952 /* apply the old routes */
9953 IPv4RouteListApply(NULL
, old_routes
, -1);
9955 if (new_routes
== NULL
) {
9956 printf("No New Routes\n");
9959 printf("New Routes ('%s') = ", new_test
->name
);
9960 IPv4RouteListPrint(new_routes
);
9963 /* apply the new routes */
9964 IPv4RouteListApply(old_routes
, new_routes
, -1);
9966 if (old_routes
!= NULL
) {
9969 if (new_routes
!= NULL
) {
9972 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
9973 EMPHASIS_CHARS
"\n",
9974 old_test
->name
, new_test
->name
);
9979 main(int argc
, char **argv
)
9981 IPv4RouteTestRef
* test
;
9983 _sc_log
= kSCLogDestinationFile
;
9984 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
9985 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
9987 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9989 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9990 if (!routelist_build_test(*test
)) {
9991 fprintf(stderr
, "%s failed\n", (*test
)->name
);
9995 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9996 IPv4RouteTestRef
* test2
;
9998 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
9999 apply_test(*test
, *test2
);
10000 apply_test(*test2
, *test
);
10007 printf("\nChecking for leaks\n");
10008 sprintf(cmd
, "leaks %d 2>&1", getpid());
10016 #endif /* TEST_IPV4_ROUTELIST */
10018 #if TEST_IPV6_ROUTELIST
10026 typedef const IPv6Address
* IPv6AddressRef
;
10029 IPv6AddressRef addr
;
10031 const char * router
;
10032 const char * ifname
;
10034 const CFStringRef
* primary_rank
;
10035 struct route
* additional_routes
;
10036 int additional_routes_count
;
10037 struct route
* excluded_routes
;
10038 int excluded_routes_count
;
10039 } IPv6ServiceContents
;
10041 typedef const IPv6ServiceContents
* IPv6ServiceContentsRef
;
10043 struct route loop_routelist
[] = {
10044 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
10045 "2620:149:4:f01:225:ff:fecc:89a2", NULL
},
10046 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
10047 "2620:149:4:f01:225:ff:fecc:89a3", NULL
},
10048 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
10049 "2620:149:4:f01:225:ff:fecc:89a4", NULL
},
10050 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
10051 "2620:149:4:f01:225:ff:fecc:89a5", NULL
},
10052 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
10053 "2620:149:4:f01:225:ff:fecc:89a6", NULL
},
10054 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
10055 "2620:149:4:f01:225:ff:fecc:89a7", NULL
},
10056 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
10057 "2620:149:4:f01:225:ff:fecc:89a8", NULL
},
10058 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
10059 "2620:149:4:f01:225:ff:fecc:89a9", NULL
},
10060 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
10061 "2620:149:4:f01:225:ff:fecc:89aa", NULL
},
10062 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
10063 "2620:149:4:f01:225:ff:fecc:89ab", NULL
},
10064 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
10065 "2620:149:4:f01:225:ff:fecc:89a1", NULL
},
10068 struct route vpn_routelist
[] = {
10069 { "2010:470:1f05:3cb::", 64,
10070 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10071 { "2010:222:3fa5:acb::", 48,
10072 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10073 { "2010:222:3fa5:1234::", 40,
10074 "fe80::2d0:bcff:fe3d:8c00", NULL
},
10075 { "2010:222:3fa5:5678::", 40,
10079 struct route vpn_routelist_ext
[] = {
10080 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL
, NULL
},
10083 struct route en1_routelist_ext
[] = {
10084 { "2020:299:abcd:ef12::", 64, NULL
, NULL
},
10088 static const IPv6Address en0_addr1
[] = {
10089 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL
},
10090 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL
}
10093 static const IPv6Address en0_addr2
[] = {
10094 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL
},
10095 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL
}
10098 static const IPv6Address en0_addr3
[] = {
10099 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL
},
10100 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL
}
10103 static const IPv6Address en0_addr4
[] = {
10104 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL
},
10105 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL
}
10108 static const IPv6Address en0_addr5
[] = {
10109 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL
},
10110 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL
}
10113 static const IPv6Address en0_addr6
[] = {
10114 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL
},
10117 static const IPv6Address en0_lladdr
[] = {
10118 { "fe80::cabc:c8ff:fe96:96af", 64, NULL
}
10121 static const IPv6Address en1_addr
[] = {
10122 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL
},
10123 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL
}
10126 static const IPv6Address utun0_addr
[] = {
10127 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL
},
10130 static const IPv6Address fw0_addr1
[] = {
10131 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL
},
10132 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL
}
10136 * address+address-count
10137 * router ifname pri rank additional-routes+count excluded-routes+count
10140 static const IPv6ServiceContents en0_10
= {
10141 en0_addr1
, countof(en0_addr1
),
10142 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
10145 static const IPv6ServiceContents en0_15
= {
10146 en0_addr2
, countof(en0_addr2
),
10147 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL
, NULL
, 0, NULL
, 0
10150 static const IPv6ServiceContents en0_30
= {
10151 en0_addr3
, countof(en0_addr3
),
10152 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL
, NULL
, 0, NULL
, 0
10155 static const IPv6ServiceContents en0_40
= {
10156 en0_addr4
, countof(en0_addr4
),
10157 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL
, NULL
, 0, NULL
, 0
10160 static const IPv6ServiceContents en0_50
= {
10161 en0_addr5
, countof(en0_addr5
),
10162 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL
, NULL
, 0, NULL
, 0
10165 static const IPv6ServiceContents en0_10_a
= {
10166 en0_addr6
, countof(en0_addr6
),
10167 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
10170 static const IPv6ServiceContents fw0_25
= {
10171 fw0_addr1
, countof(fw0_addr1
),
10172 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
10175 static const IPv6ServiceContents en1_20
= {
10176 en1_addr
, countof(en1_addr
),
10177 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL
, NULL
, 0, NULL
, 0
10180 static const IPv6ServiceContents en1_10_ext
= {
10181 en1_addr
, countof(en1_addr
),
10182 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL
, NULL
, 0,
10183 en1_routelist_ext
, countof(en1_routelist_ext
)
10186 static const IPv6ServiceContents en0_0_lladdr
= {
10187 en0_lladdr
, countof(en0_lladdr
),
10188 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL
, NULL
, 0, NULL
, 0
10191 static const IPv6ServiceContents en0_loop
= {
10192 en0_addr1
, countof(en0_addr1
),
10193 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
,
10194 loop_routelist
, countof(loop_routelist
), NULL
, 0
10197 static const IPv6ServiceContents utun0
= {
10198 utun0_addr
, countof(utun0_addr
),
10199 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL
,
10200 vpn_routelist
, countof(vpn_routelist
),
10201 vpn_routelist_ext
, countof(vpn_routelist_ext
),
10206 IPv6ServiceContentsRef test
[];
10207 } IPv6RouteTest
, * IPv6RouteTestRef
;
10209 static IPv6RouteTest test1
= {
10223 static IPv6RouteTest test2
= {
10236 static IPv6RouteTest test3
= {
10245 static IPv6RouteTest test4
= {
10254 static IPv6RouteTest test5
= {
10266 static IPv6RouteTestRef ipv6_tests
[] = {
10277 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10280 CFStringRef prop_val
;
10285 prop_val
= CFStringCreateWithCString(NULL
,
10287 kCFStringEncodingASCII
);
10288 CFDictionarySetValue(dict
, prop_name
, prop_val
);
10289 CFRelease(prop_val
);
10294 dict_add_int(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10299 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10300 CFDictionarySetValue(dict
, prop_name
, num
);
10306 dict_insert_v6_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10307 struct route
* routes
, int routes_count
)
10310 CFMutableArrayRef route_list
;
10311 struct route
* scan
;
10313 if (routes
== NULL
|| routes_count
== 0) {
10316 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
10317 &kCFTypeArrayCallBacks
);
10318 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
10319 CFMutableDictionaryRef route_dict
;
10321 route_dict
= CFDictionaryCreateMutable(NULL
, 0,
10322 &kCFTypeDictionaryKeyCallBacks
,
10323 &kCFTypeDictionaryValueCallBacks
);
10324 dict_add_string(route_dict
, kSCPropNetIPv6RouteDestinationAddress
,
10326 dict_add_int(route_dict
, kSCPropNetIPv6PrefixLength
,
10327 scan
->prefix_length
);
10328 dict_add_string(route_dict
, kSCPropNetIPv6RouteGatewayAddress
,
10330 dict_add_string(route_dict
, kSCPropNetIPv6RouteInterfaceName
,
10332 CFArrayAppendValue(route_list
, route_dict
);
10333 CFRelease(route_dict
);
10335 CFDictionarySetValue(dict
, prop_name
, route_list
);
10336 CFRelease(route_list
);
10341 array_add_string(CFMutableArrayRef array
, const char * c_str
)
10345 str
= CFStringCreateWithCString(NULL
,
10347 kCFStringEncodingUTF8
);
10348 CFArrayAppendValue(array
, str
);
10354 array_add_int(CFMutableArrayRef array
, int int_val
)
10358 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10359 CFArrayAppendValue(array
, num
);
10365 dict_add_ipv6_addressing(CFMutableDictionaryRef dict
,
10366 IPv6AddressRef list
, int list_count
)
10368 CFMutableArrayRef addr
= NULL
;
10369 CFMutableArrayRef dest
= NULL
;
10371 CFMutableArrayRef prefix
= NULL
;
10372 IPv6AddressRef scan
;
10374 if (list
== NULL
|| list_count
== 0) {
10377 for (i
= 0, scan
= list
; i
< list_count
; i
++, scan
++) {
10378 if (scan
->addr
!= NULL
) {
10379 if (addr
== NULL
) {
10380 addr
= CFArrayCreateMutable(NULL
, list_count
,
10381 &kCFTypeArrayCallBacks
);
10383 array_add_string(addr
, scan
->addr
);
10385 if (scan
->prefix_length
>= 0) {
10386 if (prefix
== NULL
) {
10387 prefix
= CFArrayCreateMutable(NULL
, list_count
,
10388 &kCFTypeArrayCallBacks
);
10390 array_add_int(prefix
, scan
->prefix_length
);
10392 if (scan
->dest
!= NULL
) {
10393 if (dest
== NULL
) {
10394 dest
= CFArrayCreateMutable(NULL
, list_count
,
10395 &kCFTypeArrayCallBacks
);
10397 array_add_string(dest
, scan
->dest
);
10400 if (addr
!= NULL
) {
10401 CFDictionarySetValue(dict
, kSCPropNetIPv6Addresses
, addr
);
10404 if (dest
!= NULL
) {
10405 CFDictionarySetValue(dict
, kSCPropNetIPv6DestAddresses
, dest
);
10408 if (prefix
!= NULL
) {
10409 CFDictionarySetValue(dict
, kSCPropNetIPv6PrefixLength
, prefix
);
10415 static CFDictionaryRef
10416 make_IPv6_dict(IPv6ServiceContentsRef t
)
10418 CFMutableDictionaryRef dict
;
10420 dict
= CFDictionaryCreateMutable(NULL
, 0,
10421 &kCFTypeDictionaryKeyCallBacks
,
10422 &kCFTypeDictionaryValueCallBacks
);
10423 dict_add_ipv6_addressing(dict
, t
->addr
, t
->addr_count
);
10424 dict_add_string(dict
, kSCPropNetIPv6Router
, t
->router
);
10425 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
10426 dict_insert_v6_routes(dict
, kSCPropNetIPv6AdditionalRoutes
,
10427 t
->additional_routes
, t
->additional_routes_count
);
10428 dict_insert_v6_routes(dict
, kSCPropNetIPv6ExcludedRoutes
,
10429 t
->excluded_routes
, t
->excluded_routes_count
);
10434 kDirectionForwards
= 0,
10435 kDirectionBackwards
= 1
10439 kLogRouteDisabled
= 0,
10440 kLogRouteEnabled
= 1
10443 static IPv6RouteListRef
10444 make_IPv6RouteList_for_test(IPv6RouteListRef list
,
10445 IPv6ServiceContentsRef test
,
10448 CFDictionaryRef dict
;
10449 IPv6RouteListRef r
;
10451 Rank rank_assertion
= kRankAssertionDefault
;
10452 CFNumberRef rank_assertion_cf
= NULL
;
10453 Boolean rank_assertion_is_set
= FALSE
;
10454 IPv6RouteListRef ret
= NULL
;
10455 IPV6_ROUTES_BUF_DECL(routes
);
10457 dict
= make_IPv6_dict(test
);
10458 if (dict
== NULL
) {
10459 fprintf(stderr
, "make_IPv6_dict failed\n");
10462 if (test
->primary_rank
!= NULL
) {
10464 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
10465 &rank_assertion_is_set
);
10466 if (rank_assertion_is_set
) {
10468 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
10471 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion_cf
);
10472 my_CFRelease(&rank_assertion_cf
);
10474 fprintf(stderr
, "IPv6RouteListCreateWithDictionary failed\n");
10478 if (rank_assertion
== kRankAssertionScoped
) {
10479 rank_assertion
= kRankAssertionNever
;
10481 rank
= RankMake(test
->rank
, rank_assertion
);
10482 if (log_it
== kLogRouteEnabled
10483 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10486 descr
= IPv6RouteListCopyDescription(r
);
10487 SCPrint(TRUE
, stdout
, CFSTR("Adding %@\n"), descr
);
10490 ret
= IPv6RouteListAddRouteList(list
, 1, r
, rank
);
10498 static IPv6RouteListRef
10499 make_IPv6RouteList(IPv6ServiceContentsRef
* test
, Direction direction
,
10502 IPv6RouteListRef ret
= NULL
;
10503 IPv6ServiceContentsRef
* scan
;
10505 switch (direction
) {
10506 case kDirectionBackwards
:
10507 for (scan
= test
; *scan
!= NULL
; scan
++) {
10508 /* find the end of the list */
10510 for (scan
--; scan
>= test
; scan
--) {
10511 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10515 case kDirectionForwards
:
10516 for (scan
= test
; *scan
!= NULL
; scan
++) {
10517 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10521 IPv6RouteListFinalize(ret
);
10525 #define EMPHASIS_CHARS "================="
10528 * Function: routelist_build_test
10530 * Runs through the given set of routes first in the forward direction,
10531 * then again backwards. We should end up with exactly the same set of
10532 * routes at the end.
10535 routelist_build_test(IPv6RouteTestRef test
)
10538 boolean_t ret
= FALSE
;
10539 IPv6RouteListRef routes1
;
10540 IPv6RouteListRef routes2
;
10542 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
10543 EMPHASIS_CHARS
"\n",
10546 routes1
= make_IPv6RouteList(test
->test
, kDirectionForwards
,
10548 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10549 if (routes1
!= NULL
) {
10550 descr
= IPv6RouteListCopyDescription(routes1
);
10551 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10555 routes2
= make_IPv6RouteList(test
->test
, kDirectionBackwards
,
10557 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10558 if (routes2
!= NULL
) {
10559 descr
= IPv6RouteListCopyDescription(routes2
);
10560 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10564 if ((routes1
!= NULL
&& routes2
== NULL
)
10565 || (routes1
== NULL
&& routes2
!= NULL
)) {
10566 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
10567 (routes1
!= NULL
) ? "not " : "",
10568 (routes2
!= NULL
) ? "not " : "");
10570 else if (routes1
!= NULL
&& routes2
!= NULL
) {
10571 /* check if they are different */
10572 if (routes1
->count
!= routes2
->count
) {
10573 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
10574 routes1
->count
, routes2
->count
);
10576 else if (bcmp(routes1
, routes2
,
10577 IPv6RouteListComputeSize(routes1
->count
)) != 0) {
10578 fprintf(stderr
, "routes1 and routes2 are different\n");
10581 printf("routes1 and routes2 are the same\n");
10585 if (routes1
!= NULL
) {
10588 if (routes2
!= NULL
) {
10591 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
10592 EMPHASIS_CHARS
"\n",
10593 test
->name
, ret
? "PASSED" : "FAILED");
10598 apply_test(IPv6RouteTestRef old_test
, IPv6RouteTestRef new_test
)
10600 IPv6RouteListRef new_routes
;
10601 IPv6RouteListRef old_routes
;
10603 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
10604 EMPHASIS_CHARS
"\n",
10605 old_test
->name
, new_test
->name
);
10607 old_routes
= make_IPv6RouteList(old_test
->test
, kDirectionForwards
,
10608 kLogRouteDisabled
);
10609 new_routes
= make_IPv6RouteList(new_test
->test
, kDirectionForwards
,
10610 kLogRouteDisabled
);
10611 if (old_routes
== NULL
) {
10612 printf("No Old Routes\n");
10615 printf("Old routes ('%s') = ", old_test
->name
);
10616 IPv6RouteListPrint(old_routes
);
10619 /* apply the old routes */
10620 IPv6RouteListApply(NULL
, old_routes
, -1);
10621 if (new_routes
== NULL
) {
10622 printf("No New Routes\n");
10625 printf("New Routes ('%s') = ", new_test
->name
);
10626 IPv6RouteListPrint(new_routes
);
10629 /* apply the new routes */
10630 IPv6RouteListApply(old_routes
, new_routes
, -1);
10631 if (old_routes
!= NULL
) {
10634 if (new_routes
!= NULL
) {
10637 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10638 EMPHASIS_CHARS
"\n",
10639 old_test
->name
, new_test
->name
);
10644 main(int argc
, char **argv
)
10646 IPv6RouteTestRef
* test
;
10648 _sc_log
= kSCLogDestinationFile
;
10649 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10650 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10652 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10654 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10655 if (!routelist_build_test(*test
)) {
10656 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10660 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10661 IPv6RouteTestRef
* test2
;
10663 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10664 apply_test(*test
, *test2
);
10665 apply_test(*test2
, *test
);
10672 printf("\nChecking for leaks\n");
10673 sprintf(cmd
, "leaks %d 2>&1", getpid());
10681 #endif /* TEST_IPV6_ROUTELIST */
10683 #ifdef TEST_DNS_ORDER
10685 #define kProtocolFlagsIPv4v6 (kProtocolFlagsIPv4 | kProtocolFlagsIPv6)
10687 #define V4_ADDR_LOOP CFSTR("127.0.0.1")
10688 #define V4_ADDR_1 CFSTR("192.168.1.1")
10689 #define V4_ADDR_2 CFSTR("192.168.1.2")
10690 #define V4_ADDR_3 CFSTR("8.8.8.8")
10691 #define V4_ADDR_4 CFSTR("8.8.4.4")
10693 #define V6_ADDR_LOOP CFSTR("::1")
10694 #define V6_ADDR_1 CFSTR("fe80::0123:4567:89ab:cdef%en0")
10695 #define V6_ADDR_2 CFSTR("fd00::2acf:e9ff:fe14:8c59")
10696 #define V6_ADDR_3 CFSTR("2001:4860:4860::8888")
10700 const ProtocolFlags flags
;
10701 const CFStringRef server_addrs
[];
10702 } DNSOrderTest
, * DNSOrderTestRef
;
10704 static DNSOrderTest test0a
= {
10706 kProtocolFlagsIPv4
,
10708 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, NULL
10712 static DNSOrderTest test0b
= {
10714 kProtocolFlagsIPv6
,
10716 V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10720 static DNSOrderTest test1a
= {
10722 kProtocolFlagsIPv4v6
,
10724 V4_ADDR_1
, V6_ADDR_1
, NULL
10728 static DNSOrderTest test2a
= {
10730 kProtocolFlagsIPv4v6
,
10732 V4_ADDR_1
, V6_ADDR_2
, NULL
10736 static DNSOrderTest test3a
= {
10738 kProtocolFlagsIPv4v6
,
10740 V4_ADDR_1
, V6_ADDR_3
, NULL
10744 static DNSOrderTest test1b
= {
10746 kProtocolFlagsIPv4v6
,
10748 V4_ADDR_3
, V6_ADDR_1
, NULL
10752 static DNSOrderTest test2b
= {
10754 kProtocolFlagsIPv4v6
,
10756 V4_ADDR_3
, V6_ADDR_2
, NULL
10760 static DNSOrderTest test3b
= {
10762 kProtocolFlagsIPv4v6
,
10764 V4_ADDR_3
, V6_ADDR_3
, NULL
10768 static DNSOrderTest test1c
= {
10770 kProtocolFlagsIPv4v6
,
10772 V6_ADDR_1
, V4_ADDR_1
, NULL
10776 static DNSOrderTest test2c
= {
10778 kProtocolFlagsIPv4v6
,
10780 V6_ADDR_2
, V4_ADDR_1
, NULL
10784 static DNSOrderTest test3c
= {
10786 kProtocolFlagsIPv4v6
,
10788 V6_ADDR_3
, V4_ADDR_1
, NULL
10792 static DNSOrderTest test1d
= {
10794 kProtocolFlagsIPv4v6
,
10796 V6_ADDR_1
, V4_ADDR_3
, NULL
10800 static DNSOrderTest test2d
= {
10802 kProtocolFlagsIPv4v6
,
10804 V6_ADDR_2
, V4_ADDR_3
, NULL
10808 static DNSOrderTest test3d
= {
10810 kProtocolFlagsIPv4v6
,
10812 V6_ADDR_3
, V4_ADDR_3
, NULL
10816 static DNSOrderTest test4
= {
10818 kProtocolFlagsIPv4v6
,
10820 V4_ADDR_LOOP
, V4_ADDR_3
, V6_ADDR_2
, NULL
10824 static DNSOrderTest test5
= {
10826 kProtocolFlagsIPv4v6
,
10828 V4_ADDR_3
, V6_ADDR_LOOP
, V6_ADDR_2
, NULL
10832 static DNSOrderTest test6
= {
10834 kProtocolFlagsIPv4v6
,
10836 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10840 static DNSOrderTest test7
= {
10842 kProtocolFlagsIPv4v6
,
10844 V4_ADDR_1
, V6_ADDR_1
, V4_ADDR_3
, V6_ADDR_2
, NULL
10848 static DNSOrderTestRef dns_order_tests
[] = {
10850 &test1a
, &test2a
, &test3a
,
10851 &test1b
, &test2b
, &test3b
,
10852 &test1c
, &test2c
, &test3c
,
10853 &test1d
, &test2d
, &test3d
,
10861 #define EMPHASIS_CHARS "================="
10864 apply_order(CFArrayRef servers
, ProtocolFlags flags
)
10866 CFArrayRef ordered_servers
;
10868 ordered_servers
= order_dns_servers(servers
, flags
);
10869 if (ordered_servers
!= NULL
) {
10870 SCPrint(TRUE
, stdout
, CFSTR("After :\n%@\n"), ordered_servers
);
10871 CFRelease(ordered_servers
);
10873 printf("FAIL: No ordered servers\n");
10880 apply_test(DNSOrderTestRef test
)
10882 CFMutableArrayRef servers
;
10884 printf("\n" EMPHASIS_CHARS
"> '%s' Begin <" EMPHASIS_CHARS
"\n\n", test
->name
);
10886 servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
10887 for (int i
= 0; test
->server_addrs
[i
] != NULL
; i
++) {
10888 CFStringRef server_addr
= test
->server_addrs
[i
];
10890 CFArrayAppendValue(servers
, server_addr
);
10893 SCPrint(TRUE
, stdout
, CFSTR("Before :\n%@\n"), servers
);
10895 apply_order(servers
, test
->flags
);
10897 CFRelease(servers
);
10903 main(int argc
, char **argv
)
10905 _sc_log
= kSCLogDestinationFile
;
10906 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10907 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10909 S_IPMonitor_debug
= (uint32
)strtoul(argv
[1], NULL
, 0);
10912 for (DNSOrderTestRef
* test
= dns_order_tests
; *test
!= NULL
; test
++) {
10919 printf("\nChecking for leaks\n");
10920 sprintf(cmd
, "leaks %d 2>&1", getpid());
10929 #endif /* TEST_DNS_ORDER */