2 * Copyright (c) 2000-2018 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 #if __has_include(<si_compare.h>)
90 #include <si_compare.h>
91 #else // __has_include(<si_compare.h>)
92 #include <network/sa_compare.h>
93 #endif // __has_include(<si_compare.h>)
94 #include <arpa/inet.h>
95 #include <sys/sysctl.h>
98 #include <mach/mach_time.h>
99 #include <dispatch/dispatch.h>
100 #include <CommonCrypto/CommonDigest.h>
102 #include "ip_plugin.h"
104 #include <SystemConfiguration/SystemConfiguration.h>
105 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
106 #include <SystemConfiguration/SCValidation.h>
107 #include <SystemConfiguration/scprefs_observer.h>
108 #include <SystemConfiguration/SCPrivate.h>
109 #include "SCNetworkReachabilityInternal.h"
110 #include "SCNetworkSignaturePrivate.h"
112 #include "dnsinfo_server.h"
114 #include <ppp/PPPControllerPriv.h>
117 #include <dns_sd_private.h>
119 #include <network_information.h>
120 #include "network_state_information_priv.h"
121 #include "network_state_information_logging.h"
122 #include "network_information_server.h"
123 #include <ppp/ppp_msg.h>
124 #if !TARGET_OS_SIMULATOR
125 #include "set-hostname.h"
126 #endif /* !TARGET_OS_SIMULATOR */
128 #include "dns-configuration.h"
130 #if !TARGET_OS_SIMULATOR
131 #include "nat64-configuration.h"
132 #endif /* !TARGET_OS_SIMULATOR */
134 #include "proxy-configuration.h"
136 #if !TARGET_OS_SIMULATOR
137 #include "agent-monitor.h"
138 #endif // !TARGET_OS_SIMULATOR
140 #if !TARGET_OS_IPHONE
141 #include "smb-configuration.h"
142 #endif /* !TARGET_OS_IPHONE */
144 #define kLoopbackInterface "lo0"
145 #define EROUTENOTAPPLIED 1001
147 typedef CF_ENUM(uint8_t, ProtocolFlags
) {
148 kProtocolFlagsNone
= 0x0,
149 kProtocolFlagsIPv4
= 0x1,
150 kProtocolFlagsIPv6
= 0x2
154 kDebugFlag1
= 0x00000001,
155 kDebugFlag2
= 0x00000002,
156 kDebugFlag4
= 0x00000004,
157 kDebugFlag8
= 0x00000008,
158 kDebugFlagDefault
= kDebugFlag1
,
159 kDebugFlagAll
= 0xffffffff
162 typedef unsigned int IFIndex
;
164 static dispatch_queue_t
__network_change_queue(void);
171 __private_extern__ os_log_t
172 __log_IPMonitor(void)
174 static os_log_t log
= NULL
;
177 log
= os_log_create("com.apple.SystemConfiguration", "IPMonitor");
185 #pragma mark interface index
188 #ifndef TEST_ROUTELIST
190 #define ROUTELIST_DEBUG(flag, fmt, ...)
192 static struct if_nameindex
* S_if_nameindex_cache
;
194 static dispatch_queue_t
195 __my_if_nametoindex_queue()
197 static dispatch_once_t once
;
198 static dispatch_queue_t q
;
200 dispatch_once(&once
, ^{
201 q
= dispatch_queue_create("my_if_nametoindex queue", NULL
);
207 __private_extern__ IFIndex
208 my_if_nametoindex(const char * ifname
)
210 __block IFIndex idx
= 0;
212 dispatch_sync(__my_if_nametoindex_queue(), ^{
213 struct if_nameindex
* scan
;
215 if (S_if_nameindex_cache
== NULL
) {
216 idx
= if_nametoindex(ifname
);
219 for (scan
= S_if_nameindex_cache
;
220 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
222 if (strcmp(scan
->if_name
, ifname
) == 0) {
223 idx
= scan
->if_index
;
232 __private_extern__
const char *
233 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
235 __block
const char * name
= NULL
;
237 dispatch_sync(__my_if_nametoindex_queue(), ^{
238 struct if_nameindex
* scan
;
240 if (S_if_nameindex_cache
== NULL
) {
241 name
= if_indextoname(idx
, if_name
);
244 for (scan
= S_if_nameindex_cache
;
245 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
247 if (scan
->if_index
== idx
) {
249 strlcpy(if_name
, scan
->if_name
, IFNAMSIZ
);
259 my_if_freenameindex(void)
261 dispatch_sync(__my_if_nametoindex_queue(), ^{
262 if (S_if_nameindex_cache
!= NULL
) {
263 if_freenameindex(S_if_nameindex_cache
);
264 S_if_nameindex_cache
= NULL
;
272 my_if_nameindex(void)
274 my_if_freenameindex();
275 dispatch_sync(__my_if_nametoindex_queue(), ^{
276 S_if_nameindex_cache
= if_nameindex();
283 #else /* TEST_ROUTELIST */
285 #define ROUTELIST_DEBUG(flags, format, ...) { if (((S_IPMonitor_debug & (flags)) != 0)) printf((format), ## __VA_ARGS__ ); }
288 static const char * * list
;
289 static int list_count
;
290 static int list_size
;
292 __private_extern__ IFIndex
293 my_if_nametoindex(const char * ifname
)
300 list
= (const char * *)malloc(sizeof(*list
) * list_size
);
301 list
[0] = strdup("");
302 list
[1] = strdup(kLoopbackInterface
);
307 for (i
= 1; i
< list_count
; i
++) {
308 if (strcmp(list
[i
], ifname
) == 0) {
314 if (list_count
== list_size
) {
316 list
= (const char * *)realloc(list
, sizeof(*list
) * list_size
);
318 list
[list_count
] = strdup(ifname
);
325 __private_extern__
const char *
326 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
328 const char * name
= NULL
;
330 if (idx
< list_count
) {
332 strlcpy(if_name
, list
[idx
], IFNAMSIZ
);
338 my_if_nameindex(void)
343 my_if_freenameindex(void)
347 #endif /* TEST_ROUTELIST */
350 my_if_indextoname2(IFIndex ifindex
, char ifname
[IFNAMSIZ
])
355 if (my_if_indextoname(ifindex
, ifname
) == NULL
) {
356 snprintf(ifname
, IFNAMSIZ
, "[%d]", ifindex
);
368 idx
= my_if_nametoindex(kLoopbackInterface
);
378 * Property: kServiceOptionRankAssertion
380 * Key used in the service options dictionary to hold the RankAssertion
381 * derived from the kSCPropNetServicePrimaryRank string.
383 #define kServiceOptionRankAssertion CFSTR("RankAssertion") /* number */
386 * Property: kIPIsCoupled
388 * Used to indicate that the IPv4 and IPv6 services are coupled.
389 * Neither the IPv4 part nor the IPv6 part of a coupled service
390 * may become primary if IPv4 or IPv6 is primary for another interface.
392 * For example, if the service over en3 is "coupled" and has IPv6,
393 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible
394 * to become primary for IPv6.
396 #define kIPIsCoupled CFSTR("IPIsCoupled")
398 #define PPP_PREFIX "ppp"
400 #define IP_FORMAT "%d.%d.%d.%d"
401 #define IP_CH(ip) ((u_char *)(ip))
402 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
404 static Boolean S_bundle_logging_verbose
;
407 * IPv4 Route management
410 typedef CF_ENUM(uint16_t, RouteFlags
) {
411 kRouteFlagsIsScoped
= 0x0001,
412 kRouteFlagsHasGateway
= 0x0002,
413 kRouteFlagsIsHost
= 0x0004,
414 kRouteFlagsIsNULL
= 0x0008,
415 kRouteFlagsKernelManaged
= 0x0010
418 typedef CF_ENUM(uint16_t, ControlFlags
) {
419 kControlFlagsProcessed
= 0x0001,
420 kControlFlagsAdded
= 0x0002,
423 #define ROUTE_COMMON \
426 IFIndex exclude_ifindex; \
429 ControlFlags control_flags;
435 #define PREFIX_LENGTH_IN_CLASSC 24
436 #define PREFIX_LENGTH_IN_CLASSD 4
442 struct in_addr gateway
;
444 } IPv4Route
, * IPv4RouteRef
;
448 struct in6_addr dest
;
449 struct in6_addr gateway
;
451 } IPv6Route
, * IPv6RouteRef
;
453 typedef CF_ENUM(uint16_t, RouteListFlags
) {
454 kRouteListFlagsExcludeNWI
= 0x0001,
455 kRouteListFlagsHasDefault
= 0x0002,
456 kRouteListFlagsScopedOnly
= 0x0004
459 #define ROUTELIST_COMMON \
462 RouteListFlags flags;
466 } RouteListCommon
, * RouteListRef
;
470 IPv4Route list
[1]; /* variable length */
471 } IPv4RouteList
, * IPv4RouteListRef
;
475 IPv6Route list
[1]; /* variable length */
476 } IPv6RouteList
, * IPv6RouteListRef
;
491 * Election Information
492 * - information about the current best services
500 struct sockaddr_in v4
;
501 struct sockaddr_in6 v6
;
504 typedef struct Candidate
{
505 CFStringRef serviceID
;
508 boolean_t ip_is_coupled
;
509 boolean_t ineligible
;
510 SCNetworkReachabilityFlags reachability_flags
;
512 in_sockaddr vpn_server_addr
;
513 CFStringRef signature
;
514 } Candidate
, * CandidateRef
;
516 typedef struct ElectionResults
{
520 Candidate candidates
[1];
521 } ElectionResults
, * ElectionResultsRef
;
523 static __inline__
size_t
524 ElectionResultsComputeSize(unsigned int n
)
526 return (offsetof(ElectionResults
, candidates
[n
]));
533 static __inline__ Rank
534 RankMake(uint32_t service_index
, Rank primary_rank
)
536 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
540 InterfaceRankGetRankAssertion(CFNumberRef rank_cf
, Boolean
* ret_is_set
)
542 SCNetworkServicePrimaryRank if_rank
;
543 Boolean is_set
= FALSE
;
544 Rank rank
= kRankAssertionDefault
;
547 && CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &if_rank
)
548 && if_rank
!= kSCNetworkServicePrimaryRankDefault
) {
549 if (if_rank
== kSCNetworkServicePrimaryRankFirst
) {
550 rank
= kRankAssertionFirst
;
553 rank
= RANK_ASSERTION_MAKE(if_rank
);
557 if (ret_is_set
!= NULL
) {
558 *ret_is_set
= is_set
;
564 PrimaryRankGetRankAssertion(CFStringRef rank_str
, Boolean
* is_set
)
567 const CFStringRef
* name
;
570 { &kSCValNetServicePrimaryRankFirst
, kRankAssertionFirst
},
571 { &kSCValNetServicePrimaryRankLast
, kRankAssertionLast
},
572 { &kSCValNetServicePrimaryRankNever
, kRankAssertionNever
},
573 { &kSCValNetServicePrimaryRankScoped
, kRankAssertionScoped
}
576 if (rank_str
!= NULL
) {
577 for (size_t i
= 0; i
< countof(values
); i
++) {
578 if (CFEqual(rank_str
, *(values
[i
].name
))) {
579 if (is_set
!= NULL
) {
582 return (values
[i
].rank_assertion
);
586 if (is_set
!= NULL
) {
589 return (kRankAssertionDefault
);
592 /* SCDynamicStore session */
593 static SCDynamicStoreRef S_session
= NULL
;
595 /* debug output flags */
596 static uint32_t S_IPMonitor_debug
= 0;
597 static Boolean S_IPMonitor_verbose
= FALSE
;
599 /* are we netbooted? If so, don't touch the default route */
600 static boolean_t S_netboot
= FALSE
;
602 /* dictionary to hold per-service state: key is the serviceID */
603 static CFMutableDictionaryRef S_service_state_dict
= NULL
;
604 static CFMutableDictionaryRef S_ipv4_service_rank_dict
= NULL
;
605 static CFMutableDictionaryRef S_ipv6_service_rank_dict
= NULL
;
607 /* dictionary to hold per-interface rank information */
608 static CFDictionaryRef S_if_rank_dict
;
610 /* if set, a PPP interface overrides the primary */
611 static boolean_t S_ppp_override_primary
= FALSE
;
613 /* the current primary serviceID's */
614 static CFStringRef S_primary_ipv4
= NULL
;
615 static CFStringRef S_primary_ipv6
= NULL
;
616 static CFStringRef S_primary_dns
= NULL
;
617 static CFStringRef S_primary_proxies
= NULL
;
619 /* the current election results */
620 static ElectionResultsRef S_ipv4_results
;
621 static ElectionResultsRef S_ipv6_results
;
623 static CFStringRef S_state_global_ipv4
= NULL
;
624 static CFStringRef S_state_global_ipv6
= NULL
;
625 static CFStringRef S_state_global_dns
= NULL
;
626 static CFStringRef S_state_global_proxies
= NULL
;
627 static CFStringRef S_state_service_prefix
= NULL
;
628 static CFStringRef S_setup_global_ipv4
= NULL
;
629 static CFStringRef S_setup_service_prefix
= NULL
;
631 static CFStringRef S_multicast_resolvers
= NULL
;
632 static CFStringRef S_private_resolvers
= NULL
;
634 #if !TARGET_OS_SIMULATOR
635 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
636 static IPv6RouteListRef S_ipv6_routelist
= NULL
;
637 #endif /* !TARGET_OS_SIMULATOR */
639 static boolean_t S_append_state
= FALSE
;
641 static CFDictionaryRef S_dns_dict
= NULL
;
643 static Boolean S_dnsinfo_synced
= TRUE
;
645 #if !TARGET_OS_SIMULATOR
646 // Note: access should be gated with __network_change_queue()
647 static CFMutableSetRef S_nat64_prefix_changes
= NULL
;
648 static CFMutableSetRef S_nat64_prefix_requests
= NULL
;
649 #endif /* !TARGET_OS_SIMULATOR */
651 static nwi_state_t S_nwi_state
= NULL
;
652 static Boolean S_nwi_synced
= TRUE
;
654 static CFDictionaryRef S_proxies_dict
= NULL
;
656 // Note: access should be gated with __network_change_queue()
657 static uint32_t S_network_change_needed
= 0;
658 #define NETWORK_CHANGE_NET 1<<0
659 #define NETWORK_CHANGE_DNS 1<<1
660 #define NETWORK_CHANGE_PROXY 1<<2
661 #if !TARGET_OS_IPHONE
662 #define NETWORK_CHANGE_SMB 1<<3
663 #endif /* !TARGET_OS_IPHONE */
664 #define NETWORK_CHANGE_NAT64 1<<4
665 static struct timeval S_network_change_start
;
666 static Boolean S_network_change_timeout
= FALSE
;
667 static dispatch_source_t S_network_change_timer
= NULL
;
669 #if !TARGET_OS_IPHONE
670 static CFStringRef S_primary_smb
= NULL
;
671 static CFStringRef S_state_global_smb
= NULL
;
672 static CFDictionaryRef S_smb_dict
= NULL
;
673 #endif /* !TARGET_OS_IPHONE */
675 #if !TARGET_OS_IPHONE
676 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
677 #endif /* !TARGET_OS_IPHONE */
680 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
681 #endif /* KERN_NETBOOT */
684 ** entityType*, GetEntityChanges*
685 ** - definitions for the entity types we handle
692 #if !TARGET_OS_IPHONE
694 #endif /* !TARGET_OS_IPHONE */
696 kEntityTypeTransientStatus
,
697 kEntityTypeServiceOptions
= 31
700 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
701 &kSCEntNetIPv4
, /* 0 */
702 &kSCEntNetIPv6
, /* 1 */
703 &kSCEntNetDNS
, /* 2 */
704 &kSCEntNetProxies
, /* 3 */
705 #if !TARGET_OS_IPHONE
706 &kSCEntNetSMB
, /* 4 */
707 #endif /* !TARGET_OS_IPHONE */
711 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
);
713 static __inline__
char
716 return ((af
== AF_INET
) ? '4' : '6');
719 static __inline__
char
720 ipvx_other_char(int af
)
722 return ((af
== AF_INET
) ? '6' : '4');
726 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
728 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
729 * Routes CFData containing IPv4RouteList/IPv6RouteList
730 * Service dictionary containing kSCEntNetIPv[46] service entity
732 #define kIPDictRoutes CFSTR("Routes") /* data */
733 #define kIPDictService CFSTR("Service") /* dict */
735 static CFDictionaryRef
736 ipdict_create(CFDictionaryRef dict
, CFDataRef routes_data
)
741 keys
[0] = kIPDictService
;
743 keys
[1] = kIPDictRoutes
;
744 values
[1] = routes_data
;
745 return (CFDictionaryCreate(NULL
,
746 (const void * *)keys
,
749 &kCFTypeDictionaryKeyCallBacks
,
750 &kCFTypeDictionaryValueCallBacks
));
754 ipdict_get_routelist(CFDictionaryRef dict
)
756 void * routes_list
= NULL
;
761 routes
= CFDictionaryGetValue(dict
, kIPDictRoutes
);
762 if (routes
!= NULL
) {
763 routes_list
= (void *)CFDataGetBytePtr(routes
);
766 return (routes_list
);
769 static CFDictionaryRef
770 ipdict_get_service(CFDictionaryRef dict
)
772 CFDictionaryRef ip_dict
= NULL
;
775 ip_dict
= CFDictionaryGetValue(dict
, kIPDictService
);
781 ipdict_get_ifname(CFDictionaryRef dict
)
783 CFStringRef ifname
= NULL
;
784 CFDictionaryRef ip_dict
;
786 ip_dict
= ipdict_get_service(dict
);
787 if (ip_dict
!= NULL
) {
788 ifname
= CFDictionaryGetValue(ip_dict
, kSCPropInterfaceName
);
793 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
794 CFDictionaryRef state_dict
,
795 CFDictionaryRef setup_dict
,
796 CFDictionaryRef info
);
797 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
799 static GetEntityChangesFunc get_ipv4_changes
;
800 static GetEntityChangesFunc get_ipv6_changes
;
801 static GetEntityChangesFunc get_dns_changes
;
802 static GetEntityChangesFunc get_proxies_changes
;
803 #if !TARGET_OS_IPHONE
804 static GetEntityChangesFunc get_smb_changes
;
805 #endif /* !TARGET_OS_IPHONE */
807 static __inline__
void
808 my_CFRelease(void * t
)
810 void * * obj
= (void * *)t
;
820 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
823 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
825 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
826 get_ipv4_changes
, /* 0 */
827 get_ipv6_changes
, /* 1 */
828 get_dns_changes
, /* 2 */
829 get_proxies_changes
,/* 3 */
830 #if !TARGET_OS_IPHONE
831 get_smb_changes
, /* 4 */
832 #endif /* !TARGET_OS_IPHONE */
837 ** - mechanism to do an atomic update of the SCDynamicStore
838 ** when the content needs to be changed across multiple functions
841 CFMutableArrayRef notify
;
842 CFMutableArrayRef remove
;
843 CFMutableDictionaryRef set
;
844 } keyChangeList
, * keyChangeListRef
;
847 keyChangeListInit(keyChangeListRef keys
)
849 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
850 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
851 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
852 &kCFTypeDictionaryKeyCallBacks
,
853 &kCFTypeDictionaryValueCallBacks
);
858 keyChangeListFree(keyChangeListRef keys
)
860 my_CFRelease(&keys
->notify
);
861 my_CFRelease(&keys
->remove
);
862 my_CFRelease(&keys
->set
);
867 keyChangeListActive(keyChangeListRef keys
)
869 return ((CFDictionaryGetCount(keys
->set
) > 0) ||
870 (CFArrayGetCount(keys
->remove
) > 0) ||
871 (CFArrayGetCount(keys
->notify
) > 0));
875 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
877 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
882 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
884 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
885 CFDictionaryRemoveValue(keys
->set
, key
);
890 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
892 my_CFArrayRemoveValue(keys
->remove
, key
);
893 CFDictionarySetValue(keys
->set
, key
, value
);
898 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
900 CFArrayRef notify
= keys
->notify
;
901 CFArrayRef remove
= keys
->remove
;
902 CFDictionaryRef set
= keys
->set
;
904 if (CFArrayGetCount(notify
) == 0) {
907 if (CFArrayGetCount(remove
) == 0) {
910 if (CFDictionaryGetCount(set
) == 0) {
913 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
916 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
918 my_log(LOG_DEBUG
, "Setting:\n%@", set
);
920 if (remove
!= NULL
) {
921 my_log(LOG_DEBUG
, "Removing:\n%@", remove
);
923 if (notify
!= NULL
) {
924 my_log(LOG_DEBUG
, "Notifying:\n%@", notify
);
927 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
940 mib
[1] = KERN_NETBOOT
;
941 len
= sizeof(netboot
);
942 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
946 static int rtm_seq
= 0;
948 #if !TARGET_OS_SIMULATOR
950 open_routing_socket(void)
954 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
955 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
960 static __inline__
int
961 inet6_dgram_socket(void)
965 sockfd
= socket(AF_INET6
, SOCK_DGRAM
, 0);
967 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
974 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
976 struct in6_defrouter dr
;
977 struct sockaddr_in6
* sin6
;
979 bzero(&dr
, sizeof(dr
));
981 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
982 sin6
->sin6_family
= AF_INET6
;
983 sin6
->sin6_addr
= *addr
;
985 dr
.if_index
= if_index
;
986 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
990 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
992 struct in6_defrouter dr
;
993 struct sockaddr_in6
* sin6
;
995 bzero(&dr
, sizeof(dr
));
997 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
998 sin6
->sin6_family
= AF_INET6
;
999 sin6
->sin6_addr
= *addr
;
1000 dr
.if_index
= if_index
;
1001 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
1004 #endif /* !TARGET_OS_SIMULATOR */
1007 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
1009 CFIndex n
= CFArrayGetCount(arr
);
1011 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
1014 CFArrayAppendValue(arr
, new);
1019 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
1023 i
= CFArrayGetFirstIndexOfValue(arr
,
1024 CFRangeMake(0, CFArrayGetCount(arr
)),
1026 if (i
!= kCFNotFound
) {
1027 CFArrayRemoveValueAtIndex(arr
, i
);
1033 my_CFArrayCreateCombinedArray(CFArrayRef array1
, CFArrayRef array2
)
1035 CFMutableArrayRef combined
;
1037 combined
= CFArrayCreateMutableCopy(NULL
, 0, array1
);
1038 CFArrayAppendArray(combined
,
1040 CFRangeMake(0, CFArrayGetCount(array2
)));
1044 static CFDictionaryRef
1045 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
1047 if (isA_CFDictionary(dict
) == NULL
) {
1050 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
1054 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
1056 if (isA_CFDictionary(dict
) == NULL
) {
1059 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
1062 #if !TARGET_OS_SIMULATOR
1064 my_CFSetAddValue_async(dispatch_queue_t queue
, CFMutableSetRef
*set
, CFTypeRef value
)
1067 dispatch_async(queue
, ^{
1069 *set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1071 CFSetAddValue(*set
, value
);
1077 #endif /* !TARGET_OS_SIMULATOR */
1080 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, size_t addr_size
)
1084 if (isA_CFString(str
) == NULL
) {
1090 if (addr_size
< sizeof(struct in_addr
)) {
1095 if (addr_size
< sizeof(struct in6_addr
)) {
1102 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
1103 if (inet_pton(family
, buf
, addr
) == 1) {
1107 bzero(addr
, addr_size
);
1113 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
1115 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
1120 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
1122 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
1126 cfnumber_to_int(CFNumberRef num
, int * int_val
)
1128 if (isA_CFNumber(num
) == NULL
) {
1131 return (CFNumberGetValue(num
, kCFNumberIntType
, int_val
));
1134 static CF_RETURNS_RETAINED CFStringRef
1135 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
1137 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1138 kSCDynamicStoreDomainSetup
,
1143 static CF_RETURNS_RETAINED CFStringRef
1144 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
1146 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1147 kSCDynamicStoreDomainState
,
1153 interface_entity_key_copy(CFStringRef ifname
, CFStringRef entity
)
1155 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1156 kSCDynamicStoreDomainState
,
1161 static CFDictionaryRef
1162 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1165 CFStringRef setup_key
;
1166 CFDictionaryRef setup_dict
;
1168 setup_key
= setup_service_key(serviceID
, entity
);
1169 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
1170 my_CFRelease(&setup_key
);
1171 return (setup_dict
);
1174 static CFDictionaryRef
1175 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1178 CFStringRef state_key
;
1179 CFDictionaryRef state_dict
;
1181 state_key
= state_service_key(serviceID
, entity
);
1182 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
1183 my_CFRelease(&state_key
);
1184 return (state_dict
);
1188 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1192 ip_list
= CFDictionaryGetValue(dict
, prop
);
1193 if (isA_CFArray(ip_list
) != NULL
1194 && CFArrayGetCount(ip_list
) > 0
1195 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1202 dict_get_first_ipv6(CFDictionaryRef dict
, CFStringRef prop
,
1203 struct in6_addr
* ip_p
)
1207 ip_list
= CFDictionaryGetValue(dict
, prop
);
1208 if (isA_CFArray(ip_list
) != NULL
1209 && CFArrayGetCount(ip_list
) > 0
1210 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1217 dict_get_first_int(CFDictionaryRef dict
, CFStringRef prop
,
1222 list
= CFDictionaryGetValue(dict
, prop
);
1223 if (isA_CFArray(list
) != NULL
1224 && CFArrayGetCount(list
) > 0
1225 && cfnumber_to_int(CFArrayGetValueAtIndex(list
, 0), val
)) {
1232 dict_get_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1236 val
= CFDictionaryGetValue(dict
, prop
);
1237 return (cfstring_to_ip(val
, ip_p
));
1241 dict_get_ipv6(CFDictionaryRef dict
, CFStringRef prop
, struct in6_addr
* ip_p
)
1245 val
= CFDictionaryGetValue(dict
, prop
);
1246 return (cfstring_to_ip6(val
, ip_p
));
1250 dict_get_int(CFDictionaryRef dict
, CFStringRef prop
, int * intval
)
1254 val
= CFDictionaryGetValue(dict
, prop
);
1255 return (cfnumber_to_int(val
, intval
));
1259 get_override_primary(CFDictionaryRef dict
)
1263 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
1264 if (isA_CFNumber(override
) != NULL
) {
1267 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
1272 else if (isA_CFBoolean(override
) != NULL
) {
1273 if (CFBooleanGetValue(override
)) {
1285 (*RouteListComputeSize
)(CFIndex n
);
1288 (*RouteIsEqual
)(RouteRef a
, RouteRef b
);
1291 (*RouteApply
)(RouteRef route
, int cmd
, int sockfd
);
1293 typedef const void *
1294 (*RouteGateway
)(RouteRef route
);
1297 (*RouteSetGateway
)(RouteRef route
, const void * address
);
1299 typedef const void *
1300 (*RouteDestination
)(RouteRef route
);
1303 (*RouteSameSubnet
)(RouteRef route
, const void * address
);
1306 (*RouteCopyDescription
)(RouteRef route
);
1309 (*RouteLog
)(int priority
, RouteRef route
, const char * msg
);
1312 RouteListComputeSize list_compute_size
;
1314 RouteIsEqual route_equal
;
1315 RouteApply route_apply
;
1316 RouteGateway route_gateway
;
1317 RouteSetGateway route_set_gateway
;
1318 RouteDestination route_destination
;
1319 RouteSameSubnet route_same_subnet
;
1321 RouteCopyDescription route_copy_description
;
1328 typedef const RouteListInfo
* RouteListInfoRef
;
1331 RouteListInfoRef info
;
1332 RouteListRef old_routes
;
1333 RouteListRef new_routes
;
1336 } RouteListApplyContext
, * RouteListApplyContextRef
;
1340 RouteAddressCompare(RouteListInfoRef info
,
1344 return (memcmp(addr1
, addr2
, info
->address_size
));
1348 RouteCompare(RouteListInfoRef info
,
1349 RouteRef a
, Rank a_rank
,
1350 RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
1353 RouteDestination route_destination
;
1354 RouteCopyDescription route_copy_description
;
1357 route_destination
= info
->route_destination
;
1358 route_copy_description
= info
->route_copy_description
;
1359 cmp
= RouteAddressCompare(info
,
1360 (*route_destination
)(a
),
1361 (*route_destination
)(b
));
1363 cmp
= a
->prefix_length
- b
->prefix_length
;
1365 int index_cmp
= a
->ifindex
- b
->ifindex
;
1367 if (index_cmp
== 0) {
1370 else if ((a
->ifindex
== 0 || b
->ifindex
== 0)
1371 && (a
->flags
& kRouteFlagsIsScoped
) == 0
1372 && (b
->flags
& kRouteFlagsIsScoped
) == 0) {
1374 * Either of the routes specifies no interface and neither
1375 * route is scoped. Claim they are equal to eliminate the
1382 cmp
= RankCompare(a_rank
, b_rank
);
1389 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
1397 else if (cmp
== 0) {
1403 a_str
= (*route_copy_description
)(a
);
1404 b_str
= (*route_copy_description
)(b
);
1405 my_log(LOG_DEBUG
, "%@ rank 0x%x %c %@ rank 0x%x",
1406 a_str
, a_rank
, ch
, b_str
, b_rank
);
1414 RouteListGetRouteAtIndexSimple(RouteListInfoRef info
, RouteListRef routes
,
1417 return ((void *)routes
+ (*info
->list_compute_size
)(where
));
1421 RouteListGetRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1424 if (routes
->count
== 0
1425 || where
>= routes
->count
) {
1428 return (RouteListGetRouteAtIndexSimple(info
, routes
, where
));
1432 RouteListGetFirstRoute(RouteListInfoRef info
, RouteListRef routes
)
1434 return (RouteListGetRouteAtIndexSimple(info
, routes
, 0));
1437 #if !TARGET_OS_SIMULATOR
1439 RouteListRouteIndex(RouteListInfoRef info
, RouteListRef routes
,
1442 return (((void *)route
1443 - (void *)RouteListGetFirstRoute(info
, routes
))
1444 / info
->element_size
);
1446 #endif /* !TARGET_OS_SIMULATOR */
1449 RouteGetNextRoute(RouteListInfoRef info
, RouteRef route
)
1451 return ((RouteRef
)(((void *)route
) + info
->element_size
));
1455 RouteListAddRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1456 RouteRef this_route
, CFIndex where
)
1458 RouteRef insert_route
;
1460 if (where
== kCFNotFound
) {
1461 /* add it to the end */
1463 = RouteListGetRouteAtIndexSimple(info
, routes
, routes
->count
);
1466 /* make space at [where] */
1467 insert_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1469 (void *)insert_route
+ info
->element_size
,
1470 info
->element_size
* (routes
->count
- where
));
1472 /* copy the route */
1473 bcopy(this_route
, insert_route
, info
->element_size
);
1475 return (insert_route
);
1479 RouteListRemoveRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1482 if (routes
->count
== 0
1483 || where
>= routes
->count
) {
1487 if (where
== routes
->count
) {
1488 /* last slot, decrementing gets rid of it */
1491 RouteRef remove_route
;
1493 remove_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1494 bcopy((void *)remove_route
+ info
->element_size
,
1496 info
->element_size
* (routes
->count
- where
));
1502 * Function: RouteListAddRoute
1505 * Add the given route to the list of routes, eliminating lower-ranked
1506 * duplicates on the same interface, and marking any lower ranked duplicates
1507 * on other interfaces with kRouteFlagsIsScoped.
1509 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1512 * Route list updated with the given route, possibly a different pointer,
1513 * due to using realloc'd memory.
1523 RouteListAddRoute(RouteListInfoRef info
,
1524 RouteListRef routes
, int init_size
,
1525 RouteRef this_route
, Rank this_rank
)
1528 RouteRef first_scan
= NULL
;
1531 Scope scope_which
= kScopeNone
;
1532 CFIndex where
= kCFNotFound
;
1534 if (routes
== NULL
) {
1535 size_t alloc_size
= (*info
->list_compute_size
)(init_size
);
1537 routes
= (RouteListRef
)malloc(alloc_size
);
1538 bzero(routes
, sizeof(*routes
));
1539 routes
->size
= init_size
;
1541 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1543 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1545 boolean_t same_dest
;
1547 cmp
= RouteCompare(info
, this_route
, this_rank
, scan
, scan
->rank
,
1549 if (same_dest
&& (first_scan
== NULL
)) {
1553 if (where
== kCFNotFound
) {
1555 && (first_scan
!= NULL
)
1556 && (first_scan
->flags
& kRouteFlagsIsScoped
) == 0) {
1557 if ((scan
->flags
& kRouteFlagsIsScoped
) != 0) {
1558 ROUTELIST_DEBUG(kDebugFlag8
,
1559 "Hit 1: set scope on self\n");
1560 scope_which
= kScopeThis
;
1563 ROUTELIST_DEBUG(kDebugFlag8
,
1564 "Hit 2: set scope on next\n");
1565 scope_which
= kScopeNext
;
1568 /* remember our insertion point, but keep going to find a dup */
1572 else if (cmp
== 0) {
1575 if (where
!= kCFNotFound
1576 && scan
->ifindex
== this_route
->ifindex
1577 && scan
->exclude_ifindex
== 0
1578 && this_route
->exclude_ifindex
== 0) {
1579 /* this route is a duplicate */
1580 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 3: removing [%ld]\n", i
);
1581 RouteListRemoveRouteAtIndex(info
, routes
, i
);
1585 * this_route is "better" than scan if this_route is not excluded
1586 * and scan is excluded or this_route sorts ahead of scan
1588 if (this_route
->exclude_ifindex
== 0
1589 && (scan
->exclude_ifindex
!= 0 || this_rank
< scan
->rank
)) {
1590 IFIndex ifindex
= 0;
1591 boolean_t is_scoped
= FALSE
;
1593 if (scan
->flags
& kRouteFlagsIsScoped
) {
1596 if (this_rank
< scan
->rank
) {
1597 ROUTELIST_DEBUG(kDebugFlag8
,
1598 "Hit 4a: replacing [%ld]"
1599 " rank 0x%x < 0x%x\n",
1600 i
, this_rank
, scan
->rank
);
1603 ROUTELIST_DEBUG(kDebugFlag8
,
1604 "Hit 4b: replacing [%ld] excluded route\n",
1607 if (scan
->ifindex
!= 0) {
1608 ifindex
= scan
->ifindex
;
1610 else if (this_route
->ifindex
!= 0) {
1611 ifindex
= this_route
->ifindex
;
1613 bcopy(this_route
, scan
, info
->element_size
);
1614 scan
->rank
= this_rank
;
1615 scan
->ifindex
= ifindex
;
1616 scan
->exclude_ifindex
= 0;
1618 /* preserve whether route was scoped */
1619 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 5: preserved scope\n");
1620 scan
->flags
|= kRouteFlagsIsScoped
;
1628 if (scope_which
== kScopeNone
) {
1629 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 6: set scope on self\n");
1630 scope_which
= kScopeThis
;
1633 #ifdef TEST_ROUTELIST
1634 else if (where
!= kCFNotFound
) {
1635 /* not possible because we maintain a sorted list */
1637 "Hit 7: moved past routes - can't happen\n");
1641 #endif /* TEST_ROUTELIST */
1645 if (routes
->size
== routes
->count
) {
1647 RouteListRef new_routes
;
1650 /* double the size */
1651 old_size
= routes
->size
;
1652 how_many
= old_size
* 2;
1653 new_routes
= (RouteListRef
)
1654 reallocf(routes
, (*info
->list_compute_size
)(how_many
));
1655 if (new_routes
== NULL
) {
1660 ROUTELIST_DEBUG(kDebugFlag8
, "increasing size from %d to %d\n",
1661 old_size
, how_many
);
1662 new_routes
->size
= how_many
;
1663 routes
= new_routes
;
1666 /* add/insert the new route */
1667 this_route
= RouteListAddRouteAtIndex(info
, routes
, this_route
, where
);
1668 this_route
->rank
= this_rank
;
1670 if (RANK_ASSERTION_MASK(this_rank
) == kRankAssertionNever
) {
1671 flags
|= kRouteFlagsIsScoped
;
1673 switch (scope_which
) {
1675 flags
|= kRouteFlagsIsScoped
;
1678 this_route
= RouteListGetRouteAtIndex(info
, routes
, where
+ 1);
1679 flags
|= kRouteFlagsIsScoped
;
1685 if (this_route
!= NULL
&& flags
!= 0) {
1686 this_route
->flags
|= flags
;
1694 * Function: RouteListAddRouteList
1696 * Invoke RouteListAddRoute for each route in the given list
1697 * 'service_routes' combining them into a combined list 'routes'.
1700 * See RouteListAddRoute for more information.
1703 RouteListAddRouteList(RouteListInfoRef info
,
1704 RouteListRef routes
, int init_size
,
1705 RouteListRef service_routes
, Rank rank
)
1710 for (i
= 0, scan
= RouteListGetFirstRoute(info
, service_routes
);
1711 i
< service_routes
->count
;
1712 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1716 && (service_routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
1717 /* only apply rank to first element of the list (default route) */
1721 this_rank
= RANK_INDEX_MASK(rank
) | RANK_ASSERTION_MASK(scan
->rank
);
1723 routes
= RouteListAddRoute(info
, routes
, init_size
, scan
, this_rank
);
1729 RouteAddInterfaceToDescription(RouteRef r
, CFMutableStringRef str
)
1731 char if_name
[IFNAMSIZ
];
1733 if (my_if_indextoname2(r
->ifindex
, if_name
) != NULL
) {
1734 CFStringAppendFormat(str
, NULL
,
1738 if (my_if_indextoname2(r
->exclude_ifindex
, if_name
) != NULL
) {
1739 CFStringAppendFormat(str
, NULL
,
1747 RouteAddFlagsToDescription(RouteRef r
, CFMutableStringRef str
)
1749 if ((r
->flags
& kRouteFlagsIsNULL
) != 0) {
1750 CFStringAppend(str
, CFSTR(" [null]"));
1753 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
1755 switch (rank_assertion
) {
1756 case kRankAssertionFirst
:
1757 CFStringAppend(str
, CFSTR(" [first]"));
1759 case kRankAssertionLast
:
1760 CFStringAppend(str
, CFSTR(" [last]"));
1762 case kRankAssertionNever
:
1763 CFStringAppend(str
, CFSTR(" [never]"));
1768 if ((r
->flags
& kRouteFlagsKernelManaged
) != 0) {
1769 CFStringAppend(str
, CFSTR(" [kern]"));
1771 if ((r
->flags
& kRouteFlagsIsScoped
) != 0) {
1772 CFStringAppend(str
, CFSTR(" [SCOPED]"));
1778 #if !TARGET_OS_SIMULATOR
1780 RouteListFindRoute(RouteListInfoRef info
, RouteListRef routes
, RouteRef route
)
1783 RouteRef match
= NULL
;
1786 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1788 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1789 if ((*info
->route_equal
)(scan
, route
)) {
1799 kRouteLookupFlagsNone
= 0x0,
1800 kRouteLookupFlagsExcludeInterface
= 0x1
1804 RouteListLookup(RouteListInfoRef info
,
1805 RouteListRef routes
,
1806 const void * address
,
1809 RouteLookupFlags lookup_flags
)
1811 RouteRef best_match
= NULL
;
1815 for (i
= 0, candidate
= RouteListGetFirstRoute(info
, routes
);
1817 i
++, candidate
= RouteGetNextRoute(info
, candidate
)) {
1818 if (candidate
->ifindex
== 0 || candidate
->exclude_ifindex
!= 0) {
1819 /* ignore exclude routes */
1822 if ((lookup_flags
& kRouteLookupFlagsExcludeInterface
) != 0) {
1823 /* exclude interfaces with the same interface index */
1824 if (ifindex
== candidate
->ifindex
) {
1828 else if (ifindex
!= candidate
->ifindex
) {
1831 if ((candidate
->flags
& kRouteFlagsHasGateway
) != 0
1832 && RouteAddressCompare(info
,
1833 (*info
->route_gateway
)(candidate
),
1835 /* skip route whose gateway is the address we're looking for */
1838 if ((candidate
->flags
& kRouteFlagsIsHost
) != 0) {
1839 /* if host route and we're looking for an exact match */
1840 if (n_bits
== info
->all_bits_set
1841 && RouteAddressCompare(info
,
1842 (*info
->route_destination
)(candidate
),
1844 /* found exact match */
1845 best_match
= candidate
;
1851 /* verify that address is on the same subnet */
1852 if ((*info
->route_same_subnet
)(candidate
, address
) == FALSE
) {
1853 /* different subnet */
1857 if (candidate
->prefix_length
== n_bits
) {
1859 best_match
= candidate
;
1862 if (candidate
->prefix_length
> n_bits
) {
1863 /* matched too many bits */
1866 if (best_match
== NULL
1867 || candidate
->prefix_length
> best_match
->prefix_length
) {
1868 best_match
= candidate
;
1871 return (best_match
);
1876 * Function: RouteProcess
1878 * Function to process adding or removing the specified route.
1879 * In the case of adding, that may involve first processing the gateway
1880 * route (recursively).
1883 RouteProcess(RouteRef route
,
1885 RouteListApplyContextRef context
)
1887 RouteLog route_log
= context
->info
->route_log
;
1888 RouteApply route_apply
= context
->info
->route_apply
;
1889 RouteGateway route_gateway
= context
->info
->route_gateway
;
1893 case kRouteCommandAdd
:
1894 if ((route
->control_flags
& kControlFlagsProcessed
) != 0) {
1895 return ((route
->control_flags
& kControlFlagsAdded
) != 0);
1897 route
->control_flags
|= kControlFlagsProcessed
;
1898 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
1900 RouteRef gateway_route
;
1903 = RouteListLookup(context
->info
,
1904 context
->new_routes
,
1905 (*route_gateway
)(route
),
1906 context
->info
->all_bits_set
,
1908 kRouteLookupFlagsNone
);
1909 if (gateway_route
== NULL
) {
1910 (*route_log
)(LOG_NOTICE
, route
, "no gateway route");
1913 #define MAX_RECURSE_DEPTH 10
1914 /* avoid infinite recursion */
1915 if (context
->depth
== MAX_RECURSE_DEPTH
) {
1916 (*route_log
)(LOG_NOTICE
, route
, "routing loop detected, not adding");
1919 /* recurse to add gateway route */
1921 added
= RouteProcess(gateway_route
,
1926 (*route_log
)(LOG_NOTICE
, route
, "failed to add");
1931 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1932 if (retval
== EEXIST
) {
1933 /* delete and add again */
1934 (void)(*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
1935 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1940 "failed to add route, %s:",
1942 (*route_log
)(LOG_NOTICE
, route
, NULL
);
1945 case EROUTENOTAPPLIED
:
1946 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1950 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
1951 snprintf(buf
, sizeof(buf
), "%sAdd new[%ld]",
1953 RouteListRouteIndex(context
->info
,
1954 context
->new_routes
,
1956 (*route_log
)(LOG_DEBUG
, route
, buf
);
1958 route
->control_flags
|= kControlFlagsAdded
;
1962 case kRouteCommandRemove
:
1963 retval
= (*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
1967 case EROUTENOTAPPLIED
:
1968 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1972 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
1973 snprintf(buf
, sizeof(buf
), "%sRemove old[%ld]%s",
1975 RouteListRouteIndex(context
->info
,
1976 context
->old_routes
,
1978 (retval
== ESRCH
) ? "(ESRCH)" : "");
1979 (*route_log
)(LOG_DEBUG
, route
, buf
);
1984 "failed to remove route, %s",
1986 (*route_log
)(LOG_NOTICE
, route
, NULL
);
1997 RouteListApply(RouteListInfoRef info
,
1998 RouteListRef old_routes
, RouteListRef new_routes
,
2001 RouteListApplyContext context
;
2005 if (old_routes
== new_routes
&& old_routes
== NULL
) {
2006 /* both old and new are NULL, so there's nothing to do */
2009 bzero(&context
, sizeof(context
));
2010 context
.old_routes
= old_routes
;
2011 context
.new_routes
= new_routes
;
2012 context
.sockfd
= sockfd
;
2013 context
.info
= info
;
2014 if (old_routes
!= NULL
) {
2015 for (i
= 0, scan
= RouteListGetFirstRoute(info
, old_routes
);
2016 i
< old_routes
->count
;
2017 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2018 RouteRef new_route
= NULL
;
2020 if (new_routes
!= NULL
) {
2021 new_route
= RouteListFindRoute(info
, new_routes
, scan
);
2023 if (new_route
== NULL
) {
2024 if ((scan
->control_flags
& kControlFlagsAdded
) != 0) {
2025 RouteProcess(scan
, kRouteCommandRemove
, &context
);
2030 if (new_routes
!= NULL
) {
2031 if (old_routes
!= NULL
) {
2032 /* preserve the control flags from any old routes */
2033 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2034 i
< new_routes
->count
;
2035 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2036 RouteRef old_route
= NULL
;
2038 old_route
= RouteListFindRoute(info
, old_routes
, scan
);
2039 if (old_route
!= NULL
) {
2040 /* preserve the control state in the new route */
2041 scan
->control_flags
= old_route
->control_flags
;
2045 /* add any routes that need to be added */
2046 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2047 i
< new_routes
->count
;
2048 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2049 if ((scan
->control_flags
& kControlFlagsProcessed
) != 0) {
2052 RouteProcess(scan
, kRouteCommandAdd
, &context
);
2058 * Function: RouteListFinalize
2060 * Look for excluded routes. If the excluded route does not have an assigned
2061 * interface, search for a route that *does not* go over the excluded
2064 * If the excluded route does have an assigned interface, search for a route
2065 * that *does* go over the assigned interface.
2067 * Set the gateway on the excluded route to match the gateway of the found
2071 RouteListFinalize(RouteListInfoRef info
, RouteListRef routes
)
2076 if (routes
== NULL
) {
2079 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
2081 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2084 RouteLookupFlags flags
;
2086 if (scan
->exclude_ifindex
== 0) {
2089 if (scan
->ifindex
== 0) {
2090 ifindex
= scan
->exclude_ifindex
;
2091 flags
= kRouteLookupFlagsExcludeInterface
;
2094 ifindex
= scan
->ifindex
;
2095 flags
= kRouteLookupFlagsNone
;
2097 route
= RouteListLookup(info
, routes
,
2098 (*info
->route_destination
)(scan
),
2099 scan
->prefix_length
, ifindex
, flags
);
2100 if (route
== NULL
) {
2101 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
, "can't resolve excluded route");
2104 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
2105 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)scan
, "Excluded route");
2106 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)route
, "Resolved to");
2108 scan
->ifindex
= route
->ifindex
;
2109 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2110 (*info
->route_set_gateway
)(scan
, (*info
->route_gateway
)(route
));
2111 scan
->flags
|= kRouteFlagsHasGateway
;
2112 if (scan
->prefix_length
== info
->all_bits_set
) {
2113 scan
->flags
|= kRouteFlagsIsHost
;
2117 /* routes directly to interface */
2118 scan
->flags
&= ~(kRouteFlagsHasGateway
| kRouteFlagsIsHost
);
2124 #endif /* !TARGET_OS_SIMULATOR */
2130 #define IPV4_ROUTE_ALL_BITS_SET 32
2132 static __inline__
struct in_addr
2133 subnet_addr(struct in_addr addr
, struct in_addr mask
)
2137 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
2142 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
2144 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
2145 CFStringAppendFormat(str
, NULL
,
2146 CFSTR("Host " IP_FORMAT
),
2150 CFStringAppendFormat(str
, NULL
,
2151 CFSTR("Net " IP_FORMAT
),
2153 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
2156 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
2157 CFStringAppendFormat(str
, NULL
,
2158 CFSTR(" Gate " IP_FORMAT
),
2159 IP_LIST(&r
->gateway
));
2161 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
2162 if (r
->ifa
.s_addr
!= 0) {
2163 CFStringAppendFormat(str
, NULL
,
2164 CFSTR(" Ifa " IP_FORMAT
),
2167 RouteAddFlagsToDescription((RouteRef
)r
, str
);
2172 IPv4RouteCopyDescription(RouteRef r
)
2174 CFMutableStringRef str
;
2176 str
= CFStringCreateMutable(NULL
, 0);
2177 IPv4RouteCopyDescriptionWithString((IPv4RouteRef
)r
, str
);
2181 #ifdef TEST_IPV4_ROUTELIST
2182 static CFMutableStringRef
2183 IPv4RouteListCopyDescription(IPv4RouteListRef routes
);
2186 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2188 CFStringRef str
= IPv4RouteCopyDescription(route
);
2191 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2194 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
2200 static __inline__
void
2201 IPv4RouteListPrint(IPv4RouteListRef routes
)
2203 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2205 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2210 #else /* TEST_IPV4_ROUTELIST */
2212 static __inline__
void
2213 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2215 CFStringRef str
= IPv4RouteCopyDescription(route
);
2218 my_log(level
, "%@", str
);
2221 my_log(level
, "%s: %@", msg
, str
);
2227 #endif /* TEST_IPV4_ROUTELIST */
2230 IPv4RouteIsEqual(RouteRef r_scan
, RouteRef r_route
)
2232 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2233 IPv4RouteRef scan
= (IPv4RouteRef
)r_scan
;
2235 return ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
2236 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
2237 && (scan
->ifindex
== route
->ifindex
)
2238 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
2239 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
2240 && (scan
->flags
== route
->flags
));
2243 static CFMutableStringRef
2244 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
2248 CFMutableStringRef str
;
2250 str
= CFStringCreateMutable(NULL
, 0);
2251 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
2253 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
2254 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
2255 IPv4RouteCopyDescriptionWithString(r
, str
);
2257 CFStringAppend(str
, CFSTR("\n}"));
2262 IPv4RouteListComputeSize(CFIndex n
)
2264 return (offsetof(IPv4RouteList
, list
[n
]));
2268 count_prefix_bits_set(uint32_t n
)
2271 const static int8_t bits
[16] = {
2290 for (count
= 0; n
!= 0; n
>>= 4) {
2291 int nbits
= bits
[n
& 0x0f];
2302 prefix_to_mask32(unsigned int prefix_length
)
2304 if (prefix_length
> 32 || prefix_length
== 0) {
2307 return (0xffffffff << (32 - prefix_length
));
2311 mask_get_prefix_length(struct in_addr mask
)
2315 count
= count_prefix_bits_set(mask
.s_addr
);
2319 val
= prefix_to_mask32(count
);
2320 if (ntohl(mask
.s_addr
) != val
) {
2321 /* expected mask based on prefix length doesn't match */
2329 IPv4RouteSetPrefixLength(IPv4RouteRef route
)
2333 length
= mask_get_prefix_length(route
->mask
);
2337 route
->prefix_length
= length
;
2342 IPv4RouteGateway(RouteRef r_route
)
2344 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2345 return (&route
->gateway
);
2349 IPv4RouteSetGateway(RouteRef r_route
, const void * address
)
2351 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2353 route
->gateway
= *((struct in_addr
*)address
);
2358 IPv4RouteDestination(RouteRef r_route
)
2360 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2361 return (&route
->dest
);
2365 IPv4RouteSameSubnet(RouteRef r_route
, const void * addr
)
2367 const struct in_addr
* address
;
2368 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2370 address
= (const struct in_addr
*)addr
;
2371 return ((address
->s_addr
& route
->mask
.s_addr
) == route
->dest
.s_addr
);
2375 * Define: ROUTE_MSG_ADDRS_SPACE
2377 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2378 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2379 * someone changes the code and doesn't think to modify this.
2381 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2382 + 2 * sizeof(struct sockaddr_dl) \
2385 struct rt_msghdr hdr
;
2386 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2390 * Function: IPv4RouteApply
2392 * Add or remove the specified route to/from the kernel routing table.
2395 IPv4RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
2399 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2402 struct sockaddr_in
* in_p
;
2403 struct sockaddr_dl
* dl_p
;
2407 if (S_netboot
&& route
->dest
.s_addr
== 0) {
2408 /* don't touch the default route */
2409 return (EROUTENOTAPPLIED
);
2411 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
2412 return (EROUTENOTAPPLIED
);
2414 if (route
->ifindex
== 0) {
2416 IP_FORMAT
" no interface specified, ignoring",
2417 IP_LIST(&route
->dest
));
2421 #ifdef TEST_IPV4_ROUTELIST
2423 #else /* TEST_IPV4_ROUTELIST */
2425 #endif /* TEST_IPV4_ROUTELIST */
2427 memset(&rtmsg
, 0, sizeof(rtmsg
));
2428 rtmsg
.hdr
.rtm_type
= cmd
;
2429 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2430 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2431 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
2432 if (route
->ifa
.s_addr
!= 0) {
2433 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
2435 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2436 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
2437 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
2440 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
2441 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
2442 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
2445 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2446 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
2448 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
2449 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
2450 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2453 rtaddr
.ptr
= rtmsg
.addrs
;
2456 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2457 rtaddr
.in_p
->sin_family
= AF_INET
;
2458 rtaddr
.in_p
->sin_addr
= route
->dest
;
2459 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2462 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2463 /* gateway is an IP address */
2464 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2465 rtaddr
.in_p
->sin_family
= AF_INET
;
2466 rtaddr
.in_p
->sin_addr
= route
->gateway
;
2467 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2470 /* gateway is the interface itself */
2471 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2472 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2473 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2474 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2478 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
2479 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2480 rtaddr
.in_p
->sin_family
= AF_INET
;
2481 rtaddr
.in_p
->sin_addr
= route
->mask
;
2482 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2486 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
2487 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2488 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2489 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2490 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2492 /* interface address */
2493 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
2494 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2495 rtaddr
.in_p
->sin_family
= AF_INET
;
2496 rtaddr
.in_p
->sin_addr
= route
->ifa
;
2497 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2500 /* apply the route */
2501 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
2502 rtmsg
.hdr
.rtm_msglen
= len
;
2503 if (write(sockfd
, &rtmsg
, len
) == -1) {
2509 static const RouteListInfo IPv4RouteListInfo
= {
2510 IPv4RouteListComputeSize
,
2515 IPv4RouteSetGateway
,
2516 IPv4RouteDestination
,
2517 IPv4RouteSameSubnet
,
2519 IPv4RouteCopyDescription
,
2522 sizeof(struct in_addr
),
2523 IPV4_ROUTE_ALL_BITS_SET
2526 #if !TARGET_OS_SIMULATOR
2527 static __inline__
void
2528 IPv4RouteListLog(int level
, IPv4RouteListRef routes
)
2530 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2532 my_log(level
, "%@", str
);
2538 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
2541 RouteListApply(&IPv4RouteListInfo
,
2542 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
2548 IPv4RouteListFinalize(IPv4RouteListRef routes
)
2550 RouteListFinalize(&IPv4RouteListInfo
, (RouteListRef
)routes
);
2553 #endif /* !TARGET_OS_SIMULATOR */
2555 #ifdef TEST_IPV4_ROUTELIST
2556 static IPv4RouteListRef
2557 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
2558 IPv4RouteListRef service_routes
, Rank rank
)
2560 return ((IPv4RouteListRef
)
2561 RouteListAddRouteList(&IPv4RouteListInfo
,
2562 (RouteListRef
)routes
, init_size
,
2563 (RouteListRef
)service_routes
, rank
));
2565 #endif /* TEST_IPV4_ROUTELIST */
2568 plist_get_string(CFDictionaryRef dict
, CFStringRef prop_name
,
2569 char * buf
, int buf_size
)
2573 val
= CFDictionaryGetValue(dict
, prop_name
);
2574 if (isA_CFString(val
) == NULL
) {
2577 if (!CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingUTF8
)) {
2584 struct in_addr addr
;
2587 IFIndex exclude_ifindex
;
2588 IPv4RouteRef
* route_p
;
2591 } AddIPv4RouteContext
, * AddIPv4RouteContextRef
;
2594 AddIPv4Route(const void * value
, void * context
)
2596 AddIPv4RouteContextRef ctx
= (AddIPv4RouteContextRef
)context
;
2597 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
2598 IPv4RouteRef r
= *ctx
->route_p
;
2600 dict
= isA_CFDictionary(dict
);
2602 || !dict_get_ip(dict
, kSCPropNetIPv4RouteDestinationAddress
, &r
->dest
)
2603 || !dict_get_ip(dict
, kSCPropNetIPv4RouteSubnetMask
, &r
->mask
)) {
2604 /* one less route than we expected */
2606 my_log(LOG_NOTICE
, "%s route is not a dictionary",
2610 my_log(LOG_NOTICE
, "%s route is invalid, %@",
2615 if (!IPv4RouteSetPrefixLength(r
)) {
2616 my_log(LOG_NOTICE
, "%s route has invalid subnet mask, %@",
2620 r
->rank
= ctx
->rank
;
2621 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
2622 if (ctx
->ifindex
!= 0) {
2623 r
->ifindex
= ctx
->ifindex
;
2625 if (ctx
->exclude_ifindex
== 0
2626 && dict_get_ip(dict
,
2627 kSCPropNetIPv4RouteGatewayAddress
,
2629 r
->flags
|= kRouteFlagsHasGateway
;
2630 if (r
->prefix_length
== IPV4_ROUTE_ALL_BITS_SET
) {
2631 r
->flags
|= kRouteFlagsIsHost
;
2636 char ifname
[IFNAMSIZ
];
2638 if (plist_get_string(dict
, kSCPropNetIPv4RouteInterfaceName
,
2639 ifname
, sizeof(ifname
)) != NULL
) {
2642 ifindex
= my_if_nametoindex(ifname
);
2645 "%s: interface %s does not exist, %@",
2646 ctx
->descr
, ifname
, dict
);
2649 else if (ifindex
== ctx
->ifindex
) {
2651 "%s: interface %s unexpected, %@",
2652 ctx
->descr
, ifname
, dict
);
2655 r
->ifindex
= ifindex
;
2668 confirm_interface_name(CFDictionaryRef dict
, CFStringRef ifname
)
2670 CFStringRef confirmed_ifname
;
2671 boolean_t confirmed
;
2674 = CFDictionaryGetValue(dict
, kSCPropConfirmedInterfaceName
);
2675 if (isA_CFString(confirmed_ifname
) != NULL
) {
2676 confirmed
= CFEqual(confirmed_ifname
, ifname
);
2685 * Function: IPv4RouteListCreateWithDictionary
2688 * Given the service ipv4 entity dictionary, generate the list of routes.
2689 * Currently, this includes just the default route and subnet route,
2690 * if the service has a subnet mask.
2693 * If the passed in route_list is NULL or too small, this routine
2694 * allocates malloc'd memory to hold the routes.
2696 static IPv4RouteListRef
2697 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
2698 CFDictionaryRef dict
,
2699 CFNumberRef rank_assertion
)
2701 boolean_t add_broadcast_multicast
= FALSE
;
2702 boolean_t add_default
= FALSE
;
2703 boolean_t add_router_subnet
= FALSE
;
2704 boolean_t add_subnet
= FALSE
;
2705 struct in_addr addr
= { 0 };
2706 CFArrayRef additional_routes
= NULL
;
2707 CFIndex additional_routes_count
;
2708 boolean_t allow_additional_routes
= FALSE
;
2709 boolean_t exclude_from_nwi
= FALSE
;
2710 CFArrayRef excluded_routes
= NULL
;
2711 CFIndex excluded_routes_count
;
2712 RouteFlags flags
= 0;
2714 char ifname
[IFNAMSIZ
];
2715 CFStringRef ifname_cf
;
2716 struct in_addr mask
= { 0 };
2718 int prefix_length
= 0;
2719 Rank primary_rank
= kRankAssertionDefault
;
2721 Rank rank
= kRankAssertionDefault
;
2722 struct in_addr router
= { 0 };
2723 boolean_t scoped_only
= FALSE
;
2724 struct in_addr subnet
= { 0 };
2729 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
2730 ifname
, sizeof(ifname
));
2731 if (ifname_cf
== NULL
) {
2734 ifindex
= my_if_nametoindex(ifname
);
2736 /* interface doesn't exist */
2739 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
2740 if (!dict_get_ip(dict
, kSCPropNetIPv4Router
, &router
)) {
2741 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
2743 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
2744 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
2746 subnet
= subnet_addr(addr
, mask
);
2747 prefix_length
= mask_get_prefix_length(mask
);
2748 if (prefix_length
< 0) {
2750 "ignoring bad subnet mask "
2752 IP_LIST(&mask
), ifname
);
2759 if (addr
.s_addr
== 0) {
2760 /* invalid/non-existent address */
2763 if (rank_assertion
!= NULL
) {
2764 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
2767 if (router
.s_addr
== 0) {
2768 /* if no router is configured, demote the rank if necessary */
2769 switch (primary_rank
) {
2770 case kRankAssertionLast
:
2771 case kRankAssertionNever
:
2772 case kRankAssertionScoped
:
2773 /* rank is already demoted */
2776 /* demote to RankLast */
2777 primary_rank
= kRankAssertionLast
;
2783 * If the router address is our address and the subnet mask is
2784 * not 255.255.255.255, assume all routes are local to the interface.
2786 if (addr
.s_addr
== router
.s_addr
2787 && mask
.s_addr
!= INADDR_BROADCAST
) {
2788 ; /* all routes local */
2791 flags
|= kRouteFlagsHasGateway
;
2793 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
2794 primary_rank
= kRankAssertionFirst
;
2798 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
2799 exclude_from_nwi
= TRUE
;
2800 flags
|= kRouteFlagsIsNULL
;
2803 switch (primary_rank
) {
2804 case kRankAssertionScoped
:
2805 /* Scoped means all routes for the service get scoped */
2806 primary_rank
= rank
= kRankAssertionNever
;
2807 flags
|= kRouteFlagsIsScoped
;
2810 case kRankAssertionNever
:
2811 /* Never means just the default route gets scoped */
2812 rank
= kRankAssertionLast
;
2813 flags
|= kRouteFlagsIsScoped
;
2816 rank
= primary_rank
;
2820 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2821 add_router_subnet
= TRUE
;
2825 if (ifindex
!= lo0_ifindex()) {
2826 if (router
.s_addr
!= 0) {
2830 add_broadcast_multicast
= TRUE
;
2833 if (allow_additional_routes
) {
2835 = CFDictionaryGetValue(dict
, kSCPropNetIPv4AdditionalRoutes
);
2836 additional_routes
= isA_CFArray(additional_routes
);
2837 if (additional_routes
!= NULL
) {
2838 additional_routes_count
= CFArrayGetCount(additional_routes
);
2839 n
+= additional_routes_count
;
2842 = CFDictionaryGetValue(dict
, kSCPropNetIPv4ExcludedRoutes
);
2843 excluded_routes
= isA_CFArray(excluded_routes
);
2844 if (excluded_routes
!= NULL
) {
2845 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
2846 n
+= excluded_routes_count
;
2849 if (routes
== NULL
|| routes
->size
< n
) {
2850 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
2851 bzero(routes
, IPv4RouteListComputeSize(n
));
2855 bzero(routes
->list
, sizeof(routes
->list
[0]) * n
);
2858 if (exclude_from_nwi
) {
2859 routes
->flags
|= kRouteListFlagsExcludeNWI
;
2861 else if (scoped_only
) {
2862 routes
->flags
|= kRouteListFlagsScopedOnly
;
2865 /* start at the beginning */
2869 /* add the default route */
2870 routes
->flags
|= kRouteListFlagsHasDefault
;
2871 r
->ifindex
= ifindex
;
2874 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2875 r
->gateway
= router
;
2880 r
->rank
= primary_rank
;
2883 if (add_broadcast_multicast
) {
2884 /* add the broadcast route (rdar://problem/22149738) */
2885 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2886 r
->flags
|= kRouteFlagsIsNULL
;
2888 r
->dest
.s_addr
= INADDR_BROADCAST
;
2889 r
->mask
.s_addr
= INADDR_BROADCAST
;
2890 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2891 r
->ifindex
= ifindex
;
2896 /* add multicast route (rdar://problem/26457121) */
2897 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2898 r
->flags
|= kRouteFlagsIsNULL
;
2900 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
2901 r
->mask
.s_addr
= htonl(IN_CLASSD_NET
);
2902 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSD
;
2903 r
->ifindex
= ifindex
;
2910 /* add the subnet route */
2912 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2913 r
->flags
|= kRouteFlagsIsNULL
;
2915 r
->ifindex
= ifindex
;
2919 r
->prefix_length
= prefix_length
;
2925 /* add the router subnet route */
2926 if (add_router_subnet
) {
2927 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2928 r
->flags
|= kRouteFlagsIsNULL
;
2930 r
->ifindex
= ifindex
;
2933 r
->mask
.s_addr
= INADDR_BROADCAST
;
2934 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2940 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
2941 AddIPv4RouteContext context
;
2943 bzero(&context
, sizeof(context
));
2944 context
.count_p
= &routes
->count
;
2945 context
.route_p
= &r
;
2946 context
.rank
= rank
;
2948 /* additional routes */
2949 if (additional_routes
!= NULL
) {
2950 context
.ifindex
= ifindex
;
2951 context
.addr
= addr
;
2952 context
.descr
= "AdditionalRoutes";
2953 CFArrayApplyFunction(additional_routes
,
2954 CFRangeMake(0, additional_routes_count
),
2955 AddIPv4Route
, &context
);
2957 /* excluded routes */
2958 if (excluded_routes
!= NULL
) {
2959 context
.descr
= "ExcludedRoutes";
2960 /* exclude this interface */
2961 context
.ifindex
= 0;
2962 context
.exclude_ifindex
= ifindex
;
2963 CFArrayApplyFunction(excluded_routes
,
2964 CFRangeMake(0, excluded_routes_count
),
2965 AddIPv4Route
, &context
);
2971 #if !TARGET_OS_SIMULATOR
2972 static IPv4RouteListRef
2973 IPv4RouteListCopyMulticastLoopback(void)
2976 IPv4RouteListRef routes
;
2978 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(1));
2979 bzero(routes
, IPv4RouteListComputeSize(1));
2980 routes
->count
= routes
->size
= 1;
2983 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
2984 r
->mask
.s_addr
= htonl(IN_CLASSC_NET
);
2985 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSC
;
2986 r
->ifindex
= lo0_ifindex();
2989 #endif /* !TARGET_OS_SIMULATOR */
2994 #define IPV6_ROUTE_ALL_BITS_SET 128
2997 ipv6_prefix_length_is_valid(int prefix_length
)
2999 if (prefix_length
< 0 || prefix_length
> IPV6_ROUTE_ALL_BITS_SET
) {
3006 * from netinet6/in6.c
3009 in6_len2mask(struct in6_addr
* mask
, int len
)
3013 bzero(mask
, sizeof(*mask
));
3014 for (i
= 0; i
< len
/ 8; i
++)
3015 mask
->s6_addr
[i
] = 0xff;
3017 mask
->s6_addr
[i
] = (0xff00 >> (len
% 8)) & 0xff;
3021 in6_maskaddr(struct in6_addr
* addr
, const struct in6_addr
* mask
)
3023 for (size_t i
= 0; i
< sizeof(addr
->s6_addr
); i
++) {
3024 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3030 in6_netaddr(struct in6_addr
* addr
, int len
)
3032 struct in6_addr mask
;
3034 in6_len2mask(&mask
, len
);
3035 in6_maskaddr(addr
, &mask
);
3040 in6_addr_scope_linklocal(struct in6_addr
* addr
, IFIndex ifindex
)
3042 if (IN6_IS_ADDR_LINKLOCAL(addr
)) {
3043 addr
->__u6_addr
.__u6_addr16
[1] = htons(ifindex
);
3049 string_append_in6_addr(CFMutableStringRef str
, const struct in6_addr
* addr
)
3051 char ntopbuf
[INET6_ADDRSTRLEN
];
3053 CFStringAppendCString(str
,
3054 inet_ntop(AF_INET6
, addr
, ntopbuf
, sizeof(ntopbuf
)),
3055 kCFStringEncodingASCII
);
3060 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r
, CFMutableStringRef str
)
3062 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
3063 CFStringAppend(str
, CFSTR("Host "));
3064 string_append_in6_addr(str
, &r
->dest
);
3067 CFStringAppend(str
, CFSTR("Net "));
3068 string_append_in6_addr(str
, &r
->dest
);
3069 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
3072 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
3073 CFStringAppend(str
, CFSTR(" Gate "));
3074 string_append_in6_addr(str
, &r
->gateway
);
3076 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
3077 if (!IN6_ARE_ADDR_EQUAL(&r
->ifa
, &in6addr_any
)) {
3078 CFStringAppend(str
, CFSTR(" Ifa "));
3079 string_append_in6_addr(str
, &r
->ifa
);
3081 RouteAddFlagsToDescription((RouteRef
)r
, str
);
3086 IPv6RouteCopyDescription(RouteRef r
)
3088 CFMutableStringRef str
;
3090 str
= CFStringCreateMutable(NULL
, 0);
3091 IPv6RouteCopyDescriptionWithString((IPv6RouteRef
)r
, str
);
3095 static CFMutableStringRef
3096 IPv6RouteListCopyDescription(IPv6RouteListRef routes
)
3100 CFMutableStringRef str
;
3102 str
= CFStringCreateMutable(NULL
, 0);
3103 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv6RouteList[%d]> = {"),
3105 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
3106 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
3107 IPv6RouteCopyDescriptionWithString(r
, str
);
3109 CFStringAppend(str
, CFSTR("\n}"));
3113 #ifdef TEST_IPV6_ROUTELIST
3116 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3118 CFStringRef str
= IPv6RouteCopyDescription(route
);
3121 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3124 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
3130 static __inline__
void
3131 IPv6RouteListPrint(IPv6RouteListRef routes
)
3133 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3135 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3140 #else /* TEST_IPV6_ROUTELIST */
3142 static __inline__
void
3143 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3145 CFStringRef str
= IPv6RouteCopyDescription(route
);
3148 my_log(level
, "%@", str
);
3151 my_log(level
, "%s: %@", msg
, str
);
3157 #endif /* TEST_IPV6_ROUTELIST */
3160 IPv6RouteListComputeSize(CFIndex n
)
3162 return (offsetof(IPv6RouteList
, list
[n
]));
3167 struct in6_addr
* addr
;
3170 IFIndex exclude_ifindex
;
3171 IPv6RouteRef
* route_p
;
3174 } AddIPv6RouteContext
, * AddIPv6RouteContextRef
;
3177 AddIPv6Route(const void * value
, void * context
)
3179 AddIPv6RouteContextRef ctx
= (AddIPv6RouteContextRef
)context
;
3180 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
3181 IPv6RouteRef r
= *ctx
->route_p
;
3183 dict
= isA_CFDictionary(dict
);
3185 || !dict_get_ipv6(dict
, kSCPropNetIPv6RouteDestinationAddress
, &r
->dest
)
3186 || !dict_get_int(dict
, kSCPropNetIPv6RoutePrefixLength
,
3188 || !ipv6_prefix_length_is_valid(r
->prefix_length
)) {
3189 /* one less route than we expected */
3191 my_log(LOG_NOTICE
, "%s route is not a dictionary",
3195 my_log(LOG_NOTICE
, "%s route is invalid, %@",
3200 r
->rank
= ctx
->rank
;
3201 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
3202 if (ctx
->ifindex
!= 0) {
3203 r
->ifindex
= ctx
->ifindex
;
3204 r
->ifa
= *ctx
->addr
;
3205 if (ctx
->exclude_ifindex
== 0
3206 && dict_get_ipv6(dict
,
3207 kSCPropNetIPv6RouteGatewayAddress
,
3209 r
->flags
|= kRouteFlagsHasGateway
;
3210 if (r
->prefix_length
== IPV6_ROUTE_ALL_BITS_SET
) {
3211 r
->flags
|= kRouteFlagsIsHost
;
3216 char ifname
[IFNAMSIZ
];
3218 if (plist_get_string(dict
, kSCPropNetIPv6RouteInterfaceName
,
3219 ifname
, sizeof(ifname
)) != NULL
) {
3222 ifindex
= my_if_nametoindex(ifname
);
3225 "%s: interface %s does not exist, %@",
3226 ctx
->descr
, ifname
, dict
);
3229 else if (ifindex
== ctx
->ifindex
) {
3231 "%s: interface %s unexpected, %@",
3232 ctx
->descr
, ifname
, dict
);
3235 r
->ifindex
= ifindex
;
3248 * Function: IPv6RouteListCreateWithDictionary
3251 * Given the service IPv6 entity dictionary, generate the list of routes.
3254 * If the passed in route_list is NULL or too small, this routine
3255 * allocates malloc'd memory to hold the routes.
3257 static IPv6RouteListRef
3258 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes
,
3259 CFDictionaryRef dict
,
3260 CFNumberRef rank_assertion
)
3262 boolean_t add_default
= FALSE
;
3263 boolean_t add_prefix
= FALSE
;
3264 struct in6_addr addr
;
3265 CFArrayRef additional_routes
= NULL
;
3266 CFIndex additional_routes_count
;
3267 boolean_t allow_additional_routes
= FALSE
;
3268 boolean_t exclude_from_nwi
= FALSE
;
3269 CFArrayRef excluded_routes
= NULL
;
3270 CFIndex excluded_routes_count
;
3271 RouteFlags flags
= 0;
3273 char ifname
[IFNAMSIZ
];
3274 CFStringRef ifname_cf
;
3276 int prefix_length
= 0;
3277 Rank primary_rank
= kRankAssertionDefault
;
3279 Rank rank
= kRankAssertionDefault
;
3280 struct in6_addr router
= in6addr_any
;
3281 boolean_t scoped_only
= FALSE
;
3286 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
3287 ifname
, sizeof(ifname
));
3288 if (ifname_cf
== NULL
) {
3291 ifindex
= my_if_nametoindex(ifname
);
3293 /* interface doesn't exist */
3296 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
3297 if (!dict_get_ipv6(dict
, kSCPropNetIPv6Router
, &router
)) {
3298 (void)dict_get_first_ipv6(dict
, kSCPropNetIPv6DestAddresses
, &router
);
3300 if (dict_get_first_ipv6(dict
, kSCPropNetIPv6Addresses
, &addr
)) {
3301 if (IN6_IS_ADDR_UNSPECIFIED(&addr
)) {
3304 if (dict_get_first_int(dict
, kSCPropNetIPv6PrefixLength
,
3306 && !IN6_IS_ADDR_LINKLOCAL(&addr
)
3307 && ipv6_prefix_length_is_valid(prefix_length
)) {
3319 if (rank_assertion
!= NULL
) {
3320 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
3323 if (!IN6_IS_ADDR_UNSPECIFIED(&router
)) {
3324 if (ifindex
!= lo0_ifindex()) {
3329 * If the router address is our address and the prefix length is
3330 * not 128, assume all routes are local to the interface.
3332 if (IN6_ARE_ADDR_EQUAL(&router
, &addr
)
3333 && prefix_length
!= IPV6_ROUTE_ALL_BITS_SET
) {
3334 ; /* all routes local */
3337 flags
|= kRouteFlagsHasGateway
;
3339 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
3340 primary_rank
= kRankAssertionFirst
;
3343 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
3344 exclude_from_nwi
= TRUE
;
3345 flags
|= kRouteFlagsIsNULL
;
3348 switch (primary_rank
) {
3349 case kRankAssertionScoped
:
3350 /* Scoped means all routes for the service get scoped */
3351 primary_rank
= rank
= kRankAssertionNever
;
3352 flags
|= kRouteFlagsIsScoped
;
3355 case kRankAssertionNever
:
3356 /* Never means just the default route gets scoped */
3357 rank
= kRankAssertionLast
;
3358 flags
|= kRouteFlagsIsScoped
;
3361 rank
= primary_rank
;
3365 if (allow_additional_routes
) {
3367 = CFDictionaryGetValue(dict
, kSCPropNetIPv6AdditionalRoutes
);
3368 additional_routes
= isA_CFArray(additional_routes
);
3369 if (additional_routes
!= NULL
) {
3370 additional_routes_count
= CFArrayGetCount(additional_routes
);
3371 n
+= additional_routes_count
;
3373 excluded_routes
= CFDictionaryGetValue(dict
,
3374 kSCPropNetIPv6ExcludedRoutes
);
3375 excluded_routes
= isA_CFArray(excluded_routes
);
3376 if (excluded_routes
!= NULL
) {
3377 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
3378 n
+= excluded_routes_count
;
3385 /* need IPv6LL subnet route */
3388 if (routes
== NULL
|| routes
->size
< n
) {
3389 routes
= (IPv6RouteListRef
)malloc(IPv6RouteListComputeSize(n
));
3390 bzero(routes
, IPv6RouteListComputeSize(n
));
3394 bzero(routes
->list
, sizeof(routes
->list
[0]) * n
);
3397 if (exclude_from_nwi
) {
3398 routes
->flags
|= kRouteListFlagsExcludeNWI
;
3400 else if (scoped_only
) {
3401 routes
->flags
|= kRouteListFlagsScopedOnly
;
3404 /* start at the beginning */
3407 /* add the default route */
3408 routes
->flags
|= kRouteListFlagsHasDefault
;
3409 r
->ifindex
= ifindex
;
3412 if ((flags
& kRouteFlagsHasGateway
) != 0) {
3413 r
->gateway
= router
;
3418 r
->rank
= primary_rank
;
3419 r
->flags
|= kRouteFlagsKernelManaged
;
3424 /* add IPv6LL route */
3425 r
->ifindex
= ifindex
;
3426 r
->dest
.s6_addr
[0] = 0xfe;
3427 r
->dest
.s6_addr
[1] = 0x80;
3428 r
->prefix_length
= 64;
3430 r
->flags
|= kRouteFlagsKernelManaged
;
3434 /* add the prefix route(s) */
3436 r
->flags
|= kRouteFlagsKernelManaged
;
3437 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3438 r
->flags
|= kRouteFlagsIsNULL
;
3440 r
->ifindex
= ifindex
;
3443 in6_netaddr(&r
->dest
, prefix_length
);
3444 r
->prefix_length
= prefix_length
;
3450 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3451 AddIPv6RouteContext context
;
3453 bzero(&context
, sizeof(context
));
3454 context
.count_p
= &routes
->count
;
3455 context
.route_p
= &r
;
3456 context
.rank
= rank
;
3458 /* additional routes */
3459 if (additional_routes
!= NULL
) {
3460 context
.ifindex
= ifindex
;
3461 context
.addr
= &addr
;
3462 context
.descr
= "AdditionalRoutes";
3463 CFArrayApplyFunction(additional_routes
,
3464 CFRangeMake(0, additional_routes_count
),
3465 AddIPv6Route
, &context
);
3467 /* excluded routes */
3468 if (excluded_routes
!= NULL
) {
3469 context
.descr
= "ExcludedRoutes";
3470 /* exclude this interface */
3471 context
.ifindex
= 0;
3472 context
.exclude_ifindex
= ifindex
;
3473 context
.addr
= NULL
;
3474 CFArrayApplyFunction(excluded_routes
,
3475 CFRangeMake(0, excluded_routes_count
),
3476 AddIPv6Route
, &context
);
3483 IPv6RouteGateway(RouteRef r_route
)
3485 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3486 return (&route
->gateway
);
3490 IPv6RouteSetGateway(RouteRef r_route
, const void * address
)
3492 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3494 route
->gateway
= *((struct in6_addr
*)address
);
3499 IPv6RouteDestination(RouteRef r_route
)
3501 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3502 return (&route
->dest
);
3505 static __inline__
int
3506 in6_addr_cmp(const struct in6_addr
* a
, const struct in6_addr
* b
)
3508 return (memcmp(a
->s6_addr
, b
->s6_addr
, sizeof(struct in6_addr
)));
3512 IPv6RouteIsEqual(RouteRef r_route1
, RouteRef r_route2
)
3514 IPv6RouteRef route1
= (IPv6RouteRef
)r_route1
;
3515 IPv6RouteRef route2
= (IPv6RouteRef
)r_route2
;
3517 return (route1
->prefix_length
== route2
->prefix_length
3518 && route1
->ifindex
== route2
->ifindex
3519 && route1
->flags
== route2
->flags
3520 && in6_addr_cmp(&route1
->dest
, &route2
->dest
) == 0
3521 && in6_addr_cmp(&route1
->ifa
, &route2
->ifa
) == 0
3522 && in6_addr_cmp(&route1
->gateway
, &route2
->gateway
) == 0);
3526 IPv6RouteSameSubnet(RouteRef r_route
, const void * addr
)
3528 const struct in6_addr
* address
= (const struct in6_addr
*)addr
;
3529 struct in6_addr netaddr
;
3530 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3533 in6_netaddr(&netaddr
, route
->prefix_length
);
3534 return (in6_addr_cmp(&netaddr
, &route
->dest
) == 0);
3538 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3541 struct rt_msghdr hdr
;
3542 char addrs
[V6_ROUTE_MSG_ADDRS_SPACE
];
3546 * Function: IPv6RouteApply
3548 * Add or remove the specified route to/from the kernel routing table.
3551 IPv6RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
3555 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3558 struct sockaddr_in6
* in_p
;
3559 struct sockaddr_dl
* dl_p
;
3563 if ((route
->flags
& kRouteFlagsKernelManaged
) != 0) {
3564 /* the kernel manages this route, don't touch it */
3565 return (EROUTENOTAPPLIED
);
3567 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
3568 return (EROUTENOTAPPLIED
);
3570 if (route
->ifindex
== 0) {
3571 IPv6RouteLog(LOG_NOTICE
, (RouteRef
)route
,
3572 "no interface specified");
3576 #ifdef TEST_IPV6_ROUTELIST
3578 #else /* TEST_IPV6_ROUTELIST */
3580 #endif /* TEST_IPV6_ROUTELIST */
3582 memset(&rtmsg
, 0, sizeof(rtmsg
));
3583 rtmsg
.hdr
.rtm_type
= cmd
;
3584 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3585 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3586 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
3587 if (!IN6_IS_ADDR_UNSPECIFIED(&route
->ifa
)) {
3588 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
3590 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3591 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
3592 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
3595 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
3596 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
3597 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
3600 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
3601 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
3603 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
3604 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
3605 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
3608 rtaddr
.ptr
= rtmsg
.addrs
;
3611 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3612 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3613 rtaddr
.in_p
->sin6_addr
= route
->dest
;
3614 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3615 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3618 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
3619 /* gateway is an IP address */
3620 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3621 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3622 rtaddr
.in_p
->sin6_addr
= route
->gateway
;
3623 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3624 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3627 /* gateway is the interface itself */
3628 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3629 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3630 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3631 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3635 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
3636 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3637 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3638 in6_len2mask(&rtaddr
.in_p
->sin6_addr
, route
->prefix_length
);
3639 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3643 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
3644 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3645 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3646 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3647 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3649 /* interface address */
3650 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
3651 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3652 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3653 rtaddr
.in_p
->sin6_addr
= route
->ifa
;
3654 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3657 /* apply the route */
3658 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
3659 rtmsg
.hdr
.rtm_msglen
= len
;
3660 if (write(sockfd
, &rtmsg
, len
) == -1) {
3666 static const RouteListInfo IPv6RouteListInfo
= {
3667 IPv6RouteListComputeSize
,
3672 IPv6RouteSetGateway
,
3673 IPv6RouteDestination
,
3674 IPv6RouteSameSubnet
,
3676 IPv6RouteCopyDescription
,
3679 sizeof(struct in6_addr
),
3680 IPV6_ROUTE_ALL_BITS_SET
3683 #ifdef TEST_IPV6_ROUTELIST
3684 static IPv6RouteListRef
3685 IPv6RouteListAddRouteList(IPv6RouteListRef routes
, int init_size
,
3686 IPv6RouteListRef service_routes
, Rank rank
)
3688 return ((IPv6RouteListRef
)
3689 RouteListAddRouteList(&IPv6RouteListInfo
,
3690 (RouteListRef
)routes
, init_size
,
3691 (RouteListRef
)service_routes
, rank
));
3693 #endif /* TEST_IPV6_ROUTELIST */
3695 #if !TARGET_OS_SIMULATOR
3696 static __inline__
void
3697 IPv6RouteListLog(int level
, IPv6RouteListRef routes
)
3699 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3701 my_log(level
, "%@", str
);
3707 IPv6RouteListFinalize(IPv6RouteListRef routes
)
3709 RouteListFinalize(&IPv6RouteListInfo
, (RouteListRef
)routes
);
3714 IPv6RouteListApply(IPv6RouteListRef old_routes
, IPv6RouteListRef new_routes
,
3717 RouteListApply(&IPv6RouteListInfo
,
3718 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
3722 #endif /* !TARGET_OS_SIMULATOR */
3725 * Function: parse_component
3727 * Given a string 'key' and a string prefix 'prefix',
3728 * return the next component in the slash '/' separated
3732 * 1. key = "a/b/c" prefix = "a/"
3734 * 2. key = "a/b/c" prefix = "a/b/"
3737 static CF_RETURNS_RETAINED CFStringRef
3738 parse_component(CFStringRef key
, CFStringRef prefix
)
3740 CFMutableStringRef comp
;
3743 if (!CFStringHasPrefix(key
, prefix
)) {
3746 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
3750 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
3751 range
= CFStringFind(comp
, CFSTR("/"), 0);
3752 if (range
.location
== kCFNotFound
) {
3755 range
.length
= CFStringGetLength(comp
) - range
.location
;
3756 CFStringDelete(comp
, range
);
3762 entity_routes_protocol(CFDictionaryRef entity_dict
)
3764 RouteListRef routes
;
3766 routes
= ipdict_get_routelist(entity_dict
);
3767 if (routes
== NULL
) {
3772 if ((routes
->flags
& kRouteListFlagsHasDefault
) == 0) {
3773 // if service has no default route
3777 if ((routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
3778 // if service should be excluded from NWI
3786 __private_extern__ boolean_t
3787 service_contains_protocol(CFDictionaryRef service_dict
, int af
)
3789 boolean_t contains_protocol
;
3791 CFDictionaryRef entity_dict
;
3793 entity
= (af
== AF_INET
) ? kSCEntNetIPv4
: kSCEntNetIPv6
;
3794 entity_dict
= CFDictionaryGetValue(service_dict
, entity
);
3795 if (entity_dict
== NULL
) {
3799 contains_protocol
= entity_routes_protocol(entity_dict
);
3800 return contains_protocol
;
3804 static CFMutableDictionaryRef
3805 service_dict_copy(CFStringRef serviceID
)
3807 CFDictionaryRef d
= NULL
;
3808 CFMutableDictionaryRef service_dict
;
3810 /* create a modifyable dictionary, a copy or a new one */
3811 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3814 = CFDictionaryCreateMutable(NULL
, 0,
3815 &kCFTypeDictionaryKeyCallBacks
,
3816 &kCFTypeDictionaryValueCallBacks
);
3819 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
3821 return (service_dict
);
3825 __private_extern__ boolean_t
3826 service_is_scoped_only(CFDictionaryRef service_dict
)
3828 nwi_ifstate_t alias
;
3829 CFDictionaryRef dict
;
3830 char ifname
[IFNAMSIZ
];
3831 nwi_ifstate_t ifstate
;
3832 CFStringRef interface
= NULL
;
3834 // get IPv4 (or IPv6) info
3835 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3837 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
3840 // if no connectivity
3845 interface
= ipdict_get_ifname(dict
);
3846 if ((interface
== NULL
) ||
3847 !CFStringGetCString(interface
, ifname
, sizeof(ifname
), kCFStringEncodingUTF8
)) {
3848 // if no interface / interface name
3853 if (S_nwi_state
== NULL
) {
3854 S_nwi_state
= nwi_state_copy();
3858 // get [nwi] interface state
3859 ifstate
= nwi_state_get_ifstate(S_nwi_state
, ifname
);
3860 if (ifstate
== NULL
) {
3863 } else if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3864 // if scoped (i.e. not in list)
3868 // check both both IPv4 and IPv6
3869 alias
= nwi_ifstate_get_alias(ifstate
, ifstate
->af
== AF_INET
? AF_INET6
: AF_INET
);
3870 if (alias
== NULL
) {
3871 // if only one address family
3873 } else if ((alias
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3874 // if scoped (i.e. not in list)
3882 log_service_entity(int level
, CFStringRef serviceID
, CFStringRef entity
,
3883 CFStringRef operation
, CFTypeRef val
)
3885 CFMutableStringRef this_val
= NULL
;
3891 if ((is_ipv4
= CFEqual(entity
, kSCEntNetIPv4
))
3892 || (is_ipv6
= CFEqual(entity
, kSCEntNetIPv6
))) {
3893 RouteListUnion routes
;
3895 routes
.ptr
= ipdict_get_routelist(val
);
3896 if (routes
.ptr
!= NULL
) {
3897 CFDictionaryRef service_dict
= NULL
;
3900 this_val
= IPv4RouteListCopyDescription(routes
.v4
);
3903 this_val
= IPv6RouteListCopyDescription(routes
.v6
);
3905 service_dict
= ipdict_get_service(val
);
3906 if (service_dict
!= NULL
) {
3907 CFStringAppendFormat(this_val
, NULL
,
3908 CFSTR("\n<Service> = %@"),
3916 val
= CFSTR("<none>");
3918 my_log(level
, "serviceID %@ %@ %@ value = %@",
3919 serviceID
, operation
, entity
, val
);
3920 my_CFRelease(&this_val
);
3925 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
3928 boolean_t changed
= FALSE
;
3930 CFMutableDictionaryRef service_dict
;
3932 service_dict
= service_dict_copy(serviceID
);
3933 old_val
= CFDictionaryGetValue(service_dict
, entity
);
3934 if (new_val
== NULL
) {
3935 if (old_val
!= NULL
) {
3936 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3937 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3938 CFSTR("Removed:"), old_val
);
3940 CFDictionaryRemoveValue(service_dict
, entity
);
3945 if (old_val
== NULL
|| !CFEqual(new_val
, old_val
)) {
3946 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3947 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3948 CFSTR("Changed: old"), old_val
);
3949 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3950 CFSTR("Changed: new"), new_val
);
3952 CFDictionarySetValue(service_dict
, entity
, new_val
);
3956 if (CFDictionaryGetCount(service_dict
) == 0) {
3957 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
3960 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
3962 my_CFRelease(&service_dict
);
3966 static CFDictionaryRef
3967 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
3969 CFDictionaryRef service_dict
;
3971 if (S_service_state_dict
== NULL
) {
3974 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3975 if (service_dict
== NULL
) {
3978 return (CFDictionaryGetValue(service_dict
, entity
));
3981 #if !TARGET_OS_SIMULATOR
3983 service_copy_interface(CFStringRef serviceID
, CFDictionaryRef new_service
)
3985 CFDictionaryRef dict
;
3986 CFStringRef interface
= NULL
;
3988 if (new_service
!= NULL
) {
3989 interface
= ipdict_get_ifname(new_service
);
3991 if (interface
== NULL
) {
3992 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
3994 interface
= ipdict_get_ifname(dict
);
3997 if (interface
== NULL
) {
3998 dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4000 interface
= ipdict_get_ifname(dict
);
4003 if (interface
!= NULL
) {
4004 CFRetain(interface
);
4008 #endif /* !TARGET_OS_SIMULATOR */
4011 service_has_clat46_address(CFStringRef serviceID
)
4013 CFDictionaryRef ip_dict
;
4015 ip_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4016 if (ip_dict
!= NULL
) {
4017 CFBooleanRef clat46
= NULL
;
4018 CFDictionaryRef ipv4
;
4020 ipv4
= ipdict_get_service(ip_dict
);
4021 if (isA_CFDictionary(ipv4
) &&
4022 CFDictionaryGetValueIfPresent(ipv4
,
4023 kSCPropNetIPv4CLAT46
,
4024 (const void **)&clat46
) &&
4025 isA_CFBoolean(clat46
)) {
4026 return CFBooleanGetValue(clat46
);
4033 #ifndef kSCPropNetHostname
4034 #define kSCPropNetHostname CFSTR("Hostname")
4039 copy_dhcp_hostname(CFStringRef serviceID
)
4041 CFDictionaryRef dict
= NULL
;
4042 CFStringRef hostname
= NULL
;
4043 CFDictionaryRef service_dict
= NULL
;
4045 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4049 service_dict
= ipdict_get_service(dict
);
4050 if (service_dict
== NULL
) {
4053 hostname
= CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
4054 if (hostname
!= NULL
) {
4060 #if !TARGET_OS_SIMULATOR
4062 static struct in6_addr
*
4063 ipv6_service_get_router(CFDictionaryRef service
,
4064 IFIndex
* ifindex_p
, CFStringRef
* ifname_p
)
4066 IPv6RouteListRef routes
;
4067 struct in6_addr
* router
= NULL
;
4069 routes
= ipdict_get_routelist(service
);
4071 && (routes
->flags
& kRouteListFlagsExcludeNWI
) == 0
4072 && (routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
4073 router
= &routes
->list
[0].gateway
;
4074 if (*ifindex_p
== 0) {
4075 *ifindex_p
= routes
->list
[0].ifindex
;
4077 if (*ifname_p
== NULL
) {
4078 *ifname_p
= ipdict_get_ifname(service
);
4085 ipv6_service_update_router(CFStringRef serviceID
, CFDictionaryRef new_service
)
4087 IFIndex ifindex
= 0;
4088 CFStringRef ifname
= NULL
;
4089 char ntopbuf
[INET6_ADDRSTRLEN
];
4090 CFDictionaryRef old_service
;
4091 struct in6_addr
* old_router
;
4092 struct in6_addr
* new_router
;
4095 old_service
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4096 old_router
= ipv6_service_get_router(old_service
, &ifindex
, &ifname
);
4097 new_router
= ipv6_service_get_router(new_service
, &ifindex
, &ifname
);
4098 if (ifname
== NULL
|| ifindex
== 0) {
4101 s
= inet6_dgram_socket();
4105 /* remove the old router if it was defined */
4106 if (old_router
!= NULL
4107 && (new_router
== NULL
4108 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4109 if (siocdrdel_in6(s
, ifindex
, old_router
) < 0) {
4110 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4111 "siocdrdel_in6(%@, %s) failed: %s",
4113 inet_ntop(AF_INET6
, old_router
,
4114 ntopbuf
, sizeof(ntopbuf
)),
4119 "%@ removed default route %s",
4121 inet_ntop(AF_INET6
, old_router
, ntopbuf
, sizeof(ntopbuf
)));
4124 /* add the new router if it is defined */
4125 if (new_router
!= NULL
4126 && (old_router
== NULL
4127 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4128 if (siocdradd_in6(s
, ifindex
, new_router
, 0) < 0) {
4129 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4130 "siocdradd_in6(%@, %s) failed: %s",
4132 inet_ntop(AF_INET6
, new_router
,
4133 ntopbuf
, sizeof(ntopbuf
)),
4138 "%@ added default route %s",
4140 inet_ntop(AF_INET6
, new_router
, ntopbuf
, sizeof(ntopbuf
)));
4148 #endif /* !TARGET_OS_SIMULATOR */
4150 #define ALLOW_EMPTY_STRING 0x1
4152 static CF_RETURNS_RETAINED CFTypeRef
4153 sanitize_prop(CFTypeRef val
, uint32_t flags
)
4156 if (isA_CFString(val
)) {
4157 CFMutableStringRef str
;
4159 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
4160 CFStringTrimWhitespace(str
);
4161 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
4175 merge_array_prop(CFMutableDictionaryRef dict
,
4177 CFDictionaryRef state_dict
,
4178 CFDictionaryRef setup_dict
,
4182 CFMutableArrayRef merge_prop
;
4183 CFArrayRef setup_prop
= NULL
;
4184 CFArrayRef state_prop
= NULL
;
4186 if (setup_dict
!= NULL
) {
4187 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
4189 if (state_dict
!= NULL
) {
4190 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
4193 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
4197 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4198 if (setup_prop
!= NULL
) {
4202 n
= CFArrayGetCount(setup_prop
);
4203 for (i
= 0; i
< n
; i
++) {
4206 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
4207 val
= sanitize_prop(val
, flags
);
4209 CFArrayAppendValue(merge_prop
, val
);
4214 if (state_prop
!= NULL
4215 && (setup_prop
== NULL
|| S_append_state
)) {
4218 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
4220 n
= CFArrayGetCount(state_prop
);
4221 for (i
= 0; i
< n
; i
++) {
4224 val
= CFArrayGetValueAtIndex(state_prop
, i
);
4225 val
= sanitize_prop(val
, flags
);
4227 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
4228 CFArrayAppendValue(merge_prop
, val
);
4234 if (CFArrayGetCount(merge_prop
) > 0) {
4235 CFDictionarySetValue(dict
, key
, merge_prop
);
4237 CFRelease(merge_prop
);
4242 pick_prop(CFMutableDictionaryRef dict
,
4244 CFDictionaryRef state_dict
,
4245 CFDictionaryRef setup_dict
,
4248 CFTypeRef val
= NULL
;
4250 if (setup_dict
!= NULL
) {
4251 val
= CFDictionaryGetValue(setup_dict
, key
);
4252 val
= sanitize_prop(val
, flags
);
4254 if (val
== NULL
&& state_dict
!= NULL
) {
4255 val
= CFDictionaryGetValue(state_dict
, key
);
4256 val
= sanitize_prop(val
, flags
);
4259 CFDictionarySetValue(dict
, key
, val
);
4267 ** GetEntityChangesFunc functions
4269 #define IPV4_ROUTES_N_STATIC 5
4270 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4271 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4275 #define IPV4_ROUTES_BUF_DECL(routes) \
4276 IPv4RouteListRef routes; \
4277 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4279 routes = (IPv4RouteListRef)(void *)routes_buf; \
4280 routes->size = IPV4_ROUTES_N_STATIC; \
4281 routes->count = 0; \
4285 IPv4RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4288 CFDataRef routes_data
;
4289 IPV4_ROUTES_BUF_DECL(routes
);
4291 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4293 routes_data
= CFDataCreate(NULL
,
4295 IPv4RouteListComputeSize(r
->count
));
4303 return (routes_data
);
4305 #define IPV6_ROUTES_N_STATIC 3
4306 #define IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4307 (roundup(IPv6RouteListComputeSize(IPV6_ROUTES_N_STATIC), \
4311 #define IPV6_ROUTES_BUF_DECL(routes) \
4312 IPv6RouteListRef routes; \
4313 uint32_t routes_buf[IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4315 routes = (IPv6RouteListRef)(void *)routes_buf; \
4316 routes->size = IPV6_ROUTES_N_STATIC; \
4317 routes->count = 0; \
4321 IPv6RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4324 CFDataRef routes_data
;
4325 IPV6_ROUTES_BUF_DECL(routes
);
4327 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4329 routes_data
= CFDataCreate(NULL
,
4331 IPv6RouteListComputeSize(r
->count
));
4339 return (routes_data
);
4342 static CFDictionaryRef
4343 IPDictCreate(int af
, CFDictionaryRef state_dict
, CFDictionaryRef setup_dict
,
4344 CFNumberRef rank_assertion
)
4346 CFDictionaryRef aggregated_dict
= NULL
;
4347 CFDictionaryRef dict
;
4348 CFMutableDictionaryRef modified_dict
= NULL
;
4349 CFDataRef routes_data
;
4352 if (dict
!= NULL
&& setup_dict
!= NULL
) {
4353 /* look for keys in Setup: that override/merge with State: */
4354 CFArrayRef additional_routes
;
4357 CFStringRef router_prop
;
4358 CFStringRef route_list_prop
;
4363 router_prop
= kSCPropNetIPv4Router
;
4364 route_list_prop
= kSCPropNetIPv4AdditionalRoutes
;
4368 router_prop
= kSCPropNetIPv6Router
;
4369 route_list_prop
= kSCPropNetIPv6AdditionalRoutes
;
4372 router
= CFDictionaryGetValue(setup_dict
, router_prop
);
4374 && !cfstring_to_ipvx(af
, router
, &router_ip
, sizeof(router_ip
))) {
4378 /* AdditionalRoutes */
4380 = CFDictionaryGetValue(setup_dict
, route_list_prop
);
4381 additional_routes
= isA_CFArray(additional_routes
);
4383 if (router
!= NULL
|| additional_routes
!= NULL
) {
4384 modified_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
4385 if (router
!= NULL
) {
4386 CFDictionarySetValue(modified_dict
,
4390 if (additional_routes
!= NULL
) {
4391 CFArrayRef combined_routes
= NULL
;
4392 CFArrayRef state_routes
;
4395 = CFDictionaryGetValue(state_dict
,
4397 if (isA_CFArray(state_routes
) != NULL
) {
4399 = my_CFArrayCreateCombinedArray(additional_routes
,
4401 additional_routes
= combined_routes
;
4403 CFDictionarySetValue(modified_dict
,
4406 if (combined_routes
!= NULL
) {
4407 CFRelease(combined_routes
);
4410 dict
= modified_dict
;
4415 routes_data
= IPv4RouteListDataCreate(dict
, rank_assertion
);
4419 routes_data
= IPv6RouteListDataCreate(dict
, rank_assertion
);
4422 if (routes_data
!= NULL
) {
4423 aggregated_dict
= ipdict_create(dict
, routes_data
);
4424 CFRelease(routes_data
);
4426 if (modified_dict
!= NULL
) {
4427 CFRelease(modified_dict
);
4429 return (aggregated_dict
);
4433 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4434 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4436 #pragma unused(info)
4437 CFDictionaryRef dict
= NULL
;
4438 boolean_t changed
= FALSE
;
4439 CFNumberRef rank_assertion
= NULL
;
4440 CFDictionaryRef service_options
;
4442 if (state_dict
== NULL
) {
4445 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4446 if (service_options
!= NULL
) {
4448 = CFDictionaryGetValue(service_options
,
4449 kServiceOptionRankAssertion
);
4451 dict
= IPDictCreate(AF_INET
, state_dict
, setup_dict
, rank_assertion
);
4454 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, dict
);
4456 /* clean up the rank too */
4457 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
4459 my_CFRelease(&dict
);
4465 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4466 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4468 #pragma unused(info)
4469 CFDictionaryRef dict
= NULL
;
4470 boolean_t changed
= FALSE
;
4471 #if !TARGET_OS_SIMULATOR
4472 CFStringRef interface
;
4473 #endif /* !TARGET_OS_SIMULATOR */
4474 CFNumberRef rank_assertion
= NULL
;
4475 CFDictionaryRef service_options
;
4477 if (state_dict
== NULL
) {
4482 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4483 if (service_options
!= NULL
) {
4485 = CFDictionaryGetValue(service_options
,
4486 kServiceOptionRankAssertion
);
4489 dict
= IPDictCreate(AF_INET6
, state_dict
, setup_dict
, rank_assertion
);
4493 #if !TARGET_OS_SIMULATOR
4494 interface
= service_copy_interface(serviceID
, dict
);
4495 #endif /* !TARGET_OS_SIMULATOR */
4497 #if !TARGET_OS_SIMULATOR
4498 ipv6_service_update_router(serviceID
, dict
);
4499 #endif /* !TARGET_OS_SIMULATOR */
4501 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, dict
);
4503 #if !TARGET_OS_SIMULATOR
4504 if (interface
!= NULL
) {
4506 CFBooleanRef needs_plat
= NULL
;
4508 if ((state_dict
!= NULL
) &&
4509 CFDictionaryGetValueIfPresent(state_dict
,
4510 kSCPropNetIPv6PerformPLATDiscovery
,
4511 (const void **)&needs_plat
) &&
4512 isA_CFBoolean(needs_plat
) &&
4513 CFBooleanGetValue(needs_plat
)) {
4514 // perform PLAT discovery
4515 my_CFSetAddValue_async(__network_change_queue(), &S_nat64_prefix_requests
, interface
);
4517 // IPv6 configuration changed for this interface, poke NAT64
4518 my_CFSetAddValue_async(__network_change_queue(), &S_nat64_prefix_changes
, interface
);
4521 CFRelease(interface
);
4523 #endif /* !TARGET_OS_SIMULATOR */
4526 /* service removed, clean up the rank too */
4527 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
4529 my_CFRelease(&dict
);
4535 __private_extern__ CFDictionaryRef
4536 ipv4_dict_create(CFDictionaryRef state_dict
)
4538 return (IPDictCreate(AF_INET
, state_dict
, NULL
, NULL
));
4541 __private_extern__ CFDictionaryRef
4542 ipv6_dict_create(CFDictionaryRef state_dict
)
4544 return (IPDictCreate(AF_INET6
, state_dict
, NULL
, NULL
));
4547 #endif /* TEST_DNS */
4550 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
4551 CFMutableArrayRef out_servers
, CFStringRef interface
)
4556 count
= CFArrayGetCount(in_servers
);
4557 for (i
= 0; i
< count
; i
++) {
4559 struct in6_addr ipv6_addr
;
4560 struct in_addr ip_addr
;
4562 addr
= CFArrayGetValueAtIndex(in_servers
, i
);
4563 assert(addr
!= NULL
);
4565 if (cfstring_to_ip(addr
, &ip_addr
)) {
4567 if ((active_protos
& kProtocolFlagsIPv4
) == 0
4568 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
4570 "no IPv4 connectivity, "
4571 "ignoring DNS server address " IP_FORMAT
,
4578 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
4580 if ((active_protos
& kProtocolFlagsIPv6
) == 0
4581 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
4582 char ntopbuf
[INET6_ADDRSTRLEN
];
4585 "no IPv6 connectivity, "
4586 "ignoring DNS server address %s",
4587 inet_ntop(AF_INET6
, &ipv6_addr
,
4588 ntopbuf
, sizeof(ntopbuf
)));
4592 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
4593 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
4594 && (interface
!= NULL
)
4595 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
4596 // append interface name to IPv6 link local address
4597 addr
= CFStringCreateWithFormat(NULL
, NULL
,
4606 /* bad IP address */
4607 my_log(LOG_NOTICE
, "ignoring bad DNS server address '%@'", addr
);
4611 /* DNS server is valid and one we want */
4612 CFArrayAppendValue(out_servers
, addr
);
4618 static CF_RETURNS_RETAINED CFArrayRef
4619 order_dns_servers(CFArrayRef servers
, ProtocolFlags active_protos
)
4621 Boolean favor_v4
= FALSE
;
4622 CFMutableArrayRef ordered_servers
;
4623 ProtocolFlags proto_last
= kProtocolFlagsIPv4
;
4624 struct sockaddr_in v4_dns1
= { .sin_family
= AF_INET
,
4625 .sin_len
= sizeof(struct sockaddr_in
) };
4627 struct sockaddr_in6 v6_dns1
= { .sin6_family
= AF_INET6
,
4628 .sin6_len
= sizeof(struct sockaddr_in6
),
4629 .sin6_scope_id
= 0 };
4632 if (((active_protos
& kProtocolFlagsIPv4
) == 0) ||
4633 ((active_protos
& kProtocolFlagsIPv6
) == 0)) {
4634 /* only one protocol */
4635 #ifdef TEST_DNS_ORDER
4636 printf("only one protocol\n");
4637 #endif // TEST_DNS_ORDER
4638 return CFRetain(servers
);
4641 ordered_servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4642 for (CFIndex i
= 0, n
= CFArrayGetCount(servers
); i
< n
; i
++) {
4644 struct in6_addr ia6
;
4645 ProtocolFlags proto
;
4648 server
= CFArrayGetValueAtIndex(servers
, i
);
4649 if (cfstring_to_ip(server
, &ia
)) {
4650 proto
= kProtocolFlagsIPv4
;
4652 v4_dns1
.sin_addr
= ia
;
4654 } else if (cfstring_to_ip6(server
, &ia6
)) {
4655 proto
= kProtocolFlagsIPv6
;
4657 bcopy(&ia6
, &v6_dns1
.sin6_addr
, sizeof(ia6
));
4660 CFRelease(ordered_servers
);
4661 return CFRetain(servers
);
4664 if ((i
> 0) && (proto
!= proto_last
)) {
4665 /* if the protocol of the server addresses changed */
4666 if (((proto
== kProtocolFlagsIPv4
) && (v4_n
== 1)) ||
4667 ((proto
== kProtocolFlagsIPv6
) && (v6_n
== 1))) {
4668 /* if we now have the 1st server address of another protocol */
4669 #if __has_include(<si_compare.h>)
4670 favor_v4
= (si_destination_compare_no_dependencies((struct sockaddr
*)&v4_dns1
,
4671 (struct sockaddr
*)&v6_dns1
) >= 0);
4672 #else // __has_include(<si_compare.h>)
4673 favor_v4
= (sa_dst_compare_no_dependencies((struct sockaddr
*)&v4_dns1
,
4674 (struct sockaddr
*)&v6_dns1
) >= 0);
4675 #endif // __has_include(<si_compare.h>)
4676 #ifdef TEST_DNS_ORDER
4677 char v4_buf
[INET_ADDRSTRLEN
];
4678 char v6_buf
[INET6_ADDRSTRLEN
];
4679 printf("comparing %s vs %s, favoring %s\n",
4680 inet_ntop(v4_dns1
.sin_family
, &v4_dns1
.sin_addr
, v4_buf
, sizeof(v4_buf
)),
4681 inet_ntop(v6_dns1
.sin6_family
, &v6_dns1
.sin6_addr
, v6_buf
, sizeof(v6_buf
)),
4682 favor_v4
? "v4" : "v6");
4683 #endif // TEST_DNS_ORDER
4685 /* if the server addresses array is randomly mixed */
4686 #ifdef TEST_DNS_ORDER
4687 printf("v4/v6 not ordered\n");
4688 #endif // TEST_DNS_ORDER
4689 CFRelease(ordered_servers
);
4690 return CFRetain(servers
);
4695 if ((proto
== kProtocolFlagsIPv4
) && favor_v4
) {
4696 CFArrayInsertValueAtIndex(ordered_servers
, v4_n
- 1, server
);
4697 } else if ((proto
== kProtocolFlagsIPv6
) && !favor_v4
) {
4698 CFArrayInsertValueAtIndex(ordered_servers
, v6_n
- 1, server
);
4700 CFArrayAppendValue(ordered_servers
, server
);
4704 return ordered_servers
;
4708 merge_dns_servers(CFMutableDictionaryRef new_dict
,
4709 CFArrayRef state_servers
,
4710 CFArrayRef setup_servers
,
4712 Boolean trust_state
,
4713 ProtocolFlags active_protos
,
4714 CFStringRef interface
)
4716 CFMutableArrayRef dns_servers
;
4717 Boolean have_dns_setup
= FALSE
;
4719 if (state_servers
== NULL
&& setup_servers
== NULL
) {
4720 /* no DNS servers */
4723 dns_servers
= CFArrayCreateMutable(NULL
, 0,
4724 &kCFTypeArrayCallBacks
);
4725 if (setup_servers
!= NULL
) {
4726 accumulate_dns_servers(setup_servers
, active_protos
,
4727 dns_servers
, interface
);
4728 if (CFArrayGetCount(dns_servers
) > 0) {
4729 have_dns_setup
= TRUE
;
4732 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
4733 && state_servers
!= NULL
) {
4734 CFArrayRef ordered_servers
;
4736 ordered_servers
= order_dns_servers(state_servers
, active_protos
);
4737 accumulate_dns_servers(ordered_servers
, active_protos
,
4739 CFRelease(ordered_servers
);
4743 * Here, we determine whether or not we want all queries for this DNS
4744 * configuration to be bound to the associated network interface.
4746 * For dynamically derived network configurations (i.e. from State:)
4747 * this would be the preferred option using the argument "Hey, the
4748 * server told us to use these servers on this network so let's not
4751 * But, when a DNS configuration has been provided by the user/admin
4752 * via the Network pref pane (i.e. from Setup:) we opt to not force
4753 * binding of the outbound queries. The simplest example why we take
4754 * this stance is with a multi-homing configuration. Consider a system
4755 * with one network service associated with "en0" and a second service
4756 * associated with "en1". The "en0" service has been set higher in
4757 * the network service order so it would be primary but the user/admin
4758 * wants the DNS queries to go to a server only accessible via "en1".
4759 * Without this exception we would take the DNS server addresses from
4760 * the Network pref pane (for "en0") and have the queries bound to
4761 * "en0" where they'd never reach their intended destination (via
4762 * "en1"). So, our exception to the rule is that we will not bind
4763 * user/admin configurations to any specific network interface.
4765 * We also add an exception to the "follow the dynamically derived
4766 * network configuration" path for on-the-fly (no Setup: content)
4769 * But, we add an exception to the exception to support our own
4770 * VPN code. Here, we look for a "ServiceID" property in the DNS
4771 * entity. If present, and if it matches, then we extend our
4772 * trust even when there is no Setup: content.
4774 if (CFArrayGetCount(dns_servers
) != 0) {
4775 CFDictionarySetValue(new_dict
,
4776 kSCPropNetDNSServerAddresses
, dns_servers
);
4777 if ((have_setup
&& !have_dns_setup
) || (!have_setup
&& trust_state
)) {
4778 // if this is a "setup"+"state" service with only "state" DNS content (i.e. no
4779 // setup override) or this is a TRUSTED "state"-only service
4780 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
4784 my_CFRelease(&dns_servers
);
4790 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4791 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4793 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4794 boolean_t changed
= FALSE
;
4796 Boolean have_setup
= FALSE
;
4797 CFStringRef interface
= NULL
;
4798 CFDictionaryRef ipv4
;
4799 CFDictionaryRef ipv6
;
4805 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
4806 { kSCPropNetDNSSortList
, 0, FALSE
},
4807 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4808 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
4810 CFMutableDictionaryRef new_dict
= NULL
;
4811 const CFStringRef pick_list
[] = {
4812 kSCPropNetDNSDomainName
,
4813 kSCPropNetDNSOptions
,
4814 kSCPropNetDNSSearchOrder
,
4815 kSCPropNetDNSServerPort
,
4816 kSCPropNetDNSServerTimeout
,
4817 kSCPropNetDNSServiceIdentifier
,
4818 kSCPropNetDNSSupplementalMatchDomainsNoSearch
,
4820 Boolean trust_state
= FALSE
;
4822 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4823 /* there is no DNS content */
4827 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4828 if (entity_routes_protocol(ipv4
)) {
4829 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
4832 active_protos
|= kProtocolFlagsIPv4
;
4833 interface
= ipdict_get_ifname(ipv4
);
4836 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4837 if (entity_routes_protocol(ipv6
)) {
4839 (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
) != NULL
)) {
4842 active_protos
|= kProtocolFlagsIPv6
;
4843 if (interface
== NULL
) {
4844 interface
= ipdict_get_ifname(ipv6
);
4849 if (active_protos
== kProtocolFlagsNone
) {
4850 /* there is no IPv4 nor IPv6 */
4851 if (state_dict
== NULL
) {
4852 /* ... and no DNS content that we care about */
4858 if (state_dict
!= NULL
) {
4859 CFStringRef state_serviceID
= NULL
;
4861 if (CFDictionaryGetValueIfPresent(state_dict
,
4862 kSCPropNetDNSConfirmedServiceID
,
4863 (const void **)&state_serviceID
) &&
4864 isA_CFString(state_serviceID
) &&
4865 CFEqual(serviceID
, state_serviceID
)) {
4870 /* merge DNS configuration */
4871 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
4872 &kCFTypeDictionaryKeyCallBacks
,
4873 &kCFTypeDictionaryValueCallBacks
);
4875 if (active_protos
== kProtocolFlagsNone
) {
4876 merge_dns_servers(new_dict
,
4877 my_CFDictionaryGetArray(state_dict
,
4878 kSCPropNetDNSServerAddresses
),
4882 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
4886 merge_dns_servers(new_dict
,
4887 my_CFDictionaryGetArray(state_dict
,
4888 kSCPropNetDNSServerAddresses
),
4889 my_CFDictionaryGetArray(setup_dict
,
4890 kSCPropNetDNSServerAddresses
),
4897 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
4898 merge_array_prop(new_dict
,
4902 merge_list
[i
].flags
,
4903 merge_list
[i
].append
);
4906 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
4914 if (active_protos
== kProtocolFlagsNone
) {
4915 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
4916 if (CFDictionaryContainsKey(new_dict
,
4917 kSCPropNetDNSSupplementalMatchDomains
)) {
4918 /* only keep State: supplemental */
4919 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
4920 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
4921 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
4922 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
4924 if ((interface
== NULL
) && (setup_dict
== NULL
) && (state_dict
!= NULL
)) {
4926 * for supplemental-only configurations, add any scoped (or
4927 * wild-card "*") interface
4929 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4931 } else if (CFDictionaryContainsKey(new_dict
, kSCPropNetDNSServiceIdentifier
) &&
4932 (interface
== NULL
) &&
4933 (state_dict
!= NULL
)) {
4934 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4940 if (CFDictionaryGetCount(new_dict
) == 0) {
4941 my_CFRelease(&new_dict
);
4945 if (interface
!= NULL
) {
4946 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
4949 if (S_append_state
) {
4951 * ensure any specified domain name (e.g. the domain returned by
4952 * a DHCP server) is in the search list.
4954 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
4955 if (isA_CFString(domain
)) {
4958 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
4959 if (isA_CFArray(search
) &&
4960 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
4961 CFMutableArrayRef new_search
;
4963 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
4964 CFArrayAppendValue(new_search
, domain
);
4965 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
4966 my_CFRelease(&new_search
);
4973 #if !TARGET_OS_SIMULATOR
4974 if (interface
!= NULL
) {
4975 CFRetain(interface
);
4977 #endif /* !TARGET_OS_SIMULATOR */
4979 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
4981 #if !TARGET_OS_SIMULATOR
4982 if (interface
!= NULL
) {
4984 // DNS configuration changed for this interface, poke NAT64
4985 my_CFSetAddValue_async(__network_change_queue(), &S_nat64_prefix_changes
, interface
);
4987 CFRelease(interface
);
4989 #endif /* !TARGET_OS_SIMULATOR */
4991 my_CFRelease(&new_dict
);
4996 merge_dict(const void *key
, const void *value
, void *context
)
4998 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
5000 CFDictionarySetValue(dict
, key
, value
);
5004 #define PROXY_AUTO_DISCOVERY_URL 252
5006 static CF_RETURNS_RETAINED CFStringRef
5007 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
5009 CFStringRef urlString
= NULL
;
5011 if (dhcp_options
!= NULL
) {
5014 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
5017 const UInt8
*urlBytes
;
5020 urlBytes
= CFDataGetBytePtr(data
);
5021 urlLen
= CFDataGetLength(data
);
5022 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
5023 // remove trailing NUL
5031 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
5033 urlString
= CFURLGetString(url
);
5034 if (urlString
!= NULL
) {
5035 CFRetain(urlString
);
5045 static CF_RETURNS_RETAINED CFStringRef
5049 CFStringRef urlString
= NULL
;
5051 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
5053 urlString
= CFURLGetString(url
);
5054 if (urlString
!= NULL
) {
5055 CFRetain(urlString
);
5064 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5065 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5067 ProtocolFlags active_protos
= kProtocolFlagsNone
;
5068 boolean_t changed
= FALSE
;
5069 CFStringRef interface
= NULL
;
5070 CFDictionaryRef ipv4
;
5071 CFDictionaryRef ipv6
;
5072 CFMutableDictionaryRef new_dict
= NULL
;
5078 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
5079 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
5082 CFStringRef key1
; /* an "enable" key */
5086 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
5087 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
5088 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
5089 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
5090 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
5091 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
5092 { kSCPropNetProxiesProxyAutoConfigEnable
,
5093 kSCPropNetProxiesProxyAutoConfigURLString
,
5094 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
5095 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5100 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
5101 /* there is no proxy content */
5104 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
5105 if (entity_routes_protocol(ipv4
)) {
5106 active_protos
|= kProtocolFlagsIPv4
;
5107 interface
= ipdict_get_ifname(ipv4
);
5109 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
5110 if (entity_routes_protocol(ipv6
)) {
5111 active_protos
|= kProtocolFlagsIPv6
;
5112 if (interface
== NULL
) {
5113 interface
= ipdict_get_ifname(ipv6
);
5116 if (active_protos
== kProtocolFlagsNone
) {
5117 /* there is no IPv4 nor IPv6 */
5118 if (state_dict
== NULL
) {
5119 /* ... and no proxy content that we care about */
5125 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
5126 CFMutableDictionaryRef setup_copy
;
5129 * Merge the per-service "Setup:" and "State:" proxy information with
5130 * the "Setup:" information always taking precedence. Additionally,
5131 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
5132 * Port) is defined than all of the values for that group will be
5133 * used. That is, we don't allow mixing some of the values from
5134 * the "Setup:" keys and others from the "State:" keys.
5136 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5137 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
5138 merge_array_prop(new_dict
,
5142 merge_list
[i
].flags
,
5143 merge_list
[i
].append
);
5146 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5147 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5148 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
5150 * if a "Setup:" enabled key has been provided than we want to
5151 * ignore all of the "State:" keys
5153 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
5154 if (pick_list
[i
].key2
!= NULL
) {
5155 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
5157 if (pick_list
[i
].key3
!= NULL
) {
5158 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
5160 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
5161 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
5162 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
5164 * if a "Setup:" enabled key has not been provided and we have
5165 * some" "State:" keys than we remove all of of "Setup:" keys
5167 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
5168 if (pick_list
[i
].key2
!= NULL
) {
5169 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
5171 if (pick_list
[i
].key3
!= NULL
) {
5172 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
5177 /* merge the "Setup:" keys */
5178 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
5179 CFRelease(setup_copy
);
5181 else if (setup_dict
!= NULL
) {
5182 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5184 else if (state_dict
!= NULL
) {
5185 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5188 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
5189 CFRelease(new_dict
);
5193 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
5194 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5198 if (new_dict
!= NULL
) {
5199 CFDictionaryRef dhcp_options
;
5201 CFNumberRef wpad
= NULL
;
5202 int wpadEnabled
= 0;
5203 CFStringRef wpadURL
= NULL
;
5205 if (CFDictionaryGetValueIfPresent(new_dict
,
5206 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5207 (const void **)&num
) &&
5208 isA_CFNumber(num
)) {
5209 /* if we have a WPAD key */
5211 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
5212 /* if we don't like the enabled key/value */
5220 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
5221 if (!isA_CFNumber(num
) ||
5222 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
5223 /* if we don't like the enabled key/value */
5230 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
5231 if (pacURL
!= NULL
) {
5232 if (!isA_CFString(pacURL
) || (CFStringGetLength(pacURL
) == 0)) {
5233 /* if we don't like the PAC URL */
5239 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
5240 if (!isA_CFString(pacJS
) || (CFStringGetLength(pacJS
) == 0)) {
5241 /* if we don't have (or like) the PAC JavaScript */
5249 * we already have a PAC URL so disable WPAD.
5256 * if WPAD is enabled and we don't already have a PAC URL then
5257 * we check for a DHCP provided URL. If not available, we use
5258 * a PAC URL pointing to a well-known file (wpad.dat) on a
5259 * well-known host (wpad.<domain>).
5261 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
5262 wpadURL
= wpadURL_dhcp(dhcp_options
);
5263 if (wpadURL
== NULL
) {
5264 wpadURL
= wpadURL_dns();
5266 if (wpadURL
== NULL
) {
5267 wpadEnabled
= 0; /* if we don't have a WPAD URL */
5272 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
5273 CFDictionarySetValue(new_dict
,
5274 kSCPropNetProxiesProxyAutoConfigEnable
,
5277 CFDictionarySetValue(new_dict
,
5278 kSCPropNetProxiesProxyAutoConfigURLString
,
5285 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
5286 CFDictionarySetValue(new_dict
,
5287 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5294 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
5295 my_CFRelease(&new_dict
);
5299 #if !TARGET_OS_IPHONE
5301 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5302 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5304 #pragma unused(info)
5305 boolean_t changed
= FALSE
;
5306 CFMutableDictionaryRef new_dict
= NULL
;
5307 const CFStringRef pick_list
[] = {
5308 kSCPropNetSMBNetBIOSName
,
5309 kSCPropNetSMBNetBIOSNodeType
,
5310 #ifdef ADD_NETBIOS_SCOPE
5311 kSCPropNetSMBNetBIOSScope
,
5312 #endif // ADD_NETBIOS_SCOPE
5313 kSCPropNetSMBWorkgroup
,
5316 if (state_dict
== NULL
&& setup_dict
== NULL
) {
5317 /* there is no SMB */
5320 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
5321 && service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
) {
5322 /* there is no IPv4 or IPv6 */
5326 /* merge SMB configuration */
5327 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5328 &kCFTypeDictionaryKeyCallBacks
,
5329 &kCFTypeDictionaryValueCallBacks
);
5330 merge_array_prop(new_dict
,
5331 kSCPropNetSMBWINSAddresses
,
5336 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5344 if (CFDictionaryGetCount(new_dict
) == 0) {
5345 my_CFRelease(&new_dict
);
5350 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
5351 my_CFRelease(&new_dict
);
5354 #endif /* !TARGET_OS_IPHONE */
5357 services_info_get_interface(CFDictionaryRef services_info
,
5358 CFStringRef serviceID
)
5360 CFStringRef interface
= NULL
;
5361 CFDictionaryRef ipv4_dict
;
5363 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
5365 if (ipv4_dict
!= NULL
) {
5366 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
5369 CFDictionaryRef ipv6_dict
;
5371 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
5373 if (ipv6_dict
!= NULL
) {
5374 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5381 static const struct {
5382 const CFStringRef
* entityName
;
5383 const CFStringRef
* statusKey
;
5384 } transientServiceInfo
[] = {
5385 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
5386 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
5387 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
5391 get_transient_status_changes(CFStringRef serviceID
,
5392 CFDictionaryRef services_info
)
5394 boolean_t changed
= FALSE
;
5396 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5397 CFDictionaryRef dict
;
5398 CFNumberRef status
= NULL
;
5399 CFMutableDictionaryRef ts_dict
= NULL
;
5401 dict
= get_service_state_entity(services_info
, serviceID
,
5402 *transientServiceInfo
[i
].entityName
);
5405 status
= CFDictionaryGetValue(dict
,
5406 *transientServiceInfo
[i
].statusKey
);
5409 if (isA_CFNumber(status
) != NULL
) {
5410 ts_dict
= CFDictionaryCreateMutable(NULL
,
5412 &kCFTypeDictionaryKeyCallBacks
,
5413 &kCFTypeDictionaryValueCallBacks
);
5414 CFDictionaryAddValue(ts_dict
,
5415 *transientServiceInfo
[i
].statusKey
,
5419 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
,
5424 if (ts_dict
!= NULL
) {
5432 if_dict_is_expensive(CFDictionaryRef if_dict
)
5434 boolean_t is_expensive
= FALSE
;
5436 if (isA_CFDictionary(if_dict
) != NULL
) {
5437 CFBooleanRef expensive
;
5438 expensive
= CFDictionaryGetValue(if_dict
, kSCPropNetLinkExpensive
);
5439 if (isA_CFBoolean(expensive
) != NULL
5440 && CFBooleanGetValue(expensive
)) {
5441 is_expensive
= TRUE
;
5444 return is_expensive
;
5448 service_is_expensive(CFStringRef serviceID
, CFDictionaryRef services_info
)
5451 boolean_t is_expensive
= FALSE
;
5453 ifname
= services_info_get_interface(services_info
, serviceID
);
5454 if (ifname
!= NULL
) {
5455 CFDictionaryRef if_dict
;
5458 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5459 if_dict
= CFDictionaryGetValue(services_info
, key
);
5461 is_expensive
= if_dict_is_expensive(if_dict
);
5463 return (is_expensive
);
5467 interface_is_expensive(CFStringRef ifname
)
5469 boolean_t is_expensive
= FALSE
;
5471 if (ifname
!= NULL
) {
5472 CFDictionaryRef if_dict
;
5475 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5476 if_dict
= SCDynamicStoreCopyValue(S_session
, key
);
5478 if (if_dict
!= NULL
) {
5479 is_expensive
= if_dict_is_expensive(if_dict
);
5483 return (is_expensive
);
5487 service_rank_entity_get_index(CFDictionaryRef dict
, CFStringRef serviceID
,
5488 CFStringRef which
, uint32_t * ret_val
)
5490 CFNumberRef service_index
= NULL
;
5493 service_index
= CFDictionaryGetValue(dict
,
5494 kSCPropNetServiceServiceIndex
);
5495 service_index
= isA_CFNumber(service_index
);
5497 if (service_index
!= NULL
) {
5500 if (!CFNumberGetValue(service_index
, kCFNumberSInt32Type
,
5502 || index_val
<= 0) {
5503 /* ServiceIndex must be >= 1 */
5505 "%@%@ ServiceIndex %@ is invalid, ignoring",
5506 which
, serviceID
, service_index
);
5507 service_index
= NULL
;
5509 else if (ret_val
!= NULL
) {
5510 *ret_val
= (uint32_t)index_val
;
5513 return (service_index
);
5517 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
5518 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
5520 boolean_t changed
= FALSE
;
5521 CFStringRef interface
;
5522 boolean_t ip_is_coupled
= FALSE
;
5523 CFMutableDictionaryRef new_dict
= NULL
;
5524 Rank rank_assertion
= kRankAssertionDefault
;
5525 Boolean rank_assertion_is_set
= FALSE
;
5526 CFStringRef setup_rank
= NULL
;
5527 CFStringRef state_rank
= NULL
;
5528 CFNumberRef service_index
= NULL
;
5531 if (setup_options
!= NULL
) {
5532 CFBooleanRef coupled
;
5535 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
5536 setup_rank
= isA_CFString(setup_rank
);
5537 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
5538 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5539 ip_is_coupled
= TRUE
;
5542 = service_rank_entity_get_index(setup_options
,
5544 kSCDynamicStoreDomainSetup
,
5547 if (state_options
!= NULL
) {
5548 CFBooleanRef coupled
;
5551 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
5552 state_rank
= isA_CFString(state_rank
);
5553 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
5554 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5555 ip_is_coupled
= TRUE
;
5557 if (service_index
== NULL
) {
5559 = service_rank_entity_get_index(state_options
,
5561 kSCDynamicStoreDomainState
,
5566 if (!ip_is_coupled
) {
5567 ip_is_coupled
= service_is_expensive(serviceID
, services_info
);
5569 if (setup_rank
!= NULL
|| state_rank
!= NULL
) {
5570 /* rank assertion is set on the service */
5571 Rank setup_assertion
;
5572 Boolean setup_assertion_is_set
= FALSE
;
5573 Rank state_assertion
;
5574 Boolean state_assertion_is_set
= FALSE
;
5576 setup_assertion
= PrimaryRankGetRankAssertion(setup_rank
,
5577 &setup_assertion_is_set
);
5578 state_assertion
= PrimaryRankGetRankAssertion(state_rank
,
5579 &state_assertion_is_set
);
5580 if (setup_assertion_is_set
&& state_assertion_is_set
) {
5581 if (setup_assertion
> state_assertion
) {
5582 rank_assertion
= setup_assertion
;
5585 rank_assertion
= state_assertion
;
5587 rank_assertion_is_set
= TRUE
;
5589 else if (setup_assertion_is_set
) {
5590 rank_assertion
= setup_assertion
;
5591 rank_assertion_is_set
= TRUE
;
5593 else if (state_assertion_is_set
) {
5594 rank_assertion
= state_assertion
;
5595 rank_assertion_is_set
= TRUE
;
5599 interface
= services_info_get_interface(services_info
, serviceID
);
5600 if (interface
!= NULL
) {
5601 if (!rank_assertion_is_set
) {
5602 /* check for a rank assertion on the interface */
5603 CFNumberRef if_rank
= NULL
;
5605 if (S_if_rank_dict
!= NULL
) {
5606 if_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
5609 = InterfaceRankGetRankAssertion(if_rank
,
5610 &rank_assertion_is_set
);
5611 #define kNotSetString ((CFTypeRef)CFSTR("not set"))
5613 "serviceID %@ interface %@ rank = 0x%x (%@)%s",
5617 (if_rank
!= NULL
) ? (CFTypeRef
)if_rank
: kNotSetString
,
5618 ip_is_coupled
? " [coupled]" : "");
5622 "serviceID %@ interface %@ rank = 0x%x%s",
5623 serviceID
, interface
, rank_assertion
,
5624 ip_is_coupled
? " [coupled]" : "");
5629 if (service_index
!= NULL
|| rank_assertion_is_set
|| ip_is_coupled
) {
5630 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5631 &kCFTypeDictionaryKeyCallBacks
,
5632 &kCFTypeDictionaryValueCallBacks
);
5633 if (rank_assertion_is_set
) {
5634 CFNumberRef new_rank
;
5636 new_rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
,
5637 (const void *)&rank_assertion
);
5638 CFDictionarySetValue(new_dict
, kServiceOptionRankAssertion
,
5640 CFRelease(new_rank
);
5642 if (ip_is_coupled
) {
5643 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
5645 if (service_index
!= NULL
) {
5646 CFDictionarySetValue(new_dict
, kSCPropNetServiceServiceIndex
,
5650 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
5651 my_CFRelease(&new_dict
);
5656 add_service_keys(CFStringRef serviceID
,
5657 CFMutableArrayRef keys
, CFMutableArrayRef patterns
)
5662 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
5666 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5667 CFStringRef name
= *entityTypeNames
[i
];
5669 key
= setup_service_key(serviceID
, name
);
5670 CFArrayAppendValue(keys
, key
);
5672 key
= state_service_key(serviceID
, name
);
5673 CFArrayAppendValue(keys
, key
);
5677 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
5678 CFArrayAppendValue(patterns
, key
);
5681 key
= setup_service_key(serviceID
, NULL
);
5682 CFArrayAppendValue(patterns
, key
);
5684 key
= state_service_key(serviceID
, NULL
);
5685 CFArrayAppendValue(patterns
, key
);
5692 add_transient_status_keys(CFStringRef service_id
, CFMutableArrayRef patterns
)
5694 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5695 CFStringRef pattern
;
5697 pattern
= state_service_key(service_id
,
5698 *transientServiceInfo
[i
].entityName
);
5699 CFArrayAppendValue(patterns
, pattern
);
5706 static const CFStringRef
*reachabilitySetupKeys
[] = {
5708 &kSCEntNetInterface
,
5715 add_reachability_patterns(CFMutableArrayRef patterns
)
5717 for (size_t i
= 0; i
< countof(reachabilitySetupKeys
); i
++) {
5718 CFStringRef pattern
;
5719 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
5720 CFArrayAppendValue(patterns
, pattern
);
5727 add_vpn_pattern(CFMutableArrayRef patterns
)
5729 CFStringRef pattern
;
5731 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
5732 CFArrayAppendValue(patterns
, pattern
);
5737 add_interface_link_pattern(CFMutableArrayRef patterns
)
5739 CFStringRef pattern
;
5741 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetLink
);
5742 CFArrayAppendValue(patterns
, pattern
);
5746 static CFDictionaryRef
5747 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
5750 CFMutableArrayRef get_keys
;
5751 CFMutableArrayRef get_patterns
;
5752 CFDictionaryRef info
;
5754 count
= CFArrayGetCount(service_list
);
5755 get_keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5756 get_patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5758 CFArrayAppendValue(get_keys
, S_setup_global_ipv4
);
5759 CFArrayAppendValue(get_keys
, S_multicast_resolvers
);
5760 CFArrayAppendValue(get_keys
, S_private_resolvers
);
5762 for (CFIndex s
= 0; s
< count
; s
++) {
5763 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
5765 add_service_keys(serviceID
, get_keys
, get_patterns
);
5766 add_transient_status_keys(serviceID
, get_keys
);
5769 add_reachability_patterns(get_patterns
);
5771 add_vpn_pattern(get_patterns
);
5773 add_interface_link_pattern(get_patterns
);
5775 info
= SCDynamicStoreCopyMultiple(session
, get_keys
, get_patterns
);
5776 my_CFRelease(&get_keys
);
5777 my_CFRelease(&get_patterns
);
5781 #if !TARGET_OS_SIMULATOR
5784 set_ipv6_default_interface(IFIndex ifindex
)
5786 struct in6_ndifreq ndifreq
;
5788 boolean_t success
= FALSE
;
5790 bzero((char *)&ndifreq
, sizeof(ndifreq
));
5791 strlcpy(ndifreq
.ifname
, kLoopbackInterface
, sizeof(ndifreq
.ifname
));
5793 ndifreq
.ifindex
= ifindex
;
5796 ndifreq
.ifindex
= lo0_ifindex();
5798 sock
= inet6_dgram_socket();
5802 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
5804 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5815 #endif /* !TARGET_OS_SIMULATOR */
5817 #if !TARGET_OS_IPHONE
5818 static __inline__
void
5821 (void)unlink(VAR_RUN_RESOLV_CONF
);
5825 set_dns(CFArrayRef val_search_domains
,
5826 CFStringRef val_domain_name
,
5827 CFArrayRef val_servers
,
5828 CFArrayRef val_sortlist
)
5830 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
5832 /* publish new resolv.conf */
5837 SCPrint(TRUE
, f
, CFSTR("#\n"));
5838 SCPrint(TRUE
, f
, CFSTR("# macOS Notice\n"));
5839 SCPrint(TRUE
, f
, CFSTR("#\n"));
5840 SCPrint(TRUE
, f
, CFSTR("# This file is not consulted for DNS hostname resolution, address\n"));
5841 SCPrint(TRUE
, f
, CFSTR("# resolution, or the DNS query routing mechanism used by most\n"));
5842 SCPrint(TRUE
, f
, CFSTR("# processes on this system.\n"));
5843 SCPrint(TRUE
, f
, CFSTR("#\n"));
5844 SCPrint(TRUE
, f
, CFSTR("# To view the DNS configuration used by this system, use:\n"));
5845 SCPrint(TRUE
, f
, CFSTR("# scutil --dns\n"));
5846 SCPrint(TRUE
, f
, CFSTR("#\n"));
5847 SCPrint(TRUE
, f
, CFSTR("# SEE ALSO\n"));
5848 SCPrint(TRUE
, f
, CFSTR("# dns-sd(1), scutil(8)\n"));
5849 SCPrint(TRUE
, f
, CFSTR("#\n"));
5850 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
5851 SCPrint(TRUE
, f
, CFSTR("#\n"));
5853 if (isA_CFArray(val_search_domains
)) {
5854 SCPrint(TRUE
, f
, CFSTR("search"));
5855 n
= CFArrayGetCount(val_search_domains
);
5856 for (i
= 0; i
< n
; i
++) {
5859 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
5860 if (isA_CFString(domain
)) {
5861 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
5864 SCPrint(TRUE
, f
, CFSTR("\n"));
5866 else if (isA_CFString(val_domain_name
)) {
5867 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
5870 if (isA_CFArray(val_servers
)) {
5871 n
= CFArrayGetCount(val_servers
);
5872 for (i
= 0; i
< n
; i
++) {
5873 CFStringRef nameserver
;
5875 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
5876 if (isA_CFString(nameserver
)) {
5877 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
5882 if (isA_CFArray(val_sortlist
)) {
5883 SCPrint(TRUE
, f
, CFSTR("sortlist"));
5884 n
= CFArrayGetCount(val_sortlist
);
5885 for (i
= 0; i
< n
; i
++) {
5886 CFStringRef address
;
5888 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
5889 if (isA_CFString(address
)) {
5890 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
5893 SCPrint(TRUE
, f
, CFSTR("\n"));
5897 (void)rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
5901 #endif /* !TARGET_OS_IPHONE */
5904 service_get_ip_is_coupled(CFStringRef serviceID
)
5906 CFDictionaryRef dict
;
5907 boolean_t ip_is_coupled
= FALSE
;
5909 dict
= service_dict_get(serviceID
, kSCEntNetService
);
5911 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
5912 ip_is_coupled
= TRUE
;
5915 return (ip_is_coupled
);
5919 my_CFStringCreateWithInAddr(struct in_addr ip
)
5923 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(IP_FORMAT
), IP_LIST(&ip
));
5928 my_CFStringCreateWithIn6Addr(const struct in6_addr
* ip
)
5930 char ntopbuf
[INET6_ADDRSTRLEN
];
5932 (void)inet_ntop(AF_INET6
, ip
, ntopbuf
, sizeof(ntopbuf
));
5933 return (CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), ntopbuf
));
5937 * Function: update_ipv4
5939 * Update the IPv4 configuration based on the latest information.
5940 * Publish the State:/Network/Global/IPv4 information, and update the
5941 * IPv4 routing table.
5944 update_ipv4(CFStringRef primary
,
5945 IPv4RouteListRef new_routelist
,
5946 keyChangeListRef keys
)
5948 #if !TARGET_OS_SIMULATOR
5950 #endif /* !TARGET_OS_SIMULATOR */
5953 if (new_routelist
!= NULL
&& primary
!= NULL
) {
5954 const char * ifn_p
= NULL
;
5955 char ifname
[IFNAMSIZ
];
5957 CFMutableDictionaryRef dict
= NULL
;
5959 dict
= CFDictionaryCreateMutable(NULL
, 0,
5960 &kCFTypeDictionaryKeyCallBacks
,
5961 &kCFTypeDictionaryValueCallBacks
);
5962 /* the first entry is the default route */
5963 r
= new_routelist
->list
;
5964 if (r
->gateway
.s_addr
!= 0) {
5967 str
= my_CFStringCreateWithInAddr(r
->gateway
);
5968 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, str
);
5971 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
5972 if (ifn_p
!= NULL
) {
5973 CFStringRef ifname_cf
;
5975 ifname_cf
= CFStringCreateWithCString(NULL
,
5977 kCFStringEncodingASCII
);
5978 if (ifname_cf
!= NULL
) {
5979 CFDictionarySetValue(dict
,
5980 kSCDynamicStorePropNetPrimaryInterface
,
5982 CFRelease(ifname_cf
);
5985 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
5987 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
5991 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
5995 #if !TARGET_OS_SIMULATOR
5996 sockfd
= open_routing_socket();
5998 /* go through routelist and bind any unbound routes */
5999 if (new_routelist
!= NULL
) {
6000 IPv4RouteListFinalize(new_routelist
);
6003 /* provide a routelist with just loopback multicast */
6004 new_routelist
= IPv4RouteListCopyMulticastLoopback();
6006 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6007 if (S_ipv4_routelist
== NULL
) {
6008 my_log(LOG_DEBUG
, "Old Routes = <none>");
6011 my_log(LOG_DEBUG
, "Old Routes = ");
6012 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
6014 if (new_routelist
== NULL
) {
6015 my_log(LOG_DEBUG
, "New Routes = <none>");
6018 my_log(LOG_DEBUG
, "New Routes = ");
6019 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
6022 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
, sockfd
);
6025 if (S_ipv4_routelist
!= NULL
) {
6026 free(S_ipv4_routelist
);
6028 S_ipv4_routelist
= new_routelist
;
6029 #else /* !TARGET_OS_SIMULATOR */
6030 if (new_routelist
!= NULL
) {
6031 free(new_routelist
);
6033 #endif /* !TARGET_OS_SIMULATOR */
6039 * Function: update_ipv6
6041 * Update the IPv6 configuration based on the latest information.
6042 * Publish the State:/Network/Global/IPv6 information, and update the
6043 * IPv6 routing table.
6046 update_ipv6(CFStringRef primary
,
6047 IPv6RouteListRef new_routelist
,
6048 keyChangeListRef keys
)
6050 #if !TARGET_OS_SIMULATOR
6052 #endif /* !TARGET_OS_SIMULATOR */
6055 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6056 const char * ifn_p
= NULL
;
6057 char ifname
[IFNAMSIZ
];
6059 CFMutableDictionaryRef dict
= NULL
;
6061 dict
= CFDictionaryCreateMutable(NULL
, 0,
6062 &kCFTypeDictionaryKeyCallBacks
,
6063 &kCFTypeDictionaryValueCallBacks
);
6064 /* the first entry is the default route */
6065 r
= new_routelist
->list
;
6066 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
6069 router
= my_CFStringCreateWithIn6Addr(&r
->gateway
);
6070 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
, router
);
6073 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6074 if (ifn_p
!= NULL
) {
6075 CFStringRef ifname_cf
;
6077 ifname_cf
= CFStringCreateWithCString(NULL
,
6079 kCFStringEncodingASCII
);
6080 if (ifname_cf
!= NULL
) {
6081 CFDictionarySetValue(dict
,
6082 kSCDynamicStorePropNetPrimaryInterface
,
6084 CFRelease(ifname_cf
);
6087 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6089 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
6091 #if !TARGET_OS_SIMULATOR
6092 set_ipv6_default_interface(r
->ifindex
);
6093 #endif /* !TARGET_OS_SIMULATOR */
6096 #if !TARGET_OS_SIMULATOR
6097 set_ipv6_default_interface(0);
6098 #endif /* !TARGET_OS_SIMULATOR */
6099 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
6103 #if !TARGET_OS_SIMULATOR
6104 sockfd
= open_routing_socket();
6106 /* go through routelist and bind any unbound routes */
6107 IPv6RouteListFinalize(new_routelist
);
6108 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6109 if (S_ipv6_routelist
== NULL
) {
6110 my_log(LOG_DEBUG
, "Old Routes = <none>");
6113 my_log(LOG_DEBUG
, "Old Routes = ");
6114 IPv6RouteListLog(LOG_DEBUG
, S_ipv6_routelist
);
6116 if (new_routelist
== NULL
) {
6117 my_log(LOG_DEBUG
, "New Routes = <none>");
6120 my_log(LOG_DEBUG
, "New Routes = ");
6121 IPv6RouteListLog(LOG_DEBUG
, new_routelist
);
6124 IPv6RouteListApply(S_ipv6_routelist
, new_routelist
, sockfd
);
6127 if (S_ipv6_routelist
!= NULL
) {
6128 free(S_ipv6_routelist
);
6130 S_ipv6_routelist
= new_routelist
;
6131 #else /* !TARGET_OS_SIMULATOR */
6132 if (new_routelist
!= NULL
) {
6133 free(new_routelist
);
6135 #endif /* !TARGET_OS_SIMULATOR */
6141 update_dns(CFDictionaryRef services_info
,
6142 CFStringRef primary
,
6143 keyChangeListRef keys
)
6145 #pragma unused(services_info)
6146 Boolean changed
= FALSE
;
6147 CFDictionaryRef dict
= NULL
;
6149 if (primary
!= NULL
) {
6150 CFDictionaryRef service_dict
;
6152 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6153 if (service_dict
!= NULL
) {
6154 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6158 if (!_SC_CFEqual(S_dns_dict
, dict
)) {
6160 #if !TARGET_OS_IPHONE
6162 #endif /* !TARGET_OS_IPHONE */
6163 keyChangeListRemoveValue(keys
, S_state_global_dns
);
6165 CFMutableDictionaryRef new_dict
;
6167 #if !TARGET_OS_IPHONE
6168 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
6169 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
6170 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
6171 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
6172 #endif /* !TARGET_OS_IPHONE */
6173 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
6174 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
6175 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
6176 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
6177 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
6178 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
6179 CFRelease(new_dict
);
6184 if (dict
!= NULL
) CFRetain(dict
);
6185 if (S_dns_dict
!= NULL
) CFRelease(S_dns_dict
);
6192 update_dnsinfo(CFDictionaryRef services_info
,
6193 CFStringRef primary
,
6194 keyChangeListRef keys
,
6195 CFArrayRef service_order
)
6198 CFDictionaryRef dict
= NULL
;
6199 CFArrayRef multicastResolvers
;
6200 CFArrayRef privateResolvers
;
6202 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
6203 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
6205 if (primary
!= NULL
) {
6206 CFDictionaryRef service_dict
;
6208 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6209 if (service_dict
!= NULL
) {
6210 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6214 changed
= dns_configuration_set(dict
,
6215 S_service_state_dict
,
6220 keyChangeListNotifyKey(keys
, S_state_global_dns
);
6226 update_nwi(nwi_state_t state
)
6228 unsigned char signature
[CC_SHA1_DIGEST_LENGTH
];
6229 static unsigned char signature_last
[CC_SHA1_DIGEST_LENGTH
];
6231 _nwi_state_compute_sha1_hash(state
, signature
);
6232 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
6233 my_log(LOG_DEBUG
, "Not updating network information");
6237 // save [new] signature
6238 bcopy(signature
, signature_last
, sizeof(signature
));
6240 // save [new] configuration
6241 my_log(LOG_INFO
, "Updating network information");
6242 _nwi_state_log(state
, TRUE
, NULL
);
6244 if (!_nwi_state_store(state
)) {
6245 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
6252 update_proxies(CFDictionaryRef services_info
,
6253 CFStringRef primary
,
6254 keyChangeListRef keys
,
6255 CFArrayRef service_order
)
6257 Boolean changed
= FALSE
;
6258 CFDictionaryRef dict
= NULL
;
6259 CFDictionaryRef new_dict
;
6261 if (primary
!= NULL
) {
6262 CFDictionaryRef service_dict
;
6264 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6265 if (service_dict
!= NULL
) {
6266 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
6270 new_dict
= proxy_configuration_update(dict
,
6271 S_service_state_dict
,
6274 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
6275 if (new_dict
== NULL
) {
6276 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
6278 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
6283 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
6284 S_proxies_dict
= new_dict
;
6289 #if !TARGET_OS_IPHONE
6291 update_smb(CFDictionaryRef services_info
,
6292 CFStringRef primary
,
6293 keyChangeListRef keys
)
6295 #pragma unused(services_info)
6296 Boolean changed
= FALSE
;
6297 CFDictionaryRef dict
= NULL
;
6299 if (primary
!= NULL
) {
6300 CFDictionaryRef service_dict
;
6302 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6303 if (service_dict
!= NULL
) {
6304 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
6308 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
6310 keyChangeListRemoveValue(keys
, S_state_global_smb
);
6312 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
6317 if (dict
!= NULL
) CFRetain(dict
);
6318 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
6323 #endif /* !TARGET_OS_IPHONE */
6326 get_service_index(CFDictionaryRef rank_entity
,
6327 CFArrayRef order
, CFIndex n_order
, CFStringRef serviceID
)
6330 Rank rank
= kRankIndexMask
;
6331 CFNumberRef service_index
;
6334 = service_rank_entity_get_index(rank_entity
,
6338 if (service_index
!= NULL
) {
6339 /* ServiceIndex specified in service entity */
6342 "%@ specifies ServiceIndex %@, effective index is %d",
6343 serviceID
, service_index
, rank
);
6345 else if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
6346 for (i
= 0; i
< n_order
; i
++) {
6347 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
6352 if (CFEqual(serviceID
, s
)) {
6362 ** Service election:
6365 * Function: rank_dict_get_service_rank
6367 * Retrieve the service rank in the given dictionary.
6370 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
6373 Rank rank_val
= kRankAssertionDefault
;
6375 rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
6376 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
6378 if (!CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
)) {
6379 /* if we don't like the rank value */
6380 rank_val
= kRankAssertionDefault
;
6388 * Function: rank_dict_set_service_rank
6390 * Save the results of ranking the service so we can look it up later without
6391 * repeating all of the ranking code.
6394 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
6395 CFStringRef serviceID
, Rank rank_val
)
6399 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
6401 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
6407 static const CFStringRef
*transientInterfaceEntityNames
[] = {
6413 CollectTransientServices(const void * key
,
6417 #pragma unused(value)
6418 CFStringRef service
= key
;
6419 CFMutableArrayRef vif_setup_keys
= context
;
6421 /* This service is either a vpn type service or a comm center service */
6422 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
6426 for (size_t i
= 0; i
< countof(transientInterfaceEntityNames
); i
++) {
6427 if (CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
6428 CFArrayAppendValue(vif_setup_keys
, service
);
6437 static SCNetworkReachabilityFlags
6438 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
6439 CFStringRef service_id
,
6441 CFStringRef vpn_setup_key
)
6444 CFDictionaryRef dict
;
6445 SCNetworkReachabilityFlags flags
= 0;
6448 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6449 kSCDynamicStoreDomainSetup
,
6451 kSCEntNetInterface
);
6452 dict
= CFDictionaryGetValue(services_info
, key
);
6455 if (isA_CFDictionary(dict
)
6456 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
6458 flags
= (kSCNetworkReachabilityFlagsReachable
6459 | kSCNetworkReachabilityFlagsTransientConnection
6460 | kSCNetworkReachabilityFlagsConnectionRequired
);
6462 if (CFEqual(entity
, kSCEntNetPPP
)) {
6464 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
6466 if (!isA_CFDictionary(p_dict
)) {
6470 // get PPP dial-on-traffic status
6471 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
6472 if (isA_CFNumber(num
)) {
6475 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
6477 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6487 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
6489 Boolean ret
= def_value
;
6494 val
= CFDictionaryGetValue(dict
, key
);
6495 if (isA_CFBoolean(val
) != NULL
) {
6496 ret
= CFBooleanGetValue(val
);
6504 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
6505 SCNetworkReachabilityFlags
*reach_flags_v4
,
6506 SCNetworkReachabilityFlags
*reach_flags_v6
)
6510 CFMutableArrayRef vif_setup_keys
;
6512 vif_setup_keys
= CFArrayCreateMutable(NULL
,
6514 &kCFTypeArrayCallBacks
);
6515 CFDictionaryApplyFunction(services_info
, CollectTransientServices
,
6517 count
= CFArrayGetCount(vif_setup_keys
);
6518 for (i
= 0; i
< count
; i
++) {
6519 CFArrayRef components
= NULL
;
6521 CFStringRef service_id
;
6522 CFStringRef vif_setup_key
;
6524 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
6527 * setup key in the following format:
6528 * Setup:/Network/Service/<Service ID>/<Entity>
6530 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
6532 if (CFArrayGetCount(components
) != 5) {
6533 // invalid Setup key encountered
6537 /* service id is the 3rd element */
6538 service_id
= CFArrayGetValueAtIndex(components
, 3);
6540 /* entity id is the 4th element */
6541 entity
= CFArrayGetValueAtIndex(components
, 4);
6544 if (CFEqual(entity
, kSCEntNetPPP
)) {
6545 SCNetworkReachabilityFlags flags
;
6548 flags
= GetReachabilityFlagsFromVPN(services_info
,
6553 /* Check for the v4 reachability flags */
6554 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6555 kSCDynamicStoreDomainSetup
,
6559 if (CFDictionaryContainsKey(services_info
, key
)) {
6560 *reach_flags_v4
|= flags
;
6561 my_log(LOG_DEBUG
, "Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
6566 /* Check for the v6 reachability flags */
6567 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6568 kSCDynamicStoreDomainSetup
,
6572 if (CFDictionaryContainsKey(services_info
, key
)) {
6573 *reach_flags_v6
|= flags
;
6574 my_log(LOG_DEBUG
, "Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
6579 if (components
!= NULL
) {
6580 CFRelease(components
);
6586 if (components
!= NULL
) {
6587 CFRelease(components
);
6591 CFRelease(vif_setup_keys
);
6595 static SCNetworkReachabilityFlags
6596 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
6598 SCNetworkReachabilityFlags flags
= 0;
6600 if (CFEqual(entity
, kSCEntNetPPP
)) {
6603 /* if we're really UP and RUNNING */
6606 /* if we're effectively UP and RUNNING */
6609 /* if we're not connected at all */
6610 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6612 case PPP_STATERESERVED
:
6613 // if we're not connected at all
6614 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6617 /* if we're in the process of [dis]connecting */
6618 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6622 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
6624 case IPSEC_RUNNING
:
6625 /* if we're really UP and RUNNING */
6628 /* if we're not connected at all */
6629 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6632 /* if we're in the process of [dis]connecting */
6633 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6637 else if (CFEqual(entity
, kSCEntNetVPN
)) {
6640 /* if we're really UP and RUNNING */
6645 case VPN_UNLOADING
:
6646 /* if we're not connected at all */
6647 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6650 /* if we're in the process of [dis]connecting */
6651 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6659 VPNAttributesGet(CFStringRef service_id
,
6660 CFDictionaryRef services_info
,
6661 SCNetworkReachabilityFlags
*flags
,
6662 CFStringRef
*server_address
,
6665 CFDictionaryRef entity_dict
;
6667 CFDictionaryRef p_state
= NULL
;
6669 CFStringRef transient_entity
= NULL
;
6671 if (af
== AF_INET
) {
6672 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv4
);
6674 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv6
);
6676 entity_dict
= ipdict_get_service(entity_dict
);
6677 if (entity_dict
== NULL
) {
6681 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
6682 CFStringRef entity
= *transientServiceInfo
[i
].entityName
;
6684 p_state
= service_dict_get(service_id
, entity
);
6686 /* ensure that this is a VPN Type service */
6687 if (isA_CFDictionary(p_state
)) {
6688 transient_entity
= entity
;
6693 /* Did we find a vpn type service? If not, we are done.*/
6694 if (transient_entity
== NULL
) {
6698 *flags
|= (kSCNetworkReachabilityFlagsReachable
6699 | kSCNetworkReachabilityFlagsTransientConnection
);
6701 /* Get the Server Address */
6702 if (server_address
!= NULL
) {
6703 *server_address
= CFDictionaryGetValue(entity_dict
,
6704 CFSTR("ServerAddress"));
6705 *server_address
= isA_CFString(*server_address
);
6706 if (*server_address
!= NULL
) {
6707 CFRetain(*server_address
);
6712 if (!CFDictionaryGetValueIfPresent(p_state
,
6713 kSCPropNetVPNStatus
, // IPSecStatus, PPPStatus, VPNStatus
6714 (const void **)&num
) ||
6715 !isA_CFNumber(num
) ||
6716 !CFNumberGetValue(num
, kCFNumberIntType
, &status
)) {
6720 *flags
|= GetReachFlagsFromStatus(transient_entity
, status
);
6721 if (CFEqual(transient_entity
, kSCEntNetPPP
)) {
6723 CFDictionaryRef p_setup
;
6726 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6727 kSCDynamicStoreDomainSetup
,
6730 p_setup
= CFDictionaryGetValue(services_info
, key
);
6733 /* get dial-on-traffic status */
6734 if (isA_CFDictionary(p_setup
) &&
6735 CFDictionaryGetValueIfPresent(p_setup
,
6736 kSCPropNetPPPDialOnDemand
,
6737 (const void **)&num
) &&
6738 isA_CFNumber(num
) &&
6739 CFNumberGetValue(num
, kCFNumberIntType
, &ppp_demand
) &&
6740 (ppp_demand
!= 0)) {
6741 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6742 if (status
== PPP_IDLE
) {
6743 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
6751 typedef struct ElectionInfo
{
6757 ElectionResultsRef results
;
6758 CFMutableDictionaryRef rank_dict
;
6759 } ElectionInfo
, * ElectionInfoRef
;
6761 typedef CFDictionaryApplierFunction ElectionFuncRef
;
6764 CandidateRelease(CandidateRef candidate
)
6766 my_CFRelease(&candidate
->serviceID
);
6767 my_CFRelease(&candidate
->if_name
);
6768 my_CFRelease(&candidate
->signature
);
6773 CandidateCopy(CandidateRef dest
, CandidateRef src
)
6776 if (dest
->serviceID
) {
6777 CFRetain(dest
->serviceID
);
6779 if (dest
->if_name
) {
6780 CFRetain(dest
->if_name
);
6782 if(dest
->signature
) {
6783 CFRetain(dest
->signature
);
6788 static ElectionResultsRef
6789 ElectionResultsAlloc(int af
, int size
)
6791 ElectionResultsRef results
;
6793 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
6796 results
->size
= size
;
6801 ElectionResultsRelease(ElectionResultsRef results
)
6806 for (i
= 0, scan
= results
->candidates
;
6809 CandidateRelease(scan
);
6816 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
6821 if (results
== NULL
) {
6822 my_log(level
, "%s: no candidates", prefix
);
6825 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
6826 for (i
= 0, scan
= results
->candidates
;
6829 char ntopbuf
[INET6_ADDRSTRLEN
];
6831 (void)inet_ntop(results
->af
, &scan
->addr
, ntopbuf
, sizeof(ntopbuf
));
6832 my_log(level
, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6833 i
, scan
->if_name
, scan
->serviceID
, ntopbuf
, scan
->rank
,
6834 scan
->ineligible
? " [ineligible]" : "");
6840 * Function: ElectionResultsAddCandidate
6842 * Add the candidate into the election results. Find the insertion point
6843 * by comparing the rank of the candidate with existing entries.
6846 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
6851 if (results
->count
== results
->size
) {
6852 /* this should not happen */
6853 my_log(LOG_NOTICE
, "can't fit another candidate");
6857 /* find the insertion point */
6858 where
= kCFNotFound
;
6859 for (i
= 0; i
< results
->count
; i
++) {
6860 CandidateRef this_candidate
= results
->candidates
+ i
;
6862 if (candidate
->rank
< this_candidate
->rank
) {
6867 /* add it to the end */
6868 if (where
== kCFNotFound
) {
6869 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
6873 /* slide existing entries over */
6874 for (i
= results
->count
; i
> where
; i
--) {
6875 results
->candidates
[i
] = results
->candidates
[i
- 1];
6877 /* insert element */
6878 CandidateCopy(results
->candidates
+ where
, candidate
);
6884 elect_ip(const void * key
, const void * value
, void * context
);
6887 * Function: ElectionResultsCopy
6889 * Visit all of the services and invoke the protocol-specific election
6890 * function. Return the results of the election.
6892 static ElectionResultsRef
6893 ElectionResultsCopy(int af
, CFArrayRef order
)
6898 count
= (int)CFDictionaryGetCount(S_service_state_dict
);
6903 if (af
== AF_INET
) {
6904 info
.entity
= kSCEntNetIPv4
;
6905 info
.rank_dict
= S_ipv4_service_rank_dict
;
6908 info
.entity
= kSCEntNetIPv6
;
6909 info
.rank_dict
= S_ipv6_service_rank_dict
;
6911 info
.results
= ElectionResultsAlloc(af
, count
);
6912 info
.n_services
= count
;
6914 if (order
!= NULL
) {
6915 info
.n_order
= CFArrayGetCount(order
);
6920 CFDictionaryApplyFunction(S_service_state_dict
, elect_ip
, (void *)&info
);
6921 if (info
.results
->count
== 0) {
6922 ElectionResultsRelease(info
.results
);
6923 info
.results
= NULL
;
6925 return (info
.results
);
6929 * Function: ElectionResultsCandidateNeedsDemotion
6931 * Check whether the given candidate requires demotion. A candidate
6932 * might need to be demoted if its IPv4 and IPv6 services must be coupled
6933 * but a higher ranked service has IPv4 or IPv6.
6935 * The converse is also true: if the given candidate has lower rank than
6936 * the other candidate and the other candidate is coupled, this candidate
6937 * needs to be demoted.
6940 ElectionResultsCandidateNeedsDemotion(CandidateRef other_candidate
,
6941 CandidateRef candidate
)
6943 Boolean ret
= FALSE
;
6945 if (other_candidate
== NULL
) {
6946 /* no other candidate */
6949 if (other_candidate
->ineligible
) {
6950 /* other candidate can't become primary */
6953 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
6954 /* the other candidate can't become primary */
6957 if (!candidate
->ip_is_coupled
&& !other_candidate
->ip_is_coupled
) {
6958 /* neither candidate is coupled */
6961 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
6962 /* they are over the same interface, no need to demote */
6965 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
6966 /* avoid creating a feedback loop */
6969 if (candidate
->rank
< other_candidate
->rank
) {
6970 /* we're higher ranked than the other candidate, ignore */
6973 if (candidate
->ip_is_coupled
) {
6974 if (other_candidate
->ip_is_coupled
6975 && candidate
->rank
== other_candidate
->rank
) {
6976 /* same rank as another service that is coupled, ignore */
6980 else if (other_candidate
->ip_is_coupled
) { /* must be true */
6981 if (candidate
->rank
== other_candidate
->rank
) {
6982 /* other candidate will be demoted, so we don't need to */
6985 /* we're lower rank and need to be demoted */
6987 else { /* can't happen, we already tested for this above */
6988 /* neither candidate is coupled */
7000 get_signature_sha1(CFStringRef signature
,
7001 unsigned char * sha1
)
7004 CFDataRef signature_data
;
7006 signature_data
= CFStringCreateExternalRepresentation(NULL
,
7008 kCFStringEncodingUTF8
,
7012 CC_SHA1_Update(&ctx
,
7013 CFDataGetBytePtr(signature_data
),
7014 (CC_LONG
)CFDataGetLength(signature_data
));
7015 CC_SHA1_Final(sha1
, &ctx
);
7017 CFRelease(signature_data
);
7024 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
7025 CandidateRef candidate
, Boolean not_in_list
,
7026 Boolean not_in_iflist
)
7029 char ifname
[IFNAMSIZ
];
7030 nwi_ifstate_t ifstate
;
7032 if (nwi_state
== NULL
) {
7037 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
7038 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
7040 if (not_in_iflist
) {
7041 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST
;
7043 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
7044 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
7046 if ((af
== AF_INET
) && service_has_clat46_address(candidate
->serviceID
)) {
7047 flags
|= NWI_IFSTATE_FLAGS_HAS_CLAT46
;
7049 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
7050 kCFStringEncodingASCII
);
7051 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
7052 char ntopbuf
[INET6_ADDRSTRLEN
];
7054 (void)inet_ntop(af
, &candidate
->addr
, ntopbuf
, sizeof(ntopbuf
));
7056 "Adding IPv%c [%s] %s "
7057 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
7058 ipvx_char(af
), ifname
, ntopbuf
,
7059 flags
, candidate
->rank
, candidate
->reachability_flags
);
7061 ifstate
= nwi_state_add_ifstate(nwi_state
, ifname
, af
, flags
,
7063 (void *)&candidate
->addr
,
7064 (void *)&candidate
->vpn_server_addr
,
7065 candidate
->reachability_flags
);
7066 if (ifstate
!= NULL
&& candidate
->signature
) {
7067 uint8_t hash
[CC_SHA1_DIGEST_LENGTH
];
7069 get_signature_sha1(candidate
->signature
, hash
);
7070 nwi_ifstate_set_signature(ifstate
, hash
);
7077 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
7079 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
7080 CFStringRef vpn_server_address
= NULL
;
7082 assert(candidate
!= NULL
);
7083 assert(services_info
!= NULL
);
7085 VPNAttributesGet(candidate
->serviceID
,
7088 &vpn_server_address
,
7091 candidate
->reachability_flags
= flags
;
7093 if (vpn_server_address
== NULL
) {
7094 bzero(&candidate
->vpn_server_addr
, sizeof(candidate
->vpn_server_addr
));
7098 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
),
7099 kCFStringEncodingASCII
);
7100 _SC_string_to_sockaddr(buf
,
7102 (void *)&candidate
->vpn_server_addr
,
7103 sizeof(candidate
->vpn_server_addr
));
7105 CFRelease(vpn_server_address
);
7110 * Function: ElectionResultsGetPrimary
7112 * Use the results of the current protocol and the other protocol to
7113 * determine which service should become primary.
7115 * At the same time, generate the IPv4/IPv6 routing table and
7116 * the nwi_state for the protocol.
7119 ElectionResultsGetPrimary(ElectionResultsRef results
,
7120 CandidateRef other_candidate
,
7121 nwi_state_t nwi_state
, int af
,
7122 RouteListRef
* ret_routes
,
7123 CFDictionaryRef services_info
)
7125 CandidateRef primary
= NULL
;
7126 Boolean primary_is_null
= FALSE
;
7127 RouteListRef routes
= NULL
;
7129 assert(services_info
!= NULL
);
7131 if (results
!= NULL
) {
7132 CandidateRef deferred
[results
->count
];
7134 CFStringRef entity_name
;
7137 RouteListInfoRef info
;
7142 entity_name
= kSCEntNetIPv4
;
7143 info
= &IPv4RouteListInfo
;
7144 initial_size
= results
->count
* IPV4_ROUTES_N_STATIC
;
7148 entity_name
= kSCEntNetIPv6
;
7149 info
= &IPv6RouteListInfo
;
7150 initial_size
= results
->count
* IPV6_ROUTES_N_STATIC
;
7154 for (i
= 0, scan
= results
->candidates
;
7157 Boolean is_primary
= FALSE
;
7158 CFDictionaryRef service_dict
;
7159 RouteListRef service_routes
;
7160 Boolean skip
= FALSE
;
7162 if (!scan
->ineligible
7164 && RANK_ASSERTION_MASK(scan
->rank
) != kRankAssertionNever
) {
7165 if (ElectionResultsCandidateNeedsDemotion(other_candidate
,
7167 /* demote the service */
7169 "IPv%c over %@ (rank 0x%x) demoted: "
7170 "primary IPv%c %@ (rank 0x%x)",
7171 ipvx_char(af
), scan
->if_name
, scan
->rank
,
7172 ipvx_other_char(af
), other_candidate
->if_name
,
7173 other_candidate
->rank
);
7174 deferred
[deferred_count
++] = scan
;
7182 /* contribute to the routing table */
7183 service_dict
= service_dict_get(scan
->serviceID
, entity_name
);
7184 service_routes
= ipdict_get_routelist(service_dict
);
7185 if (service_routes
!= NULL
) {
7186 Rank rank
= scan
->rank
;
7189 /* routes are RankNever to prevent becoming primary */
7190 rank
= RankMake(rank
, kRankAssertionNever
);
7192 routes
= RouteListAddRouteList(info
, routes
, initial_size
,
7193 service_routes
, rank
);
7194 if ((service_routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
7202 /* if we're skipping the primary, it's NULL */
7204 primary_is_null
= TRUE
;
7207 else if (!scan
->ineligible
) {
7208 Boolean not_in_iflist
;
7210 add_reachability_flags_to_candidate(scan
, services_info
, af
);
7212 = (service_routes
->flags
& kRouteListFlagsScopedOnly
) != 0;
7213 add_candidate_to_nwi_state(nwi_state
, af
, scan
,
7218 for (i
= 0; i
< deferred_count
; i
++) {
7219 CandidateRef candidate
= deferred
[i
];
7221 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
7222 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, TRUE
, FALSE
);
7225 if (ret_routes
!= NULL
) {
7226 *ret_routes
= routes
;
7228 else if (routes
!= NULL
) {
7231 if (primary_is_null
) {
7240 service_dict_get_signature(CFDictionaryRef service_dict
)
7244 ifname
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7245 if (isA_CFString(ifname
) == NULL
7246 || !confirm_interface_name(service_dict
, ifname
)) {
7249 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
7253 * Function: elect_ip
7255 * Evaluate the service and determine what rank the service should have.
7256 * If it's a suitable candidate, add it to the election results.
7259 elect_ip(const void * key
, const void * value
, void * context
)
7261 CFDictionaryRef all_entities_dict
= (CFDictionaryRef
)value
;
7262 Candidate candidate
;
7264 ElectionInfoRef elect_info
;
7265 CFStringRef if_name
;
7266 CFDictionaryRef ipdict
;
7268 CFDictionaryRef rank_entity
;
7269 RouteListUnion routelist
;
7270 CFDictionaryRef service_dict
;
7272 elect_info
= (ElectionInfoRef
)context
;
7273 ipdict
= CFDictionaryGetValue(all_entities_dict
, elect_info
->entity
);
7274 if (ipdict
!= NULL
) {
7275 routelist
.ptr
= ipdict_get_routelist(ipdict
);
7276 service_dict
= ipdict_get_service(ipdict
);
7279 routelist
.ptr
= NULL
;
7281 if (routelist
.ptr
== NULL
|| service_dict
== NULL
) {
7282 /* no connectivity */
7285 if_name
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7286 if (if_name
== NULL
) {
7287 /* need an interface name */
7290 if (CFEqual(if_name
, CFSTR(kLoopbackInterface
))) {
7291 /* don't process loopback */
7294 bzero(&candidate
, sizeof(candidate
));
7295 candidate
.serviceID
= (CFStringRef
)key
;
7296 if ((routelist
.common
->flags
& kRouteListFlagsHasDefault
) == 0) {
7297 /* no default route means it's ineligible to become primary */
7298 candidate
.ineligible
= TRUE
;
7300 rank_entity
= CFDictionaryGetValue(all_entities_dict
, kSCEntNetService
);
7301 candidate
.rank
= get_service_index(rank_entity
,
7302 elect_info
->order
, elect_info
->n_order
,
7303 candidate
.serviceID
);
7304 if (elect_info
->af
== AF_INET
) {
7305 default_rank
= routelist
.v4
->list
->rank
;
7306 candidate
.addr
.v4
= routelist
.v4
->list
->ifa
;
7309 default_rank
= routelist
.v6
->list
->rank
;
7310 candidate
.addr
.v6
= routelist
.v6
->list
->ifa
;
7312 primary_rank
= RANK_ASSERTION_MASK(default_rank
);
7313 if (S_ppp_override_primary
) {
7316 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
7317 kCFStringEncodingASCII
)
7318 && (strncmp(PPP_PREFIX
, ifn
, sizeof(PPP_PREFIX
) - 1) == 0)) {
7319 /* PPP override: make ppp* look the best */
7320 primary_rank
= kRankAssertionFirst
;
7323 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
7324 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
7325 candidate
.if_name
= if_name
;
7326 rank_dict_set_service_rank(elect_info
->rank_dict
,
7327 candidate
.serviceID
, candidate
.rank
);
7328 candidate
.signature
= service_dict_get_signature(service_dict
);
7329 ElectionResultsAddCandidate(elect_info
->results
, &candidate
);
7335 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
7337 uint32_t changed
= 0;
7340 /* update service options first (e.g. rank) */
7341 if (get_rank_changes(serviceID
,
7342 get_service_state_entity(services_info
, serviceID
,
7344 get_service_setup_entity(services_info
, serviceID
,
7347 changed
|= (1 << kEntityTypeServiceOptions
);
7350 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7351 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
7352 GetEntityChangesFuncRef func
;
7355 func
= entityChangeFunc
[i
];
7356 name
= *entityTypeNames
[i
];
7357 if ((*func
)(serviceID
,
7358 get_service_state_entity(services_info
, serviceID
, name
),
7359 get_service_setup_entity(services_info
, serviceID
, name
),
7361 changed
|= (1 << i
);
7365 /* update transient service status */
7366 if (get_transient_status_changes(serviceID
, services_info
)) {
7367 changed
|= (1 << kEntityTypeTransientStatus
);
7374 serviceID_get_ifname(CFStringRef serviceID
)
7376 CFDictionaryRef entity_dict
;
7377 CFStringRef ifname
= NULL
;
7379 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
7380 if (entity_dict
== NULL
) {
7381 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
7383 if (entity_dict
!= NULL
) {
7384 ifname
= ipdict_get_ifname(entity_dict
);
7389 __private_extern__ boolean_t
7390 check_if_service_expensive(CFStringRef serviceID
)
7393 ifname
= serviceID_get_ifname(serviceID
);
7395 return interface_is_expensive(ifname
);
7399 service_order_get(CFDictionaryRef services_info
)
7401 CFArrayRef order
= NULL
;
7402 CFDictionaryRef ipv4_dict
;
7404 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
7405 S_setup_global_ipv4
);
7406 if (ipv4_dict
!= NULL
) {
7407 CFNumberRef ppp_override
;
7410 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
7411 order
= isA_CFArray(order
);
7413 /* get ppp override primary */
7414 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
7415 kSCPropNetPPPOverridePrimary
);
7416 ppp_override
= isA_CFNumber(ppp_override
);
7417 if (ppp_override
!= NULL
) {
7418 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
7420 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
7423 S_ppp_override_primary
= FALSE
;
7429 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
7430 const char * entity
)
7432 boolean_t changed
= FALSE
;
7433 CFStringRef primary
= *primary_p
;
7435 if (new_primary
!= NULL
) {
7436 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
7437 my_log(LOG_INFO
, "%@ is still primary %s", new_primary
, entity
);
7440 my_CFRelease(primary_p
);
7441 *primary_p
= CFRetain(new_primary
);
7442 my_log(LOG_INFO
, "%@ is the new primary %s", new_primary
, entity
);
7446 else if (primary
!= NULL
) {
7447 my_log(LOG_INFO
, "%@ is no longer primary %s", primary
, entity
);
7448 my_CFRelease(primary_p
);
7455 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
7458 if (service_dict_get(serviceID
, entity
) == NULL
) {
7459 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
7461 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
7465 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
7471 #define N_KEYS_VALUES_STATIC 10
7472 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
7475 count
= CFDictionaryGetCount(S_service_state_dict
);
7476 if (count
<= N_KEYS_VALUES_STATIC
) {
7477 keys
= keys_values_buf
;
7479 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
7481 values
= keys
+ count
;
7482 CFDictionaryGetKeysAndValues(S_service_state_dict
,
7483 (const void * *)keys
,
7484 (const void * *)values
);
7486 for (i
= 0; i
< count
; i
++) {
7487 CFDictionaryRef ipdict
= NULL
;
7488 CFStringRef interface
= NULL
;
7489 CFStringRef serviceID
;
7490 CFDictionaryRef service_dict
;
7492 serviceID
= (CFStringRef
)keys
[i
];
7493 service_dict
= (CFDictionaryRef
)values
[i
];
7495 /* check whether service has IPv4 or IPv6 */
7496 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
7497 if (ipdict
== NULL
) {
7498 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
7499 if (ipdict
== NULL
) {
7503 interface
= ipdict_get_ifname(ipdict
);
7504 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
7506 "Found IP service %@ on interface %@",
7508 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
7511 if (keys
!= keys_values_buf
) {
7517 static __inline__
const char *
7518 get_changed_str(CFStringRef serviceID
, CFStringRef entity
,
7519 CFDictionaryRef old_dict
)
7521 CFDictionaryRef new_dict
= NULL
;
7523 if (serviceID
!= NULL
) {
7524 new_dict
= service_dict_get(serviceID
, entity
);
7527 if (old_dict
== NULL
) {
7528 if (new_dict
!= NULL
) {
7532 if (new_dict
== NULL
) {
7534 } else if (!CFEqual(old_dict
, new_dict
)) {
7541 #if !TARGET_OS_SIMULATOR
7544 #define MANAGE_IF_ORDER
7545 #define MANAGE_IF_IOCTL
7546 #endif /* SIOCSIFORDER */
7548 #ifdef SIOCSIFNETSIGNATURE
7549 #define MANAGE_IF_SIGNATURE
7550 #define MANAGE_IF_IOCTL
7551 #endif /* SIOCSIFNETSIGNATURE */
7553 #ifdef MANAGE_IF_IOCTL
7555 inet_dgram_socket(void)
7559 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
7561 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
7566 #endif /* MANAGE_IF_IOCTL */
7568 #ifdef MANAGE_IF_ORDER
7570 interface_order_changed(nwi_state_t old_state
, nwi_state_t new_state
)
7572 if (old_state
== NULL
&& new_state
== NULL
) {
7573 // Both are NULL, nothing changed
7577 if (old_state
== NULL
|| new_state
== NULL
) {
7578 // One is NULL, something changed
7582 if (old_state
->if_list_count
!= new_state
->if_list_count
) {
7583 // Count is different, something changed
7587 if (new_state
->if_list_count
== 0) {
7588 // Count is same and 0, nothing changed
7593 nwi_ifindex_t
*old_scan
;
7594 nwi_ifindex_t
*new_scan
;
7595 for (i
= 0, old_scan
= nwi_state_if_list(old_state
), new_scan
= nwi_state_if_list(new_state
);
7596 i
< new_state
->if_list_count
; i
++, old_scan
++, new_scan
++) {
7597 if (strcmp(old_state
->ifstate_list
[*old_scan
].ifname
, new_state
->ifstate_list
[*new_scan
].ifname
) != 0) {
7598 // Some interface in the list is different, something changed
7603 // Count and contents are the same, nothing changed
7608 update_interface_order(nwi_state_t state
, int sockfd
)
7610 Boolean success
= FALSE
;
7612 // Set interface order into the kernel
7613 struct if_order interface_order
;
7614 interface_order
.ifo_count
= (uint32_t)state
->if_list_count
;
7615 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)calloc((size_t)interface_order
.ifo_count
, sizeof(uint32_t));
7616 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7618 nwi_ifindex_t
*scan
;
7619 for (i
= 0, scan
= nwi_state_if_list(state
);
7620 i
< state
->if_list_count
; i
++, scan
++) {
7621 const char *ifname
= state
->ifstate_list
[*scan
].ifname
;
7622 ((uint32_t *)interface_order
.ifo_ordered_indices
)[i
] = my_if_nametoindex(ifname
);
7625 if (ioctl(sockfd
, SIOCSIFORDER
, &interface_order
) != 0) {
7626 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
));
7628 my_log(LOG_INFO
, "Set kernel interface order for %u interfaces", interface_order
.ifo_count
);
7631 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7632 free((void *)interface_order
.ifo_ordered_indices
);
7633 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)NULL
;
7638 #endif /* MANAGE_IF_ORDER */
7640 #ifdef MANAGE_IF_SIGNATURE
7642 siocsifnetsignature(int s
, const char * ifname
, int af
,
7643 const uint8_t * signature
, size_t signature_length
)
7645 struct if_nsreq nsreq
;
7647 bzero(&nsreq
, sizeof(nsreq
));
7648 strlcpy(nsreq
.ifnsr_name
, ifname
, sizeof(nsreq
.ifnsr_name
));
7649 nsreq
.ifnsr_family
= af
;
7650 if (signature_length
> 0) {
7651 if (signature_length
> sizeof(nsreq
.ifnsr_data
)) {
7652 signature_length
= sizeof(nsreq
.ifnsr_data
);
7654 nsreq
.ifnsr_len
= signature_length
;
7655 memcpy(nsreq
.ifnsr_data
, signature
, signature_length
);
7657 return (ioctl(s
, SIOCSIFNETSIGNATURE
, &nsreq
));
7661 process_ifstate_difference(nwi_ifstate_t ifstate
, int af
, int sockfd
)
7663 nwi_ifstate_difference_t diff
;
7664 boolean_t set_signature
= FALSE
;
7665 int signature_length
= 0;
7667 diff
= nwi_ifstate_get_difference(ifstate
);
7669 case knwi_ifstate_difference_changed
:
7670 /* set signature for this interface */
7671 set_signature
= TRUE
;
7672 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_SIGNATURE
) != 0) {
7673 signature_length
= sizeof(ifstate
->signature
);
7676 case knwi_ifstate_difference_removed
:
7677 /* remove signature for this interface */
7678 set_signature
= TRUE
;
7683 if (set_signature
) {
7684 if (siocsifnetsignature(sockfd
, ifstate
->ifname
, af
,
7686 signature_length
) < 0) {
7688 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7689 ifstate
->ifname
, ipvx_char(af
),
7694 my_log(LOG_DEBUG
, "IPv%c Network Signature %s %s",
7696 (signature_length
> 0) ? "Set" : "Cleared",
7698 if (signature_length
> 0
7699 && (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7701 char sig_buf
[signature_length
* 3 + 1];
7704 for (i
= 0; i
< signature_length
; i
++) {
7707 snprintf(byte_buf
, sizeof(byte_buf
),
7708 "%02x ", ifstate
->signature
[i
]);
7709 strlcat(sig_buf
, byte_buf
, sizeof(sig_buf
));
7711 my_log(LOG_DEBUG
, "Signature Bytes: %s", sig_buf
);
7719 process_state_differences(nwi_state_t state
, int af
, int sockfd
)
7725 if (af
== AF_INET
) {
7726 count
= state
->ipv4_count
;
7729 count
= state
->ipv6_count
;
7731 for (i
= 0, scan
= nwi_state_ifstate_list(state
, af
);
7732 i
< count
; i
++, scan
++) {
7733 process_ifstate_difference(scan
, af
, sockfd
);
7737 #endif /* MANAGE_IF_SIGNATURE */
7739 #endif /* !TARGET_OS_SIMULATOR */
7742 process_nwi_changes(CFMutableStringRef log_output
,
7743 nwi_state_t changes_state
,
7744 nwi_state_t new_state
,
7745 nwi_state_t old_state
,
7746 boolean_t dns_changed
,
7747 boolean_t dnsinfo_changed
,
7748 CFDictionaryRef old_primary_dns
,
7749 boolean_t proxy_changed
,
7750 CFDictionaryRef old_primary_proxy
,
7751 boolean_t smb_changed
,
7752 CFDictionaryRef old_primary_smb
)
7754 #ifndef MANAGE_IF_ORDER
7755 #pragma unused(new_state)
7756 #pragma unused(old_state)
7757 #endif // !MANAGE_IF_ORDER
7758 #if TARGET_OS_IPHONE
7759 #pragma unused(smb_changed)
7760 #pragma unused(old_primary_smb)
7761 #endif // TARGET_OS_IPHONE
7763 if (changes_state
!= NULL
) {
7764 const sa_family_t af_list
[] = {AF_INET
, AF_INET6
};
7766 #ifdef MANAGE_IF_IOCTL
7767 int sockfd
= inet_dgram_socket();
7768 #endif /* MANAGE_IF_IOCTL */
7770 #ifdef MANAGE_IF_ORDER
7771 if (interface_order_changed(new_state
, old_state
)) {
7772 update_interface_order(new_state
, sockfd
);
7774 #endif /* MANAGE_IF_ORDER */
7776 for (size_t idx
= 0; idx
< countof(af_list
); idx
++) {
7777 int af
= af_list
[idx
];
7778 CFMutableStringRef changes
= NULL
;
7779 CFMutableStringRef primary_str
= NULL
;
7781 #ifdef MANAGE_IF_SIGNATURE
7782 process_state_differences(changes_state
, af
, sockfd
);
7783 #endif /* MANAGE_IF_SIGNATURE */
7784 scan
= nwi_state_get_first_ifstate(changes_state
, af
);
7785 while (scan
!= NULL
) {
7786 const char * changed_str
;
7788 changed_str
= nwi_ifstate_get_diff_str(scan
);
7789 if (changed_str
!= NULL
) {
7791 const char * addr_str
;
7792 char ntopbuf
[INET6_ADDRSTRLEN
];
7794 address
= (void *)nwi_ifstate_get_address(scan
);
7795 addr_str
= inet_ntop(scan
->af
, address
, ntopbuf
,
7797 if (primary_str
== NULL
) {
7798 primary_str
= CFStringCreateMutable(NULL
, 0);
7799 CFStringAppendFormat(primary_str
, NULL
,
7801 nwi_ifstate_get_ifname(scan
),
7802 changed_str
, addr_str
);
7804 if (changes
== NULL
) {
7805 changes
= CFStringCreateMutable(NULL
, 0);
7807 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
7808 nwi_ifstate_get_ifname(scan
));
7809 if (strcmp(changed_str
, "") != 0) {
7810 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
7811 changed_str
, addr_str
);
7815 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
7818 if (primary_str
!= NULL
) {
7819 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
7820 af
== AF_INET
? "v4" : "v6",
7823 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
7824 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
7827 CFStringAppend(log_output
, CFSTR(")"));
7829 my_CFRelease(&primary_str
);
7830 my_CFRelease(&changes
);
7833 #ifdef MANAGE_IF_IOCTL
7837 #endif /* MANAGE_IF_IOCTL */
7840 if (dns_changed
|| dnsinfo_changed
) {
7843 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
7844 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
7845 str
= "*"; // dnsinfo change w/no change to primary
7847 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
7848 } else if (S_primary_dns
!= NULL
) {
7849 CFStringAppend(log_output
, CFSTR(" DNS"));
7852 if (proxy_changed
) {
7855 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
7856 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
7857 } else if (S_primary_proxies
!= NULL
) {
7858 CFStringAppend(log_output
, CFSTR(" Proxy"));
7861 #if !TARGET_OS_IPHONE
7865 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
7866 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
7867 } else if (S_primary_smb
!= NULL
) {
7868 CFStringAppend(log_output
, CFSTR(" SMB"));
7870 #endif // !TARGET_OS_IPHONE
7876 #pragma mark Network changed notification
7878 static dispatch_queue_t
7879 __network_change_queue()
7881 static dispatch_once_t once
;
7882 static dispatch_queue_t q
;
7884 dispatch_once(&once
, ^{
7885 q
= dispatch_queue_create("network change queue", NULL
);
7891 // Note: must run on __network_change_queue()
7893 post_network_change_when_ready()
7897 dispatch_assert_queue(__network_change_queue());
7899 if (S_network_change_needed
== 0) {
7903 if (!S_network_change_timeout
&&
7904 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
7905 // if we [still] need to wait for the DNS configuration
7906 // or network information changes to be ack'd
7908 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
7909 S_dnsinfo_synced
? "DNS" : "!DNS",
7910 S_nwi_synced
? "nwi" : "!nwi");
7914 // cancel any running timer
7915 if (S_network_change_timer
!= NULL
) {
7916 dispatch_source_cancel(S_network_change_timer
);
7917 dispatch_release(S_network_change_timer
);
7918 S_network_change_timer
= NULL
;
7919 S_network_change_timeout
= FALSE
;
7922 // set (and log?) the post time
7924 struct timeval elapsed
;
7927 (void) gettimeofday(&end
, NULL
);
7928 timersub(&end
, &S_network_change_start
, &elapsed
);
7930 #define QUERY_TIME__FMT "%ld.%6.6d"
7931 #define QUERY_TIME__DIV 1
7934 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
7935 S_network_change_timeout
? "timeout" : "delayed",
7937 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
7938 S_network_change_needed
);
7942 /* We are about to post a network change to everyone, get the agents up to date */
7943 #if !TARGET_OS_SIMULATOR
7944 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
7945 /* Setup or Update config agents */
7946 process_AgentMonitor_DNS();
7948 #endif //!TARGET_OS_SIMULATOR
7950 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
7951 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
7952 if (status
!= NOTIFY_STATUS_OK
) {
7954 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%d", status
);
7958 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
7959 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
7960 if (status
!= NOTIFY_STATUS_OK
) {
7962 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%d", status
);
7966 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
7967 #if !TARGET_OS_SIMULATOR
7968 /* Setup or Update config agents */
7969 process_AgentMonitor_Proxy();
7970 #endif //!TARGET_OS_SIMULATOR
7971 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
7972 if (status
!= NOTIFY_STATUS_OK
) {
7974 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%d", status
);
7978 if ((S_network_change_needed
& NETWORK_CHANGE_NAT64
) != 0) {
7979 #if !TARGET_OS_SIMULATOR
7980 // process any NAT64 prefix update requests (and refresh existing prefixes on change)
7981 if ((S_nat64_prefix_requests
!= NULL
) || (S_nat64_prefix_changes
!= NULL
)) {
7982 nat64_configuration_update(S_nat64_prefix_requests
, S_nat64_prefix_changes
);
7983 my_CFRelease(&S_nat64_prefix_requests
);
7984 my_CFRelease(&S_nat64_prefix_changes
);
7986 #endif /* !TARGET_OS_SIMULATOR */
7988 S_network_change_needed
&= ~(NETWORK_CHANGE_NAT64
);
7991 if (S_network_change_needed
!= 0) {
7992 // if more than just a NAT64 prefix change
7993 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
7994 if (status
!= NOTIFY_STATUS_OK
) {
7996 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%d", status
);
8000 S_network_change_needed
= 0;
8004 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
8006 // Note: must run on __network_change_queue()
8008 post_network_change(uint32_t change
)
8010 dispatch_assert_queue(__network_change_queue());
8012 if (S_network_change_needed
== 0) {
8013 // set the start time
8014 (void) gettimeofday(&S_network_change_start
, NULL
);
8017 // indicate that we need to post a change for ...
8018 S_network_change_needed
|= change
;
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 // if needed, start new timer
8029 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
8030 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
8033 __network_change_queue());
8034 dispatch_source_set_event_handler(S_network_change_timer
, ^{
8035 os_activity_t activity
;
8037 activity
= os_activity_create("posting delayed network change",
8038 OS_ACTIVITY_CURRENT
,
8039 OS_ACTIVITY_FLAG_DEFAULT
);
8040 os_activity_scope(activity
);
8042 S_network_change_timeout
= TRUE
;
8043 post_network_change_when_ready();
8045 os_release(activity
);
8047 dispatch_source_set_timer(S_network_change_timer
,
8048 dispatch_time(DISPATCH_TIME_NOW
,
8049 TRAILING_EDGE_TIMEOUT_NSEC
), // start
8050 DISPATCH_TIME_FOREVER
, // interval
8051 10 * NSEC_PER_MSEC
); // leeway
8052 dispatch_resume(S_network_change_timer
);
8055 post_network_change_when_ready();
8061 #pragma mark Process network (SCDynamicStore) changes
8064 IPMonitorProcessChanges(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8065 CFArrayRef if_rank_changes
)
8068 uint32_t changes
= 0;
8069 nwi_state_t changes_state
= NULL
;
8070 boolean_t dns_changed
= FALSE
;
8071 boolean_t dnsinfo_changed
= FALSE
;
8072 boolean_t global_ipv4_changed
= FALSE
;
8073 boolean_t global_ipv6_changed
= FALSE
;
8076 boolean_t nat64_changed
= FALSE
;
8077 CFMutableStringRef network_change_msg
= NULL
;
8079 nwi_state_t old_nwi_state
= NULL
;
8080 CFDictionaryRef old_primary_dns
= NULL
;
8081 CFDictionaryRef old_primary_proxy
= NULL
;
8082 #if !TARGET_OS_IPHONE
8083 CFDictionaryRef old_primary_smb
= NULL
;
8084 #endif // !TARGET_OS_IPHONE
8085 boolean_t proxies_changed
= FALSE
;
8086 boolean_t reachability_changed
= FALSE
;
8087 CFArrayRef service_order
;
8088 CFMutableArrayRef service_changes
= NULL
;
8089 CFDictionaryRef services_info
= NULL
;
8090 #if !TARGET_OS_IPHONE
8091 boolean_t smb_changed
= FALSE
;
8092 #endif // !TARGET_OS_IPHONE
8094 /* populate name/index cache */
8097 if (changed_keys
!= NULL
) {
8098 count
= CFArrayGetCount(changed_keys
);
8099 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8101 "changed keys %@ (%ld)", changed_keys
, count
);
8104 if (if_rank_changes
== NULL
&& count
== 0) {
8108 if (S_primary_dns
!= NULL
) {
8109 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
8110 if (old_primary_dns
!= NULL
) {
8111 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
8115 if (S_primary_proxies
!= NULL
) {
8117 = service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
8118 if (old_primary_proxy
!= NULL
) {
8119 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
8123 #if !TARGET_OS_IPHONE
8124 if (S_primary_smb
!= NULL
) {
8125 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
8126 if (old_primary_smb
!= NULL
) {
8127 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
8130 #endif // !TARGET_OS_IPHONE
8132 keyChangeListInit(&keys
);
8133 service_changes
= CFArrayCreateMutable(NULL
, 0,
8134 &kCFTypeArrayCallBacks
);
8136 for (CFIndex i
= 0; i
< count
; i
++) {
8138 #if !TARGET_OS_SIMULATOR
8139 CFStringRef interface
= NULL
;
8140 #endif /* !TARGET_OS_SIMULATOR */
8142 change
= CFArrayGetValueAtIndex(changed_keys
, i
);
8143 if (CFEqual(change
, S_setup_global_ipv4
)) {
8144 global_ipv4_changed
= TRUE
;
8145 global_ipv6_changed
= TRUE
;
8147 else if (CFEqual(change
, S_multicast_resolvers
)) {
8148 dnsinfo_changed
= TRUE
;
8150 else if (CFEqual(change
, S_private_resolvers
)) {
8151 dnsinfo_changed
= TRUE
;
8153 #if !TARGET_OS_IPHONE
8154 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
8155 dnsinfo_changed
= TRUE
;
8157 #endif /* !TARGET_OS_IPHONE */
8158 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
8159 CFStringRef serviceID
;
8161 serviceID
= parse_component(change
, S_state_service_prefix
);
8163 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8164 CFRelease(serviceID
);
8167 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
8168 CFStringRef serviceID
= parse_component(change
,
8169 S_setup_service_prefix
);
8171 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8172 CFRelease(serviceID
);
8175 for (size_t j
= 0; j
< countof(transientInterfaceEntityNames
); j
++) {
8176 if (CFStringHasSuffix(change
,
8177 *transientInterfaceEntityNames
[j
])) {
8178 reachability_changed
= TRUE
;
8183 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
8184 reachability_changed
= TRUE
;
8187 #if !TARGET_OS_SIMULATOR
8188 else if (is_nat64_prefix_request(change
, &interface
)) {
8189 my_CFSetAddValue_async(__network_change_queue(), &S_nat64_prefix_requests
, interface
);
8190 nat64_changed
= TRUE
;
8192 #endif /* !TARGET_OS_SIMULATOR */
8195 /* determine which serviceIDs are impacted by the interface rank changes */
8196 if (if_rank_changes
!= NULL
) {
8197 n
= CFArrayGetCount(if_rank_changes
);
8198 for (CFIndex i
= 0; i
< n
; i
++) {
8199 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
8201 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8202 my_log(LOG_DEBUG
, "Interface rank changed %@", ifname
);
8204 append_serviceIDs_for_interface(service_changes
, ifname
);
8208 /* grab a snapshot of everything we need */
8209 services_info
= services_info_copy(session
, service_changes
);
8210 assert(services_info
!= NULL
);
8212 /* grab the service order */
8213 service_order
= service_order_get(services_info
);
8214 if (service_order
!= NULL
) {
8215 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8216 my_log(LOG_DEBUG
, "service_order %@ ", service_order
);
8220 /* process protocol (v4, v6, rank, ...) and configuration (dns, proxies, smb, ...) changes */
8221 n
= CFArrayGetCount(service_changes
);
8222 for (CFIndex i
= 0; i
< n
; i
++) {
8224 CFStringRef serviceID
;
8226 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
8227 changes
= service_changed(services_info
, serviceID
);
8228 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
8229 /* if __Service__ (e.g. PrimaryRank) changed */
8230 global_ipv4_changed
= TRUE
;
8231 global_ipv6_changed
= TRUE
;
8234 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
8235 global_ipv4_changed
= TRUE
;
8236 dnsinfo_changed
= TRUE
;
8237 proxies_changed
= TRUE
;
8239 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
8240 global_ipv6_changed
= TRUE
;
8241 dnsinfo_changed
= TRUE
;
8242 proxies_changed
= TRUE
;
8243 nat64_changed
= TRUE
;
8246 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
8247 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
8250 dnsinfo_changed
= TRUE
;
8251 nat64_changed
= TRUE
;
8253 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
8254 proxies_changed
= TRUE
;
8256 #if !TARGET_OS_IPHONE
8257 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
8258 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
8263 if ((changes
& (1 << kEntityTypeTransientStatus
)) != 0
8264 && (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
8265 || service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
)) {
8266 dnsinfo_changed
= TRUE
;
8270 /* ensure S_nwi_state can hold as many services as we have currently */
8271 n_services
= (int)CFDictionaryGetCount(S_service_state_dict
);
8272 old_nwi_state
= nwi_state_make_copy(S_nwi_state
);
8273 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
8275 if (global_ipv4_changed
) {
8276 if (S_ipv4_results
!= NULL
) {
8277 ElectionResultsRelease(S_ipv4_results
);
8280 = ElectionResultsCopy(AF_INET
, service_order
);
8281 ElectionResultsLog(LOG_INFO
, S_ipv4_results
, "IPv4");
8283 if (global_ipv6_changed
) {
8284 if (S_ipv6_results
!= NULL
) {
8285 ElectionResultsRelease(S_ipv6_results
);
8288 = ElectionResultsCopy(AF_INET6
, service_order
);
8289 ElectionResultsLog(LOG_INFO
, S_ipv6_results
, "IPv6");
8291 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
8292 CFStringRef new_primary
;
8293 CFStringRef new_primary_dns
= NULL
;
8294 CFStringRef new_primary_proxies
= NULL
;
8295 #if !TARGET_OS_IPHONE
8296 CFStringRef new_primary_smb
= NULL
;
8297 #endif /* !TARGET_OS_IPHONE */
8298 RouteListUnion new_routelist
;
8299 CandidateRef other_candidate
;
8300 CandidateRef primary_candidate
;
8302 if (S_nwi_state
!= NULL
) {
8303 nwi_state_clear(S_nwi_state
, AF_INET
);
8304 nwi_state_clear(S_nwi_state
, AF_INET6
);
8308 my_log(LOG_DEBUG
, "electing IPv4 primary");
8309 new_routelist
.ptr
= NULL
;
8310 other_candidate
= (S_ipv6_results
!= NULL
) /* get IPv6 primary */
8311 ? S_ipv6_results
->candidates
: NULL
;
8312 primary_candidate
= ElectionResultsGetPrimary(S_ipv4_results
,
8314 S_nwi_state
, AF_INET
,
8315 &new_routelist
.common
,
8317 new_primary
= (primary_candidate
!= NULL
)
8318 ? primary_candidate
->serviceID
: NULL
;
8319 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
8320 update_ipv4(S_primary_ipv4
, new_routelist
.v4
, &keys
);
8323 my_log(LOG_DEBUG
, "electing IPv6 primary");
8324 new_routelist
.ptr
= NULL
;
8325 other_candidate
= primary_candidate
; /* get IPv4 primary */
8326 primary_candidate
= ElectionResultsGetPrimary(S_ipv6_results
,
8328 S_nwi_state
, AF_INET6
,
8329 &new_routelist
.common
,
8331 new_primary
= (primary_candidate
!= NULL
)
8332 ? primary_candidate
->serviceID
: NULL
;
8333 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
8334 update_ipv6(S_primary_ipv6
, new_routelist
.v6
, &keys
);
8336 nwi_state_finalize(S_nwi_state
);
8338 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
8339 /* decide between IPv4 and IPv6 */
8340 if (rank_service_entity(S_ipv4_service_rank_dict
,
8341 S_primary_ipv4
, kSCEntNetDNS
)
8342 <= rank_service_entity(S_ipv6_service_rank_dict
,
8343 S_primary_ipv6
, kSCEntNetDNS
)) {
8344 new_primary_dns
= S_primary_ipv4
;
8347 new_primary_dns
= S_primary_ipv6
;
8349 if (rank_service_entity(S_ipv4_service_rank_dict
,
8350 S_primary_ipv4
, kSCEntNetProxies
)
8351 <= rank_service_entity(S_ipv6_service_rank_dict
,
8352 S_primary_ipv6
, kSCEntNetProxies
)) {
8353 new_primary_proxies
= S_primary_ipv4
;
8356 new_primary_proxies
= S_primary_ipv6
;
8358 #if !TARGET_OS_IPHONE
8359 if (rank_service_entity(S_ipv4_service_rank_dict
,
8360 S_primary_ipv4
, kSCEntNetSMB
)
8361 <= rank_service_entity(S_ipv6_service_rank_dict
,
8362 S_primary_ipv6
, kSCEntNetSMB
)) {
8363 new_primary_smb
= S_primary_ipv4
;
8366 new_primary_smb
= S_primary_ipv6
;
8368 #endif /* !TARGET_OS_IPHONE */
8371 else if (S_primary_ipv6
!= NULL
) {
8372 new_primary_dns
= S_primary_ipv6
;
8373 new_primary_proxies
= S_primary_ipv6
;
8374 #if !TARGET_OS_IPHONE
8375 new_primary_smb
= S_primary_ipv6
;
8376 #endif /* !TARGET_OS_IPHONE */
8378 else if (S_primary_ipv4
!= NULL
) {
8379 new_primary_dns
= S_primary_ipv4
;
8380 new_primary_proxies
= S_primary_ipv4
;
8381 #if !TARGET_OS_IPHONE
8382 new_primary_smb
= S_primary_ipv4
;
8383 #endif /* !TARGET_OS_IPHONE */
8386 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
8388 dnsinfo_changed
= TRUE
;
8390 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
,
8392 proxies_changed
= TRUE
;
8394 #if !TARGET_OS_IPHONE
8395 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
8398 #endif /* !TARGET_OS_IPHONE */
8401 if (!proxies_changed
&& dnsinfo_changed
8402 && ((G_supplemental_proxies_follow_dns
!= NULL
)
8403 && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
8404 proxies_changed
= TRUE
;
8407 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
8409 if (global_ipv4_changed
|| global_ipv6_changed
8410 || dnsinfo_changed
|| reachability_changed
) {
8411 if (S_nwi_state
!= NULL
) {
8412 S_nwi_state
->generation_count
= mach_absolute_time();
8413 if (global_ipv4_changed
|| global_ipv6_changed
8414 || reachability_changed
) {
8415 SCNetworkReachabilityFlags reach_flags_v4
= 0;
8416 SCNetworkReachabilityFlags reach_flags_v6
= 0;
8418 GetReachabilityFlagsFromTransientServices(services_info
,
8422 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
,
8426 /* Update the per-interface generation count */
8427 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
,
8431 if (update_nwi(S_nwi_state
)) {
8432 changes
|= NETWORK_CHANGE_NET
;
8435 * the DNS configuration includes per-resolver configuration
8436 * reachability flags that are based on the nwi state. Let's
8437 * make sure that we check for changes
8439 dnsinfo_changed
= TRUE
;
8443 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
8444 changes
|= NETWORK_CHANGE_DNS
;
8445 dnsinfo_changed
= TRUE
;
8447 dns_changed
= FALSE
;
8450 if (dnsinfo_changed
) {
8451 if (update_dnsinfo(services_info
, S_primary_dns
,
8452 &keys
, service_order
)) {
8453 changes
|= NETWORK_CHANGE_DNS
;
8455 dnsinfo_changed
= FALSE
;
8458 if (proxies_changed
) {
8459 // if proxy change OR supplemental Proxies follow supplemental DNS
8460 if (update_proxies(services_info
, S_primary_proxies
,
8461 &keys
, service_order
)) {
8462 changes
|= NETWORK_CHANGE_PROXY
;
8464 proxies_changed
= FALSE
;
8467 #if !TARGET_OS_IPHONE
8469 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
8470 changes
|= NETWORK_CHANGE_SMB
;
8472 smb_changed
= FALSE
;
8475 #endif /* !TARGET_OS_IPHONE */
8476 if (nat64_changed
) {
8477 changes
|= NETWORK_CHANGE_NAT64
;
8479 my_CFRelease(&service_changes
);
8480 my_CFRelease(&services_info
);
8483 network_change_msg
= CFStringCreateMutable(NULL
, 0);
8484 process_nwi_changes(network_change_msg
,
8493 #if !TARGET_OS_IPHONE
8496 #else // !TARGET_OS_IPHONE
8497 FALSE
, // smb_changed
8498 NULL
// old_primary_smb
8499 #endif // !TARGET_OS_IPHONE
8503 keyChangeListApplyToStore(&keys
, session
);
8504 my_CFRelease(&old_primary_dns
);
8505 my_CFRelease(&old_primary_proxy
);
8506 #if !TARGET_OS_IPHONE
8507 my_CFRelease(&old_primary_smb
);
8508 #endif // !TARGET_OS_IPHONE
8511 dispatch_async(__network_change_queue(), ^{
8512 post_network_change(changes
);
8516 if ((network_change_msg
!= NULL
)
8517 && (CFStringGetLength(network_change_msg
) != 0)) {
8518 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
8519 } else if (keyChangeListActive(&keys
)) {
8520 my_log(LOG_NOTICE
, "network changed");
8521 } else if (nat64_changed
) {
8522 my_log(LOG_NOTICE
, "nat64 update");
8524 my_log(LOG_INFO
, "network event w/no changes");
8527 my_CFRelease(&network_change_msg
);
8529 if (changes_state
!= NULL
) {
8530 nwi_state_free(changes_state
);
8532 if (old_nwi_state
!= NULL
) {
8533 nwi_state_free(old_nwi_state
);
8535 keyChangeListFree(&keys
);
8537 /* release the name/index cache */
8538 my_if_freenameindex();
8544 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8547 #pragma unused(info)
8548 IPMonitorProcessChanges(session
, changed_keys
, NULL
);
8552 #if !TARGET_OS_IPHONE
8553 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_mcx
8555 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_global
8561 static dispatch_queue_t proxy_cb_queue
;
8563 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
8564 _scprefs_observer_watch(PROXY_GLOBAL_OBSERVER_TYPE
,
8565 "com.apple.SystemConfiguration.plist",
8568 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
8569 #if !TARGET_OS_SIMULATOR
8570 /* Setup or Update config agents */
8571 process_AgentMonitor_Proxy();
8572 #endif //!TARGET_OS_SIMULATOR
8573 (void)notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8574 my_log(LOG_INFO
, "Notifying:\n%@",
8575 S_state_global_proxies
);
8580 #include "IPMonitorControlPrefs.h"
8583 prefs_changed(SCPreferencesRef prefs
)
8585 #pragma unused(prefs)
8586 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
8587 S_IPMonitor_debug
= kDebugFlagDefault
;
8588 S_IPMonitor_verbose
= TRUE
;
8589 my_log(LOG_DEBUG
, "Setting logging verbose mode on");
8591 my_log(LOG_DEBUG
, "Setting logging verbose mode off");
8592 S_IPMonitor_debug
= 0;
8593 S_IPMonitor_verbose
= FALSE
;
8598 #if !TARGET_OS_SIMULATOR
8609 struct rt_msghdr
* rtm
;
8610 struct sockaddr_in
*sin
;
8616 mib
[4] = NET_RT_FLAGS
;
8617 mib
[5] = RTF_STATIC
| RTF_DYNAMIC
;
8618 for (i
= 0; i
< 3; i
++) {
8619 if (sysctl(mib
, N_MIB
, NULL
, &needed
, NULL
, 0) < 0) {
8622 if ((buf
= malloc(needed
)) == NULL
) {
8625 if (sysctl(mib
, N_MIB
, buf
, &needed
, NULL
, 0) >= 0) {
8635 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
8638 /* ALIGN: assume kernel provides necessary alignment */
8639 rtm
= (struct rt_msghdr
*)(void *)next
;
8640 sin
= (struct sockaddr_in
*)(rtm
+ 1);
8642 addr
= ntohl(sin
->sin_addr
.s_addr
);
8643 if (IN_LOOPBACK(addr
)) {
8645 "flush_routes: ignoring loopback route");
8648 if (IN_LOCAL_GROUP(addr
)) {
8650 "flush_routes: ignoring multicast route");
8653 rtm
->rtm_type
= RTM_DELETE
;
8654 rtm
->rtm_seq
= ++rtm_seq
;
8655 if (write(s
, rtm
, rtm
->rtm_msglen
) < 0) {
8657 "flush_routes: removing route for "
8658 IP_FORMAT
" failed: %s",
8659 IP_LIST(&sin
->sin_addr
),
8664 "flush_routes: removed route for " IP_FORMAT
,
8665 IP_LIST(&sin
->sin_addr
));
8673 flush_inet_routes(void)
8677 s
= open_routing_socket();
8684 #else /* !TARGET_OS_SIMULATOR */
8687 flush_inet_routes(void)
8691 #endif /* !TARGET_OS_SIMULATOR */
8698 CFMutableArrayRef keys
= NULL
;
8699 CFStringRef pattern
;
8700 CFMutableArrayRef patterns
= NULL
;
8701 CFRunLoopSourceRef rls
= NULL
;
8703 if (S_is_network_boot() != 0) {
8708 flush_inet_routes();
8711 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
8712 IPMonitorNotify
, NULL
);
8713 if (S_session
== NULL
) {
8715 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8716 SCErrorString(SCError()));
8720 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8721 kSCDynamicStoreDomainState
,
8724 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8725 kSCDynamicStoreDomainState
,
8728 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8729 kSCDynamicStoreDomainState
,
8731 S_state_global_proxies
8732 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8733 kSCDynamicStoreDomainState
,
8735 #if !TARGET_OS_IPHONE
8737 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8738 kSCDynamicStoreDomainState
,
8740 #endif /* !TARGET_OS_IPHONE */
8742 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8743 kSCDynamicStoreDomainSetup
,
8745 S_state_service_prefix
8746 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8747 kSCDynamicStoreDomainState
,
8750 S_setup_service_prefix
8751 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8752 kSCDynamicStoreDomainSetup
,
8755 S_service_state_dict
8756 = CFDictionaryCreateMutable(NULL
, 0,
8757 &kCFTypeDictionaryKeyCallBacks
,
8758 &kCFTypeDictionaryValueCallBacks
);
8760 S_ipv4_service_rank_dict
8761 = CFDictionaryCreateMutable(NULL
, 0,
8762 &kCFTypeDictionaryKeyCallBacks
,
8763 &kCFTypeDictionaryValueCallBacks
);
8765 S_ipv6_service_rank_dict
8766 = CFDictionaryCreateMutable(NULL
, 0,
8767 &kCFTypeDictionaryKeyCallBacks
,
8768 &kCFTypeDictionaryValueCallBacks
);
8770 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8771 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8773 /* register for State: and Setup: per-service notifications */
8774 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
8776 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
8777 CFArrayAppendValue(patterns
, pattern
);
8780 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
8781 CFArrayAppendValue(patterns
, pattern
);
8784 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
8785 CFArrayAppendValue(patterns
, pattern
);
8788 /* register for State: per-service PPP/VPN/IPSec status notifications */
8789 add_transient_status_keys(kSCCompAnyRegex
, patterns
);
8791 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
8792 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
8794 /* add notifier for multicast DNS configuration (Bonjour/.local) */
8795 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8796 kSCDynamicStoreDomainState
,
8798 CFSTR(kDNSServiceCompMulticastDNS
));
8799 CFArrayAppendValue(keys
, S_multicast_resolvers
);
8801 /* add notifier for private DNS configuration (Back to My Mac) */
8802 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8803 kSCDynamicStoreDomainState
,
8805 CFSTR(kDNSServiceCompPrivateDNS
));
8806 CFArrayAppendValue(keys
, S_private_resolvers
);
8808 #if !TARGET_OS_SIMULATOR
8809 /* add NAT64 prefix request pattern */
8810 nat64_prefix_request_add_pattern(patterns
);
8811 #endif /* !TARGET_OS_SIMULATOR */
8813 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
8815 "SCDynamicStoreSetNotificationKeys() failed: %s",
8816 SCErrorString(SCError()));
8820 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
8823 "SCDynamicStoreCreateRunLoopSource() failed: %s",
8824 SCErrorString(SCError()));
8828 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
8831 /* initialize dns configuration */
8832 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
8833 #if !TARGET_OS_IPHONE
8835 #endif /* !TARGET_OS_IPHONE */
8836 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
8838 #if !TARGET_OS_IPHONE
8839 /* initialize SMB configuration */
8840 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
8841 #endif /* !TARGET_OS_IPHONE */
8846 my_CFRelease(&keys
);
8847 my_CFRelease(&patterns
);
8855 /* initialize multicast route */
8856 update_ipv4(NULL
, NULL
, NULL
);
8858 #if !TARGET_OS_SIMULATOR
8859 process_AgentMonitor();
8860 #endif // !TARGET_OS_SIMULATOR
8866 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
8870 boolean_t ret
= def
;
8872 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
8874 ret
= CFBooleanGetValue(b
);
8879 #if !TARGET_OS_SIMULATOR
8880 #include "IPMonitorControlServer.h"
8883 InterfaceRankChanged(void * info
)
8885 #pragma unused(info)
8886 os_activity_t activity
;
8887 CFDictionaryRef assertions
= NULL
;
8890 activity
= os_activity_create("processing IPMonitor [rank] change",
8891 OS_ACTIVITY_CURRENT
,
8892 OS_ACTIVITY_FLAG_DEFAULT
);
8893 os_activity_scope(activity
);
8895 changes
= IPMonitorControlServerCopyInterfaceRankInformation(&assertions
);
8896 if (S_if_rank_dict
!= NULL
) {
8897 CFRelease(S_if_rank_dict
);
8899 S_if_rank_dict
= assertions
;
8900 if (changes
!= NULL
) {
8901 IPMonitorProcessChanges(S_session
, NULL
, changes
);
8905 os_release(activity
);
8911 StartIPMonitorControlServer(void)
8913 CFRunLoopSourceContext context
;
8914 CFRunLoopSourceRef rls
;
8916 bzero(&context
, sizeof(context
));
8917 context
.perform
= InterfaceRankChanged
;
8918 rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
8919 if (!IPMonitorControlServerStart(CFRunLoopGetCurrent(),
8921 &S_bundle_logging_verbose
)) {
8922 my_log(LOG_ERR
, "IPMonitorControlServerStart failed");
8925 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
8926 kCFRunLoopDefaultMode
);
8932 #endif /* !TARGET_OS_SIMULATOR */
8936 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
8938 CFDictionaryRef info_dict
;
8940 info_dict
= CFBundleGetInfoDictionary(bundle
);
8942 if (info_dict
!= NULL
) {
8944 = S_get_plist_boolean(info_dict
,
8945 CFSTR("AppendStateArrayToSetupArray"),
8948 if (bundleVerbose
) {
8949 S_IPMonitor_debug
= kDebugFlagDefault
;
8950 S_bundle_logging_verbose
= TRUE
;
8951 S_IPMonitor_verbose
= TRUE
;
8954 /* register to receive changes to the "verbose" flag and read the initial setting */
8955 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
8956 prefs_changed(NULL
);
8958 /* start DNS configuration (dnsinfo) server */
8959 load_DNSConfiguration(bundle
, // bundle
8960 ^(Boolean inSync
) { // syncHandler
8961 dispatch_async(__network_change_queue(), ^{
8962 S_dnsinfo_synced
= inSync
;
8965 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
8966 // all of the DNS service ack's should result
8967 // in a [new] network change being posted
8968 post_network_change(NETWORK_CHANGE_DNS
);
8970 post_network_change_when_ready();
8975 /* start Network Information (nwi) server */
8976 load_NetworkInformation(bundle
, // bundle
8977 ^(Boolean inSync
) { // syncHandler
8978 dispatch_async(__network_change_queue(), ^{
8979 S_nwi_synced
= inSync
;
8980 post_network_change_when_ready();
8984 #if !TARGET_OS_SIMULATOR
8985 /* start IPMonitor Control (InterfaceRank) server */
8986 StartIPMonitorControlServer();
8987 #endif /* !TARGET_OS_IPHONE */
8989 /* initialize DNS configuration */
8990 dns_configuration_init(bundle
);
8992 #if !TARGET_OS_SIMULATOR
8993 /* initialize NAT64 configuration */
8994 nat64_configuration_init(bundle
);
8995 #endif /* !TARGET_OS_SIMULATOR */
8997 /* initialize proxy configuration */
8998 proxy_configuration_init(bundle
);
9002 #if !TARGET_OS_IPHONE
9003 if (S_session
!= NULL
) {
9004 dns_configuration_monitor(S_session
, IPMonitorNotify
);
9006 #endif /* !TARGET_OS_IPHONE */
9008 #if !TARGET_OS_SIMULATOR
9009 load_hostname(TRUE
);
9010 #endif /* !TARGET_OS_SIMULATOR */
9012 #if !TARGET_OS_IPHONE
9013 load_smb_configuration(TRUE
);
9014 #endif /* !TARGET_OS_IPHONE */
9021 #pragma mark Standalone test code
9024 #ifdef TEST_IPMONITOR
9027 main(int argc
, char **argv
)
9031 S_IPMonitor_debug
= kDebugFlag1
;
9033 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9036 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
9038 S_IPMonitor_debug
= kDebugFlag1
;
9044 #endif /* TEST_IPMONITOR */
9046 #ifdef TEST_ROUTELIST
9051 const char * gateway
;
9052 const char * ifname
;
9057 #ifdef TEST_IPV4_ROUTELIST
9063 const char * router
;
9064 const char * ifname
;
9066 const CFStringRef
* primary_rank
;
9067 struct route
* additional_routes
;
9068 int additional_routes_count
;
9069 struct route
* excluded_routes
;
9070 int excluded_routes_count
;
9071 } IPv4ServiceContents
;
9073 typedef const IPv4ServiceContents
* IPv4ServiceContentsRef
;
9075 struct route loop_routelist
[] = {
9076 { "1.1.1.1", 32, "1.1.1.2", NULL
},
9077 { "1.1.1.2", 32, "1.1.1.3", NULL
},
9078 { "1.1.1.3", 32, "1.1.1.4", NULL
},
9079 { "1.1.1.4", 32, "1.1.1.5", NULL
},
9080 { "1.1.1.5", 32, "1.1.1.6", NULL
},
9081 { "1.1.1.6", 32, "1.1.1.7", NULL
},
9082 { "1.1.1.7", 32, "1.1.1.8", NULL
},
9083 { "1.1.1.8", 32, "1.1.1.9", NULL
},
9084 { "1.1.1.9", 32, "1.1.1.10", NULL
},
9085 { "1.1.1.10", 32, "1.1.1.11", NULL
},
9086 { "1.1.1.11", 32, "1.1.1.1", NULL
},
9089 struct route vpn_routelist
[] = {
9090 { "10.1.3.0", 24, "17.153.46.24", NULL
},
9091 { "10.1.4.0", 24, "17.153.46.24", NULL
},
9092 { "10.1.5.0", 24, "17.153.46.24", NULL
},
9093 { "10.1.6.0", 24, "17.153.46.24", NULL
},
9094 { "10.1.7.0", 24, "17.153.46.24", NULL
},
9095 { "10.16.0.0", 12, "17.153.46.24", NULL
},
9096 { "10.45.0.0", 16, "17.153.46.24", NULL
},
9097 { "10.53.0.0", 16, "17.153.46.24", NULL
},
9098 { "10.70.0.0", 15, "17.153.46.24", NULL
},
9099 { "10.74.0.0", 15, "17.153.46.24", NULL
},
9100 { "10.90.0.0", 15, "17.153.46.24", NULL
},
9101 { "10.91.0.0", 16, "17.153.46.24", NULL
},
9102 { "10.100.0.0", 16, "17.153.46.24", NULL
},
9103 { "10.113.0.0", 16, "17.153.46.24", NULL
},
9104 { "10.128.0.0", 9, "17.153.46.24", NULL
},
9105 { "17.0.0.0", 9, "17.153.46.24", NULL
},
9106 { "17.34.0.0", 16, "17.153.46.24", NULL
},
9107 { "17.112.156.53", 32, "17.153.46.24", NULL
},
9108 { "17.128.0.0", 10, "17.153.46.24", NULL
},
9109 { "17.149.0.121", 32, "17.153.46.24", NULL
},
9110 { "17.149.7.200", 32, "17.153.46.24", NULL
},
9111 { "17.153.46.24", 32, "17.153.46.24", NULL
},
9112 { "17.192.0.0", 12, "17.153.46.24", NULL
},
9113 { "17.208.0.0", 15, "17.153.46.24", NULL
},
9114 { "17.211.0.0", 16, "17.153.46.24", NULL
},
9115 { "17.212.0.0", 14, "17.153.46.24", NULL
},
9116 { "17.216.0.0", 13, "17.153.46.24", NULL
},
9117 { "17.224.0.0", 12, "17.153.46.24", NULL
},
9118 { "17.240.0.0", 16, "17.153.46.24", NULL
},
9119 { "17.241.0.0", 16, "17.153.46.24", NULL
},
9120 { "17.248.0.0", 14, "17.153.46.24", NULL
},
9121 { "17.251.104.200", 32, "17.153.46.24", NULL
},
9122 { "17.252.0.0", 16, "17.153.46.24", NULL
},
9123 { "17.253.0.0", 16, "17.153.46.24", NULL
},
9124 { "17.254.0.0", 16, "17.153.46.24", NULL
},
9125 { "17.255.0.0", 16, "17.153.46.24", NULL
},
9126 { "151.193.141.0", 27, "17.153.46.24", NULL
},
9127 { "172.16.2.0", 24, "17.153.46.24", NULL
},
9128 { "192.35.50.0", 24, "17.153.46.24", NULL
},
9129 { "204.179.20.0", 24, "17.153.46.24", NULL
},
9130 { "206.112.116.0", 24, "17.153.46.24", NULL
},
9133 struct route vpn_routelist_ext
[] = {
9134 { "17.151.63.82", 32, "10.0.0.1", "en0" },
9135 { "17.151.63.81", 32, "17.151.63.81", "en0" },
9136 { "17.151.63.80", 32, NULL
, NULL
},
9137 { "17.1.0.0", 16, NULL
, NULL
},
9138 { "17.2.0.0", 24, NULL
, NULL
},
9139 { "10.0.0.0", 24, NULL
, NULL
},
9143 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
9145 const IPv4ServiceContents en0_10
= {
9146 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9149 const IPv4ServiceContents en0_15
= {
9150 "10.0.0.19", 24, NULL
, "10.0.0.1", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9153 const IPv4ServiceContents en0_30
= {
9154 "10.0.0.11", 24, NULL
, "10.0.0.1", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9157 const IPv4ServiceContents en0_40
= {
9158 "10.0.0.12", 24, NULL
, "10.0.0.1", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9161 const IPv4ServiceContents en0_50
= {
9162 "10.0.0.13", 24, NULL
, "10.0.0.1", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9165 const IPv4ServiceContents en0_110
= {
9166 "192.168.2.10", 24, NULL
, "192.168.2.1", "en0", 110, NULL
, NULL
, 0, NULL
, 0
9169 const IPv4ServiceContents en0_1
= {
9170 "17.202.40.191", 22, NULL
, "17.202.20.1", "en0", 1, NULL
, NULL
, 0, NULL
, 0
9173 const IPv4ServiceContents en1_20
= {
9174 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9177 const IPv4ServiceContents en1_2
= {
9178 "17.202.42.24", 22, NULL
, "17.202.20.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9181 const IPv4ServiceContents en1_125
= {
9182 "192.168.2.20", 24, NULL
, "192.168.2.1", "en1", 125, NULL
, NULL
, 0, NULL
, 0
9185 const IPv4ServiceContents fw0_25
= {
9186 "192.168.2.30", 24, NULL
, "192.168.2.1", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9189 const IPv4ServiceContents fw0_21
= {
9190 "192.168.3.30", 24, NULL
, "192.168.3.1", "fw0", 21, NULL
, NULL
, 0, NULL
, 0
9193 const IPv4ServiceContents ppp0_0_1
= {
9194 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
, NULL
, 0, NULL
, 0
9197 const IPv4ServiceContents utun0
= {
9198 "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
)
9201 const IPv4ServiceContents en0_test6
= {
9202 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 2, NULL
, NULL
, 0, NULL
, 0
9205 const IPv4ServiceContents en1_test6
= {
9206 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 3, NULL
, NULL
, 0, NULL
, 0
9209 const IPv4ServiceContents en2_test6
= {
9210 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9213 const IPv4ServiceContents en0_test7
= {
9214 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 3, NULL
, NULL
, 0, NULL
, 0
9217 const IPv4ServiceContents en1_test7
= {
9218 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9221 const IPv4ServiceContents en2_test7
= {
9222 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9225 const IPv4ServiceContents fw0_test6_and_7
= {
9226 "169.254.11.33", 16, NULL
, NULL
, "fw0", 0x0ffffff, NULL
, NULL
, 0, NULL
, 0
9229 const IPv4ServiceContents en0_10_last
= {
9230 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
, NULL
, 0, NULL
, 0
9233 const IPv4ServiceContents en0_10_never
= {
9234 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9237 const IPv4ServiceContents en1_20_first
= {
9238 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
, NULL
, 0, NULL
, 0
9241 const IPv4ServiceContents en1_20_never
= {
9242 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9245 const IPv4ServiceContents en1_20_other_never
= {
9246 "192.168.2.50", 24, NULL
, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9249 const IPv4ServiceContents en0_linklocal
= {
9250 "169.254.22.44", 16, NULL
, NULL
, "en0", 0xfffff, NULL
, NULL
, 0, NULL
, 0
9253 const IPv4ServiceContents en0_route_loop
= {
9254 "192.168.130.16", 24, NULL
, "192.168.130.1", "en0", 2, NULL
, loop_routelist
, countof(loop_routelist
), NULL
, 0
9259 IPv4ServiceContentsRef test
[];
9260 } IPv4RouteTest
, * IPv4RouteTestRef
;
9262 static IPv4RouteTest test1
= {
9276 static IPv4RouteTest test2
= {
9289 static IPv4RouteTest test3
= {
9306 static IPv4RouteTest test4
= {
9318 static IPv4RouteTest test5
= {
9331 static IPv4RouteTest test6
= {
9342 static IPv4RouteTest test7
= {
9353 static IPv4RouteTest test8
= {
9362 static IPv4RouteTest test9
= {
9372 static IPv4RouteTest test10
= {
9382 static IPv4RouteTest test11
= {
9392 static IPv4RouteTest test12
= {
9401 static IPv4RouteTest test13
= {
9410 static IPv4RouteTest test14
= {
9418 static IPv4RouteTest test15
= {
9426 static IPv4RouteTest test16
= {
9435 static IPv4RouteTest test17
= {
9439 &en1_20_other_never
,
9444 static IPv4RouteTest test18
= {
9452 static IPv4RouteTestRef ipv4_tests
[] = {
9475 ipv4_prefix_length_is_valid(int prefix_length
)
9477 if (prefix_length
< 0 || prefix_length
> IPV4_ROUTE_ALL_BITS_SET
) {
9484 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9487 CFStringRef prop_val
;
9492 prop_val
= CFStringCreateWithCString(NULL
,
9494 kCFStringEncodingASCII
);
9495 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9496 CFRelease(prop_val
);
9501 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9505 CFStringRef prop_val
;
9510 prop_val
= CFStringCreateWithCString(NULL
,
9512 kCFStringEncodingASCII
);
9513 array
= CFArrayCreate(NULL
,
9514 (const void **)&prop_val
, 1,
9515 &kCFTypeArrayCallBacks
);
9516 CFRelease(prop_val
);
9517 CFDictionarySetValue(dict
, prop_name
, array
);
9523 dict_add_ip(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9528 str
= my_CFStringCreateWithInAddr(ip
);
9529 CFDictionarySetValue(dict
, prop_name
, str
);
9535 dict_add_ip_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9541 str
= my_CFStringCreateWithInAddr(ip
);
9542 array
= CFArrayCreate(NULL
,
9543 (const void **)&str
, 1,
9544 &kCFTypeArrayCallBacks
);
9546 CFDictionarySetValue(dict
, prop_name
, array
);
9552 dict_insert_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9553 struct route
* routes
, int routes_count
)
9556 CFMutableArrayRef route_list
;
9557 struct route
* scan
;
9559 if (routes
== NULL
|| routes_count
== 0) {
9562 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9563 &kCFTypeArrayCallBacks
);
9564 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9565 struct in_addr mask
;
9566 CFMutableDictionaryRef route_dict
;
9569 = CFDictionaryCreateMutable(NULL
, 0,
9570 &kCFTypeDictionaryKeyCallBacks
,
9571 &kCFTypeDictionaryValueCallBacks
);
9572 dict_add_string(route_dict
, kSCPropNetIPv4RouteDestinationAddress
,
9574 if (ipv4_prefix_length_is_valid(scan
->prefix_length
)) {
9575 mask
.s_addr
= htonl(prefix_to_mask32(scan
->prefix_length
));
9576 dict_add_ip(route_dict
, kSCPropNetIPv4RouteSubnetMask
, mask
);
9578 dict_add_string(route_dict
, kSCPropNetIPv4RouteGatewayAddress
,
9580 dict_add_string(route_dict
, kSCPropNetIPv4RouteInterfaceName
,
9582 CFArrayAppendValue(route_list
, route_dict
);
9583 CFRelease(route_dict
);
9585 CFDictionarySetValue(dict
, prop_name
, route_list
);
9586 CFRelease(route_list
);
9590 static CFDictionaryRef
9591 make_IPv4_dict(IPv4ServiceContentsRef t
)
9593 CFMutableDictionaryRef dict
;
9595 dict
= CFDictionaryCreateMutable(NULL
, 0,
9596 &kCFTypeDictionaryKeyCallBacks
,
9597 &kCFTypeDictionaryValueCallBacks
);
9598 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
9599 if (ipv4_prefix_length_is_valid(t
->prefix_length
)) {
9600 struct in_addr mask
;
9602 mask
.s_addr
= htonl(prefix_to_mask32(t
->prefix_length
));
9603 dict_add_ip_as_array(dict
, kSCPropNetIPv4SubnetMasks
, mask
);
9605 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
9606 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
9607 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9608 dict_add_string(dict
, kSCPropConfirmedInterfaceName
, t
->ifname
);
9609 dict_insert_routes(dict
, kSCPropNetIPv4AdditionalRoutes
,
9610 t
->additional_routes
, t
->additional_routes_count
);
9611 dict_insert_routes(dict
, kSCPropNetIPv4ExcludedRoutes
,
9612 t
->excluded_routes
, t
->excluded_routes_count
);
9617 kDirectionForwards
= 0,
9618 kDirectionBackwards
= 1
9622 kLogRouteDisabled
= 0,
9623 kLogRouteEnabled
= 1
9626 static IPv4RouteListRef
9627 make_IPv4RouteList_for_test(IPv4RouteListRef list
,
9628 IPv4ServiceContentsRef test
,
9631 CFDictionaryRef dict
;
9634 Rank rank_assertion
= kRankAssertionDefault
;
9635 CFNumberRef rank_assertion_cf
= NULL
;
9636 Boolean rank_assertion_is_set
= FALSE
;
9637 IPv4RouteListRef ret
= NULL
;
9638 IPV4_ROUTES_BUF_DECL(routes
);
9640 dict
= make_IPv4_dict(test
);
9642 fprintf(stderr
, "make_IPv4_dict failed\n");
9645 if (test
->primary_rank
!= NULL
) {
9647 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9648 &rank_assertion_is_set
);
9649 if (rank_assertion_is_set
) {
9651 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9654 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
9656 my_CFRelease(&rank_assertion_cf
);
9658 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
9662 if (rank_assertion
== kRankAssertionScoped
) {
9663 rank_assertion
= kRankAssertionNever
;
9665 rank
= RankMake(test
->rank
, rank_assertion
);
9666 if (log_it
== kLogRouteEnabled
9667 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9670 descr
= IPv4RouteListCopyDescription(r
);
9671 SCPrint(TRUE
, stdout
, CFSTR("Adding %@"), descr
);
9674 ret
= IPv4RouteListAddRouteList(list
, 1, r
, rank
);
9682 static IPv4RouteListRef
9683 make_IPv4RouteList(IPv4ServiceContentsRef
* test
, Direction direction
,
9686 IPv4RouteListRef ret
= NULL
;
9687 IPv4ServiceContentsRef
* scan
;
9689 switch (direction
) {
9690 case kDirectionBackwards
:
9691 for (scan
= test
; *scan
!= NULL
; scan
++) {
9692 /* find the end of the list */
9694 for (scan
--; scan
>= test
; scan
--) {
9695 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9699 case kDirectionForwards
:
9700 for (scan
= test
; *scan
!= NULL
; scan
++) {
9701 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9705 IPv4RouteListFinalize(ret
);
9709 #define EMPHASIS_CHARS "================="
9712 * Function: routelist_build_test
9714 * Runs through the given set of routes first in the forward direction,
9715 * then again backwards. We should end up with exactly the same set of
9716 * routes at the end.
9719 routelist_build_test(IPv4RouteTestRef test
)
9722 boolean_t ret
= FALSE
;
9723 IPv4RouteListRef routes1
;
9724 IPv4RouteListRef routes2
;
9726 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
9727 EMPHASIS_CHARS
"\n",
9730 routes1
= make_IPv4RouteList(test
->test
, kDirectionForwards
,
9732 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9733 if (routes1
!= NULL
) {
9734 descr
= IPv4RouteListCopyDescription(routes1
);
9735 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9739 routes2
= make_IPv4RouteList(test
->test
, kDirectionBackwards
,
9741 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9742 if (routes2
!= NULL
) {
9743 descr
= IPv4RouteListCopyDescription(routes2
);
9744 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9748 if ((routes1
!= NULL
&& routes2
== NULL
)
9749 || (routes1
== NULL
&& routes2
!= NULL
)) {
9750 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
9751 (routes1
!= NULL
) ? "not " : "",
9752 (routes2
!= NULL
) ? "not " : "");
9754 else if (routes1
!= NULL
&& routes2
!= NULL
) {
9755 /* check if they are different */
9756 if (routes1
->count
!= routes2
->count
) {
9757 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
9758 routes1
->count
, routes2
->count
);
9760 else if (bcmp(routes1
, routes2
,
9761 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
9762 fprintf(stderr
, "routes1 and routes2 are different\n");
9765 printf("routes1 and routes2 are the same\n");
9769 if (routes1
!= NULL
) {
9772 if (routes2
!= NULL
) {
9775 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
9776 EMPHASIS_CHARS
"\n",
9777 test
->name
, ret
? "PASSED" : "FAILED");
9782 apply_test(IPv4RouteTestRef old_test
, IPv4RouteTestRef new_test
)
9784 IPv4RouteListRef new_routes
;
9785 IPv4RouteListRef old_routes
;
9787 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
9788 EMPHASIS_CHARS
"\n",
9789 old_test
->name
, new_test
->name
);
9791 old_routes
= make_IPv4RouteList(old_test
->test
, kDirectionForwards
,
9793 new_routes
= make_IPv4RouteList(new_test
->test
, kDirectionForwards
,
9795 if (old_routes
== NULL
) {
9796 printf("No Old Routes\n");
9799 printf("Old routes ('%s') = ", old_test
->name
);
9800 IPv4RouteListPrint(old_routes
);
9803 /* apply the old routes */
9804 IPv4RouteListApply(NULL
, old_routes
, -1);
9806 if (new_routes
== NULL
) {
9807 printf("No New Routes\n");
9810 printf("New Routes ('%s') = ", new_test
->name
);
9811 IPv4RouteListPrint(new_routes
);
9814 /* apply the new routes */
9815 IPv4RouteListApply(old_routes
, new_routes
, -1);
9817 if (old_routes
!= NULL
) {
9820 if (new_routes
!= NULL
) {
9823 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
9824 EMPHASIS_CHARS
"\n",
9825 old_test
->name
, new_test
->name
);
9830 main(int argc
, char **argv
)
9832 IPv4RouteTestRef
* test
;
9835 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
9836 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
9838 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9840 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9841 if (!routelist_build_test(*test
)) {
9842 fprintf(stderr
, "%s failed\n", (*test
)->name
);
9846 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9847 IPv4RouteTestRef
* test2
;
9849 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
9850 apply_test(*test
, *test2
);
9851 apply_test(*test2
, *test
);
9858 printf("\nChecking for leaks\n");
9859 sprintf(cmd
, "leaks %d 2>&1", getpid());
9867 #endif /* TEST_IPV4_ROUTELIST */
9869 #ifdef TEST_IPV6_ROUTELIST
9877 typedef const IPv6Address
* IPv6AddressRef
;
9880 IPv6AddressRef addr
;
9882 const char * router
;
9883 const char * ifname
;
9885 const CFStringRef
* primary_rank
;
9886 struct route
* additional_routes
;
9887 int additional_routes_count
;
9888 struct route
* excluded_routes
;
9889 int excluded_routes_count
;
9890 } IPv6ServiceContents
;
9892 typedef const IPv6ServiceContents
* IPv6ServiceContentsRef
;
9894 struct route loop_routelist
[] = {
9895 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
9896 "2620:149:4:f01:225:ff:fecc:89a2", NULL
},
9897 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
9898 "2620:149:4:f01:225:ff:fecc:89a3", NULL
},
9899 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
9900 "2620:149:4:f01:225:ff:fecc:89a4", NULL
},
9901 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
9902 "2620:149:4:f01:225:ff:fecc:89a5", NULL
},
9903 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
9904 "2620:149:4:f01:225:ff:fecc:89a6", NULL
},
9905 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
9906 "2620:149:4:f01:225:ff:fecc:89a7", NULL
},
9907 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
9908 "2620:149:4:f01:225:ff:fecc:89a8", NULL
},
9909 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
9910 "2620:149:4:f01:225:ff:fecc:89a9", NULL
},
9911 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
9912 "2620:149:4:f01:225:ff:fecc:89aa", NULL
},
9913 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
9914 "2620:149:4:f01:225:ff:fecc:89ab", NULL
},
9915 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
9916 "2620:149:4:f01:225:ff:fecc:89a1", NULL
},
9919 struct route vpn_routelist
[] = {
9920 { "2010:470:1f05:3cb::", 64,
9921 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9922 { "2010:222:3fa5:acb::", 48,
9923 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9924 { "2010:222:3fa5:1234::", 40,
9925 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9926 { "2010:222:3fa5:5678::", 40,
9930 struct route vpn_routelist_ext
[] = {
9931 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL
, NULL
},
9934 struct route en1_routelist_ext
[] = {
9935 { "2020:299:abcd:ef12::", 64, NULL
, NULL
},
9939 static const IPv6Address en0_addr1
[] = {
9940 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL
},
9941 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL
}
9944 static const IPv6Address en0_addr2
[] = {
9945 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL
},
9946 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL
}
9949 static const IPv6Address en0_addr3
[] = {
9950 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL
},
9951 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL
}
9954 static const IPv6Address en0_addr4
[] = {
9955 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL
},
9956 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL
}
9959 static const IPv6Address en0_addr5
[] = {
9960 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL
},
9961 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL
}
9964 static const IPv6Address en0_addr6
[] = {
9965 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL
},
9968 static const IPv6Address en0_lladdr
[] = {
9969 { "fe80::cabc:c8ff:fe96:96af", 64, NULL
}
9972 static const IPv6Address en1_addr
[] = {
9973 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL
},
9974 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL
}
9977 static const IPv6Address utun0_addr
[] = {
9978 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL
},
9981 static const IPv6Address fw0_addr1
[] = {
9982 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL
},
9983 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL
}
9987 * address+address-count
9988 * router ifname pri rank additional-routes+count excluded-routes+count
9991 static const IPv6ServiceContents en0_10
= {
9992 en0_addr1
, countof(en0_addr1
),
9993 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9996 static const IPv6ServiceContents en0_15
= {
9997 en0_addr2
, countof(en0_addr2
),
9998 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL
, NULL
, 0, NULL
, 0
10001 static const IPv6ServiceContents en0_30
= {
10002 en0_addr3
, countof(en0_addr3
),
10003 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL
, NULL
, 0, NULL
, 0
10006 static const IPv6ServiceContents en0_40
= {
10007 en0_addr4
, countof(en0_addr4
),
10008 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL
, NULL
, 0, NULL
, 0
10011 static const IPv6ServiceContents en0_50
= {
10012 en0_addr5
, countof(en0_addr5
),
10013 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL
, NULL
, 0, NULL
, 0
10016 static const IPv6ServiceContents en0_10_a
= {
10017 en0_addr6
, countof(en0_addr6
),
10018 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
10021 static const IPv6ServiceContents fw0_25
= {
10022 fw0_addr1
, countof(fw0_addr1
),
10023 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
10026 static const IPv6ServiceContents en1_20
= {
10027 en1_addr
, countof(en1_addr
),
10028 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL
, NULL
, 0, NULL
, 0
10031 static const IPv6ServiceContents en1_10_ext
= {
10032 en1_addr
, countof(en1_addr
),
10033 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL
, NULL
, 0,
10034 en1_routelist_ext
, countof(en1_routelist_ext
)
10037 static const IPv6ServiceContents en0_0_lladdr
= {
10038 en0_lladdr
, countof(en0_lladdr
),
10039 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL
, NULL
, 0, NULL
, 0
10042 static const IPv6ServiceContents en0_loop
= {
10043 en0_addr1
, countof(en0_addr1
),
10044 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
,
10045 loop_routelist
, countof(loop_routelist
), NULL
, 0
10048 static const IPv6ServiceContents utun0
= {
10049 utun0_addr
, countof(utun0_addr
),
10050 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL
,
10051 vpn_routelist
, countof(vpn_routelist
),
10052 vpn_routelist_ext
, countof(vpn_routelist_ext
),
10057 IPv6ServiceContentsRef test
[];
10058 } IPv6RouteTest
, * IPv6RouteTestRef
;
10060 static IPv6RouteTest test1
= {
10074 static IPv6RouteTest test2
= {
10087 static IPv6RouteTest test3
= {
10096 static IPv6RouteTest test4
= {
10105 static IPv6RouteTest test5
= {
10117 static IPv6RouteTestRef ipv6_tests
[] = {
10128 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10131 CFStringRef prop_val
;
10136 prop_val
= CFStringCreateWithCString(NULL
,
10138 kCFStringEncodingASCII
);
10139 CFDictionarySetValue(dict
, prop_name
, prop_val
);
10140 CFRelease(prop_val
);
10145 dict_add_int(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10150 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10151 CFDictionarySetValue(dict
, prop_name
, num
);
10157 dict_insert_v6_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10158 struct route
* routes
, int routes_count
)
10161 CFMutableArrayRef route_list
;
10162 struct route
* scan
;
10164 if (routes
== NULL
|| routes_count
== 0) {
10167 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
10168 &kCFTypeArrayCallBacks
);
10169 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
10170 CFMutableDictionaryRef route_dict
;
10172 route_dict
= CFDictionaryCreateMutable(NULL
, 0,
10173 &kCFTypeDictionaryKeyCallBacks
,
10174 &kCFTypeDictionaryValueCallBacks
);
10175 dict_add_string(route_dict
, kSCPropNetIPv6RouteDestinationAddress
,
10177 dict_add_int(route_dict
, kSCPropNetIPv6PrefixLength
,
10178 scan
->prefix_length
);
10179 dict_add_string(route_dict
, kSCPropNetIPv6RouteGatewayAddress
,
10181 dict_add_string(route_dict
, kSCPropNetIPv6RouteInterfaceName
,
10183 CFArrayAppendValue(route_list
, route_dict
);
10184 CFRelease(route_dict
);
10186 CFDictionarySetValue(dict
, prop_name
, route_list
);
10187 CFRelease(route_list
);
10192 array_add_string(CFMutableArrayRef array
, const char * c_str
)
10196 str
= CFStringCreateWithCString(NULL
,
10198 kCFStringEncodingUTF8
);
10199 CFArrayAppendValue(array
, str
);
10205 array_add_int(CFMutableArrayRef array
, int int_val
)
10209 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10210 CFArrayAppendValue(array
, num
);
10216 dict_add_ipv6_addressing(CFMutableDictionaryRef dict
,
10217 IPv6AddressRef list
, int list_count
)
10219 CFMutableArrayRef addr
= NULL
;
10220 CFMutableArrayRef dest
= NULL
;
10222 CFMutableArrayRef prefix
= NULL
;
10223 IPv6AddressRef scan
;
10225 if (list
== NULL
|| list_count
== 0) {
10228 for (i
= 0, scan
= list
; i
< list_count
; i
++, scan
++) {
10229 if (scan
->addr
!= NULL
) {
10230 if (addr
== NULL
) {
10231 addr
= CFArrayCreateMutable(NULL
, list_count
,
10232 &kCFTypeArrayCallBacks
);
10234 array_add_string(addr
, scan
->addr
);
10236 if (scan
->prefix_length
>= 0) {
10237 if (prefix
== NULL
) {
10238 prefix
= CFArrayCreateMutable(NULL
, list_count
,
10239 &kCFTypeArrayCallBacks
);
10241 array_add_int(prefix
, scan
->prefix_length
);
10243 if (scan
->dest
!= NULL
) {
10244 if (dest
== NULL
) {
10245 dest
= CFArrayCreateMutable(NULL
, list_count
,
10246 &kCFTypeArrayCallBacks
);
10248 array_add_string(dest
, scan
->dest
);
10251 if (addr
!= NULL
) {
10252 CFDictionarySetValue(dict
, kSCPropNetIPv6Addresses
, addr
);
10255 if (dest
!= NULL
) {
10256 CFDictionarySetValue(dict
, kSCPropNetIPv6DestAddresses
, dest
);
10259 if (prefix
!= NULL
) {
10260 CFDictionarySetValue(dict
, kSCPropNetIPv6PrefixLength
, prefix
);
10266 static CFDictionaryRef
10267 make_IPv6_dict(IPv6ServiceContentsRef t
)
10269 CFMutableDictionaryRef dict
;
10271 dict
= CFDictionaryCreateMutable(NULL
, 0,
10272 &kCFTypeDictionaryKeyCallBacks
,
10273 &kCFTypeDictionaryValueCallBacks
);
10274 dict_add_ipv6_addressing(dict
, t
->addr
, t
->addr_count
);
10275 dict_add_string(dict
, kSCPropNetIPv6Router
, t
->router
);
10276 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
10277 dict_insert_v6_routes(dict
, kSCPropNetIPv6AdditionalRoutes
,
10278 t
->additional_routes
, t
->additional_routes_count
);
10279 dict_insert_v6_routes(dict
, kSCPropNetIPv6ExcludedRoutes
,
10280 t
->excluded_routes
, t
->excluded_routes_count
);
10285 kDirectionForwards
= 0,
10286 kDirectionBackwards
= 1
10290 kLogRouteDisabled
= 0,
10291 kLogRouteEnabled
= 1
10294 static IPv6RouteListRef
10295 make_IPv6RouteList_for_test(IPv6RouteListRef list
,
10296 IPv6ServiceContentsRef test
,
10299 CFDictionaryRef dict
;
10300 IPv6RouteListRef r
;
10302 Rank rank_assertion
= kRankAssertionDefault
;
10303 CFNumberRef rank_assertion_cf
= NULL
;
10304 Boolean rank_assertion_is_set
= FALSE
;
10305 IPv6RouteListRef ret
= NULL
;
10306 IPV6_ROUTES_BUF_DECL(routes
);
10308 dict
= make_IPv6_dict(test
);
10309 if (dict
== NULL
) {
10310 fprintf(stderr
, "make_IPv6_dict failed\n");
10313 if (test
->primary_rank
!= NULL
) {
10315 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
10316 &rank_assertion_is_set
);
10317 if (rank_assertion_is_set
) {
10319 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
10322 r
= IPv6RouteListCreateWithDictionary(routes
, dict
,
10323 rank_assertion_cf
);
10324 my_CFRelease(&rank_assertion_cf
);
10326 fprintf(stderr
, "IPv6RouteListCreateWithDictionary failed\n");
10330 if (rank_assertion
== kRankAssertionScoped
) {
10331 rank_assertion
= kRankAssertionNever
;
10333 rank
= RankMake(test
->rank
, rank_assertion
);
10334 if (log_it
== kLogRouteEnabled
10335 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10338 descr
= IPv6RouteListCopyDescription(r
);
10339 SCPrint(TRUE
, stdout
, CFSTR("Adding %@"), descr
);
10342 ret
= IPv6RouteListAddRouteList(list
, 1, r
, rank
);
10350 static IPv6RouteListRef
10351 make_IPv6RouteList(IPv6ServiceContentsRef
* test
, Direction direction
,
10354 IPv6RouteListRef ret
= NULL
;
10355 IPv6ServiceContentsRef
* scan
;
10357 switch (direction
) {
10358 case kDirectionBackwards
:
10359 for (scan
= test
; *scan
!= NULL
; scan
++) {
10360 /* find the end of the list */
10362 for (scan
--; scan
>= test
; scan
--) {
10363 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10367 case kDirectionForwards
:
10368 for (scan
= test
; *scan
!= NULL
; scan
++) {
10369 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10373 IPv6RouteListFinalize(ret
);
10377 #define EMPHASIS_CHARS "================="
10380 * Function: routelist_build_test
10382 * Runs through the given set of routes first in the forward direction,
10383 * then again backwards. We should end up with exactly the same set of
10384 * routes at the end.
10387 routelist_build_test(IPv6RouteTestRef test
)
10390 boolean_t ret
= FALSE
;
10391 IPv6RouteListRef routes1
;
10392 IPv6RouteListRef routes2
;
10394 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
10395 EMPHASIS_CHARS
"\n",
10398 routes1
= make_IPv6RouteList(test
->test
, kDirectionForwards
,
10400 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10401 if (routes1
!= NULL
) {
10402 descr
= IPv6RouteListCopyDescription(routes1
);
10403 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10407 routes2
= make_IPv6RouteList(test
->test
, kDirectionBackwards
,
10409 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10410 if (routes2
!= NULL
) {
10411 descr
= IPv6RouteListCopyDescription(routes2
);
10412 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10416 if ((routes1
!= NULL
&& routes2
== NULL
)
10417 || (routes1
== NULL
&& routes2
!= NULL
)) {
10418 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
10419 (routes1
!= NULL
) ? "not " : "",
10420 (routes2
!= NULL
) ? "not " : "");
10422 else if (routes1
!= NULL
&& routes2
!= NULL
) {
10423 /* check if they are different */
10424 if (routes1
->count
!= routes2
->count
) {
10425 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
10426 routes1
->count
, routes2
->count
);
10428 else if (bcmp(routes1
, routes2
,
10429 IPv6RouteListComputeSize(routes1
->count
)) != 0) {
10430 fprintf(stderr
, "routes1 and routes2 are different\n");
10433 printf("routes1 and routes2 are the same\n");
10437 if (routes1
!= NULL
) {
10440 if (routes2
!= NULL
) {
10443 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
10444 EMPHASIS_CHARS
"\n",
10445 test
->name
, ret
? "PASSED" : "FAILED");
10450 apply_test(IPv6RouteTestRef old_test
, IPv6RouteTestRef new_test
)
10452 IPv6RouteListRef new_routes
;
10453 IPv6RouteListRef old_routes
;
10455 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
10456 EMPHASIS_CHARS
"\n",
10457 old_test
->name
, new_test
->name
);
10459 old_routes
= make_IPv6RouteList(old_test
->test
, kDirectionForwards
,
10460 kLogRouteDisabled
);
10461 new_routes
= make_IPv6RouteList(new_test
->test
, kDirectionForwards
,
10462 kLogRouteDisabled
);
10463 if (old_routes
== NULL
) {
10464 printf("No Old Routes\n");
10467 printf("Old routes ('%s') = ", old_test
->name
);
10468 IPv6RouteListPrint(old_routes
);
10471 /* apply the old routes */
10472 IPv6RouteListApply(NULL
, old_routes
, -1);
10473 if (new_routes
== NULL
) {
10474 printf("No New Routes\n");
10477 printf("New Routes ('%s') = ", new_test
->name
);
10478 IPv6RouteListPrint(new_routes
);
10481 /* apply the new routes */
10482 IPv6RouteListApply(old_routes
, new_routes
, -1);
10483 if (old_routes
!= NULL
) {
10486 if (new_routes
!= NULL
) {
10489 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10490 EMPHASIS_CHARS
"\n",
10491 old_test
->name
, new_test
->name
);
10496 main(int argc
, char **argv
)
10498 IPv6RouteTestRef
* test
;
10501 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10502 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10504 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10506 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10507 if (!routelist_build_test(*test
)) {
10508 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10512 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10513 IPv6RouteTestRef
* test2
;
10515 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10516 apply_test(*test
, *test2
);
10517 apply_test(*test2
, *test
);
10524 printf("\nChecking for leaks\n");
10525 sprintf(cmd
, "leaks %d 2>&1", getpid());
10533 #endif /* TEST_IPV6_ROUTELIST */
10535 #ifdef TEST_DNS_ORDER
10537 #define kProtocolFlagsIPv4v6 (kProtocolFlagsIPv4 | kProtocolFlagsIPv6)
10539 #define V4_ADDR_LOOP CFSTR("127.0.0.1")
10540 #define V4_ADDR_1 CFSTR("192.168.1.1")
10541 #define V4_ADDR_2 CFSTR("192.168.1.2")
10542 #define V4_ADDR_3 CFSTR("8.8.8.8")
10543 #define V4_ADDR_4 CFSTR("8.8.4.4")
10545 #define V6_ADDR_LOOP CFSTR("::1")
10546 #define V6_ADDR_1 CFSTR("fe80::0123:4567:89ab:cdef%en0")
10547 #define V6_ADDR_2 CFSTR("fd00::2acf:e9ff:fe14:8c59")
10548 #define V6_ADDR_3 CFSTR("2001:4860:4860::8888")
10552 const ProtocolFlags flags
;
10553 const CFStringRef server_addrs
[];
10554 } DNSOrderTest
, * DNSOrderTestRef
;
10556 static DNSOrderTest test0a
= {
10558 kProtocolFlagsIPv4
,
10560 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, NULL
10564 static DNSOrderTest test0b
= {
10566 kProtocolFlagsIPv6
,
10568 V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10572 static DNSOrderTest test1a
= {
10574 kProtocolFlagsIPv4v6
,
10576 V4_ADDR_1
, V6_ADDR_1
, NULL
10580 static DNSOrderTest test2a
= {
10582 kProtocolFlagsIPv4v6
,
10584 V4_ADDR_1
, V6_ADDR_2
, NULL
10588 static DNSOrderTest test3a
= {
10590 kProtocolFlagsIPv4v6
,
10592 V4_ADDR_1
, V6_ADDR_3
, NULL
10596 static DNSOrderTest test1b
= {
10598 kProtocolFlagsIPv4v6
,
10600 V4_ADDR_3
, V6_ADDR_1
, NULL
10604 static DNSOrderTest test2b
= {
10606 kProtocolFlagsIPv4v6
,
10608 V4_ADDR_3
, V6_ADDR_2
, NULL
10612 static DNSOrderTest test3b
= {
10614 kProtocolFlagsIPv4v6
,
10616 V4_ADDR_3
, V6_ADDR_3
, NULL
10620 static DNSOrderTest test1c
= {
10622 kProtocolFlagsIPv4v6
,
10624 V6_ADDR_1
, V4_ADDR_1
, NULL
10628 static DNSOrderTest test2c
= {
10630 kProtocolFlagsIPv4v6
,
10632 V6_ADDR_2
, V4_ADDR_1
, NULL
10636 static DNSOrderTest test3c
= {
10638 kProtocolFlagsIPv4v6
,
10640 V6_ADDR_3
, V4_ADDR_1
, NULL
10644 static DNSOrderTest test1d
= {
10646 kProtocolFlagsIPv4v6
,
10648 V6_ADDR_1
, V4_ADDR_3
, NULL
10652 static DNSOrderTest test2d
= {
10654 kProtocolFlagsIPv4v6
,
10656 V6_ADDR_2
, V4_ADDR_3
, NULL
10660 static DNSOrderTest test3d
= {
10662 kProtocolFlagsIPv4v6
,
10664 V6_ADDR_3
, V4_ADDR_3
, NULL
10668 static DNSOrderTest test4
= {
10670 kProtocolFlagsIPv4v6
,
10672 V4_ADDR_LOOP
, V4_ADDR_3
, V6_ADDR_2
, NULL
10676 static DNSOrderTest test5
= {
10678 kProtocolFlagsIPv4v6
,
10680 V4_ADDR_3
, V6_ADDR_LOOP
, V6_ADDR_2
, NULL
10684 static DNSOrderTest test6
= {
10686 kProtocolFlagsIPv4v6
,
10688 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10692 static DNSOrderTest test7
= {
10694 kProtocolFlagsIPv4v6
,
10696 V4_ADDR_1
, V6_ADDR_1
, V4_ADDR_3
, V6_ADDR_2
, NULL
10700 static DNSOrderTestRef dns_order_tests
[] = {
10702 &test1a
, &test2a
, &test3a
,
10703 &test1b
, &test2b
, &test3b
,
10704 &test1c
, &test2c
, &test3c
,
10705 &test1d
, &test2d
, &test3d
,
10713 #define EMPHASIS_CHARS "================="
10716 apply_order(CFArrayRef servers
, ProtocolFlags flags
)
10718 CFArrayRef ordered_servers
;
10720 ordered_servers
= order_dns_servers(servers
, flags
);
10721 if (ordered_servers
!= NULL
) {
10722 SCPrint(TRUE
, stdout
, CFSTR("After :\n%@\n"), ordered_servers
);
10723 CFRelease(ordered_servers
);
10725 printf("FAIL: No ordered servers\n");
10732 apply_test(DNSOrderTestRef test
)
10734 CFMutableArrayRef servers
;
10736 printf("\n" EMPHASIS_CHARS
"> '%s' Begin <" EMPHASIS_CHARS
"\n\n", test
->name
);
10738 servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
10739 for (int i
= 0; test
->server_addrs
[i
] != NULL
; i
++) {
10740 CFStringRef server_addr
= test
->server_addrs
[i
];
10742 CFArrayAppendValue(servers
, server_addr
);
10745 SCPrint(TRUE
, stdout
, CFSTR("Before :\n%@\n"), servers
);
10747 apply_order(servers
, test
->flags
);
10749 CFRelease(servers
);
10755 main(int argc
, char **argv
)
10758 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10759 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10761 S_IPMonitor_debug
= (uint32
)strtoul(argv
[1], NULL
, 0);
10764 for (DNSOrderTestRef
* test
= dns_order_tests
; *test
!= NULL
; test
++) {
10771 printf("\nChecking for leaks\n");
10772 sprintf(cmd
, "leaks %d 2>&1", getpid());
10781 #endif /* TEST_DNS_ORDER */