2 * Copyright (c) 2000-2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 * - decides which interface will be made the "primary" interface,
27 * that is, the one with the default route assigned
31 * Modification History
33 * July 19, 2000 Dieter Siegmund (dieter@apple.com)
36 * November 15, 2000 Dieter Siegmund (dieter@apple.com)
37 * - changed to use new configuration model
39 * March 19, 2001 Dieter Siegmund (dieter@apple.com)
40 * - use service state instead of interface state
42 * July 16, 2001 Allan Nathanson (ajn@apple.com)
43 * - update to public SystemConfiguration.framework APIs
45 * August 28, 2001 Dieter Siegmund (dieter@apple.com)
46 * - specify the interface name when installing the default route
47 * - this ensures that default traffic goes to the highest priority
48 * service when multiple interfaces are configured to be on the same subnet
50 * September 16, 2002 Dieter Siegmund (dieter@apple.com)
51 * - don't elect a link-local service to be primary unless it's the only
52 * one that's available
54 * July 16, 2003 Dieter Siegmund (dieter@apple.com)
55 * - modifications to support IPv6
56 * - don't elect a service to be primary if it doesn't have a default route
58 * July 29, 2003 Dieter Siegmund (dieter@apple.com)
59 * - support installing a default route to a router that's not on our subnet
61 * March 22, 2004 Allan Nathanson (ajn@apple.com)
62 * - create expanded DNS configuration
64 * June 20, 2006 Allan Nathanson (ajn@apple.com)
65 * - add SMB configuration
67 * December 5, 2007 Dieter Siegmund (dieter@apple.com)
68 * - added support for multiple scoped routes
70 * November 13, 2013 Dieter Siegmund (dieter@apple.com)
71 * - added generic IPv4 routing support
78 #include <sys/fcntl.h>
79 #include <sys/ioctl.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <net/route.h>
84 #include <net/if_dl.h>
85 #include <netinet/in.h>
86 #include <netinet/icmp6.h>
87 #include <netinet6/in6_var.h>
88 #include <netinet6/nd6.h>
89 #include <network/sa_compare.h>
90 #include <arpa/inet.h>
91 #include <sys/sysctl.h>
94 #include <mach/mach_time.h>
95 #include <dispatch/dispatch.h>
96 #include <CommonCrypto/CommonDigest.h>
98 #include "ip_plugin.h"
100 #include <SystemConfiguration/SystemConfiguration.h>
101 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
102 #include <SystemConfiguration/SCValidation.h>
103 #include <SystemConfiguration/scprefs_observer.h>
104 #include <SystemConfiguration/SCPrivate.h>
105 #include "SCNetworkReachabilityInternal.h"
106 #include "SCNetworkSignaturePrivate.h"
108 #include "dnsinfo_server.h"
110 #include <ppp/PPPControllerPriv.h>
113 #include <dns_sd_private.h>
115 #include <network_information.h>
116 #include "network_state_information_priv.h"
117 #include "network_state_information_logging.h"
118 #include "network_information_server.h"
119 #include <ppp/ppp_msg.h>
120 #if !TARGET_OS_SIMULATOR
121 #include "set-hostname.h"
122 #endif /* !TARGET_OS_SIMULATOR */
124 #include "dns-configuration.h"
126 #if !TARGET_OS_SIMULATOR
127 #include "nat64-configuration.h"
128 #endif /* !TARGET_OS_SIMULATOR */
130 #include "proxy-configuration.h"
132 #if !TARGET_OS_SIMULATOR
133 #include "agent-monitor.h"
134 #endif // !TARGET_OS_SIMULATOR
136 #if !TARGET_OS_IPHONE
137 #include "smb-configuration.h"
138 #endif /* !TARGET_OS_IPHONE */
140 #define kLoopbackInterface "lo0"
141 #define EROUTENOTAPPLIED 1001
143 typedef CF_ENUM(uint8_t, ProtocolFlags
) {
144 kProtocolFlagsNone
= 0x0,
145 kProtocolFlagsIPv4
= 0x1,
146 kProtocolFlagsIPv6
= 0x2
150 kDebugFlag1
= 0x00000001,
151 kDebugFlag2
= 0x00000002,
152 kDebugFlag4
= 0x00000004,
153 kDebugFlag8
= 0x00000008,
154 kDebugFlagDefault
= kDebugFlag1
,
155 kDebugFlagAll
= 0xffffffff
158 typedef unsigned int IFIndex
;
160 static dispatch_queue_t
__network_change_queue();
167 __private_extern__ os_log_t
170 static os_log_t log
= NULL
;
173 log
= os_log_create("com.apple.SystemConfiguration", "IPMonitor");
181 #pragma mark interface index
184 #ifndef TEST_ROUTELIST
186 #define ROUTELIST_DEBUG(flag, fmt, ...)
188 static struct if_nameindex
* S_if_nameindex_cache
;
190 static dispatch_queue_t
191 __my_if_nametoindex_queue()
193 static dispatch_once_t once
;
194 static dispatch_queue_t q
;
196 dispatch_once(&once
, ^{
197 q
= dispatch_queue_create("my_if_nametoindex queue", NULL
);
203 __private_extern__ IFIndex
204 my_if_nametoindex(const char * ifname
)
206 __block IFIndex idx
= 0;
208 dispatch_sync(__my_if_nametoindex_queue(), ^{
209 struct if_nameindex
* scan
;
211 if (S_if_nameindex_cache
== NULL
) {
212 idx
= if_nametoindex(ifname
);
215 for (scan
= S_if_nameindex_cache
;
216 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
218 if (strcmp(scan
->if_name
, ifname
) == 0) {
219 idx
= scan
->if_index
;
228 __private_extern__
const char *
229 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
231 __block
const char * name
= NULL
;
233 dispatch_sync(__my_if_nametoindex_queue(), ^{
234 struct if_nameindex
* scan
;
236 if (S_if_nameindex_cache
== NULL
) {
237 name
= if_indextoname(idx
, if_name
);
240 for (scan
= S_if_nameindex_cache
;
241 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
243 if (scan
->if_index
== idx
) {
245 strlcpy(if_name
, scan
->if_name
, IFNAMSIZ
);
255 my_if_freenameindex(void)
257 dispatch_sync(__my_if_nametoindex_queue(), ^{
258 if (S_if_nameindex_cache
!= NULL
) {
259 if_freenameindex(S_if_nameindex_cache
);
260 S_if_nameindex_cache
= NULL
;
268 my_if_nameindex(void)
270 my_if_freenameindex();
271 dispatch_sync(__my_if_nametoindex_queue(), ^{
272 S_if_nameindex_cache
= if_nameindex();
279 #else /* TEST_ROUTELIST */
281 #define ROUTELIST_DEBUG(flags, format, ...) { if (((S_IPMonitor_debug & (flags)) != 0)) printf((format), ## __VA_ARGS__ ); }
284 static const char * * list
;
285 static int list_count
;
286 static int list_size
;
288 __private_extern__ IFIndex
289 my_if_nametoindex(const char * ifname
)
296 list
= (const char * *)malloc(sizeof(*list
) * list_size
);
297 list
[0] = strdup("");
298 list
[1] = strdup(kLoopbackInterface
);
303 for (i
= 1; i
< list_count
; i
++) {
304 if (strcmp(list
[i
], ifname
) == 0) {
310 if (list_count
== list_size
) {
312 list
= (const char * *)realloc(list
, sizeof(*list
) * list_size
);
314 list
[list_count
] = strdup(ifname
);
321 __private_extern__
const char *
322 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
324 const char * name
= NULL
;
326 if (idx
< list_count
) {
328 strlcpy(if_name
, list
[idx
], IFNAMSIZ
);
334 my_if_nameindex(void)
339 my_if_freenameindex(void)
343 #endif /* TEST_ROUTELIST */
346 my_if_indextoname2(IFIndex ifindex
, char ifname
[IFNAMSIZ
])
351 if (my_if_indextoname(ifindex
, ifname
) == NULL
) {
352 snprintf(ifname
, IFNAMSIZ
, "[%d]", ifindex
);
364 idx
= my_if_nametoindex(kLoopbackInterface
);
374 * Property: kServiceOptionRankAssertion
376 * Key used in the service options dictionary to hold the RankAssertion
377 * derived from the kSCPropNetServicePrimaryRank string.
379 #define kServiceOptionRankAssertion CFSTR("RankAssertion") /* number */
382 * Property: kIPIsCoupled
384 * Used to indicate that the IPv4 and IPv6 services are coupled.
385 * Neither the IPv4 part nor the IPv6 part of a coupled service
386 * may become primary if IPv4 or IPv6 is primary for another interface.
388 * For example, if the service over en3 is "coupled" and has IPv6,
389 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible
390 * to become primary for IPv6.
392 #define kIPIsCoupled CFSTR("IPIsCoupled")
394 #define PPP_PREFIX "ppp"
396 #define IP_FORMAT "%d.%d.%d.%d"
397 #define IP_CH(ip) ((u_char *)(ip))
398 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
400 static Boolean S_bundle_logging_verbose
;
403 * IPv4 Route management
406 typedef CF_ENUM(uint16_t, RouteFlags
) {
407 kRouteFlagsIsScoped
= 0x0001,
408 kRouteFlagsHasGateway
= 0x0002,
409 kRouteFlagsIsHost
= 0x0004,
410 kRouteFlagsIsNULL
= 0x0008,
411 kRouteFlagsKernelManaged
= 0x0010
414 typedef CF_ENUM(uint16_t, ControlFlags
) {
415 kControlFlagsProcessed
= 0x0001,
416 kControlFlagsAdded
= 0x0002,
419 #define ROUTE_COMMON \
422 IFIndex exclude_ifindex; \
425 ControlFlags control_flags;
431 #define PREFIX_LENGTH_IN_CLASSC 24
432 #define PREFIX_LENGTH_IN_CLASSD 4
438 struct in_addr gateway
;
440 } IPv4Route
, * IPv4RouteRef
;
444 struct in6_addr dest
;
445 struct in6_addr gateway
;
447 } IPv6Route
, * IPv6RouteRef
;
449 typedef CF_ENUM(uint16_t, RouteListFlags
) {
450 kRouteListFlagsExcludeNWI
= 0x0001,
451 kRouteListFlagsHasDefault
= 0x0002,
452 kRouteListFlagsScopedOnly
= 0x0004
455 #define ROUTELIST_COMMON \
458 RouteListFlags flags;
462 } RouteListCommon
, * RouteListRef
;
466 IPv4Route list
[1]; /* variable length */
467 } IPv4RouteList
, * IPv4RouteListRef
;
471 IPv6Route list
[1]; /* variable length */
472 } IPv6RouteList
, * IPv6RouteListRef
;
487 * Election Information
488 * - information about the current best services
496 struct sockaddr_in v4
;
497 struct sockaddr_in6 v6
;
500 typedef struct Candidate
{
501 CFStringRef serviceID
;
504 boolean_t ip_is_coupled
;
505 boolean_t ineligible
;
506 SCNetworkReachabilityFlags reachability_flags
;
508 in_sockaddr vpn_server_addr
;
509 CFStringRef signature
;
510 } Candidate
, * CandidateRef
;
512 typedef struct ElectionResults
{
516 Candidate candidates
[1];
517 } ElectionResults
, * ElectionResultsRef
;
519 static __inline__
size_t
520 ElectionResultsComputeSize(unsigned int n
)
522 return (offsetof(ElectionResults
, candidates
[n
]));
529 static __inline__ Rank
530 RankMake(uint32_t service_index
, Rank primary_rank
)
532 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
536 InterfaceRankGetRankAssertion(CFNumberRef rank_cf
, Boolean
* ret_is_set
)
538 SCNetworkServicePrimaryRank if_rank
;
539 Boolean is_set
= FALSE
;
540 Rank rank
= kRankAssertionDefault
;
543 && CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &if_rank
)
544 && if_rank
!= kSCNetworkServicePrimaryRankDefault
) {
545 if (if_rank
== kSCNetworkServicePrimaryRankFirst
) {
546 rank
= kRankAssertionFirst
;
549 rank
= RANK_ASSERTION_MAKE(if_rank
);
553 if (ret_is_set
!= NULL
) {
554 *ret_is_set
= is_set
;
560 PrimaryRankGetRankAssertion(CFStringRef rank_str
, Boolean
* is_set
)
563 const CFStringRef
* name
;
566 { &kSCValNetServicePrimaryRankFirst
, kRankAssertionFirst
},
567 { &kSCValNetServicePrimaryRankLast
, kRankAssertionLast
},
568 { &kSCValNetServicePrimaryRankNever
, kRankAssertionNever
},
569 { &kSCValNetServicePrimaryRankScoped
, kRankAssertionScoped
}
572 if (rank_str
!= NULL
) {
573 for (size_t i
= 0; i
< countof(values
); i
++) {
574 if (CFEqual(rank_str
, *(values
[i
].name
))) {
575 if (is_set
!= NULL
) {
578 return (values
[i
].rank_assertion
);
582 if (is_set
!= NULL
) {
585 return (kRankAssertionDefault
);
588 /* SCDynamicStore session */
589 static SCDynamicStoreRef S_session
= NULL
;
591 /* debug output flags */
592 static uint32_t S_IPMonitor_debug
= 0;
593 static Boolean S_IPMonitor_verbose
= FALSE
;
595 /* are we netbooted? If so, don't touch the default route */
596 static boolean_t S_netboot
= FALSE
;
598 /* dictionary to hold per-service state: key is the serviceID */
599 static CFMutableDictionaryRef S_service_state_dict
= NULL
;
600 static CFMutableDictionaryRef S_ipv4_service_rank_dict
= NULL
;
601 static CFMutableDictionaryRef S_ipv6_service_rank_dict
= NULL
;
603 /* dictionary to hold per-interface rank information */
604 static CFDictionaryRef S_if_rank_dict
;
606 /* if set, a PPP interface overrides the primary */
607 static boolean_t S_ppp_override_primary
= FALSE
;
609 /* the current primary serviceID's */
610 static CFStringRef S_primary_ipv4
= NULL
;
611 static CFStringRef S_primary_ipv6
= NULL
;
612 static CFStringRef S_primary_dns
= NULL
;
613 static CFStringRef S_primary_proxies
= NULL
;
615 /* the current election results */
616 static ElectionResultsRef S_ipv4_results
;
617 static ElectionResultsRef S_ipv6_results
;
619 static CFStringRef S_state_global_ipv4
= NULL
;
620 static CFStringRef S_state_global_ipv6
= NULL
;
621 static CFStringRef S_state_global_dns
= NULL
;
622 static CFStringRef S_state_global_proxies
= NULL
;
623 static CFStringRef S_state_service_prefix
= NULL
;
624 static CFStringRef S_setup_global_ipv4
= NULL
;
625 static CFStringRef S_setup_service_prefix
= NULL
;
627 static CFStringRef S_multicast_resolvers
= NULL
;
628 static CFStringRef S_private_resolvers
= NULL
;
630 #if !TARGET_OS_SIMULATOR
631 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
632 static IPv6RouteListRef S_ipv6_routelist
= NULL
;
633 #endif /* !TARGET_OS_SIMULATOR */
635 static boolean_t S_append_state
= FALSE
;
637 static CFDictionaryRef S_dns_dict
= NULL
;
639 static Boolean S_dnsinfo_synced
= TRUE
;
641 #if !TARGET_OS_SIMULATOR
642 // Note: access should be gated with __network_change_queue()
643 static CFMutableSetRef S_nat64_prefix_changes
= NULL
;
644 static CFMutableSetRef S_nat64_prefix_requests
= NULL
;
645 #endif /* !TARGET_OS_SIMULATOR */
647 static nwi_state_t S_nwi_state
= NULL
;
648 static Boolean S_nwi_synced
= TRUE
;
650 static CFDictionaryRef S_proxies_dict
= NULL
;
652 // Note: access should be gated with __network_change_queue()
653 static uint32_t S_network_change_needed
= 0;
654 #define NETWORK_CHANGE_NET 1<<0
655 #define NETWORK_CHANGE_DNS 1<<1
656 #define NETWORK_CHANGE_PROXY 1<<2
657 #if !TARGET_OS_IPHONE
658 #define NETWORK_CHANGE_SMB 1<<3
659 #endif /* !TARGET_OS_IPHONE */
660 #define NETWORK_CHANGE_NAT64 1<<4
661 static struct timeval S_network_change_start
;
662 static Boolean S_network_change_timeout
= FALSE
;
663 static dispatch_source_t S_network_change_timer
= NULL
;
665 #if !TARGET_OS_IPHONE
666 static CFStringRef S_primary_smb
= NULL
;
667 static CFStringRef S_state_global_smb
= NULL
;
668 static CFDictionaryRef S_smb_dict
= NULL
;
669 #endif /* !TARGET_OS_IPHONE */
671 #if !TARGET_OS_IPHONE
672 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
673 #endif /* !TARGET_OS_IPHONE */
676 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
677 #endif /* KERN_NETBOOT */
680 ** entityType*, GetEntityChanges*
681 ** - definitions for the entity types we handle
688 #if !TARGET_OS_IPHONE
690 #endif /* !TARGET_OS_IPHONE */
692 kEntityTypeTransientStatus
,
693 kEntityTypeServiceOptions
= 31
696 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
697 &kSCEntNetIPv4
, /* 0 */
698 &kSCEntNetIPv6
, /* 1 */
699 &kSCEntNetDNS
, /* 2 */
700 &kSCEntNetProxies
, /* 3 */
701 #if !TARGET_OS_IPHONE
702 &kSCEntNetSMB
, /* 4 */
703 #endif /* !TARGET_OS_IPHONE */
707 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
);
709 static __inline__
char
712 return ((af
== AF_INET
) ? '4' : '6');
715 static __inline__
char
716 ipvx_other_char(int af
)
718 return ((af
== AF_INET
) ? '6' : '4');
722 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
724 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
725 * Routes CFData containing IPv4RouteList/IPv6RouteList
726 * Service dictionary containing kSCEntNetIPv[46] service entity
728 #define kIPDictRoutes CFSTR("Routes") /* data */
729 #define kIPDictService CFSTR("Service") /* dict */
731 static CFDictionaryRef
732 ipdict_create(CFDictionaryRef dict
, CFDataRef routes_data
)
737 keys
[0] = kIPDictService
;
739 keys
[1] = kIPDictRoutes
;
740 values
[1] = routes_data
;
741 return (CFDictionaryCreate(NULL
,
742 (const void * *)keys
,
745 &kCFTypeDictionaryKeyCallBacks
,
746 &kCFTypeDictionaryValueCallBacks
));
750 ipdict_get_routelist(CFDictionaryRef dict
)
752 void * routes_list
= NULL
;
757 routes
= CFDictionaryGetValue(dict
, kIPDictRoutes
);
758 if (routes
!= NULL
) {
759 routes_list
= (void *)CFDataGetBytePtr(routes
);
762 return (routes_list
);
765 static CFDictionaryRef
766 ipdict_get_service(CFDictionaryRef dict
)
768 CFDictionaryRef ip_dict
= NULL
;
771 ip_dict
= CFDictionaryGetValue(dict
, kIPDictService
);
777 ipdict_get_ifname(CFDictionaryRef dict
)
779 CFStringRef ifname
= NULL
;
780 CFDictionaryRef ip_dict
;
782 ip_dict
= ipdict_get_service(dict
);
783 if (ip_dict
!= NULL
) {
784 ifname
= CFDictionaryGetValue(ip_dict
, kSCPropInterfaceName
);
789 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
790 CFDictionaryRef state_dict
,
791 CFDictionaryRef setup_dict
,
792 CFDictionaryRef info
);
793 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
795 static GetEntityChangesFunc get_ipv4_changes
;
796 static GetEntityChangesFunc get_ipv6_changes
;
797 static GetEntityChangesFunc get_dns_changes
;
798 static GetEntityChangesFunc get_proxies_changes
;
799 #if !TARGET_OS_IPHONE
800 static GetEntityChangesFunc get_smb_changes
;
801 #endif /* !TARGET_OS_IPHONE */
803 static __inline__
void
804 my_CFRelease(void * t
)
806 void * * obj
= (void * *)t
;
816 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
819 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
821 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
822 get_ipv4_changes
, /* 0 */
823 get_ipv6_changes
, /* 1 */
824 get_dns_changes
, /* 2 */
825 get_proxies_changes
,/* 3 */
826 #if !TARGET_OS_IPHONE
827 get_smb_changes
, /* 4 */
828 #endif /* !TARGET_OS_IPHONE */
833 ** - mechanism to do an atomic update of the SCDynamicStore
834 ** when the content needs to be changed across multiple functions
837 CFMutableArrayRef notify
;
838 CFMutableArrayRef remove
;
839 CFMutableDictionaryRef set
;
840 } keyChangeList
, * keyChangeListRef
;
843 keyChangeListInit(keyChangeListRef keys
)
845 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
846 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
847 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
848 &kCFTypeDictionaryKeyCallBacks
,
849 &kCFTypeDictionaryValueCallBacks
);
854 keyChangeListFree(keyChangeListRef keys
)
856 my_CFRelease(&keys
->notify
);
857 my_CFRelease(&keys
->remove
);
858 my_CFRelease(&keys
->set
);
863 keyChangeListActive(keyChangeListRef keys
)
865 return ((CFDictionaryGetCount(keys
->set
) > 0) ||
866 (CFArrayGetCount(keys
->remove
) > 0) ||
867 (CFArrayGetCount(keys
->notify
) > 0));
871 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
873 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
878 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
880 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
881 CFDictionaryRemoveValue(keys
->set
, key
);
886 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
888 my_CFArrayRemoveValue(keys
->remove
, key
);
889 CFDictionarySetValue(keys
->set
, key
, value
);
894 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
896 CFArrayRef notify
= keys
->notify
;
897 CFArrayRef remove
= keys
->remove
;
898 CFDictionaryRef set
= keys
->set
;
900 if (CFArrayGetCount(notify
) == 0) {
903 if (CFArrayGetCount(remove
) == 0) {
906 if (CFDictionaryGetCount(set
) == 0) {
909 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
912 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
914 my_log(LOG_DEBUG
, "Setting:\n%@", set
);
916 if (remove
!= NULL
) {
917 my_log(LOG_DEBUG
, "Removing:\n%@", remove
);
919 if (notify
!= NULL
) {
920 my_log(LOG_DEBUG
, "Notifying:\n%@", notify
);
923 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
936 mib
[1] = KERN_NETBOOT
;
937 len
= sizeof(netboot
);
938 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
942 static int rtm_seq
= 0;
944 #if !TARGET_OS_SIMULATOR
946 open_routing_socket(void)
950 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
951 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
956 static __inline__
int
957 inet6_dgram_socket(void)
961 sockfd
= socket(AF_INET6
, SOCK_DGRAM
, 0);
963 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
970 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
972 struct in6_defrouter dr
;
973 struct sockaddr_in6
* sin6
;
975 bzero(&dr
, sizeof(dr
));
977 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
978 sin6
->sin6_family
= AF_INET6
;
979 sin6
->sin6_addr
= *addr
;
981 dr
.if_index
= if_index
;
982 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
986 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
988 struct in6_defrouter dr
;
989 struct sockaddr_in6
* sin6
;
991 bzero(&dr
, sizeof(dr
));
993 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
994 sin6
->sin6_family
= AF_INET6
;
995 sin6
->sin6_addr
= *addr
;
996 dr
.if_index
= if_index
;
997 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
1000 #endif /* !TARGET_OS_SIMULATOR */
1003 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
1005 CFIndex n
= CFArrayGetCount(arr
);
1007 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
1010 CFArrayAppendValue(arr
, new);
1015 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
1019 i
= CFArrayGetFirstIndexOfValue(arr
,
1020 CFRangeMake(0, CFArrayGetCount(arr
)),
1022 if (i
!= kCFNotFound
) {
1023 CFArrayRemoveValueAtIndex(arr
, i
);
1029 my_CFArrayCreateCombinedArray(CFArrayRef array1
, CFArrayRef array2
)
1031 CFMutableArrayRef combined
;
1033 combined
= CFArrayCreateMutableCopy(NULL
, 0, array1
);
1034 CFArrayAppendArray(combined
,
1036 CFRangeMake(0, CFArrayGetCount(array2
)));
1040 static CFDictionaryRef
1041 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
1043 if (isA_CFDictionary(dict
) == NULL
) {
1046 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
1050 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
1052 if (isA_CFDictionary(dict
) == NULL
) {
1055 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
1058 #if !TARGET_OS_SIMULATOR
1060 my_CFSetAddValue_async(dispatch_queue_t queue
, CFMutableSetRef
*set
, CFTypeRef value
)
1063 dispatch_async(queue
, ^{
1065 *set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
1067 CFSetAddValue(*set
, value
);
1073 #endif /* !TARGET_OS_SIMULATOR */
1076 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, size_t addr_size
)
1080 if (isA_CFString(str
) == NULL
) {
1086 if (addr_size
< sizeof(struct in_addr
)) {
1091 if (addr_size
< sizeof(struct in6_addr
)) {
1098 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
1099 if (inet_pton(family
, buf
, addr
) == 1) {
1103 bzero(addr
, addr_size
);
1109 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
1111 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
1116 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
1118 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
1122 cfnumber_to_int(CFNumberRef num
, int * int_val
)
1124 if (isA_CFNumber(num
) == NULL
) {
1127 return (CFNumberGetValue(num
, kCFNumberIntType
, int_val
));
1130 static CF_RETURNS_RETAINED CFStringRef
1131 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
1133 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1134 kSCDynamicStoreDomainSetup
,
1139 static CF_RETURNS_RETAINED CFStringRef
1140 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
1142 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1143 kSCDynamicStoreDomainState
,
1149 interface_entity_key_copy(CFStringRef ifname
, CFStringRef entity
)
1151 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1152 kSCDynamicStoreDomainState
,
1157 static CFDictionaryRef
1158 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1161 CFStringRef setup_key
;
1162 CFDictionaryRef setup_dict
;
1164 setup_key
= setup_service_key(serviceID
, entity
);
1165 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
1166 my_CFRelease(&setup_key
);
1167 return (setup_dict
);
1170 static CFDictionaryRef
1171 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1174 CFStringRef state_key
;
1175 CFDictionaryRef state_dict
;
1177 state_key
= state_service_key(serviceID
, entity
);
1178 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
1179 my_CFRelease(&state_key
);
1180 return (state_dict
);
1184 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1188 ip_list
= CFDictionaryGetValue(dict
, prop
);
1189 if (isA_CFArray(ip_list
) != NULL
1190 && CFArrayGetCount(ip_list
) > 0
1191 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1198 dict_get_first_ipv6(CFDictionaryRef dict
, CFStringRef prop
,
1199 struct in6_addr
* ip_p
)
1203 ip_list
= CFDictionaryGetValue(dict
, prop
);
1204 if (isA_CFArray(ip_list
) != NULL
1205 && CFArrayGetCount(ip_list
) > 0
1206 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1213 dict_get_first_int(CFDictionaryRef dict
, CFStringRef prop
,
1218 list
= CFDictionaryGetValue(dict
, prop
);
1219 if (isA_CFArray(list
) != NULL
1220 && CFArrayGetCount(list
) > 0
1221 && cfnumber_to_int(CFArrayGetValueAtIndex(list
, 0), val
)) {
1228 dict_get_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1232 val
= CFDictionaryGetValue(dict
, prop
);
1233 return (cfstring_to_ip(val
, ip_p
));
1237 dict_get_ipv6(CFDictionaryRef dict
, CFStringRef prop
, struct in6_addr
* ip_p
)
1241 val
= CFDictionaryGetValue(dict
, prop
);
1242 return (cfstring_to_ip6(val
, ip_p
));
1246 dict_get_int(CFDictionaryRef dict
, CFStringRef prop
, int * intval
)
1250 val
= CFDictionaryGetValue(dict
, prop
);
1251 return (cfnumber_to_int(val
, intval
));
1255 get_override_primary(CFDictionaryRef dict
)
1259 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
1260 if (isA_CFNumber(override
) != NULL
) {
1263 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
1268 else if (isA_CFBoolean(override
) != NULL
) {
1269 if (CFBooleanGetValue(override
)) {
1281 (*RouteListComputeSize
)(CFIndex n
);
1284 (*RouteIsEqual
)(RouteRef a
, RouteRef b
);
1287 (*RouteApply
)(RouteRef route
, int cmd
, int sockfd
);
1289 typedef const void *
1290 (*RouteGateway
)(RouteRef route
);
1293 (*RouteSetGateway
)(RouteRef route
, const void * address
);
1295 typedef const void *
1296 (*RouteDestination
)(RouteRef route
);
1299 (*RouteSameSubnet
)(RouteRef route
, const void * address
);
1302 (*RouteCopyDescription
)(RouteRef route
);
1305 (*RouteLog
)(int priority
, RouteRef route
, const char * msg
);
1308 RouteListComputeSize list_compute_size
;
1310 RouteIsEqual route_equal
;
1311 RouteApply route_apply
;
1312 RouteGateway route_gateway
;
1313 RouteSetGateway route_set_gateway
;
1314 RouteDestination route_destination
;
1315 RouteSameSubnet route_same_subnet
;
1317 RouteCopyDescription route_copy_description
;
1324 typedef const RouteListInfo
* RouteListInfoRef
;
1327 RouteListInfoRef info
;
1328 RouteListRef old_routes
;
1329 RouteListRef new_routes
;
1332 } RouteListApplyContext
, * RouteListApplyContextRef
;
1336 RouteAddressCompare(RouteListInfoRef info
,
1340 return (memcmp(addr1
, addr2
, info
->address_size
));
1344 RouteCompare(RouteListInfoRef info
,
1345 RouteRef a
, Rank a_rank
,
1346 RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
1349 RouteDestination route_destination
;
1350 RouteCopyDescription route_copy_description
;
1353 route_destination
= info
->route_destination
;
1354 route_copy_description
= info
->route_copy_description
;
1355 cmp
= RouteAddressCompare(info
,
1356 (*route_destination
)(a
),
1357 (*route_destination
)(b
));
1359 cmp
= a
->prefix_length
- b
->prefix_length
;
1361 int index_cmp
= a
->ifindex
- b
->ifindex
;
1363 if (index_cmp
== 0) {
1366 else if ((a
->ifindex
== 0 || b
->ifindex
== 0)
1367 && (a
->flags
& kRouteFlagsIsScoped
) == 0
1368 && (b
->flags
& kRouteFlagsIsScoped
) == 0) {
1370 * Either of the routes specifies no interface and neither
1371 * route is scoped. Claim they are equal to eliminate the
1378 cmp
= RankCompare(a_rank
, b_rank
);
1385 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
1393 else if (cmp
== 0) {
1399 a_str
= (*route_copy_description
)(a
);
1400 b_str
= (*route_copy_description
)(b
);
1401 my_log(LOG_DEBUG
, "%@ rank 0x%x %c %@ rank 0x%x",
1402 a_str
, a_rank
, ch
, b_str
, b_rank
);
1410 RouteListGetRouteAtIndexSimple(RouteListInfoRef info
, RouteListRef routes
,
1413 return ((void *)routes
+ (*info
->list_compute_size
)(where
));
1417 RouteListGetRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1420 if (routes
->count
== 0
1421 || where
>= routes
->count
) {
1424 return (RouteListGetRouteAtIndexSimple(info
, routes
, where
));
1428 RouteListGetFirstRoute(RouteListInfoRef info
, RouteListRef routes
)
1430 return (RouteListGetRouteAtIndexSimple(info
, routes
, 0));
1433 #if !TARGET_OS_SIMULATOR
1435 RouteListRouteIndex(RouteListInfoRef info
, RouteListRef routes
,
1438 return (((void *)route
1439 - (void *)RouteListGetFirstRoute(info
, routes
))
1440 / info
->element_size
);
1442 #endif /* !TARGET_OS_SIMULATOR */
1445 RouteGetNextRoute(RouteListInfoRef info
, RouteRef route
)
1447 return ((RouteRef
)(((void *)route
) + info
->element_size
));
1451 RouteListAddRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1452 RouteRef this_route
, CFIndex where
)
1454 RouteRef insert_route
;
1456 if (where
== kCFNotFound
) {
1457 /* add it to the end */
1459 = RouteListGetRouteAtIndexSimple(info
, routes
, routes
->count
);
1462 /* make space at [where] */
1463 insert_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1465 (void *)insert_route
+ info
->element_size
,
1466 info
->element_size
* (routes
->count
- where
));
1468 /* copy the route */
1469 bcopy(this_route
, insert_route
, info
->element_size
);
1471 return (insert_route
);
1475 RouteListRemoveRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1478 if (routes
->count
== 0
1479 || where
>= routes
->count
) {
1483 if (where
== routes
->count
) {
1484 /* last slot, decrementing gets rid of it */
1487 RouteRef remove_route
;
1489 remove_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1490 bcopy((void *)remove_route
+ info
->element_size
,
1492 info
->element_size
* (routes
->count
- where
));
1498 * Function: RouteListAddRoute
1501 * Add the given route to the list of routes, eliminating lower-ranked
1502 * duplicates on the same interface, and marking any lower ranked duplicates
1503 * on other interfaces with kRouteFlagsIsScoped.
1505 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1508 * Route list updated with the given route, possibly a different pointer,
1509 * due to using realloc'd memory.
1519 RouteListAddRoute(RouteListInfoRef info
,
1520 RouteListRef routes
, int init_size
,
1521 RouteRef this_route
, Rank this_rank
)
1524 RouteRef first_scan
= NULL
;
1527 Scope scope_which
= kScopeNone
;
1528 CFIndex where
= kCFNotFound
;
1530 if (routes
== NULL
) {
1531 size_t alloc_size
= (*info
->list_compute_size
)(init_size
);
1533 routes
= (RouteListRef
)malloc(alloc_size
);
1534 bzero(routes
, sizeof(*routes
));
1535 routes
->size
= init_size
;
1537 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1539 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1541 boolean_t same_dest
;
1543 cmp
= RouteCompare(info
, this_route
, this_rank
, scan
, scan
->rank
,
1545 if (same_dest
&& (first_scan
== NULL
)) {
1549 if (where
== kCFNotFound
) {
1551 && (first_scan
!= NULL
)
1552 && (first_scan
->flags
& kRouteFlagsIsScoped
) == 0) {
1553 if ((scan
->flags
& kRouteFlagsIsScoped
) != 0) {
1554 ROUTELIST_DEBUG(kDebugFlag8
,
1555 "Hit 1: set scope on self\n");
1556 scope_which
= kScopeThis
;
1559 ROUTELIST_DEBUG(kDebugFlag8
,
1560 "Hit 2: set scope on next\n");
1561 scope_which
= kScopeNext
;
1564 /* remember our insertion point, but keep going to find a dup */
1568 else if (cmp
== 0) {
1571 if (where
!= kCFNotFound
1572 && scan
->ifindex
== this_route
->ifindex
1573 && scan
->exclude_ifindex
== 0
1574 && this_route
->exclude_ifindex
== 0) {
1575 /* this route is a duplicate */
1576 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 3: removing [%ld]\n", i
);
1577 RouteListRemoveRouteAtIndex(info
, routes
, i
);
1581 * this_route is "better" than scan if this_route is not excluded
1582 * and scan is excluded or this_route sorts ahead of scan
1584 if (this_route
->exclude_ifindex
== 0
1585 && (scan
->exclude_ifindex
!= 0 || this_rank
< scan
->rank
)) {
1586 IFIndex ifindex
= 0;
1587 boolean_t is_scoped
= FALSE
;
1589 if (scan
->flags
& kRouteFlagsIsScoped
) {
1592 if (this_rank
< scan
->rank
) {
1593 ROUTELIST_DEBUG(kDebugFlag8
,
1594 "Hit 4a: replacing [%ld]"
1595 " rank 0x%x < 0x%x\n",
1596 i
, this_rank
, scan
->rank
);
1599 ROUTELIST_DEBUG(kDebugFlag8
,
1600 "Hit 4b: replacing [%ld] excluded route\n",
1603 if (scan
->ifindex
!= 0) {
1604 ifindex
= scan
->ifindex
;
1606 else if (this_route
->ifindex
!= 0) {
1607 ifindex
= this_route
->ifindex
;
1609 bcopy(this_route
, scan
, info
->element_size
);
1610 scan
->rank
= this_rank
;
1611 scan
->ifindex
= ifindex
;
1612 scan
->exclude_ifindex
= 0;
1614 /* preserve whether route was scoped */
1615 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 5: preserved scope\n");
1616 scan
->flags
|= kRouteFlagsIsScoped
;
1624 if (scope_which
== kScopeNone
) {
1625 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 6: set scope on self\n");
1626 scope_which
= kScopeThis
;
1629 #ifdef TEST_ROUTELIST
1630 else if (where
!= kCFNotFound
) {
1631 /* not possible because we maintain a sorted list */
1633 "Hit 7: moved past routes - can't happen\n");
1637 #endif /* TEST_ROUTELIST */
1641 if (routes
->size
== routes
->count
) {
1643 RouteListRef new_routes
;
1646 /* double the size */
1647 old_size
= routes
->size
;
1648 how_many
= old_size
* 2;
1649 new_routes
= (RouteListRef
)
1650 reallocf(routes
, (*info
->list_compute_size
)(how_many
));
1651 if (new_routes
== NULL
) {
1656 ROUTELIST_DEBUG(kDebugFlag8
, "increasing size from %d to %d\n",
1657 old_size
, how_many
);
1658 new_routes
->size
= how_many
;
1659 routes
= new_routes
;
1662 /* add/insert the new route */
1663 this_route
= RouteListAddRouteAtIndex(info
, routes
, this_route
, where
);
1664 this_route
->rank
= this_rank
;
1666 if (RANK_ASSERTION_MASK(this_rank
) == kRankAssertionNever
) {
1667 flags
|= kRouteFlagsIsScoped
;
1669 switch (scope_which
) {
1671 flags
|= kRouteFlagsIsScoped
;
1674 this_route
= RouteListGetRouteAtIndex(info
, routes
, where
+ 1);
1675 flags
|= kRouteFlagsIsScoped
;
1681 if (this_route
!= NULL
&& flags
!= 0) {
1682 this_route
->flags
|= flags
;
1690 * Function: RouteListAddRouteList
1692 * Invoke RouteListAddRoute for each route in the given list
1693 * 'service_routes' combining them into a combined list 'routes'.
1696 * See RouteListAddRoute for more information.
1699 RouteListAddRouteList(RouteListInfoRef info
,
1700 RouteListRef routes
, int init_size
,
1701 RouteListRef service_routes
, Rank rank
)
1706 for (i
= 0, scan
= RouteListGetFirstRoute(info
, service_routes
);
1707 i
< service_routes
->count
;
1708 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1712 && (service_routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
1713 /* only apply rank to first element of the list (default route) */
1717 this_rank
= RANK_INDEX_MASK(rank
) | RANK_ASSERTION_MASK(scan
->rank
);
1719 routes
= RouteListAddRoute(info
, routes
, init_size
, scan
, this_rank
);
1725 RouteAddInterfaceToDescription(RouteRef r
, CFMutableStringRef str
)
1727 char if_name
[IFNAMSIZ
];
1729 if (my_if_indextoname2(r
->ifindex
, if_name
) != NULL
) {
1730 CFStringAppendFormat(str
, NULL
,
1734 if (my_if_indextoname2(r
->exclude_ifindex
, if_name
) != NULL
) {
1735 CFStringAppendFormat(str
, NULL
,
1743 RouteAddFlagsToDescription(RouteRef r
, CFMutableStringRef str
)
1745 if ((r
->flags
& kRouteFlagsIsNULL
) != 0) {
1746 CFStringAppend(str
, CFSTR(" [null]"));
1749 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
1751 switch (rank_assertion
) {
1752 case kRankAssertionFirst
:
1753 CFStringAppend(str
, CFSTR(" [first]"));
1755 case kRankAssertionLast
:
1756 CFStringAppend(str
, CFSTR(" [last]"));
1758 case kRankAssertionNever
:
1759 CFStringAppend(str
, CFSTR(" [never]"));
1764 if ((r
->flags
& kRouteFlagsKernelManaged
) != 0) {
1765 CFStringAppend(str
, CFSTR(" [kern]"));
1767 if ((r
->flags
& kRouteFlagsIsScoped
) != 0) {
1768 CFStringAppend(str
, CFSTR(" [SCOPED]"));
1774 #if !TARGET_OS_SIMULATOR
1776 RouteListFindRoute(RouteListInfoRef info
, RouteListRef routes
, RouteRef route
)
1779 RouteRef match
= NULL
;
1782 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1784 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1785 if ((*info
->route_equal
)(scan
, route
)) {
1795 kRouteLookupFlagsNone
= 0x0,
1796 kRouteLookupFlagsExcludeInterface
= 0x1
1800 RouteListLookup(RouteListInfoRef info
,
1801 RouteListRef routes
,
1802 const void * address
,
1805 RouteLookupFlags lookup_flags
)
1807 RouteRef best_match
= NULL
;
1811 for (i
= 0, candidate
= RouteListGetFirstRoute(info
, routes
);
1813 i
++, candidate
= RouteGetNextRoute(info
, candidate
)) {
1814 if (candidate
->ifindex
== 0 || candidate
->exclude_ifindex
!= 0) {
1815 /* ignore exclude routes */
1818 if ((lookup_flags
& kRouteLookupFlagsExcludeInterface
) != 0) {
1819 /* exclude interfaces with the same interface index */
1820 if (ifindex
== candidate
->ifindex
) {
1824 else if (ifindex
!= candidate
->ifindex
) {
1827 if ((candidate
->flags
& kRouteFlagsHasGateway
) != 0
1828 && RouteAddressCompare(info
,
1829 (*info
->route_gateway
)(candidate
),
1831 /* skip route whose gateway is the address we're looking for */
1834 if ((candidate
->flags
& kRouteFlagsIsHost
) != 0) {
1835 /* if host route and we're looking for an exact match */
1836 if (n_bits
== info
->all_bits_set
1837 && RouteAddressCompare(info
,
1838 (*info
->route_destination
)(candidate
),
1840 /* found exact match */
1841 best_match
= candidate
;
1847 /* verify that address is on the same subnet */
1848 if ((*info
->route_same_subnet
)(candidate
, address
) == FALSE
) {
1849 /* different subnet */
1853 if (candidate
->prefix_length
== n_bits
) {
1855 best_match
= candidate
;
1858 if (candidate
->prefix_length
> n_bits
) {
1859 /* matched too many bits */
1862 if (best_match
== NULL
1863 || candidate
->prefix_length
> best_match
->prefix_length
) {
1864 best_match
= candidate
;
1867 return (best_match
);
1872 * Function: RouteProcess
1874 * Function to process adding or removing the specified route.
1875 * In the case of adding, that may involve first processing the gateway
1876 * route (recursively).
1879 RouteProcess(RouteRef route
,
1881 RouteListApplyContextRef context
)
1883 RouteLog route_log
= context
->info
->route_log
;
1884 RouteApply route_apply
= context
->info
->route_apply
;
1885 RouteGateway route_gateway
= context
->info
->route_gateway
;
1889 case kRouteCommandAdd
:
1890 if ((route
->control_flags
& kControlFlagsProcessed
) != 0) {
1891 return ((route
->control_flags
& kControlFlagsAdded
) != 0);
1893 route
->control_flags
|= kControlFlagsProcessed
;
1894 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
1896 RouteRef gateway_route
;
1899 = RouteListLookup(context
->info
,
1900 context
->new_routes
,
1901 (*route_gateway
)(route
),
1902 context
->info
->all_bits_set
,
1904 kRouteLookupFlagsNone
);
1905 if (gateway_route
== NULL
) {
1906 (*route_log
)(LOG_NOTICE
, route
, "no gateway route");
1909 #define MAX_RECURSE_DEPTH 10
1910 /* avoid infinite recursion */
1911 if (context
->depth
== MAX_RECURSE_DEPTH
) {
1912 (*route_log
)(LOG_NOTICE
, route
, "routing loop detected, not adding");
1915 /* recurse to add gateway route */
1917 added
= RouteProcess(gateway_route
,
1922 (*route_log
)(LOG_NOTICE
, route
, "failed to add");
1927 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1928 if (retval
== EEXIST
) {
1929 /* delete and add again */
1930 (void)(*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
1931 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1936 "failed to add route, %s:",
1938 (*route_log
)(LOG_NOTICE
, route
, NULL
);
1941 case EROUTENOTAPPLIED
:
1942 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1946 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
1947 snprintf(buf
, sizeof(buf
), "%sAdd new[%ld]",
1949 RouteListRouteIndex(context
->info
,
1950 context
->new_routes
,
1952 (*route_log
)(LOG_DEBUG
, route
, buf
);
1954 route
->control_flags
|= kControlFlagsAdded
;
1958 case kRouteCommandRemove
:
1959 retval
= (*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
1963 case EROUTENOTAPPLIED
:
1964 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1968 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
1969 snprintf(buf
, sizeof(buf
), "%sRemove old[%ld]%s",
1971 RouteListRouteIndex(context
->info
,
1972 context
->old_routes
,
1974 (retval
== ESRCH
) ? "(ESRCH)" : "");
1975 (*route_log
)(LOG_DEBUG
, route
, buf
);
1980 "failed to remove route, %s",
1982 (*route_log
)(LOG_NOTICE
, route
, NULL
);
1993 RouteListApply(RouteListInfoRef info
,
1994 RouteListRef old_routes
, RouteListRef new_routes
,
1997 RouteListApplyContext context
;
2001 if (old_routes
== new_routes
&& old_routes
== NULL
) {
2002 /* both old and new are NULL, so there's nothing to do */
2005 bzero(&context
, sizeof(context
));
2006 context
.old_routes
= old_routes
;
2007 context
.new_routes
= new_routes
;
2008 context
.sockfd
= sockfd
;
2009 context
.info
= info
;
2010 if (old_routes
!= NULL
) {
2011 for (i
= 0, scan
= RouteListGetFirstRoute(info
, old_routes
);
2012 i
< old_routes
->count
;
2013 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2014 RouteRef new_route
= NULL
;
2016 if (new_routes
!= NULL
) {
2017 new_route
= RouteListFindRoute(info
, new_routes
, scan
);
2019 if (new_route
== NULL
) {
2020 if ((scan
->control_flags
& kControlFlagsAdded
) != 0) {
2021 RouteProcess(scan
, kRouteCommandRemove
, &context
);
2026 if (new_routes
!= NULL
) {
2027 if (old_routes
!= NULL
) {
2028 /* preserve the control flags from any old routes */
2029 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2030 i
< new_routes
->count
;
2031 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2032 RouteRef old_route
= NULL
;
2034 old_route
= RouteListFindRoute(info
, old_routes
, scan
);
2035 if (old_route
!= NULL
) {
2036 /* preserve the control state in the new route */
2037 scan
->control_flags
= old_route
->control_flags
;
2041 /* add any routes that need to be added */
2042 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2043 i
< new_routes
->count
;
2044 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2045 if ((scan
->control_flags
& kControlFlagsProcessed
) != 0) {
2048 RouteProcess(scan
, kRouteCommandAdd
, &context
);
2054 * Function: RouteListFinalize
2056 * Look for excluded routes. If the excluded route does not have an assigned
2057 * interface, search for a route that *does not* go over the excluded
2060 * If the excluded route does have an assigned interface, search for a route
2061 * that *does* go over the assigned interface.
2063 * Set the gateway on the excluded route to match the gateway of the found
2067 RouteListFinalize(RouteListInfoRef info
, RouteListRef routes
)
2072 if (routes
== NULL
) {
2075 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
2077 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2080 RouteLookupFlags flags
;
2082 if (scan
->exclude_ifindex
== 0) {
2085 if (scan
->ifindex
== 0) {
2086 ifindex
= scan
->exclude_ifindex
;
2087 flags
= kRouteLookupFlagsExcludeInterface
;
2090 ifindex
= scan
->ifindex
;
2091 flags
= kRouteLookupFlagsNone
;
2093 route
= RouteListLookup(info
, routes
,
2094 (*info
->route_destination
)(scan
),
2095 scan
->prefix_length
, ifindex
, flags
);
2096 if (route
== NULL
) {
2097 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
, "can't resolve excluded route");
2100 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
2101 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)scan
, "Excluded route");
2102 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)route
, "Resolved to");
2104 scan
->ifindex
= route
->ifindex
;
2105 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2106 (*info
->route_set_gateway
)(scan
, (*info
->route_gateway
)(route
));
2107 scan
->flags
|= kRouteFlagsHasGateway
;
2108 if (scan
->prefix_length
== info
->all_bits_set
) {
2109 scan
->flags
|= kRouteFlagsIsHost
;
2113 /* routes directly to interface */
2114 scan
->flags
&= ~(kRouteFlagsHasGateway
| kRouteFlagsIsHost
);
2120 #endif /* !TARGET_OS_SIMULATOR */
2126 #define IPV4_ROUTE_ALL_BITS_SET 32
2128 static __inline__
struct in_addr
2129 subnet_addr(struct in_addr addr
, struct in_addr mask
)
2133 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
2138 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
2140 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
2141 CFStringAppendFormat(str
, NULL
,
2142 CFSTR("Host " IP_FORMAT
),
2146 CFStringAppendFormat(str
, NULL
,
2147 CFSTR("Net " IP_FORMAT
),
2149 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
2152 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
2153 CFStringAppendFormat(str
, NULL
,
2154 CFSTR(" Gate " IP_FORMAT
),
2155 IP_LIST(&r
->gateway
));
2157 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
2158 if (r
->ifa
.s_addr
!= 0) {
2159 CFStringAppendFormat(str
, NULL
,
2160 CFSTR(" Ifa " IP_FORMAT
),
2163 RouteAddFlagsToDescription((RouteRef
)r
, str
);
2168 IPv4RouteCopyDescription(RouteRef r
)
2170 CFMutableStringRef str
;
2172 str
= CFStringCreateMutable(NULL
, 0);
2173 IPv4RouteCopyDescriptionWithString((IPv4RouteRef
)r
, str
);
2177 #ifdef TEST_IPV4_ROUTELIST
2178 static CFMutableStringRef
2179 IPv4RouteListCopyDescription(IPv4RouteListRef routes
);
2182 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2184 CFStringRef str
= IPv4RouteCopyDescription(route
);
2187 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2190 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
2196 static __inline__
void
2197 IPv4RouteListPrint(IPv4RouteListRef routes
)
2199 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2201 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2206 #else /* TEST_IPV4_ROUTELIST */
2208 static __inline__
void
2209 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2211 CFStringRef str
= IPv4RouteCopyDescription(route
);
2214 my_log(level
, "%@", str
);
2217 my_log(level
, "%s: %@", msg
, str
);
2223 #endif /* TEST_IPV4_ROUTELIST */
2226 IPv4RouteIsEqual(RouteRef r_scan
, RouteRef r_route
)
2228 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2229 IPv4RouteRef scan
= (IPv4RouteRef
)r_scan
;
2231 return ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
2232 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
2233 && (scan
->ifindex
== route
->ifindex
)
2234 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
2235 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
2236 && (scan
->flags
== route
->flags
));
2239 static CFMutableStringRef
2240 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
2244 CFMutableStringRef str
;
2246 str
= CFStringCreateMutable(NULL
, 0);
2247 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
2249 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
2250 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
2251 IPv4RouteCopyDescriptionWithString(r
, str
);
2253 CFStringAppend(str
, CFSTR("\n}"));
2258 IPv4RouteListComputeSize(CFIndex n
)
2260 return (offsetof(IPv4RouteList
, list
[n
]));
2264 count_prefix_bits_set(uint32_t n
)
2267 const static int8_t bits
[16] = {
2286 for (count
= 0; n
!= 0; n
>>= 4) {
2287 int nbits
= bits
[n
& 0x0f];
2298 prefix_to_mask32(unsigned int prefix_length
)
2300 if (prefix_length
> 32 || prefix_length
== 0) {
2303 return (0xffffffff << (32 - prefix_length
));
2307 mask_get_prefix_length(struct in_addr mask
)
2311 count
= count_prefix_bits_set(mask
.s_addr
);
2315 val
= prefix_to_mask32(count
);
2316 if (ntohl(mask
.s_addr
) != val
) {
2317 /* expected mask based on prefix length doesn't match */
2325 IPv4RouteSetPrefixLength(IPv4RouteRef route
)
2329 length
= mask_get_prefix_length(route
->mask
);
2333 route
->prefix_length
= length
;
2338 IPv4RouteGateway(RouteRef r_route
)
2340 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2341 return (&route
->gateway
);
2345 IPv4RouteSetGateway(RouteRef r_route
, const void * address
)
2347 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2349 route
->gateway
= *((struct in_addr
*)address
);
2354 IPv4RouteDestination(RouteRef r_route
)
2356 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2357 return (&route
->dest
);
2361 IPv4RouteSameSubnet(RouteRef r_route
, const void * addr
)
2363 const struct in_addr
* address
;
2364 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2366 address
= (const struct in_addr
*)addr
;
2367 return ((address
->s_addr
& route
->mask
.s_addr
) == route
->dest
.s_addr
);
2371 * Define: ROUTE_MSG_ADDRS_SPACE
2373 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2374 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2375 * someone changes the code and doesn't think to modify this.
2377 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2378 + 2 * sizeof(struct sockaddr_dl) \
2381 struct rt_msghdr hdr
;
2382 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2386 * Function: IPv4RouteApply
2388 * Add or remove the specified route to/from the kernel routing table.
2391 IPv4RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
2395 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2398 struct sockaddr_in
* in_p
;
2399 struct sockaddr_dl
* dl_p
;
2403 if (S_netboot
&& route
->dest
.s_addr
== 0) {
2404 /* don't touch the default route */
2405 return (EROUTENOTAPPLIED
);
2407 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
2408 return (EROUTENOTAPPLIED
);
2410 if (route
->ifindex
== 0) {
2412 IP_FORMAT
" no interface specified, ignoring",
2413 IP_LIST(&route
->dest
));
2417 #ifdef TEST_IPV4_ROUTELIST
2419 #else /* TEST_IPV4_ROUTELIST */
2421 #endif /* TEST_IPV4_ROUTELIST */
2423 memset(&rtmsg
, 0, sizeof(rtmsg
));
2424 rtmsg
.hdr
.rtm_type
= cmd
;
2425 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2426 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2427 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
2428 if (route
->ifa
.s_addr
!= 0) {
2429 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
2431 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2432 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
2433 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
2436 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
2437 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
2438 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
2441 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2442 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
2444 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
2445 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
2446 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2449 rtaddr
.ptr
= rtmsg
.addrs
;
2452 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2453 rtaddr
.in_p
->sin_family
= AF_INET
;
2454 rtaddr
.in_p
->sin_addr
= route
->dest
;
2455 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2458 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2459 /* gateway is an IP address */
2460 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2461 rtaddr
.in_p
->sin_family
= AF_INET
;
2462 rtaddr
.in_p
->sin_addr
= route
->gateway
;
2463 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2466 /* gateway is the interface itself */
2467 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2468 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2469 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2470 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2474 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
2475 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2476 rtaddr
.in_p
->sin_family
= AF_INET
;
2477 rtaddr
.in_p
->sin_addr
= route
->mask
;
2478 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2482 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
2483 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2484 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2485 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2486 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2488 /* interface address */
2489 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
2490 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2491 rtaddr
.in_p
->sin_family
= AF_INET
;
2492 rtaddr
.in_p
->sin_addr
= route
->ifa
;
2493 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2496 /* apply the route */
2497 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
2498 rtmsg
.hdr
.rtm_msglen
= len
;
2499 if (write(sockfd
, &rtmsg
, len
) == -1) {
2505 static const RouteListInfo IPv4RouteListInfo
= {
2506 IPv4RouteListComputeSize
,
2511 IPv4RouteSetGateway
,
2512 IPv4RouteDestination
,
2513 IPv4RouteSameSubnet
,
2515 IPv4RouteCopyDescription
,
2518 sizeof(struct in_addr
),
2519 IPV4_ROUTE_ALL_BITS_SET
2522 #if !TARGET_OS_SIMULATOR
2523 static __inline__
void
2524 IPv4RouteListLog(int level
, IPv4RouteListRef routes
)
2526 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2528 my_log(level
, "%@", str
);
2534 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
2537 RouteListApply(&IPv4RouteListInfo
,
2538 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
2544 IPv4RouteListFinalize(IPv4RouteListRef routes
)
2546 RouteListFinalize(&IPv4RouteListInfo
, (RouteListRef
)routes
);
2549 #endif /* !TARGET_OS_SIMULATOR */
2551 #ifdef TEST_IPV4_ROUTELIST
2552 static IPv4RouteListRef
2553 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
2554 IPv4RouteListRef service_routes
, Rank rank
)
2556 return ((IPv4RouteListRef
)
2557 RouteListAddRouteList(&IPv4RouteListInfo
,
2558 (RouteListRef
)routes
, init_size
,
2559 (RouteListRef
)service_routes
, rank
));
2561 #endif /* TEST_IPV4_ROUTELIST */
2564 plist_get_string(CFDictionaryRef dict
, CFStringRef prop_name
,
2565 char * buf
, int buf_size
)
2569 val
= CFDictionaryGetValue(dict
, prop_name
);
2570 if (isA_CFString(val
) == NULL
) {
2573 if (!CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingUTF8
)) {
2580 struct in_addr addr
;
2583 IFIndex exclude_ifindex
;
2584 IPv4RouteRef
* route_p
;
2587 } AddIPv4RouteContext
, * AddIPv4RouteContextRef
;
2590 AddIPv4Route(const void * value
, void * context
)
2592 AddIPv4RouteContextRef ctx
= (AddIPv4RouteContextRef
)context
;
2593 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
2594 IPv4RouteRef r
= *ctx
->route_p
;
2596 dict
= isA_CFDictionary(dict
);
2598 || !dict_get_ip(dict
, kSCPropNetIPv4RouteDestinationAddress
, &r
->dest
)
2599 || !dict_get_ip(dict
, kSCPropNetIPv4RouteSubnetMask
, &r
->mask
)) {
2600 /* one less route than we expected */
2602 my_log(LOG_NOTICE
, "%s route is not a dictionary",
2606 my_log(LOG_NOTICE
, "%s route is invalid, %@",
2611 if (!IPv4RouteSetPrefixLength(r
)) {
2612 my_log(LOG_NOTICE
, "%s route has invalid subnet mask, %@",
2616 r
->rank
= ctx
->rank
;
2617 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
2618 if (ctx
->ifindex
!= 0) {
2619 r
->ifindex
= ctx
->ifindex
;
2621 if (ctx
->exclude_ifindex
== 0
2622 && dict_get_ip(dict
,
2623 kSCPropNetIPv4RouteGatewayAddress
,
2625 r
->flags
|= kRouteFlagsHasGateway
;
2626 if (r
->prefix_length
== IPV4_ROUTE_ALL_BITS_SET
) {
2627 r
->flags
|= kRouteFlagsIsHost
;
2632 char ifname
[IFNAMSIZ
];
2634 if (plist_get_string(dict
, kSCPropNetIPv4RouteInterfaceName
,
2635 ifname
, sizeof(ifname
)) != NULL
) {
2638 ifindex
= my_if_nametoindex(ifname
);
2641 "%s: interface %s does not exist, %@",
2642 ctx
->descr
, ifname
, dict
);
2645 else if (ifindex
== ctx
->ifindex
) {
2647 "%s: interface %s unexpected, %@",
2648 ctx
->descr
, ifname
, dict
);
2651 r
->ifindex
= ifindex
;
2664 confirm_interface_name(CFDictionaryRef dict
, CFStringRef ifname
)
2666 CFStringRef confirmed_ifname
;
2667 boolean_t confirmed
;
2670 = CFDictionaryGetValue(dict
, kSCPropConfirmedInterfaceName
);
2671 if (isA_CFString(confirmed_ifname
) != NULL
) {
2672 confirmed
= CFEqual(confirmed_ifname
, ifname
);
2681 * Function: IPv4RouteListCreateWithDictionary
2684 * Given the service ipv4 entity dictionary, generate the list of routes.
2685 * Currently, this includes just the default route and subnet route,
2686 * if the service has a subnet mask.
2689 * If the passed in route_list is NULL or too small, this routine
2690 * allocates malloc'd memory to hold the routes.
2692 static IPv4RouteListRef
2693 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
2694 CFDictionaryRef dict
,
2695 CFNumberRef rank_assertion
)
2697 boolean_t add_broadcast_multicast
= FALSE
;
2698 boolean_t add_default
= FALSE
;
2699 boolean_t add_router_subnet
= FALSE
;
2700 boolean_t add_subnet
= FALSE
;
2701 struct in_addr addr
= { 0 };
2702 CFArrayRef additional_routes
= NULL
;
2703 CFIndex additional_routes_count
;
2704 boolean_t allow_additional_routes
= FALSE
;
2705 boolean_t exclude_from_nwi
= FALSE
;
2706 CFArrayRef excluded_routes
= NULL
;
2707 CFIndex excluded_routes_count
;
2708 RouteFlags flags
= 0;
2710 char ifname
[IFNAMSIZ
];
2711 CFStringRef ifname_cf
;
2712 struct in_addr mask
= { 0 };
2714 int prefix_length
= 0;
2715 Rank primary_rank
= kRankAssertionDefault
;
2717 Rank rank
= kRankAssertionDefault
;
2718 struct in_addr router
= { 0 };
2719 boolean_t scoped_only
= FALSE
;
2720 struct in_addr subnet
= { 0 };
2725 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
2726 ifname
, sizeof(ifname
));
2727 if (ifname_cf
== NULL
) {
2730 ifindex
= my_if_nametoindex(ifname
);
2732 /* interface doesn't exist */
2735 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
2736 if (!dict_get_ip(dict
, kSCPropNetIPv4Router
, &router
)) {
2737 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
2739 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
2740 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
2742 subnet
= subnet_addr(addr
, mask
);
2743 prefix_length
= mask_get_prefix_length(mask
);
2744 if (prefix_length
< 0) {
2746 "ignoring bad subnet mask "
2748 IP_LIST(&mask
), ifname
);
2755 if (addr
.s_addr
== 0) {
2756 /* invalid/non-existent address */
2759 if (rank_assertion
!= NULL
) {
2760 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
2763 if (router
.s_addr
== 0) {
2764 /* if no router is configured, demote the rank if necessary */
2765 switch (primary_rank
) {
2766 case kRankAssertionLast
:
2767 case kRankAssertionNever
:
2768 case kRankAssertionScoped
:
2769 /* rank is already demoted */
2772 /* demote to RankLast */
2773 primary_rank
= kRankAssertionLast
;
2779 * If the router address is our address and the subnet mask is
2780 * not 255.255.255.255, assume all routes are local to the interface.
2782 if (addr
.s_addr
== router
.s_addr
2783 && mask
.s_addr
!= INADDR_BROADCAST
) {
2784 ; /* all routes local */
2787 flags
|= kRouteFlagsHasGateway
;
2789 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
2790 primary_rank
= kRankAssertionFirst
;
2794 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
2795 exclude_from_nwi
= TRUE
;
2796 flags
|= kRouteFlagsIsNULL
;
2799 switch (primary_rank
) {
2800 case kRankAssertionScoped
:
2801 /* Scoped means all routes for the service get scoped */
2802 primary_rank
= rank
= kRankAssertionNever
;
2803 flags
|= kRouteFlagsIsScoped
;
2806 case kRankAssertionNever
:
2807 /* Never means just the default route gets scoped */
2808 rank
= kRankAssertionLast
;
2809 flags
|= kRouteFlagsIsScoped
;
2812 rank
= primary_rank
;
2816 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2817 add_router_subnet
= TRUE
;
2821 if (ifindex
!= lo0_ifindex()) {
2822 if (router
.s_addr
!= 0) {
2826 add_broadcast_multicast
= TRUE
;
2829 if (allow_additional_routes
) {
2831 = CFDictionaryGetValue(dict
, kSCPropNetIPv4AdditionalRoutes
);
2832 additional_routes
= isA_CFArray(additional_routes
);
2833 if (additional_routes
!= NULL
) {
2834 additional_routes_count
= CFArrayGetCount(additional_routes
);
2835 n
+= additional_routes_count
;
2838 = CFDictionaryGetValue(dict
, kSCPropNetIPv4ExcludedRoutes
);
2839 excluded_routes
= isA_CFArray(excluded_routes
);
2840 if (excluded_routes
!= NULL
) {
2841 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
2842 n
+= excluded_routes_count
;
2845 if (routes
== NULL
|| routes
->size
< n
) {
2846 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
2847 bzero(routes
, IPv4RouteListComputeSize(n
));
2851 bzero(routes
->list
, sizeof(routes
->list
[0]) * n
);
2854 if (exclude_from_nwi
) {
2855 routes
->flags
|= kRouteListFlagsExcludeNWI
;
2857 else if (scoped_only
) {
2858 routes
->flags
|= kRouteListFlagsScopedOnly
;
2861 /* start at the beginning */
2865 /* add the default route */
2866 routes
->flags
|= kRouteListFlagsHasDefault
;
2867 r
->ifindex
= ifindex
;
2870 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2871 r
->gateway
= router
;
2876 r
->rank
= primary_rank
;
2879 if (add_broadcast_multicast
) {
2880 /* add the broadcast route (rdar://problem/22149738) */
2881 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2882 r
->flags
|= kRouteFlagsIsNULL
;
2884 r
->dest
.s_addr
= INADDR_BROADCAST
;
2885 r
->mask
.s_addr
= INADDR_BROADCAST
;
2886 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2887 r
->ifindex
= ifindex
;
2892 /* add multicast route (rdar://problem/26457121) */
2893 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2894 r
->flags
|= kRouteFlagsIsNULL
;
2896 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
2897 r
->mask
.s_addr
= htonl(IN_CLASSD_NET
);
2898 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSD
;
2899 r
->ifindex
= ifindex
;
2906 /* add the subnet route */
2908 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2909 r
->flags
|= kRouteFlagsIsNULL
;
2911 r
->ifindex
= ifindex
;
2915 r
->prefix_length
= prefix_length
;
2921 /* add the router subnet route */
2922 if (add_router_subnet
) {
2923 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2924 r
->flags
|= kRouteFlagsIsNULL
;
2926 r
->ifindex
= ifindex
;
2929 r
->mask
.s_addr
= INADDR_BROADCAST
;
2930 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2936 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
2937 AddIPv4RouteContext context
;
2939 bzero(&context
, sizeof(context
));
2940 context
.count_p
= &routes
->count
;
2941 context
.route_p
= &r
;
2942 context
.rank
= rank
;
2944 /* additional routes */
2945 if (additional_routes
!= NULL
) {
2946 context
.ifindex
= ifindex
;
2947 context
.addr
= addr
;
2948 context
.descr
= "AdditionalRoutes";
2949 CFArrayApplyFunction(additional_routes
,
2950 CFRangeMake(0, additional_routes_count
),
2951 AddIPv4Route
, &context
);
2953 /* excluded routes */
2954 if (excluded_routes
!= NULL
) {
2955 context
.descr
= "ExcludedRoutes";
2956 /* exclude this interface */
2957 context
.ifindex
= 0;
2958 context
.exclude_ifindex
= ifindex
;
2959 CFArrayApplyFunction(excluded_routes
,
2960 CFRangeMake(0, excluded_routes_count
),
2961 AddIPv4Route
, &context
);
2967 #if !TARGET_OS_SIMULATOR
2968 static IPv4RouteListRef
2969 IPv4RouteListCopyMulticastLoopback(void)
2972 IPv4RouteListRef routes
;
2974 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(1));
2975 bzero(routes
, IPv4RouteListComputeSize(1));
2976 routes
->count
= routes
->size
= 1;
2979 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
2980 r
->mask
.s_addr
= htonl(IN_CLASSC_NET
);
2981 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSC
;
2982 r
->ifindex
= lo0_ifindex();
2985 #endif /* !TARGET_OS_SIMULATOR */
2990 #define IPV6_ROUTE_ALL_BITS_SET 128
2993 ipv6_prefix_length_is_valid(int prefix_length
)
2995 if (prefix_length
< 0 || prefix_length
> IPV6_ROUTE_ALL_BITS_SET
) {
3002 * from netinet6/in6.c
3005 in6_len2mask(struct in6_addr
* mask
, int len
)
3009 bzero(mask
, sizeof(*mask
));
3010 for (i
= 0; i
< len
/ 8; i
++)
3011 mask
->s6_addr
[i
] = 0xff;
3013 mask
->s6_addr
[i
] = (0xff00 >> (len
% 8)) & 0xff;
3017 in6_maskaddr(struct in6_addr
* addr
, const struct in6_addr
* mask
)
3019 for (size_t i
= 0; i
< sizeof(addr
->s6_addr
); i
++) {
3020 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3026 in6_netaddr(struct in6_addr
* addr
, int len
)
3028 struct in6_addr mask
;
3030 in6_len2mask(&mask
, len
);
3031 in6_maskaddr(addr
, &mask
);
3036 in6_addr_scope_linklocal(struct in6_addr
* addr
, IFIndex ifindex
)
3038 if (IN6_IS_ADDR_LINKLOCAL(addr
)) {
3039 addr
->__u6_addr
.__u6_addr16
[1] = htons(ifindex
);
3045 string_append_in6_addr(CFMutableStringRef str
, const struct in6_addr
* addr
)
3047 char ntopbuf
[INET6_ADDRSTRLEN
];
3049 CFStringAppendCString(str
,
3050 inet_ntop(AF_INET6
, addr
, ntopbuf
, sizeof(ntopbuf
)),
3051 kCFStringEncodingASCII
);
3056 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r
, CFMutableStringRef str
)
3058 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
3059 CFStringAppend(str
, CFSTR("Host "));
3060 string_append_in6_addr(str
, &r
->dest
);
3063 CFStringAppend(str
, CFSTR("Net "));
3064 string_append_in6_addr(str
, &r
->dest
);
3065 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
3068 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
3069 CFStringAppend(str
, CFSTR(" Gate "));
3070 string_append_in6_addr(str
, &r
->gateway
);
3072 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
3073 if (!IN6_ARE_ADDR_EQUAL(&r
->ifa
, &in6addr_any
)) {
3074 CFStringAppend(str
, CFSTR(" Ifa "));
3075 string_append_in6_addr(str
, &r
->ifa
);
3077 RouteAddFlagsToDescription((RouteRef
)r
, str
);
3082 IPv6RouteCopyDescription(RouteRef r
)
3084 CFMutableStringRef str
;
3086 str
= CFStringCreateMutable(NULL
, 0);
3087 IPv6RouteCopyDescriptionWithString((IPv6RouteRef
)r
, str
);
3091 static CFMutableStringRef
3092 IPv6RouteListCopyDescription(IPv6RouteListRef routes
)
3096 CFMutableStringRef str
;
3098 str
= CFStringCreateMutable(NULL
, 0);
3099 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv6RouteList[%d]> = {"),
3101 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
3102 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
3103 IPv6RouteCopyDescriptionWithString(r
, str
);
3105 CFStringAppend(str
, CFSTR("\n}"));
3109 #ifdef TEST_IPV6_ROUTELIST
3112 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3114 CFStringRef str
= IPv6RouteCopyDescription(route
);
3117 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3120 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
3126 static __inline__
void
3127 IPv6RouteListPrint(IPv6RouteListRef routes
)
3129 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3131 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3136 #else /* TEST_IPV6_ROUTELIST */
3138 static __inline__
void
3139 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3141 CFStringRef str
= IPv6RouteCopyDescription(route
);
3144 my_log(level
, "%@", str
);
3147 my_log(level
, "%s: %@", msg
, str
);
3153 #endif /* TEST_IPV6_ROUTELIST */
3156 IPv6RouteListComputeSize(CFIndex n
)
3158 return (offsetof(IPv6RouteList
, list
[n
]));
3163 struct in6_addr
* addr
;
3166 IFIndex exclude_ifindex
;
3167 IPv6RouteRef
* route_p
;
3170 } AddIPv6RouteContext
, * AddIPv6RouteContextRef
;
3173 AddIPv6Route(const void * value
, void * context
)
3175 AddIPv6RouteContextRef ctx
= (AddIPv6RouteContextRef
)context
;
3176 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
3177 IPv6RouteRef r
= *ctx
->route_p
;
3179 dict
= isA_CFDictionary(dict
);
3181 || !dict_get_ipv6(dict
, kSCPropNetIPv6RouteDestinationAddress
, &r
->dest
)
3182 || !dict_get_int(dict
, kSCPropNetIPv6RoutePrefixLength
,
3184 || !ipv6_prefix_length_is_valid(r
->prefix_length
)) {
3185 /* one less route than we expected */
3187 my_log(LOG_NOTICE
, "%s route is not a dictionary",
3191 my_log(LOG_NOTICE
, "%s route is invalid, %@",
3196 r
->rank
= ctx
->rank
;
3197 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
3198 if (ctx
->ifindex
!= 0) {
3199 r
->ifindex
= ctx
->ifindex
;
3200 r
->ifa
= *ctx
->addr
;
3201 if (ctx
->exclude_ifindex
== 0
3202 && dict_get_ipv6(dict
,
3203 kSCPropNetIPv6RouteGatewayAddress
,
3205 r
->flags
|= kRouteFlagsHasGateway
;
3206 if (r
->prefix_length
== IPV6_ROUTE_ALL_BITS_SET
) {
3207 r
->flags
|= kRouteFlagsIsHost
;
3212 char ifname
[IFNAMSIZ
];
3214 if (plist_get_string(dict
, kSCPropNetIPv6RouteInterfaceName
,
3215 ifname
, sizeof(ifname
)) != NULL
) {
3218 ifindex
= my_if_nametoindex(ifname
);
3221 "%s: interface %s does not exist, %@",
3222 ctx
->descr
, ifname
, dict
);
3225 else if (ifindex
== ctx
->ifindex
) {
3227 "%s: interface %s unexpected, %@",
3228 ctx
->descr
, ifname
, dict
);
3231 r
->ifindex
= ifindex
;
3244 * Function: IPv6RouteListCreateWithDictionary
3247 * Given the service IPv6 entity dictionary, generate the list of routes.
3250 * If the passed in route_list is NULL or too small, this routine
3251 * allocates malloc'd memory to hold the routes.
3253 static IPv6RouteListRef
3254 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes
,
3255 CFDictionaryRef dict
,
3256 CFNumberRef rank_assertion
)
3258 boolean_t add_default
= FALSE
;
3259 boolean_t add_prefix
= FALSE
;
3260 struct in6_addr addr
;
3261 CFArrayRef additional_routes
= NULL
;
3262 CFIndex additional_routes_count
;
3263 boolean_t allow_additional_routes
= FALSE
;
3264 boolean_t exclude_from_nwi
= FALSE
;
3265 CFArrayRef excluded_routes
= NULL
;
3266 CFIndex excluded_routes_count
;
3267 RouteFlags flags
= 0;
3269 char ifname
[IFNAMSIZ
];
3270 CFStringRef ifname_cf
;
3272 int prefix_length
= 0;
3273 Rank primary_rank
= kRankAssertionDefault
;
3275 Rank rank
= kRankAssertionDefault
;
3276 struct in6_addr router
= in6addr_any
;
3277 boolean_t scoped_only
= FALSE
;
3282 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
3283 ifname
, sizeof(ifname
));
3284 if (ifname_cf
== NULL
) {
3287 ifindex
= my_if_nametoindex(ifname
);
3289 /* interface doesn't exist */
3292 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
3293 if (!dict_get_ipv6(dict
, kSCPropNetIPv6Router
, &router
)) {
3294 (void)dict_get_first_ipv6(dict
, kSCPropNetIPv6DestAddresses
, &router
);
3296 if (dict_get_first_ipv6(dict
, kSCPropNetIPv6Addresses
, &addr
)) {
3297 if (IN6_IS_ADDR_UNSPECIFIED(&addr
)) {
3300 if (dict_get_first_int(dict
, kSCPropNetIPv6PrefixLength
,
3302 && !IN6_IS_ADDR_LINKLOCAL(&addr
)
3303 && ipv6_prefix_length_is_valid(prefix_length
)) {
3315 if (rank_assertion
!= NULL
) {
3316 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
3319 if (!IN6_IS_ADDR_UNSPECIFIED(&router
)) {
3320 if (ifindex
!= lo0_ifindex()) {
3325 * If the router address is our address and the prefix length is
3326 * not 128, assume all routes are local to the interface.
3328 if (IN6_ARE_ADDR_EQUAL(&router
, &addr
)
3329 && prefix_length
!= IPV6_ROUTE_ALL_BITS_SET
) {
3330 ; /* all routes local */
3333 flags
|= kRouteFlagsHasGateway
;
3335 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
3336 primary_rank
= kRankAssertionFirst
;
3339 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
3340 exclude_from_nwi
= TRUE
;
3341 flags
|= kRouteFlagsIsNULL
;
3344 switch (primary_rank
) {
3345 case kRankAssertionScoped
:
3346 /* Scoped means all routes for the service get scoped */
3347 primary_rank
= rank
= kRankAssertionNever
;
3348 flags
|= kRouteFlagsIsScoped
;
3351 case kRankAssertionNever
:
3352 /* Never means just the default route gets scoped */
3353 rank
= kRankAssertionLast
;
3354 flags
|= kRouteFlagsIsScoped
;
3357 rank
= primary_rank
;
3361 if (allow_additional_routes
) {
3363 = CFDictionaryGetValue(dict
, kSCPropNetIPv6AdditionalRoutes
);
3364 additional_routes
= isA_CFArray(additional_routes
);
3365 if (additional_routes
!= NULL
) {
3366 additional_routes_count
= CFArrayGetCount(additional_routes
);
3367 n
+= additional_routes_count
;
3369 excluded_routes
= CFDictionaryGetValue(dict
,
3370 kSCPropNetIPv6ExcludedRoutes
);
3371 excluded_routes
= isA_CFArray(excluded_routes
);
3372 if (excluded_routes
!= NULL
) {
3373 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
3374 n
+= excluded_routes_count
;
3381 /* need IPv6LL subnet route */
3384 if (routes
== NULL
|| routes
->size
< n
) {
3385 routes
= (IPv6RouteListRef
)malloc(IPv6RouteListComputeSize(n
));
3386 bzero(routes
, IPv6RouteListComputeSize(n
));
3390 bzero(routes
->list
, sizeof(routes
->list
[0]) * n
);
3393 if (exclude_from_nwi
) {
3394 routes
->flags
|= kRouteListFlagsExcludeNWI
;
3396 else if (scoped_only
) {
3397 routes
->flags
|= kRouteListFlagsScopedOnly
;
3400 /* start at the beginning */
3403 /* add the default route */
3404 routes
->flags
|= kRouteListFlagsHasDefault
;
3405 r
->ifindex
= ifindex
;
3408 if ((flags
& kRouteFlagsHasGateway
) != 0) {
3409 r
->gateway
= router
;
3414 r
->rank
= primary_rank
;
3415 r
->flags
|= kRouteFlagsKernelManaged
;
3420 /* add IPv6LL route */
3421 r
->ifindex
= ifindex
;
3422 r
->dest
.s6_addr
[0] = 0xfe;
3423 r
->dest
.s6_addr
[1] = 0x80;
3424 r
->prefix_length
= 64;
3426 r
->flags
|= kRouteFlagsKernelManaged
;
3430 /* add the prefix route(s) */
3432 r
->flags
|= kRouteFlagsKernelManaged
;
3433 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3434 r
->flags
|= kRouteFlagsIsNULL
;
3436 r
->ifindex
= ifindex
;
3439 in6_netaddr(&r
->dest
, prefix_length
);
3440 r
->prefix_length
= prefix_length
;
3446 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3447 AddIPv6RouteContext context
;
3449 bzero(&context
, sizeof(context
));
3450 context
.count_p
= &routes
->count
;
3451 context
.route_p
= &r
;
3452 context
.rank
= rank
;
3454 /* additional routes */
3455 if (additional_routes
!= NULL
) {
3456 context
.ifindex
= ifindex
;
3457 context
.addr
= &addr
;
3458 context
.descr
= "AdditionalRoutes";
3459 CFArrayApplyFunction(additional_routes
,
3460 CFRangeMake(0, additional_routes_count
),
3461 AddIPv6Route
, &context
);
3463 /* excluded routes */
3464 if (excluded_routes
!= NULL
) {
3465 context
.descr
= "ExcludedRoutes";
3466 /* exclude this interface */
3467 context
.ifindex
= 0;
3468 context
.exclude_ifindex
= ifindex
;
3469 context
.addr
= NULL
;
3470 CFArrayApplyFunction(excluded_routes
,
3471 CFRangeMake(0, excluded_routes_count
),
3472 AddIPv6Route
, &context
);
3479 IPv6RouteGateway(RouteRef r_route
)
3481 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3482 return (&route
->gateway
);
3486 IPv6RouteSetGateway(RouteRef r_route
, const void * address
)
3488 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3490 route
->gateway
= *((struct in6_addr
*)address
);
3495 IPv6RouteDestination(RouteRef r_route
)
3497 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3498 return (&route
->dest
);
3501 static __inline__
int
3502 in6_addr_cmp(const struct in6_addr
* a
, const struct in6_addr
* b
)
3504 return (memcmp(a
->s6_addr
, b
->s6_addr
, sizeof(struct in6_addr
)));
3508 IPv6RouteIsEqual(RouteRef r_route1
, RouteRef r_route2
)
3510 IPv6RouteRef route1
= (IPv6RouteRef
)r_route1
;
3511 IPv6RouteRef route2
= (IPv6RouteRef
)r_route2
;
3513 return (route1
->prefix_length
== route2
->prefix_length
3514 && route1
->ifindex
== route2
->ifindex
3515 && route1
->flags
== route2
->flags
3516 && in6_addr_cmp(&route1
->dest
, &route2
->dest
) == 0
3517 && in6_addr_cmp(&route1
->ifa
, &route2
->ifa
) == 0
3518 && in6_addr_cmp(&route1
->gateway
, &route2
->gateway
) == 0);
3522 IPv6RouteSameSubnet(RouteRef r_route
, const void * addr
)
3524 const struct in6_addr
* address
= (const struct in6_addr
*)addr
;
3525 struct in6_addr netaddr
;
3526 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3529 in6_netaddr(&netaddr
, route
->prefix_length
);
3530 return (in6_addr_cmp(&netaddr
, &route
->dest
) == 0);
3534 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3537 struct rt_msghdr hdr
;
3538 char addrs
[V6_ROUTE_MSG_ADDRS_SPACE
];
3542 * Function: IPv6RouteApply
3544 * Add or remove the specified route to/from the kernel routing table.
3547 IPv6RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
3551 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3554 struct sockaddr_in6
* in_p
;
3555 struct sockaddr_dl
* dl_p
;
3559 if ((route
->flags
& kRouteFlagsKernelManaged
) != 0) {
3560 /* the kernel manages this route, don't touch it */
3561 return (EROUTENOTAPPLIED
);
3563 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
3564 return (EROUTENOTAPPLIED
);
3566 if (route
->ifindex
== 0) {
3567 IPv6RouteLog(LOG_NOTICE
, (RouteRef
)route
,
3568 "no interface specified");
3572 #ifdef TEST_IPV6_ROUTELIST
3574 #else /* TEST_IPV6_ROUTELIST */
3576 #endif /* TEST_IPV6_ROUTELIST */
3578 memset(&rtmsg
, 0, sizeof(rtmsg
));
3579 rtmsg
.hdr
.rtm_type
= cmd
;
3580 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3581 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3582 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
3583 if (!IN6_IS_ADDR_UNSPECIFIED(&route
->ifa
)) {
3584 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
3586 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3587 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
3588 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
3591 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
3592 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
3593 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
3596 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
3597 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
3599 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
3600 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
3601 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
3604 rtaddr
.ptr
= rtmsg
.addrs
;
3607 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3608 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3609 rtaddr
.in_p
->sin6_addr
= route
->dest
;
3610 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3611 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3614 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
3615 /* gateway is an IP address */
3616 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3617 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3618 rtaddr
.in_p
->sin6_addr
= route
->gateway
;
3619 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3620 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3623 /* gateway is the interface itself */
3624 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3625 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3626 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3627 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3631 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
3632 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3633 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3634 in6_len2mask(&rtaddr
.in_p
->sin6_addr
, route
->prefix_length
);
3635 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3639 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
3640 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3641 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3642 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3643 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3645 /* interface address */
3646 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
3647 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3648 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3649 rtaddr
.in_p
->sin6_addr
= route
->ifa
;
3650 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3653 /* apply the route */
3654 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
3655 rtmsg
.hdr
.rtm_msglen
= len
;
3656 if (write(sockfd
, &rtmsg
, len
) == -1) {
3662 static const RouteListInfo IPv6RouteListInfo
= {
3663 IPv6RouteListComputeSize
,
3668 IPv6RouteSetGateway
,
3669 IPv6RouteDestination
,
3670 IPv6RouteSameSubnet
,
3672 IPv6RouteCopyDescription
,
3675 sizeof(struct in6_addr
),
3676 IPV6_ROUTE_ALL_BITS_SET
3679 #ifdef TEST_IPV6_ROUTELIST
3680 static IPv6RouteListRef
3681 IPv6RouteListAddRouteList(IPv6RouteListRef routes
, int init_size
,
3682 IPv6RouteListRef service_routes
, Rank rank
)
3684 return ((IPv6RouteListRef
)
3685 RouteListAddRouteList(&IPv6RouteListInfo
,
3686 (RouteListRef
)routes
, init_size
,
3687 (RouteListRef
)service_routes
, rank
));
3689 #endif /* TEST_IPV6_ROUTELIST */
3691 #if !TARGET_OS_SIMULATOR
3692 static __inline__
void
3693 IPv6RouteListLog(int level
, IPv6RouteListRef routes
)
3695 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3697 my_log(level
, "%@", str
);
3703 IPv6RouteListFinalize(IPv6RouteListRef routes
)
3705 RouteListFinalize(&IPv6RouteListInfo
, (RouteListRef
)routes
);
3710 IPv6RouteListApply(IPv6RouteListRef old_routes
, IPv6RouteListRef new_routes
,
3713 RouteListApply(&IPv6RouteListInfo
,
3714 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
3718 #endif /* !TARGET_OS_SIMULATOR */
3721 * Function: parse_component
3723 * Given a string 'key' and a string prefix 'prefix',
3724 * return the next component in the slash '/' separated
3728 * 1. key = "a/b/c" prefix = "a/"
3730 * 2. key = "a/b/c" prefix = "a/b/"
3733 static CF_RETURNS_RETAINED CFStringRef
3734 parse_component(CFStringRef key
, CFStringRef prefix
)
3736 CFMutableStringRef comp
;
3739 if (!CFStringHasPrefix(key
, prefix
)) {
3742 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
3746 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
3747 range
= CFStringFind(comp
, CFSTR("/"), 0);
3748 if (range
.location
== kCFNotFound
) {
3751 range
.length
= CFStringGetLength(comp
) - range
.location
;
3752 CFStringDelete(comp
, range
);
3758 entity_routes_protocol(CFDictionaryRef entity_dict
)
3760 RouteListRef routes
;
3762 routes
= ipdict_get_routelist(entity_dict
);
3763 if (routes
== NULL
) {
3768 if ((routes
->flags
& kRouteListFlagsHasDefault
) == 0) {
3769 // if service has no default route
3773 if ((routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
3774 // if service should be excluded from NWI
3782 __private_extern__ boolean_t
3783 service_contains_protocol(CFDictionaryRef service_dict
, int af
)
3785 boolean_t contains_protocol
;
3787 CFDictionaryRef entity_dict
;
3789 entity
= (af
== AF_INET
) ? kSCEntNetIPv4
: kSCEntNetIPv6
;
3790 entity_dict
= CFDictionaryGetValue(service_dict
, entity
);
3791 if (entity_dict
== NULL
) {
3795 contains_protocol
= entity_routes_protocol(entity_dict
);
3796 return contains_protocol
;
3800 static CFMutableDictionaryRef
3801 service_dict_copy(CFStringRef serviceID
)
3803 CFDictionaryRef d
= NULL
;
3804 CFMutableDictionaryRef service_dict
;
3806 /* create a modifyable dictionary, a copy or a new one */
3807 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3810 = CFDictionaryCreateMutable(NULL
, 0,
3811 &kCFTypeDictionaryKeyCallBacks
,
3812 &kCFTypeDictionaryValueCallBacks
);
3815 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
3817 return (service_dict
);
3821 __private_extern__ boolean_t
3822 service_is_scoped_only(CFDictionaryRef service_dict
)
3824 nwi_ifstate_t alias
;
3825 CFDictionaryRef dict
;
3826 char ifname
[IFNAMSIZ
];
3827 nwi_ifstate_t ifstate
;
3828 CFStringRef interface
= NULL
;
3830 // get IPv4 (or IPv6) info
3831 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3833 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
3836 // if no connectivity
3841 interface
= ipdict_get_ifname(dict
);
3842 if ((interface
== NULL
) ||
3843 !CFStringGetCString(interface
, ifname
, sizeof(ifname
), kCFStringEncodingUTF8
)) {
3844 // if no interface / interface name
3849 if (S_nwi_state
== NULL
) {
3850 S_nwi_state
= nwi_state_copy();
3854 // get [nwi] interface state
3855 ifstate
= nwi_state_get_ifstate(S_nwi_state
, ifname
);
3856 if (ifstate
== NULL
) {
3859 } else if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3860 // if scoped (i.e. not in list)
3864 // check both both IPv4 and IPv6
3865 alias
= nwi_ifstate_get_alias(ifstate
, ifstate
->af
== AF_INET
? AF_INET6
: AF_INET
);
3866 if (alias
== NULL
) {
3867 // if only one address family
3869 } else if ((alias
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3870 // if scoped (i.e. not in list)
3878 log_service_entity(int level
, CFStringRef serviceID
, CFStringRef entity
,
3879 CFStringRef operation
, CFTypeRef val
)
3881 CFMutableStringRef this_val
= NULL
;
3887 if ((is_ipv4
= CFEqual(entity
, kSCEntNetIPv4
))
3888 || (is_ipv6
= CFEqual(entity
, kSCEntNetIPv6
))) {
3889 RouteListUnion routes
;
3891 routes
.ptr
= ipdict_get_routelist(val
);
3892 if (routes
.ptr
!= NULL
) {
3893 CFDictionaryRef service_dict
= NULL
;
3896 this_val
= IPv4RouteListCopyDescription(routes
.v4
);
3899 this_val
= IPv6RouteListCopyDescription(routes
.v6
);
3901 service_dict
= ipdict_get_service(val
);
3902 if (service_dict
!= NULL
) {
3903 CFStringAppendFormat(this_val
, NULL
,
3904 CFSTR("\n<Service> = %@"),
3912 val
= CFSTR("<none>");
3914 my_log(level
, "serviceID %@ %@ %@ value = %@",
3915 serviceID
, operation
, entity
, val
);
3916 my_CFRelease(&this_val
);
3921 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
3924 boolean_t changed
= FALSE
;
3926 CFMutableDictionaryRef service_dict
;
3928 service_dict
= service_dict_copy(serviceID
);
3929 old_val
= CFDictionaryGetValue(service_dict
, entity
);
3930 if (new_val
== NULL
) {
3931 if (old_val
!= NULL
) {
3932 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3933 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3934 CFSTR("Removed:"), old_val
);
3936 CFDictionaryRemoveValue(service_dict
, entity
);
3941 if (old_val
== NULL
|| !CFEqual(new_val
, old_val
)) {
3942 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3943 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3944 CFSTR("Changed: old"), old_val
);
3945 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3946 CFSTR("Changed: new"), new_val
);
3948 CFDictionarySetValue(service_dict
, entity
, new_val
);
3952 if (CFDictionaryGetCount(service_dict
) == 0) {
3953 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
3956 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
3958 my_CFRelease(&service_dict
);
3962 static CFDictionaryRef
3963 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
3965 CFDictionaryRef service_dict
;
3967 if (S_service_state_dict
== NULL
) {
3970 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3971 if (service_dict
== NULL
) {
3974 return (CFDictionaryGetValue(service_dict
, entity
));
3977 #if !TARGET_OS_SIMULATOR
3979 service_copy_interface(CFStringRef serviceID
, CFDictionaryRef new_service
)
3981 CFDictionaryRef dict
;
3982 CFStringRef interface
= NULL
;
3984 if (new_service
!= NULL
) {
3985 interface
= ipdict_get_ifname(new_service
);
3987 if (interface
== NULL
) {
3988 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
3990 interface
= ipdict_get_ifname(dict
);
3993 if (interface
== NULL
) {
3994 dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
3996 interface
= ipdict_get_ifname(dict
);
3999 if (interface
!= NULL
) {
4000 CFRetain(interface
);
4004 #endif /* !TARGET_OS_SIMULATOR */
4006 #ifndef kSCPropNetHostname
4007 #define kSCPropNetHostname CFSTR("Hostname")
4012 copy_dhcp_hostname(CFStringRef serviceID
)
4014 CFDictionaryRef dict
= NULL
;
4015 CFStringRef hostname
= NULL
;
4016 CFDictionaryRef service_dict
= NULL
;
4018 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4022 service_dict
= ipdict_get_service(dict
);
4023 if (service_dict
== NULL
) {
4026 hostname
= CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
4027 if (hostname
!= NULL
) {
4033 #if !TARGET_OS_SIMULATOR
4035 static struct in6_addr
*
4036 ipv6_service_get_router(CFDictionaryRef service
,
4037 IFIndex
* ifindex_p
, CFStringRef
* ifname_p
)
4039 IPv6RouteListRef routes
;
4040 struct in6_addr
* router
= NULL
;
4042 routes
= ipdict_get_routelist(service
);
4044 && (routes
->flags
& kRouteListFlagsExcludeNWI
) == 0
4045 && (routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
4046 router
= &routes
->list
[0].gateway
;
4047 if (*ifindex_p
== 0) {
4048 *ifindex_p
= routes
->list
[0].ifindex
;
4050 if (*ifname_p
== NULL
) {
4051 *ifname_p
= ipdict_get_ifname(service
);
4058 ipv6_service_update_router(CFStringRef serviceID
, CFDictionaryRef new_service
)
4060 IFIndex ifindex
= 0;
4061 CFStringRef ifname
= NULL
;
4062 char ntopbuf
[INET6_ADDRSTRLEN
];
4063 CFDictionaryRef old_service
;
4064 struct in6_addr
* old_router
;
4065 struct in6_addr
* new_router
;
4068 old_service
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4069 old_router
= ipv6_service_get_router(old_service
, &ifindex
, &ifname
);
4070 new_router
= ipv6_service_get_router(new_service
, &ifindex
, &ifname
);
4071 if (ifname
== NULL
|| ifindex
== 0) {
4074 s
= inet6_dgram_socket();
4078 /* remove the old router if it was defined */
4079 if (old_router
!= NULL
4080 && (new_router
== NULL
4081 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4082 if (siocdrdel_in6(s
, ifindex
, old_router
) < 0) {
4083 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4084 "siocdrdel_in6(%@, %s) failed: %s",
4086 inet_ntop(AF_INET6
, old_router
,
4087 ntopbuf
, sizeof(ntopbuf
)),
4092 "%@ removed default route %s",
4094 inet_ntop(AF_INET6
, old_router
, ntopbuf
, sizeof(ntopbuf
)));
4097 /* add the new router if it is defined */
4098 if (new_router
!= NULL
4099 && (old_router
== NULL
4100 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4101 if (siocdradd_in6(s
, ifindex
, new_router
, 0) < 0) {
4102 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4103 "siocdradd_in6(%@, %s) failed: %s",
4105 inet_ntop(AF_INET6
, new_router
,
4106 ntopbuf
, sizeof(ntopbuf
)),
4111 "%@ added default route %s",
4113 inet_ntop(AF_INET6
, new_router
, ntopbuf
, sizeof(ntopbuf
)));
4121 #endif /* !TARGET_OS_SIMULATOR */
4123 #define ALLOW_EMPTY_STRING 0x1
4125 static CF_RETURNS_RETAINED CFTypeRef
4126 sanitize_prop(CFTypeRef val
, uint32_t flags
)
4129 if (isA_CFString(val
)) {
4130 CFMutableStringRef str
;
4132 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
4133 CFStringTrimWhitespace(str
);
4134 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
4148 merge_array_prop(CFMutableDictionaryRef dict
,
4150 CFDictionaryRef state_dict
,
4151 CFDictionaryRef setup_dict
,
4155 CFMutableArrayRef merge_prop
;
4156 CFArrayRef setup_prop
= NULL
;
4157 CFArrayRef state_prop
= NULL
;
4159 if (setup_dict
!= NULL
) {
4160 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
4162 if (state_dict
!= NULL
) {
4163 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
4166 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
4170 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4171 if (setup_prop
!= NULL
) {
4175 n
= CFArrayGetCount(setup_prop
);
4176 for (i
= 0; i
< n
; i
++) {
4179 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
4180 val
= sanitize_prop(val
, flags
);
4182 CFArrayAppendValue(merge_prop
, val
);
4187 if (state_prop
!= NULL
4188 && (setup_prop
== NULL
|| S_append_state
)) {
4191 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
4193 n
= CFArrayGetCount(state_prop
);
4194 for (i
= 0; i
< n
; i
++) {
4197 val
= CFArrayGetValueAtIndex(state_prop
, i
);
4198 val
= sanitize_prop(val
, flags
);
4200 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
4201 CFArrayAppendValue(merge_prop
, val
);
4207 if (CFArrayGetCount(merge_prop
) > 0) {
4208 CFDictionarySetValue(dict
, key
, merge_prop
);
4210 CFRelease(merge_prop
);
4215 pick_prop(CFMutableDictionaryRef dict
,
4217 CFDictionaryRef state_dict
,
4218 CFDictionaryRef setup_dict
,
4221 CFTypeRef val
= NULL
;
4223 if (setup_dict
!= NULL
) {
4224 val
= CFDictionaryGetValue(setup_dict
, key
);
4225 val
= sanitize_prop(val
, flags
);
4227 if (val
== NULL
&& state_dict
!= NULL
) {
4228 val
= CFDictionaryGetValue(state_dict
, key
);
4229 val
= sanitize_prop(val
, flags
);
4232 CFDictionarySetValue(dict
, key
, val
);
4240 ** GetEntityChangesFunc functions
4242 #define IPV4_ROUTES_N_STATIC 5
4243 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4244 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4248 #define IPV4_ROUTES_BUF_DECL(routes) \
4249 IPv4RouteListRef routes; \
4250 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4252 routes = (IPv4RouteListRef)(void *)routes_buf; \
4253 routes->size = IPV4_ROUTES_N_STATIC; \
4254 routes->count = 0; \
4258 IPv4RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4261 CFDataRef routes_data
;
4262 IPV4_ROUTES_BUF_DECL(routes
);
4264 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4266 routes_data
= CFDataCreate(NULL
,
4268 IPv4RouteListComputeSize(r
->count
));
4276 return (routes_data
);
4278 #define IPV6_ROUTES_N_STATIC 3
4279 #define IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4280 (roundup(IPv6RouteListComputeSize(IPV6_ROUTES_N_STATIC), \
4284 #define IPV6_ROUTES_BUF_DECL(routes) \
4285 IPv6RouteListRef routes; \
4286 uint32_t routes_buf[IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4288 routes = (IPv6RouteListRef)(void *)routes_buf; \
4289 routes->size = IPV6_ROUTES_N_STATIC; \
4290 routes->count = 0; \
4294 IPv6RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4297 CFDataRef routes_data
;
4298 IPV6_ROUTES_BUF_DECL(routes
);
4300 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4302 routes_data
= CFDataCreate(NULL
,
4304 IPv6RouteListComputeSize(r
->count
));
4312 return (routes_data
);
4315 static CFDictionaryRef
4316 IPDictCreate(int af
, CFDictionaryRef state_dict
, CFDictionaryRef setup_dict
,
4317 CFNumberRef rank_assertion
)
4319 CFDictionaryRef aggregated_dict
= NULL
;
4320 CFDictionaryRef dict
;
4321 CFMutableDictionaryRef modified_dict
= NULL
;
4322 CFDataRef routes_data
;
4325 if (dict
!= NULL
&& setup_dict
!= NULL
) {
4326 /* look for keys in Setup: that override/merge with State: */
4327 CFArrayRef additional_routes
;
4330 CFStringRef router_prop
;
4331 CFStringRef route_list_prop
;
4336 router_prop
= kSCPropNetIPv4Router
;
4337 route_list_prop
= kSCPropNetIPv4AdditionalRoutes
;
4341 router_prop
= kSCPropNetIPv6Router
;
4342 route_list_prop
= kSCPropNetIPv6AdditionalRoutes
;
4345 router
= CFDictionaryGetValue(setup_dict
, router_prop
);
4347 && !cfstring_to_ipvx(af
, router
, &router_ip
, sizeof(router_ip
))) {
4351 /* AdditionalRoutes */
4353 = CFDictionaryGetValue(setup_dict
, route_list_prop
);
4354 additional_routes
= isA_CFArray(additional_routes
);
4356 if (router
!= NULL
|| additional_routes
!= NULL
) {
4357 modified_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
4358 if (router
!= NULL
) {
4359 CFDictionarySetValue(modified_dict
,
4363 if (additional_routes
!= NULL
) {
4364 CFArrayRef combined_routes
= NULL
;
4365 CFArrayRef state_routes
;
4368 = CFDictionaryGetValue(state_dict
,
4370 if (isA_CFArray(state_routes
) != NULL
) {
4372 = my_CFArrayCreateCombinedArray(additional_routes
,
4374 additional_routes
= combined_routes
;
4376 CFDictionarySetValue(modified_dict
,
4379 if (combined_routes
!= NULL
) {
4380 CFRelease(combined_routes
);
4383 dict
= modified_dict
;
4388 routes_data
= IPv4RouteListDataCreate(dict
, rank_assertion
);
4392 routes_data
= IPv6RouteListDataCreate(dict
, rank_assertion
);
4395 if (routes_data
!= NULL
) {
4396 aggregated_dict
= ipdict_create(dict
, routes_data
);
4397 CFRelease(routes_data
);
4399 if (modified_dict
!= NULL
) {
4400 CFRelease(modified_dict
);
4402 return (aggregated_dict
);
4406 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4407 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4409 #pragma unused(info)
4410 CFDictionaryRef dict
= NULL
;
4411 boolean_t changed
= FALSE
;
4412 CFNumberRef rank_assertion
= NULL
;
4413 CFDictionaryRef service_options
;
4415 if (state_dict
== NULL
) {
4418 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4419 if (service_options
!= NULL
) {
4421 = CFDictionaryGetValue(service_options
,
4422 kServiceOptionRankAssertion
);
4424 dict
= IPDictCreate(AF_INET
, state_dict
, setup_dict
, rank_assertion
);
4427 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, dict
);
4429 /* clean up the rank too */
4430 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
4432 my_CFRelease(&dict
);
4438 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4439 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4441 #pragma unused(info)
4442 CFDictionaryRef dict
= NULL
;
4443 boolean_t changed
= FALSE
;
4444 #if !TARGET_OS_SIMULATOR
4445 CFStringRef interface
;
4446 #endif /* !TARGET_OS_SIMULATOR */
4447 CFNumberRef rank_assertion
= NULL
;
4448 CFDictionaryRef service_options
;
4450 if (state_dict
== NULL
) {
4455 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4456 if (service_options
!= NULL
) {
4458 = CFDictionaryGetValue(service_options
,
4459 kServiceOptionRankAssertion
);
4462 dict
= IPDictCreate(AF_INET6
, state_dict
, setup_dict
, rank_assertion
);
4466 #if !TARGET_OS_SIMULATOR
4467 interface
= service_copy_interface(serviceID
, dict
);
4468 #endif /* !TARGET_OS_SIMULATOR */
4470 #if !TARGET_OS_SIMULATOR
4471 ipv6_service_update_router(serviceID
, dict
);
4472 #endif /* !TARGET_OS_SIMULATOR */
4474 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, dict
);
4476 #if !TARGET_OS_SIMULATOR
4477 if (interface
!= NULL
) {
4479 // IPv6 configuration changed for this interface, poke NAT64
4480 my_CFSetAddValue_async(__network_change_queue(), &S_nat64_prefix_changes
, interface
);
4482 CFRelease(interface
);
4484 #endif /* !TARGET_OS_SIMULATOR */
4487 /* service removed, clean up the rank too */
4488 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
4490 my_CFRelease(&dict
);
4496 __private_extern__ CFDictionaryRef
4497 ipv4_dict_create(CFDictionaryRef state_dict
)
4499 return (IPDictCreate(AF_INET
, state_dict
, NULL
, NULL
));
4502 __private_extern__ CFDictionaryRef
4503 ipv6_dict_create(CFDictionaryRef state_dict
)
4505 return (IPDictCreate(AF_INET6
, state_dict
, NULL
, NULL
));
4508 #endif /* TEST_DNS */
4511 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
4512 CFMutableArrayRef out_servers
, CFStringRef interface
)
4517 count
= CFArrayGetCount(in_servers
);
4518 for (i
= 0; i
< count
; i
++) {
4520 struct in6_addr ipv6_addr
;
4521 struct in_addr ip_addr
;
4523 addr
= CFArrayGetValueAtIndex(in_servers
, i
);
4524 assert(addr
!= NULL
);
4526 if (cfstring_to_ip(addr
, &ip_addr
)) {
4528 if ((active_protos
& kProtocolFlagsIPv4
) == 0
4529 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
4531 "no IPv4 connectivity, "
4532 "ignoring DNS server address " IP_FORMAT
,
4539 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
4541 if ((active_protos
& kProtocolFlagsIPv6
) == 0
4542 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
4543 char ntopbuf
[INET6_ADDRSTRLEN
];
4546 "no IPv6 connectivity, "
4547 "ignoring DNS server address %s",
4548 inet_ntop(AF_INET6
, &ipv6_addr
,
4549 ntopbuf
, sizeof(ntopbuf
)));
4553 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
4554 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
4555 && (interface
!= NULL
)
4556 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
4557 // append interface name to IPv6 link local address
4558 addr
= CFStringCreateWithFormat(NULL
, NULL
,
4567 /* bad IP address */
4568 my_log(LOG_NOTICE
, "ignoring bad DNS server address '%@'", addr
);
4572 /* DNS server is valid and one we want */
4573 CFArrayAppendValue(out_servers
, addr
);
4579 static CF_RETURNS_RETAINED CFArrayRef
4580 order_dns_servers(CFArrayRef servers
, ProtocolFlags active_protos
)
4582 Boolean favor_v4
= FALSE
;
4583 CFMutableArrayRef ordered_servers
;
4584 ProtocolFlags proto_last
= kProtocolFlagsIPv4
;
4585 struct sockaddr_in v4_dns1
= { .sin_family
= AF_INET
,
4586 .sin_len
= sizeof(struct sockaddr_in
) };
4588 struct sockaddr_in6 v6_dns1
= { .sin6_family
= AF_INET6
,
4589 .sin6_len
= sizeof(struct sockaddr_in6
),
4590 .sin6_scope_id
= 0 };
4593 if (((active_protos
& kProtocolFlagsIPv4
) == 0) ||
4594 ((active_protos
& kProtocolFlagsIPv6
) == 0)) {
4595 /* only one protocol */
4596 #ifdef TEST_DNS_ORDER
4597 printf("only one protocol\n");
4598 #endif // TEST_DNS_ORDER
4599 return CFRetain(servers
);
4602 ordered_servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4603 for (CFIndex i
= 0, n
= CFArrayGetCount(servers
); i
< n
; i
++) {
4605 struct in6_addr ia6
;
4606 ProtocolFlags proto
;
4609 server
= CFArrayGetValueAtIndex(servers
, i
);
4610 if (cfstring_to_ip(server
, &ia
)) {
4611 proto
= kProtocolFlagsIPv4
;
4613 v4_dns1
.sin_addr
= ia
;
4615 } else if (cfstring_to_ip6(server
, &ia6
)) {
4616 proto
= kProtocolFlagsIPv6
;
4618 bcopy(&ia6
, &v6_dns1
.sin6_addr
, sizeof(ia6
));
4621 CFRelease(ordered_servers
);
4622 return CFRetain(servers
);
4625 if ((i
> 0) && (proto
!= proto_last
)) {
4626 /* if the protocol of the server addresses changed */
4627 if (((proto
== kProtocolFlagsIPv4
) && (v4_n
== 1)) ||
4628 ((proto
== kProtocolFlagsIPv6
) && (v6_n
== 1))) {
4629 /* if we now have the 1st server address of another protocol */
4630 favor_v4
= (sa_dst_compare_no_dependencies((struct sockaddr
*)&v4_dns1
,
4631 (struct sockaddr
*)&v6_dns1
) >= 0);
4632 #ifdef TEST_DNS_ORDER
4633 char v4_buf
[INET_ADDRSTRLEN
];
4634 char v6_buf
[INET6_ADDRSTRLEN
];
4635 printf("comparing %s vs %s, favoring %s\n",
4636 inet_ntop(v4_dns1
.sin_family
, &v4_dns1
.sin_addr
, v4_buf
, sizeof(v4_buf
)),
4637 inet_ntop(v6_dns1
.sin6_family
, &v6_dns1
.sin6_addr
, v6_buf
, sizeof(v6_buf
)),
4638 favor_v4
? "v4" : "v6");
4639 #endif // TEST_DNS_ORDER
4641 /* if the server addresses array is randomly mixed */
4642 #ifdef TEST_DNS_ORDER
4643 printf("v4/v6 not ordered\n");
4644 #endif // TEST_DNS_ORDER
4645 CFRelease(ordered_servers
);
4646 return CFRetain(servers
);
4651 if ((proto
== kProtocolFlagsIPv4
) && favor_v4
) {
4652 CFArrayInsertValueAtIndex(ordered_servers
, v4_n
- 1, server
);
4653 } else if ((proto
== kProtocolFlagsIPv6
) && !favor_v4
) {
4654 CFArrayInsertValueAtIndex(ordered_servers
, v6_n
- 1, server
);
4656 CFArrayAppendValue(ordered_servers
, server
);
4660 return ordered_servers
;
4664 merge_dns_servers(CFMutableDictionaryRef new_dict
,
4665 CFArrayRef state_servers
,
4666 CFArrayRef setup_servers
,
4668 Boolean trust_state
,
4669 ProtocolFlags active_protos
,
4670 CFStringRef interface
)
4672 CFMutableArrayRef dns_servers
;
4673 Boolean have_dns_setup
= FALSE
;
4675 if (state_servers
== NULL
&& setup_servers
== NULL
) {
4676 /* no DNS servers */
4679 dns_servers
= CFArrayCreateMutable(NULL
, 0,
4680 &kCFTypeArrayCallBacks
);
4681 if (setup_servers
!= NULL
) {
4682 accumulate_dns_servers(setup_servers
, active_protos
,
4683 dns_servers
, interface
);
4684 if (CFArrayGetCount(dns_servers
) > 0) {
4685 have_dns_setup
= TRUE
;
4688 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
4689 && state_servers
!= NULL
) {
4690 CFArrayRef ordered_servers
;
4692 ordered_servers
= order_dns_servers(state_servers
, active_protos
);
4693 accumulate_dns_servers(ordered_servers
, active_protos
,
4695 CFRelease(ordered_servers
);
4699 * Here, we determine whether or not we want all queries for this DNS
4700 * configuration to be bound to the associated network interface.
4702 * For dynamically derived network configurations (i.e. from State:)
4703 * this would be the preferred option using the argument "Hey, the
4704 * server told us to use these servers on this network so let's not
4707 * But, when a DNS configuration has been provided by the user/admin
4708 * via the Network pref pane (i.e. from Setup:) we opt to not force
4709 * binding of the outbound queries. The simplest example why we take
4710 * this stance is with a multi-homing configuration. Consider a system
4711 * with one network service associated with "en0" and a second service
4712 * associated with "en1". The "en0" service has been set higher in
4713 * the network service order so it would be primary but the user/admin
4714 * wants the DNS queries to go to a server only accessible via "en1".
4715 * Without this exception we would take the DNS server addresses from
4716 * the Network pref pane (for "en0") and have the queries bound to
4717 * "en0" where they'd never reach their intended destination (via
4718 * "en1"). So, our exception to the rule is that we will not bind
4719 * user/admin configurations to any specific network interface.
4721 * We also add an exception to the "follow the dynamically derived
4722 * network configuration" path for on-the-fly (no Setup: content)
4725 * But, we add an exception to the exception to support our own
4726 * VPN code. Here, we look for a "ServiceID" property in the DNS
4727 * entity. If present, and if it matches, then we extend our
4728 * trust even when there is no Setup: content.
4730 if (CFArrayGetCount(dns_servers
) != 0) {
4731 CFDictionarySetValue(new_dict
,
4732 kSCPropNetDNSServerAddresses
, dns_servers
);
4733 if ((have_setup
&& !have_dns_setup
) || (!have_setup
&& trust_state
)) {
4734 // if this is a "setup"+"state" service with only "state" DNS content (i.e. no
4735 // setup override) or this is a TRUSTED "state"-only service
4736 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
4740 my_CFRelease(&dns_servers
);
4746 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4747 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4749 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4750 boolean_t changed
= FALSE
;
4752 Boolean have_setup
= FALSE
;
4753 CFStringRef interface
= NULL
;
4754 CFDictionaryRef ipv4
;
4755 CFDictionaryRef ipv6
;
4761 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
4762 { kSCPropNetDNSSortList
, 0, FALSE
},
4763 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4764 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
4766 CFMutableDictionaryRef new_dict
= NULL
;
4767 const CFStringRef pick_list
[] = {
4768 kSCPropNetDNSDomainName
,
4769 kSCPropNetDNSOptions
,
4770 kSCPropNetDNSSearchOrder
,
4771 kSCPropNetDNSServerPort
,
4772 kSCPropNetDNSServerTimeout
,
4773 kSCPropNetDNSServiceIdentifier
,
4774 kSCPropNetDNSSupplementalMatchDomainsNoSearch
,
4776 Boolean trust_state
= FALSE
;
4778 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4779 /* there is no DNS content */
4783 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4784 if (entity_routes_protocol(ipv4
)) {
4785 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
4788 active_protos
|= kProtocolFlagsIPv4
;
4789 interface
= ipdict_get_ifname(ipv4
);
4792 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4793 if (entity_routes_protocol(ipv6
)) {
4795 (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
) != NULL
)) {
4798 active_protos
|= kProtocolFlagsIPv6
;
4799 if (interface
== NULL
) {
4800 interface
= ipdict_get_ifname(ipv6
);
4805 if (active_protos
== kProtocolFlagsNone
) {
4806 /* there is no IPv4 nor IPv6 */
4807 if (state_dict
== NULL
) {
4808 /* ... and no DNS content that we care about */
4814 if (state_dict
!= NULL
) {
4815 CFStringRef state_serviceID
= NULL
;
4817 if (CFDictionaryGetValueIfPresent(state_dict
,
4818 kSCPropNetDNSConfirmedServiceID
,
4819 (const void **)&state_serviceID
) &&
4820 isA_CFString(state_serviceID
) &&
4821 CFEqual(serviceID
, state_serviceID
)) {
4826 /* merge DNS configuration */
4827 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
4828 &kCFTypeDictionaryKeyCallBacks
,
4829 &kCFTypeDictionaryValueCallBacks
);
4831 if (active_protos
== kProtocolFlagsNone
) {
4832 merge_dns_servers(new_dict
,
4833 my_CFDictionaryGetArray(state_dict
,
4834 kSCPropNetDNSServerAddresses
),
4838 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
4842 merge_dns_servers(new_dict
,
4843 my_CFDictionaryGetArray(state_dict
,
4844 kSCPropNetDNSServerAddresses
),
4845 my_CFDictionaryGetArray(setup_dict
,
4846 kSCPropNetDNSServerAddresses
),
4853 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
4854 merge_array_prop(new_dict
,
4858 merge_list
[i
].flags
,
4859 merge_list
[i
].append
);
4862 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
4870 if (active_protos
== kProtocolFlagsNone
) {
4871 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
4872 if (CFDictionaryContainsKey(new_dict
,
4873 kSCPropNetDNSSupplementalMatchDomains
)) {
4874 /* only keep State: supplemental */
4875 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
4876 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
4877 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
4878 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
4880 if ((interface
== NULL
) && (setup_dict
== NULL
) && (state_dict
!= NULL
)) {
4882 * for supplemental-only configurations, add any scoped (or
4883 * wild-card "*") interface
4885 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4887 } else if (CFDictionaryContainsKey(new_dict
, kSCPropNetDNSServiceIdentifier
) &&
4888 (interface
== NULL
) &&
4889 (state_dict
!= NULL
)) {
4890 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4896 if (CFDictionaryGetCount(new_dict
) == 0) {
4897 my_CFRelease(&new_dict
);
4901 if (interface
!= NULL
) {
4902 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
4905 if (S_append_state
) {
4907 * ensure any specified domain name (e.g. the domain returned by
4908 * a DHCP server) is in the search list.
4910 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
4911 if (isA_CFString(domain
)) {
4914 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
4915 if (isA_CFArray(search
) &&
4916 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
4917 CFMutableArrayRef new_search
;
4919 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
4920 CFArrayAppendValue(new_search
, domain
);
4921 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
4922 my_CFRelease(&new_search
);
4929 #if !TARGET_OS_SIMULATOR
4930 if (interface
!= NULL
) {
4931 CFRetain(interface
);
4933 #endif /* !TARGET_OS_SIMULATOR */
4935 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
4937 #if !TARGET_OS_SIMULATOR
4938 if (interface
!= NULL
) {
4940 // DNS configuration changed for this interface, poke NAT64
4941 my_CFSetAddValue_async(__network_change_queue(), &S_nat64_prefix_changes
, interface
);
4943 CFRelease(interface
);
4945 #endif /* !TARGET_OS_SIMULATOR */
4947 my_CFRelease(&new_dict
);
4952 merge_dict(const void *key
, const void *value
, void *context
)
4954 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
4956 CFDictionarySetValue(dict
, key
, value
);
4960 #define PROXY_AUTO_DISCOVERY_URL 252
4962 static CF_RETURNS_RETAINED CFStringRef
4963 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
4965 CFStringRef urlString
= NULL
;
4967 if (dhcp_options
!= NULL
) {
4970 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
4973 const UInt8
*urlBytes
;
4976 urlBytes
= CFDataGetBytePtr(data
);
4977 urlLen
= CFDataGetLength(data
);
4978 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
4979 // remove trailing NUL
4987 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
4989 urlString
= CFURLGetString(url
);
4990 if (urlString
!= NULL
) {
4991 CFRetain(urlString
);
5001 static CF_RETURNS_RETAINED CFStringRef
5005 CFStringRef urlString
= NULL
;
5007 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
5009 urlString
= CFURLGetString(url
);
5010 if (urlString
!= NULL
) {
5011 CFRetain(urlString
);
5020 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5021 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5023 ProtocolFlags active_protos
= kProtocolFlagsNone
;
5024 boolean_t changed
= FALSE
;
5025 CFStringRef interface
= NULL
;
5026 CFDictionaryRef ipv4
;
5027 CFDictionaryRef ipv6
;
5028 CFMutableDictionaryRef new_dict
= NULL
;
5034 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
5035 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
5038 CFStringRef key1
; /* an "enable" key */
5042 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
5043 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
5044 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
5045 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
5046 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
5047 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
5048 { kSCPropNetProxiesProxyAutoConfigEnable
,
5049 kSCPropNetProxiesProxyAutoConfigURLString
,
5050 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
5051 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5056 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
5057 /* there is no proxy content */
5060 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
5061 if (entity_routes_protocol(ipv4
)) {
5062 active_protos
|= kProtocolFlagsIPv4
;
5063 interface
= ipdict_get_ifname(ipv4
);
5065 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
5066 if (entity_routes_protocol(ipv6
)) {
5067 active_protos
|= kProtocolFlagsIPv6
;
5068 if (interface
== NULL
) {
5069 interface
= ipdict_get_ifname(ipv6
);
5072 if (active_protos
== kProtocolFlagsNone
) {
5073 /* there is no IPv4 nor IPv6 */
5074 if (state_dict
== NULL
) {
5075 /* ... and no proxy content that we care about */
5081 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
5082 CFMutableDictionaryRef setup_copy
;
5085 * Merge the per-service "Setup:" and "State:" proxy information with
5086 * the "Setup:" information always taking precedence. Additionally,
5087 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
5088 * Port) is defined than all of the values for that group will be
5089 * used. That is, we don't allow mixing some of the values from
5090 * the "Setup:" keys and others from the "State:" keys.
5092 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5093 for (size_t i
= 0; i
< countof(merge_list
); i
++) {
5094 merge_array_prop(new_dict
,
5098 merge_list
[i
].flags
,
5099 merge_list
[i
].append
);
5102 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5103 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5104 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
5106 * if a "Setup:" enabled key has been provided than we want to
5107 * ignore all of the "State:" keys
5109 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
5110 if (pick_list
[i
].key2
!= NULL
) {
5111 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
5113 if (pick_list
[i
].key3
!= NULL
) {
5114 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
5116 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
5117 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
5118 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
5120 * if a "Setup:" enabled key has not been provided and we have
5121 * some" "State:" keys than we remove all of of "Setup:" keys
5123 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
5124 if (pick_list
[i
].key2
!= NULL
) {
5125 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
5127 if (pick_list
[i
].key3
!= NULL
) {
5128 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
5133 /* merge the "Setup:" keys */
5134 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
5135 CFRelease(setup_copy
);
5137 else if (setup_dict
!= NULL
) {
5138 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5140 else if (state_dict
!= NULL
) {
5141 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5144 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
5145 CFRelease(new_dict
);
5149 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
5150 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5154 if (new_dict
!= NULL
) {
5155 CFDictionaryRef dhcp_options
;
5157 CFNumberRef wpad
= NULL
;
5158 int wpadEnabled
= 0;
5159 CFStringRef wpadURL
= NULL
;
5161 if (CFDictionaryGetValueIfPresent(new_dict
,
5162 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5163 (const void **)&num
) &&
5164 isA_CFNumber(num
)) {
5165 /* if we have a WPAD key */
5167 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
5168 /* if we don't like the enabled key/value */
5176 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
5177 if (!isA_CFNumber(num
) ||
5178 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
5179 /* if we don't like the enabled key/value */
5186 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
5187 if (pacURL
!= NULL
) {
5188 if (!isA_CFString(pacURL
) || (CFStringGetLength(pacURL
) == 0)) {
5189 /* if we don't like the PAC URL */
5195 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
5196 if (!isA_CFString(pacJS
) || (CFStringGetLength(pacJS
) == 0)) {
5197 /* if we don't have (or like) the PAC JavaScript */
5205 * we already have a PAC URL so disable WPAD.
5212 * if WPAD is enabled and we don't already have a PAC URL then
5213 * we check for a DHCP provided URL. If not available, we use
5214 * a PAC URL pointing to a well-known file (wpad.dat) on a
5215 * well-known host (wpad.<domain>).
5217 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
5218 wpadURL
= wpadURL_dhcp(dhcp_options
);
5219 if (wpadURL
== NULL
) {
5220 wpadURL
= wpadURL_dns();
5222 if (wpadURL
== NULL
) {
5223 wpadEnabled
= 0; /* if we don't have a WPAD URL */
5228 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
5229 CFDictionarySetValue(new_dict
,
5230 kSCPropNetProxiesProxyAutoConfigEnable
,
5233 CFDictionarySetValue(new_dict
,
5234 kSCPropNetProxiesProxyAutoConfigURLString
,
5241 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
5242 CFDictionarySetValue(new_dict
,
5243 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5250 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
5251 my_CFRelease(&new_dict
);
5255 #if !TARGET_OS_IPHONE
5257 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5258 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5260 #pragma unused(info)
5261 boolean_t changed
= FALSE
;
5262 CFMutableDictionaryRef new_dict
= NULL
;
5263 const CFStringRef pick_list
[] = {
5264 kSCPropNetSMBNetBIOSName
,
5265 kSCPropNetSMBNetBIOSNodeType
,
5266 #ifdef ADD_NETBIOS_SCOPE
5267 kSCPropNetSMBNetBIOSScope
,
5268 #endif // ADD_NETBIOS_SCOPE
5269 kSCPropNetSMBWorkgroup
,
5272 if (state_dict
== NULL
&& setup_dict
== NULL
) {
5273 /* there is no SMB */
5276 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
5277 && service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
) {
5278 /* there is no IPv4 or IPv6 */
5282 /* merge SMB configuration */
5283 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5284 &kCFTypeDictionaryKeyCallBacks
,
5285 &kCFTypeDictionaryValueCallBacks
);
5286 merge_array_prop(new_dict
,
5287 kSCPropNetSMBWINSAddresses
,
5292 for (size_t i
= 0; i
< countof(pick_list
); i
++) {
5300 if (CFDictionaryGetCount(new_dict
) == 0) {
5301 my_CFRelease(&new_dict
);
5306 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
5307 my_CFRelease(&new_dict
);
5310 #endif /* !TARGET_OS_IPHONE */
5313 services_info_get_interface(CFDictionaryRef services_info
,
5314 CFStringRef serviceID
)
5316 CFStringRef interface
= NULL
;
5317 CFDictionaryRef ipv4_dict
;
5319 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
5321 if (ipv4_dict
!= NULL
) {
5322 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
5325 CFDictionaryRef ipv6_dict
;
5327 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
5329 if (ipv6_dict
!= NULL
) {
5330 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5337 static const struct {
5338 const CFStringRef
* entityName
;
5339 const CFStringRef
* statusKey
;
5340 } transientServiceInfo
[] = {
5341 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
5342 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
5343 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
5347 get_transient_status_changes(CFStringRef serviceID
,
5348 CFDictionaryRef services_info
)
5350 boolean_t changed
= FALSE
;
5352 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5353 CFDictionaryRef dict
;
5354 CFNumberRef status
= NULL
;
5355 CFMutableDictionaryRef ts_dict
= NULL
;
5357 dict
= get_service_state_entity(services_info
, serviceID
,
5358 *transientServiceInfo
[i
].entityName
);
5361 status
= CFDictionaryGetValue(dict
,
5362 *transientServiceInfo
[i
].statusKey
);
5365 if (isA_CFNumber(status
) != NULL
) {
5366 ts_dict
= CFDictionaryCreateMutable(NULL
,
5368 &kCFTypeDictionaryKeyCallBacks
,
5369 &kCFTypeDictionaryValueCallBacks
);
5370 CFDictionaryAddValue(ts_dict
,
5371 *transientServiceInfo
[i
].statusKey
,
5375 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
,
5380 if (ts_dict
!= NULL
) {
5388 if_dict_is_expensive(CFDictionaryRef if_dict
)
5390 boolean_t is_expensive
= FALSE
;
5392 if (isA_CFDictionary(if_dict
) != NULL
) {
5393 CFBooleanRef expensive
;
5394 expensive
= CFDictionaryGetValue(if_dict
, kSCPropNetLinkExpensive
);
5395 if (isA_CFBoolean(expensive
) != NULL
5396 && CFBooleanGetValue(expensive
)) {
5397 is_expensive
= TRUE
;
5400 return is_expensive
;
5404 service_is_expensive(CFStringRef serviceID
, CFDictionaryRef services_info
)
5407 boolean_t is_expensive
= FALSE
;
5409 ifname
= services_info_get_interface(services_info
, serviceID
);
5410 if (ifname
!= NULL
) {
5411 CFDictionaryRef if_dict
;
5414 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5415 if_dict
= CFDictionaryGetValue(services_info
, key
);
5417 is_expensive
= if_dict_is_expensive(if_dict
);
5419 return (is_expensive
);
5423 interface_is_expensive(CFStringRef ifname
)
5425 boolean_t is_expensive
= FALSE
;
5427 if (ifname
!= NULL
) {
5428 CFDictionaryRef if_dict
;
5431 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5432 if_dict
= SCDynamicStoreCopyValue(S_session
, key
);
5434 if (if_dict
!= NULL
) {
5435 is_expensive
= if_dict_is_expensive(if_dict
);
5439 return (is_expensive
);
5443 service_rank_entity_get_index(CFDictionaryRef dict
, CFStringRef serviceID
,
5444 CFStringRef which
, uint32_t * ret_val
)
5446 CFNumberRef service_index
= NULL
;
5449 service_index
= CFDictionaryGetValue(dict
,
5450 kSCPropNetServiceServiceIndex
);
5451 service_index
= isA_CFNumber(service_index
);
5453 if (service_index
!= NULL
) {
5456 if (!CFNumberGetValue(service_index
, kCFNumberSInt32Type
,
5458 || index_val
<= 0) {
5459 /* ServiceIndex must be >= 1 */
5461 "%@%@ ServiceIndex %@ is invalid, ignoring",
5462 which
, serviceID
, service_index
);
5463 service_index
= NULL
;
5465 else if (ret_val
!= NULL
) {
5466 *ret_val
= (uint32_t)index_val
;
5469 return (service_index
);
5473 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
5474 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
5476 boolean_t changed
= FALSE
;
5477 CFStringRef interface
;
5478 boolean_t ip_is_coupled
= FALSE
;
5479 CFMutableDictionaryRef new_dict
= NULL
;
5480 Rank rank_assertion
= kRankAssertionDefault
;
5481 Boolean rank_assertion_is_set
= FALSE
;
5482 CFStringRef setup_rank
= NULL
;
5483 CFStringRef state_rank
= NULL
;
5484 CFNumberRef service_index
= NULL
;
5487 if (setup_options
!= NULL
) {
5488 CFBooleanRef coupled
;
5491 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
5492 setup_rank
= isA_CFString(setup_rank
);
5493 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
5494 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5495 ip_is_coupled
= TRUE
;
5498 = service_rank_entity_get_index(setup_options
,
5500 kSCDynamicStoreDomainSetup
,
5503 if (state_options
!= NULL
) {
5504 CFBooleanRef coupled
;
5507 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
5508 state_rank
= isA_CFString(state_rank
);
5509 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
5510 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5511 ip_is_coupled
= TRUE
;
5513 if (service_index
== NULL
) {
5515 = service_rank_entity_get_index(state_options
,
5517 kSCDynamicStoreDomainState
,
5522 if (!ip_is_coupled
) {
5523 ip_is_coupled
= service_is_expensive(serviceID
, services_info
);
5525 if (setup_rank
!= NULL
|| state_rank
!= NULL
) {
5526 /* rank assertion is set on the service */
5527 Rank setup_assertion
;
5528 Boolean setup_assertion_is_set
= FALSE
;
5529 Rank state_assertion
;
5530 Boolean state_assertion_is_set
= FALSE
;
5532 setup_assertion
= PrimaryRankGetRankAssertion(setup_rank
,
5533 &setup_assertion_is_set
);
5534 state_assertion
= PrimaryRankGetRankAssertion(state_rank
,
5535 &state_assertion_is_set
);
5536 if (setup_assertion_is_set
&& state_assertion_is_set
) {
5537 if (setup_assertion
> state_assertion
) {
5538 rank_assertion
= setup_assertion
;
5541 rank_assertion
= state_assertion
;
5543 rank_assertion_is_set
= TRUE
;
5545 else if (setup_assertion_is_set
) {
5546 rank_assertion
= setup_assertion
;
5547 rank_assertion_is_set
= TRUE
;
5549 else if (state_assertion_is_set
) {
5550 rank_assertion
= state_assertion
;
5551 rank_assertion_is_set
= TRUE
;
5555 interface
= services_info_get_interface(services_info
, serviceID
);
5556 if (interface
!= NULL
) {
5557 if (!rank_assertion_is_set
) {
5558 /* check for a rank assertion on the interface */
5559 CFNumberRef if_rank
= NULL
;
5561 if (S_if_rank_dict
!= NULL
) {
5562 if_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
5565 = InterfaceRankGetRankAssertion(if_rank
,
5566 &rank_assertion_is_set
);
5567 #define kNotSetString ((CFTypeRef)CFSTR("not set"))
5569 "serviceID %@ interface %@ rank = 0x%x (%@)%s",
5573 (if_rank
!= NULL
) ? (CFTypeRef
)if_rank
: kNotSetString
,
5574 ip_is_coupled
? " [coupled]" : "");
5578 "serviceID %@ interface %@ rank = 0x%x%s",
5579 serviceID
, interface
, rank_assertion
,
5580 ip_is_coupled
? " [coupled]" : "");
5585 if (service_index
!= NULL
|| rank_assertion_is_set
|| ip_is_coupled
) {
5586 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5587 &kCFTypeDictionaryKeyCallBacks
,
5588 &kCFTypeDictionaryValueCallBacks
);
5589 if (rank_assertion_is_set
) {
5590 CFNumberRef new_rank
;
5592 new_rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
,
5593 (const void *)&rank_assertion
);
5594 CFDictionarySetValue(new_dict
, kServiceOptionRankAssertion
,
5596 CFRelease(new_rank
);
5598 if (ip_is_coupled
) {
5599 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
5601 if (service_index
!= NULL
) {
5602 CFDictionarySetValue(new_dict
, kSCPropNetServiceServiceIndex
,
5606 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
5607 my_CFRelease(&new_dict
);
5612 add_service_keys(CFStringRef serviceID
,
5613 CFMutableArrayRef keys
, CFMutableArrayRef patterns
)
5618 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
5622 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5623 CFStringRef name
= *entityTypeNames
[i
];
5625 key
= setup_service_key(serviceID
, name
);
5626 CFArrayAppendValue(keys
, key
);
5628 key
= state_service_key(serviceID
, name
);
5629 CFArrayAppendValue(keys
, key
);
5633 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
5634 CFArrayAppendValue(patterns
, key
);
5637 key
= setup_service_key(serviceID
, NULL
);
5638 CFArrayAppendValue(patterns
, key
);
5640 key
= state_service_key(serviceID
, NULL
);
5641 CFArrayAppendValue(patterns
, key
);
5648 add_transient_status_keys(CFStringRef service_id
, CFMutableArrayRef patterns
)
5650 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
5651 CFStringRef pattern
;
5653 pattern
= state_service_key(service_id
,
5654 *transientServiceInfo
[i
].entityName
);
5655 CFArrayAppendValue(patterns
, pattern
);
5662 static const CFStringRef
*reachabilitySetupKeys
[] = {
5664 &kSCEntNetInterface
,
5671 add_reachability_patterns(CFMutableArrayRef patterns
)
5673 for (size_t i
= 0; i
< countof(reachabilitySetupKeys
); i
++) {
5674 CFStringRef pattern
;
5675 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
5676 CFArrayAppendValue(patterns
, pattern
);
5683 add_vpn_pattern(CFMutableArrayRef patterns
)
5685 CFStringRef pattern
;
5687 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
5688 CFArrayAppendValue(patterns
, pattern
);
5693 add_interface_link_pattern(CFMutableArrayRef patterns
)
5695 CFStringRef pattern
;
5697 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetLink
);
5698 CFArrayAppendValue(patterns
, pattern
);
5702 static CFDictionaryRef
5703 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
5706 CFMutableArrayRef get_keys
;
5707 CFMutableArrayRef get_patterns
;
5708 CFDictionaryRef info
;
5710 count
= CFArrayGetCount(service_list
);
5711 get_keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5712 get_patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5714 CFArrayAppendValue(get_keys
, S_setup_global_ipv4
);
5715 CFArrayAppendValue(get_keys
, S_multicast_resolvers
);
5716 CFArrayAppendValue(get_keys
, S_private_resolvers
);
5718 for (CFIndex s
= 0; s
< count
; s
++) {
5719 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
5721 add_service_keys(serviceID
, get_keys
, get_patterns
);
5722 add_transient_status_keys(serviceID
, get_keys
);
5725 add_reachability_patterns(get_patterns
);
5727 add_vpn_pattern(get_patterns
);
5729 add_interface_link_pattern(get_patterns
);
5731 info
= SCDynamicStoreCopyMultiple(session
, get_keys
, get_patterns
);
5732 my_CFRelease(&get_keys
);
5733 my_CFRelease(&get_patterns
);
5737 #if !TARGET_OS_SIMULATOR
5740 set_ipv6_default_interface(IFIndex ifindex
)
5742 struct in6_ndifreq ndifreq
;
5744 boolean_t success
= FALSE
;
5746 bzero((char *)&ndifreq
, sizeof(ndifreq
));
5747 strlcpy(ndifreq
.ifname
, kLoopbackInterface
, sizeof(ndifreq
.ifname
));
5749 ndifreq
.ifindex
= ifindex
;
5752 ndifreq
.ifindex
= lo0_ifindex();
5754 sock
= inet6_dgram_socket();
5758 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
5760 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5771 #endif /* !TARGET_OS_SIMULATOR */
5773 #if !TARGET_OS_IPHONE
5774 static __inline__
void
5777 (void)unlink(VAR_RUN_RESOLV_CONF
);
5781 set_dns(CFArrayRef val_search_domains
,
5782 CFStringRef val_domain_name
,
5783 CFArrayRef val_servers
,
5784 CFArrayRef val_sortlist
)
5786 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
5788 /* publish new resolv.conf */
5793 SCPrint(TRUE
, f
, CFSTR("#\n"));
5794 SCPrint(TRUE
, f
, CFSTR("# macOS Notice\n"));
5795 SCPrint(TRUE
, f
, CFSTR("#\n"));
5796 SCPrint(TRUE
, f
, CFSTR("# This file is not consulted for DNS hostname resolution, address\n"));
5797 SCPrint(TRUE
, f
, CFSTR("# resolution, or the DNS query routing mechanism used by most\n"));
5798 SCPrint(TRUE
, f
, CFSTR("# processes on this system.\n"));
5799 SCPrint(TRUE
, f
, CFSTR("#\n"));
5800 SCPrint(TRUE
, f
, CFSTR("# To view the DNS configuration used by this system, use:\n"));
5801 SCPrint(TRUE
, f
, CFSTR("# scutil --dns\n"));
5802 SCPrint(TRUE
, f
, CFSTR("#\n"));
5803 SCPrint(TRUE
, f
, CFSTR("# SEE ALSO\n"));
5804 SCPrint(TRUE
, f
, CFSTR("# dns-sd(1), scutil(8)\n"));
5805 SCPrint(TRUE
, f
, CFSTR("#\n"));
5806 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
5807 SCPrint(TRUE
, f
, CFSTR("#\n"));
5809 if (isA_CFArray(val_search_domains
)) {
5810 SCPrint(TRUE
, f
, CFSTR("search"));
5811 n
= CFArrayGetCount(val_search_domains
);
5812 for (i
= 0; i
< n
; i
++) {
5815 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
5816 if (isA_CFString(domain
)) {
5817 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
5820 SCPrint(TRUE
, f
, CFSTR("\n"));
5822 else if (isA_CFString(val_domain_name
)) {
5823 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
5826 if (isA_CFArray(val_servers
)) {
5827 n
= CFArrayGetCount(val_servers
);
5828 for (i
= 0; i
< n
; i
++) {
5829 CFStringRef nameserver
;
5831 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
5832 if (isA_CFString(nameserver
)) {
5833 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
5838 if (isA_CFArray(val_sortlist
)) {
5839 SCPrint(TRUE
, f
, CFSTR("sortlist"));
5840 n
= CFArrayGetCount(val_sortlist
);
5841 for (i
= 0; i
< n
; i
++) {
5842 CFStringRef address
;
5844 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
5845 if (isA_CFString(address
)) {
5846 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
5849 SCPrint(TRUE
, f
, CFSTR("\n"));
5853 (void)rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
5857 #endif /* !TARGET_OS_IPHONE */
5860 service_get_ip_is_coupled(CFStringRef serviceID
)
5862 CFDictionaryRef dict
;
5863 boolean_t ip_is_coupled
= FALSE
;
5865 dict
= service_dict_get(serviceID
, kSCEntNetService
);
5867 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
5868 ip_is_coupled
= TRUE
;
5871 return (ip_is_coupled
);
5875 my_CFStringCreateWithInAddr(struct in_addr ip
)
5879 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(IP_FORMAT
), IP_LIST(&ip
));
5884 my_CFStringCreateWithIn6Addr(const struct in6_addr
* ip
)
5886 char ntopbuf
[INET6_ADDRSTRLEN
];
5888 (void)inet_ntop(AF_INET6
, ip
, ntopbuf
, sizeof(ntopbuf
));
5889 return (CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), ntopbuf
));
5893 * Function: update_ipv4
5895 * Update the IPv4 configuration based on the latest information.
5896 * Publish the State:/Network/Global/IPv4 information, and update the
5897 * IPv4 routing table.
5900 update_ipv4(CFStringRef primary
,
5901 IPv4RouteListRef new_routelist
,
5902 keyChangeListRef keys
)
5904 #if !TARGET_OS_SIMULATOR
5906 #endif /* !TARGET_OS_SIMULATOR */
5909 if (new_routelist
!= NULL
&& primary
!= NULL
) {
5910 const char * ifn_p
= NULL
;
5911 char ifname
[IFNAMSIZ
];
5913 CFMutableDictionaryRef dict
= NULL
;
5915 dict
= CFDictionaryCreateMutable(NULL
, 0,
5916 &kCFTypeDictionaryKeyCallBacks
,
5917 &kCFTypeDictionaryValueCallBacks
);
5918 /* the first entry is the default route */
5919 r
= new_routelist
->list
;
5920 if (r
->gateway
.s_addr
!= 0) {
5923 str
= my_CFStringCreateWithInAddr(r
->gateway
);
5924 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, str
);
5927 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
5928 if (ifn_p
!= NULL
) {
5929 CFStringRef ifname_cf
;
5931 ifname_cf
= CFStringCreateWithCString(NULL
,
5933 kCFStringEncodingASCII
);
5934 if (ifname_cf
!= NULL
) {
5935 CFDictionarySetValue(dict
,
5936 kSCDynamicStorePropNetPrimaryInterface
,
5938 CFRelease(ifname_cf
);
5941 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
5943 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
5947 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
5951 #if !TARGET_OS_SIMULATOR
5952 sockfd
= open_routing_socket();
5954 /* go through routelist and bind any unbound routes */
5955 if (new_routelist
!= NULL
) {
5956 IPv4RouteListFinalize(new_routelist
);
5959 /* provide a routelist with just loopback multicast */
5960 new_routelist
= IPv4RouteListCopyMulticastLoopback();
5962 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5963 if (S_ipv4_routelist
== NULL
) {
5964 my_log(LOG_DEBUG
, "Old Routes = <none>");
5967 my_log(LOG_DEBUG
, "Old Routes = ");
5968 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
5970 if (new_routelist
== NULL
) {
5971 my_log(LOG_DEBUG
, "New Routes = <none>");
5974 my_log(LOG_DEBUG
, "New Routes = ");
5975 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
5978 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
, sockfd
);
5981 if (S_ipv4_routelist
!= NULL
) {
5982 free(S_ipv4_routelist
);
5984 S_ipv4_routelist
= new_routelist
;
5985 #else /* !TARGET_OS_SIMULATOR */
5986 if (new_routelist
!= NULL
) {
5987 free(new_routelist
);
5989 #endif /* !TARGET_OS_SIMULATOR */
5995 * Function: update_ipv6
5997 * Update the IPv6 configuration based on the latest information.
5998 * Publish the State:/Network/Global/IPv6 information, and update the
5999 * IPv6 routing table.
6002 update_ipv6(CFStringRef primary
,
6003 IPv6RouteListRef new_routelist
,
6004 keyChangeListRef keys
)
6006 #if !TARGET_OS_SIMULATOR
6008 #endif /* !TARGET_OS_SIMULATOR */
6011 if (new_routelist
!= NULL
&& primary
!= NULL
) {
6012 const char * ifn_p
= NULL
;
6013 char ifname
[IFNAMSIZ
];
6015 CFMutableDictionaryRef dict
= NULL
;
6017 dict
= CFDictionaryCreateMutable(NULL
, 0,
6018 &kCFTypeDictionaryKeyCallBacks
,
6019 &kCFTypeDictionaryValueCallBacks
);
6020 /* the first entry is the default route */
6021 r
= new_routelist
->list
;
6022 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
6025 router
= my_CFStringCreateWithIn6Addr(&r
->gateway
);
6026 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
, router
);
6029 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
6030 if (ifn_p
!= NULL
) {
6031 CFStringRef ifname_cf
;
6033 ifname_cf
= CFStringCreateWithCString(NULL
,
6035 kCFStringEncodingASCII
);
6036 if (ifname_cf
!= NULL
) {
6037 CFDictionarySetValue(dict
,
6038 kSCDynamicStorePropNetPrimaryInterface
,
6040 CFRelease(ifname_cf
);
6043 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
6045 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
6047 #if !TARGET_OS_SIMULATOR
6048 set_ipv6_default_interface(r
->ifindex
);
6049 #endif /* !TARGET_OS_SIMULATOR */
6052 #if !TARGET_OS_SIMULATOR
6053 set_ipv6_default_interface(0);
6054 #endif /* !TARGET_OS_SIMULATOR */
6055 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
6059 #if !TARGET_OS_SIMULATOR
6060 sockfd
= open_routing_socket();
6062 /* go through routelist and bind any unbound routes */
6063 IPv6RouteListFinalize(new_routelist
);
6064 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6065 if (S_ipv6_routelist
== NULL
) {
6066 my_log(LOG_DEBUG
, "Old Routes = <none>");
6069 my_log(LOG_DEBUG
, "Old Routes = ");
6070 IPv6RouteListLog(LOG_DEBUG
, S_ipv6_routelist
);
6072 if (new_routelist
== NULL
) {
6073 my_log(LOG_DEBUG
, "New Routes = <none>");
6076 my_log(LOG_DEBUG
, "New Routes = ");
6077 IPv6RouteListLog(LOG_DEBUG
, new_routelist
);
6080 IPv6RouteListApply(S_ipv6_routelist
, new_routelist
, sockfd
);
6083 if (S_ipv6_routelist
!= NULL
) {
6084 free(S_ipv6_routelist
);
6086 S_ipv6_routelist
= new_routelist
;
6087 #else /* !TARGET_OS_SIMULATOR */
6088 if (new_routelist
!= NULL
) {
6089 free(new_routelist
);
6091 #endif /* !TARGET_OS_SIMULATOR */
6097 update_dns(CFDictionaryRef services_info
,
6098 CFStringRef primary
,
6099 keyChangeListRef keys
)
6101 #pragma unused(services_info)
6102 Boolean changed
= FALSE
;
6103 CFDictionaryRef dict
= NULL
;
6105 if (primary
!= NULL
) {
6106 CFDictionaryRef service_dict
;
6108 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6109 if (service_dict
!= NULL
) {
6110 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6114 if (!_SC_CFEqual(S_dns_dict
, dict
)) {
6116 #if !TARGET_OS_IPHONE
6118 #endif /* !TARGET_OS_IPHONE */
6119 keyChangeListRemoveValue(keys
, S_state_global_dns
);
6121 CFMutableDictionaryRef new_dict
;
6123 #if !TARGET_OS_IPHONE
6124 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
6125 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
6126 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
6127 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
6128 #endif /* !TARGET_OS_IPHONE */
6129 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
6130 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
6131 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
6132 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
6133 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
6134 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
6135 CFRelease(new_dict
);
6140 if (dict
!= NULL
) CFRetain(dict
);
6141 if (S_dns_dict
!= NULL
) CFRelease(S_dns_dict
);
6148 update_dnsinfo(CFDictionaryRef services_info
,
6149 CFStringRef primary
,
6150 keyChangeListRef keys
,
6151 CFArrayRef service_order
)
6154 CFDictionaryRef dict
= NULL
;
6155 CFArrayRef multicastResolvers
;
6156 CFArrayRef privateResolvers
;
6158 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
6159 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
6161 if (primary
!= NULL
) {
6162 CFDictionaryRef service_dict
;
6164 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6165 if (service_dict
!= NULL
) {
6166 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6170 changed
= dns_configuration_set(dict
,
6171 S_service_state_dict
,
6176 keyChangeListNotifyKey(keys
, S_state_global_dns
);
6182 update_nwi(nwi_state_t state
)
6184 unsigned char signature
[CC_SHA1_DIGEST_LENGTH
];
6185 static unsigned char signature_last
[CC_SHA1_DIGEST_LENGTH
];
6187 _nwi_state_compute_sha1_hash(state
, signature
);
6188 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
6189 my_log(LOG_DEBUG
, "Not updating network information");
6193 // save [new] signature
6194 bcopy(signature
, signature_last
, sizeof(signature
));
6196 // save [new] configuration
6197 my_log(LOG_INFO
, "Updating network information");
6198 _nwi_state_log(state
, TRUE
, NULL
);
6200 if (!_nwi_state_store(state
)) {
6201 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
6208 update_proxies(CFDictionaryRef services_info
,
6209 CFStringRef primary
,
6210 keyChangeListRef keys
,
6211 CFArrayRef service_order
)
6213 Boolean changed
= FALSE
;
6214 CFDictionaryRef dict
= NULL
;
6215 CFDictionaryRef new_dict
;
6217 if (primary
!= NULL
) {
6218 CFDictionaryRef service_dict
;
6220 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6221 if (service_dict
!= NULL
) {
6222 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
6226 new_dict
= proxy_configuration_update(dict
,
6227 S_service_state_dict
,
6230 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
6231 if (new_dict
== NULL
) {
6232 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
6234 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
6239 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
6240 S_proxies_dict
= new_dict
;
6245 #if !TARGET_OS_IPHONE
6247 update_smb(CFDictionaryRef services_info
,
6248 CFStringRef primary
,
6249 keyChangeListRef keys
)
6251 #pragma unused(services_info)
6252 Boolean changed
= FALSE
;
6253 CFDictionaryRef dict
= NULL
;
6255 if (primary
!= NULL
) {
6256 CFDictionaryRef service_dict
;
6258 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6259 if (service_dict
!= NULL
) {
6260 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
6264 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
6266 keyChangeListRemoveValue(keys
, S_state_global_smb
);
6268 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
6273 if (dict
!= NULL
) CFRetain(dict
);
6274 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
6279 #endif /* !TARGET_OS_IPHONE */
6282 get_service_index(CFDictionaryRef rank_entity
,
6283 CFArrayRef order
, CFIndex n_order
, CFStringRef serviceID
)
6286 Rank rank
= kRankIndexMask
;
6287 CFNumberRef service_index
;
6290 = service_rank_entity_get_index(rank_entity
,
6294 if (service_index
!= NULL
) {
6295 /* ServiceIndex specified in service entity */
6298 "%@ specifies ServiceIndex %@, effective index is %d",
6299 serviceID
, service_index
, rank
);
6301 else if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
6302 for (i
= 0; i
< n_order
; i
++) {
6303 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
6308 if (CFEqual(serviceID
, s
)) {
6318 ** Service election:
6321 * Function: rank_dict_get_service_rank
6323 * Retrieve the service rank in the given dictionary.
6326 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
6329 Rank rank_val
= kRankAssertionDefault
;
6331 rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
6332 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
6334 if (!CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
)) {
6335 /* if we don't like the rank value */
6336 rank_val
= kRankAssertionDefault
;
6344 * Function: rank_dict_set_service_rank
6346 * Save the results of ranking the service so we can look it up later without
6347 * repeating all of the ranking code.
6350 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
6351 CFStringRef serviceID
, Rank rank_val
)
6355 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
6357 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
6363 static const CFStringRef
*transientInterfaceEntityNames
[] = {
6369 CollectTransientServices(const void * key
,
6373 #pragma unused(value)
6374 CFStringRef service
= key
;
6375 CFMutableArrayRef vif_setup_keys
= context
;
6377 /* This service is either a vpn type service or a comm center service */
6378 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
6382 for (size_t i
= 0; i
< countof(transientInterfaceEntityNames
); i
++) {
6383 if (CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
6384 CFArrayAppendValue(vif_setup_keys
, service
);
6393 static SCNetworkReachabilityFlags
6394 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
6395 CFStringRef service_id
,
6397 CFStringRef vpn_setup_key
)
6400 CFDictionaryRef dict
;
6401 SCNetworkReachabilityFlags flags
= 0;
6404 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6405 kSCDynamicStoreDomainSetup
,
6407 kSCEntNetInterface
);
6408 dict
= CFDictionaryGetValue(services_info
, key
);
6411 if (isA_CFDictionary(dict
)
6412 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
6414 flags
= (kSCNetworkReachabilityFlagsReachable
6415 | kSCNetworkReachabilityFlagsTransientConnection
6416 | kSCNetworkReachabilityFlagsConnectionRequired
);
6418 if (CFEqual(entity
, kSCEntNetPPP
)) {
6420 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
6422 if (!isA_CFDictionary(p_dict
)) {
6426 // get PPP dial-on-traffic status
6427 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
6428 if (isA_CFNumber(num
)) {
6431 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
6433 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6443 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
6445 Boolean ret
= def_value
;
6450 val
= CFDictionaryGetValue(dict
, key
);
6451 if (isA_CFBoolean(val
) != NULL
) {
6452 ret
= CFBooleanGetValue(val
);
6460 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
6461 SCNetworkReachabilityFlags
*reach_flags_v4
,
6462 SCNetworkReachabilityFlags
*reach_flags_v6
)
6466 CFMutableArrayRef vif_setup_keys
;
6468 vif_setup_keys
= CFArrayCreateMutable(NULL
,
6470 &kCFTypeArrayCallBacks
);
6471 CFDictionaryApplyFunction(services_info
, CollectTransientServices
,
6473 count
= CFArrayGetCount(vif_setup_keys
);
6474 for (i
= 0; i
< count
; i
++) {
6475 CFArrayRef components
= NULL
;
6477 CFStringRef service_id
;
6478 CFStringRef vif_setup_key
;
6480 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
6483 * setup key in the following format:
6484 * Setup:/Network/Service/<Service ID>/<Entity>
6486 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
6488 if (CFArrayGetCount(components
) != 5) {
6489 // invalid Setup key encountered
6493 /* service id is the 3rd element */
6494 service_id
= CFArrayGetValueAtIndex(components
, 3);
6496 /* entity id is the 4th element */
6497 entity
= CFArrayGetValueAtIndex(components
, 4);
6500 if (CFEqual(entity
, kSCEntNetPPP
)) {
6501 SCNetworkReachabilityFlags flags
;
6504 flags
= GetReachabilityFlagsFromVPN(services_info
,
6509 /* Check for the v4 reachability flags */
6510 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6511 kSCDynamicStoreDomainSetup
,
6515 if (CFDictionaryContainsKey(services_info
, key
)) {
6516 *reach_flags_v4
|= flags
;
6517 my_log(LOG_DEBUG
, "Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
6522 /* Check for the v6 reachability flags */
6523 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6524 kSCDynamicStoreDomainSetup
,
6528 if (CFDictionaryContainsKey(services_info
, key
)) {
6529 *reach_flags_v6
|= flags
;
6530 my_log(LOG_DEBUG
, "Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
6535 if (components
!= NULL
) {
6536 CFRelease(components
);
6542 if (components
!= NULL
) {
6543 CFRelease(components
);
6547 CFRelease(vif_setup_keys
);
6551 static SCNetworkReachabilityFlags
6552 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
6554 SCNetworkReachabilityFlags flags
= 0;
6556 if (CFEqual(entity
, kSCEntNetPPP
)) {
6559 /* if we're really UP and RUNNING */
6562 /* if we're effectively UP and RUNNING */
6565 /* if we're not connected at all */
6566 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6568 case PPP_STATERESERVED
:
6569 // if we're not connected at all
6570 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6573 /* if we're in the process of [dis]connecting */
6574 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6578 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
6580 case IPSEC_RUNNING
:
6581 /* if we're really UP and RUNNING */
6584 /* if we're not connected at all */
6585 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6588 /* if we're in the process of [dis]connecting */
6589 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6593 else if (CFEqual(entity
, kSCEntNetVPN
)) {
6596 /* if we're really UP and RUNNING */
6601 case VPN_UNLOADING
:
6602 /* if we're not connected at all */
6603 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6606 /* if we're in the process of [dis]connecting */
6607 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6615 VPNAttributesGet(CFStringRef service_id
,
6616 CFDictionaryRef services_info
,
6617 SCNetworkReachabilityFlags
*flags
,
6618 CFStringRef
*server_address
,
6621 CFDictionaryRef entity_dict
;
6623 CFDictionaryRef p_state
= NULL
;
6625 CFStringRef transient_entity
= NULL
;
6627 if (af
== AF_INET
) {
6628 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv4
);
6630 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv6
);
6632 entity_dict
= ipdict_get_service(entity_dict
);
6633 if (entity_dict
== NULL
) {
6637 for (size_t i
= 0; i
< countof(transientServiceInfo
); i
++) {
6638 CFStringRef entity
= *transientServiceInfo
[i
].entityName
;
6640 p_state
= service_dict_get(service_id
, entity
);
6642 /* ensure that this is a VPN Type service */
6643 if (isA_CFDictionary(p_state
)) {
6644 transient_entity
= entity
;
6649 /* Did we find a vpn type service? If not, we are done.*/
6650 if (transient_entity
== NULL
) {
6654 *flags
|= (kSCNetworkReachabilityFlagsReachable
6655 | kSCNetworkReachabilityFlagsTransientConnection
);
6657 /* Get the Server Address */
6658 if (server_address
!= NULL
) {
6659 *server_address
= CFDictionaryGetValue(entity_dict
,
6660 CFSTR("ServerAddress"));
6661 *server_address
= isA_CFString(*server_address
);
6662 if (*server_address
!= NULL
) {
6663 CFRetain(*server_address
);
6668 if (!CFDictionaryGetValueIfPresent(p_state
,
6669 kSCPropNetVPNStatus
, // IPSecStatus, PPPStatus, VPNStatus
6670 (const void **)&num
) ||
6671 !isA_CFNumber(num
) ||
6672 !CFNumberGetValue(num
, kCFNumberIntType
, &status
)) {
6676 *flags
|= GetReachFlagsFromStatus(transient_entity
, status
);
6677 if (CFEqual(transient_entity
, kSCEntNetPPP
)) {
6679 CFDictionaryRef p_setup
;
6682 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6683 kSCDynamicStoreDomainSetup
,
6686 p_setup
= CFDictionaryGetValue(services_info
, key
);
6689 /* get dial-on-traffic status */
6690 if (isA_CFDictionary(p_setup
) &&
6691 CFDictionaryGetValueIfPresent(p_setup
,
6692 kSCPropNetPPPDialOnDemand
,
6693 (const void **)&num
) &&
6694 isA_CFNumber(num
) &&
6695 CFNumberGetValue(num
, kCFNumberIntType
, &ppp_demand
) &&
6696 (ppp_demand
!= 0)) {
6697 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6698 if (status
== PPP_IDLE
) {
6699 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
6707 typedef struct ElectionInfo
{
6713 ElectionResultsRef results
;
6714 CFMutableDictionaryRef rank_dict
;
6715 } ElectionInfo
, * ElectionInfoRef
;
6717 typedef CFDictionaryApplierFunction ElectionFuncRef
;
6720 CandidateRelease(CandidateRef candidate
)
6722 my_CFRelease(&candidate
->serviceID
);
6723 my_CFRelease(&candidate
->if_name
);
6724 my_CFRelease(&candidate
->signature
);
6729 CandidateCopy(CandidateRef dest
, CandidateRef src
)
6732 if (dest
->serviceID
) {
6733 CFRetain(dest
->serviceID
);
6735 if (dest
->if_name
) {
6736 CFRetain(dest
->if_name
);
6738 if(dest
->signature
) {
6739 CFRetain(dest
->signature
);
6744 static ElectionResultsRef
6745 ElectionResultsAlloc(int af
, int size
)
6747 ElectionResultsRef results
;
6749 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
6752 results
->size
= size
;
6757 ElectionResultsRelease(ElectionResultsRef results
)
6762 for (i
= 0, scan
= results
->candidates
;
6765 CandidateRelease(scan
);
6772 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
6777 if (results
== NULL
) {
6778 my_log(level
, "%s: no candidates", prefix
);
6781 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
6782 for (i
= 0, scan
= results
->candidates
;
6785 char ntopbuf
[INET6_ADDRSTRLEN
];
6787 (void)inet_ntop(results
->af
, &scan
->addr
, ntopbuf
, sizeof(ntopbuf
));
6788 my_log(level
, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6789 i
, scan
->if_name
, scan
->serviceID
, ntopbuf
, scan
->rank
,
6790 scan
->ineligible
? " [ineligible]" : "");
6796 * Function: ElectionResultsAddCandidate
6798 * Add the candidate into the election results. Find the insertion point
6799 * by comparing the rank of the candidate with existing entries.
6802 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
6807 if (results
->count
== results
->size
) {
6808 /* this should not happen */
6809 my_log(LOG_NOTICE
, "can't fit another candidate");
6813 /* find the insertion point */
6814 where
= kCFNotFound
;
6815 for (i
= 0; i
< results
->count
; i
++) {
6816 CandidateRef this_candidate
= results
->candidates
+ i
;
6818 if (candidate
->rank
< this_candidate
->rank
) {
6823 /* add it to the end */
6824 if (where
== kCFNotFound
) {
6825 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
6829 /* slide existing entries over */
6830 for (i
= results
->count
; i
> where
; i
--) {
6831 results
->candidates
[i
] = results
->candidates
[i
- 1];
6833 /* insert element */
6834 CandidateCopy(results
->candidates
+ where
, candidate
);
6840 elect_ip(const void * key
, const void * value
, void * context
);
6843 * Function: ElectionResultsCopy
6845 * Visit all of the services and invoke the protocol-specific election
6846 * function. Return the results of the election.
6848 static ElectionResultsRef
6849 ElectionResultsCopy(int af
, CFArrayRef order
)
6854 count
= (int)CFDictionaryGetCount(S_service_state_dict
);
6859 if (af
== AF_INET
) {
6860 info
.entity
= kSCEntNetIPv4
;
6861 info
.rank_dict
= S_ipv4_service_rank_dict
;
6864 info
.entity
= kSCEntNetIPv6
;
6865 info
.rank_dict
= S_ipv6_service_rank_dict
;
6867 info
.results
= ElectionResultsAlloc(af
, count
);
6868 info
.n_services
= count
;
6870 if (order
!= NULL
) {
6871 info
.n_order
= CFArrayGetCount(order
);
6876 CFDictionaryApplyFunction(S_service_state_dict
, elect_ip
, (void *)&info
);
6877 if (info
.results
->count
== 0) {
6878 ElectionResultsRelease(info
.results
);
6879 info
.results
= NULL
;
6881 return (info
.results
);
6885 * Function: ElectionResultsCandidateNeedsDemotion
6887 * Check whether the given candidate requires demotion. A candidate
6888 * might need to be demoted if its IPv4 and IPv6 services must be coupled
6889 * but a higher ranked service has IPv4 or IPv6.
6892 ElectionResultsCandidateNeedsDemotion(CandidateRef other_candidate
,
6893 CandidateRef candidate
)
6895 Boolean ret
= FALSE
;
6897 if (other_candidate
== NULL
6898 || !candidate
->ip_is_coupled
6899 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
6902 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
6903 /* they are over the same interface, no need to demote */
6906 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
6907 /* avoid creating a feedback loop */
6910 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
6911 /* the other candidate isn't eligible to become primary, ignore */
6914 if (candidate
->rank
< other_candidate
->rank
) {
6915 /* we're higher ranked than the other candidate, ignore */
6918 if (candidate
->rank
== other_candidate
->rank
6919 && other_candidate
->ip_is_coupled
) {
6920 /* same rank as another service that is coupled, ignore */
6932 get_signature_sha1(CFStringRef signature
,
6933 unsigned char * sha1
)
6936 CFDataRef signature_data
;
6938 signature_data
= CFStringCreateExternalRepresentation(NULL
,
6940 kCFStringEncodingUTF8
,
6944 CC_SHA1_Update(&ctx
,
6945 CFDataGetBytePtr(signature_data
),
6946 (CC_LONG
)CFDataGetLength(signature_data
));
6947 CC_SHA1_Final(sha1
, &ctx
);
6949 CFRelease(signature_data
);
6956 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
6957 CandidateRef candidate
, Boolean not_in_list
,
6958 Boolean not_in_iflist
)
6961 char ifname
[IFNAMSIZ
];
6962 nwi_ifstate_t ifstate
;
6964 if (nwi_state
== NULL
) {
6969 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
6970 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
6972 if (not_in_iflist
) {
6973 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST
;
6975 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
6976 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
6978 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
6979 kCFStringEncodingASCII
);
6980 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
6981 char ntopbuf
[INET6_ADDRSTRLEN
];
6983 (void)inet_ntop(af
, &candidate
->addr
, ntopbuf
, sizeof(ntopbuf
));
6985 "Adding IPv%c [%s] %s "
6986 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
6987 ipvx_char(af
), ifname
, ntopbuf
,
6988 flags
, candidate
->rank
, candidate
->reachability_flags
);
6990 ifstate
= nwi_state_add_ifstate(nwi_state
, ifname
, af
, flags
,
6992 (void *)&candidate
->addr
,
6993 (void *)&candidate
->vpn_server_addr
,
6994 candidate
->reachability_flags
);
6995 if (ifstate
!= NULL
&& candidate
->signature
) {
6996 uint8_t hash
[CC_SHA1_DIGEST_LENGTH
];
6998 get_signature_sha1(candidate
->signature
, hash
);
6999 nwi_ifstate_set_signature(ifstate
, hash
);
7006 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
7008 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
7009 CFStringRef vpn_server_address
= NULL
;
7011 assert(candidate
!= NULL
);
7012 assert(services_info
!= NULL
);
7014 VPNAttributesGet(candidate
->serviceID
,
7017 &vpn_server_address
,
7020 candidate
->reachability_flags
= flags
;
7022 if (vpn_server_address
== NULL
) {
7023 bzero(&candidate
->vpn_server_addr
, sizeof(candidate
->vpn_server_addr
));
7027 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
),
7028 kCFStringEncodingASCII
);
7029 _SC_string_to_sockaddr(buf
,
7031 (void *)&candidate
->vpn_server_addr
,
7032 sizeof(candidate
->vpn_server_addr
));
7034 CFRelease(vpn_server_address
);
7039 * Function: ElectionResultsGetPrimary
7041 * Use the results of the current protocol and the other protocol to
7042 * determine which service should become primary.
7044 * At the same time, generate the IPv4/IPv6 routing table and
7045 * the nwi_state for the protocol.
7048 ElectionResultsGetPrimary(ElectionResultsRef results
,
7049 CandidateRef other_candidate
,
7050 nwi_state_t nwi_state
, int af
,
7051 RouteListRef
* ret_routes
,
7052 CFDictionaryRef services_info
)
7054 CandidateRef primary
= NULL
;
7055 Boolean primary_is_null
= FALSE
;
7056 RouteListRef routes
= NULL
;
7058 assert(services_info
!= NULL
);
7060 if (results
!= NULL
) {
7061 CandidateRef deferred
[results
->count
];
7063 CFStringRef entity_name
;
7066 RouteListInfoRef info
;
7071 entity_name
= kSCEntNetIPv4
;
7072 info
= &IPv4RouteListInfo
;
7073 initial_size
= results
->count
* IPV4_ROUTES_N_STATIC
;
7077 entity_name
= kSCEntNetIPv6
;
7078 info
= &IPv6RouteListInfo
;
7079 initial_size
= results
->count
* IPV6_ROUTES_N_STATIC
;
7083 for (i
= 0, scan
= results
->candidates
;
7086 Boolean is_primary
= FALSE
;
7087 CFDictionaryRef service_dict
;
7088 RouteListRef service_routes
;
7089 Boolean skip
= FALSE
;
7091 if (!scan
->ineligible
7093 && RANK_ASSERTION_MASK(scan
->rank
) != kRankAssertionNever
) {
7094 if (ElectionResultsCandidateNeedsDemotion(other_candidate
,
7096 /* demote the service */
7098 "IPv%c over %@ demoted: not primary for IPv%c",
7099 ipvx_char(af
), scan
->if_name
, ipvx_other_char(af
));
7100 deferred
[deferred_count
++] = scan
;
7108 /* contribute to the routing table */
7109 service_dict
= service_dict_get(scan
->serviceID
, entity_name
);
7110 service_routes
= ipdict_get_routelist(service_dict
);
7111 if (service_routes
!= NULL
) {
7112 Rank rank
= scan
->rank
;
7115 /* routes are RankNever to prevent becoming primary */
7116 rank
= RankMake(rank
, kRankAssertionNever
);
7118 routes
= RouteListAddRouteList(info
, routes
, initial_size
,
7119 service_routes
, rank
);
7120 if ((service_routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
7128 /* if we're skipping the primary, it's NULL */
7130 primary_is_null
= TRUE
;
7133 else if (!scan
->ineligible
) {
7134 Boolean not_in_iflist
;
7136 add_reachability_flags_to_candidate(scan
, services_info
, af
);
7138 = (service_routes
->flags
& kRouteListFlagsScopedOnly
) != 0;
7139 add_candidate_to_nwi_state(nwi_state
, af
, scan
,
7144 for (i
= 0; i
< deferred_count
; i
++) {
7145 CandidateRef candidate
= deferred
[i
];
7147 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
7148 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, TRUE
, FALSE
);
7151 if (ret_routes
!= NULL
) {
7152 *ret_routes
= routes
;
7154 else if (routes
!= NULL
) {
7157 if (primary_is_null
) {
7166 service_dict_get_signature(CFDictionaryRef service_dict
)
7170 ifname
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7171 if (isA_CFString(ifname
) == NULL
7172 || !confirm_interface_name(service_dict
, ifname
)) {
7175 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
7179 * Function: elect_ip
7181 * Evaluate the service and determine what rank the service should have.
7182 * If it's a suitable candidate, add it to the election results.
7185 elect_ip(const void * key
, const void * value
, void * context
)
7187 CFDictionaryRef all_entities_dict
= (CFDictionaryRef
)value
;
7188 Candidate candidate
;
7190 ElectionInfoRef elect_info
;
7191 CFStringRef if_name
;
7192 CFDictionaryRef ipdict
;
7194 CFDictionaryRef rank_entity
;
7195 RouteListUnion routelist
;
7196 CFDictionaryRef service_dict
;
7198 elect_info
= (ElectionInfoRef
)context
;
7199 ipdict
= CFDictionaryGetValue(all_entities_dict
, elect_info
->entity
);
7200 if (ipdict
!= NULL
) {
7201 routelist
.ptr
= ipdict_get_routelist(ipdict
);
7202 service_dict
= ipdict_get_service(ipdict
);
7205 routelist
.ptr
= NULL
;
7207 if (routelist
.ptr
== NULL
|| service_dict
== NULL
) {
7208 /* no connectivity */
7211 if_name
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7212 if (if_name
== NULL
) {
7213 /* need an interface name */
7216 if (CFEqual(if_name
, CFSTR(kLoopbackInterface
))) {
7217 /* don't process loopback */
7220 bzero(&candidate
, sizeof(candidate
));
7221 candidate
.serviceID
= (CFStringRef
)key
;
7222 if ((routelist
.common
->flags
& kRouteListFlagsHasDefault
) == 0) {
7223 /* no default route means it's ineligible to become primary */
7224 candidate
.ineligible
= TRUE
;
7226 rank_entity
= CFDictionaryGetValue(all_entities_dict
, kSCEntNetService
);
7227 candidate
.rank
= get_service_index(rank_entity
,
7228 elect_info
->order
, elect_info
->n_order
,
7229 candidate
.serviceID
);
7230 if (elect_info
->af
== AF_INET
) {
7231 default_rank
= routelist
.v4
->list
->rank
;
7232 candidate
.addr
.v4
= routelist
.v4
->list
->ifa
;
7235 default_rank
= routelist
.v6
->list
->rank
;
7236 candidate
.addr
.v6
= routelist
.v6
->list
->ifa
;
7238 primary_rank
= RANK_ASSERTION_MASK(default_rank
);
7239 if (S_ppp_override_primary
) {
7242 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
7243 kCFStringEncodingASCII
)
7244 && (strncmp(PPP_PREFIX
, ifn
, sizeof(PPP_PREFIX
) - 1) == 0)) {
7245 /* PPP override: make ppp* look the best */
7246 primary_rank
= kRankAssertionFirst
;
7249 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
7250 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
7251 candidate
.if_name
= if_name
;
7252 rank_dict_set_service_rank(elect_info
->rank_dict
,
7253 candidate
.serviceID
, candidate
.rank
);
7254 candidate
.signature
= service_dict_get_signature(service_dict
);
7255 ElectionResultsAddCandidate(elect_info
->results
, &candidate
);
7261 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
7263 uint32_t changed
= 0;
7266 /* update service options first (e.g. rank) */
7267 if (get_rank_changes(serviceID
,
7268 get_service_state_entity(services_info
, serviceID
,
7270 get_service_setup_entity(services_info
, serviceID
,
7273 changed
|= (1 << kEntityTypeServiceOptions
);
7276 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7277 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
7278 GetEntityChangesFuncRef func
;
7281 func
= entityChangeFunc
[i
];
7282 name
= *entityTypeNames
[i
];
7283 if ((*func
)(serviceID
,
7284 get_service_state_entity(services_info
, serviceID
, name
),
7285 get_service_setup_entity(services_info
, serviceID
, name
),
7287 changed
|= (1 << i
);
7291 /* update transient service status */
7292 if (get_transient_status_changes(serviceID
, services_info
)) {
7293 changed
|= (1 << kEntityTypeTransientStatus
);
7300 serviceID_get_ifname(CFStringRef serviceID
)
7302 CFDictionaryRef entity_dict
;
7303 CFStringRef ifname
= NULL
;
7305 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
7306 if (entity_dict
== NULL
) {
7307 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
7309 if (entity_dict
!= NULL
) {
7310 ifname
= ipdict_get_ifname(entity_dict
);
7315 __private_extern__ boolean_t
7316 check_if_service_expensive(CFStringRef serviceID
)
7319 ifname
= serviceID_get_ifname(serviceID
);
7321 return interface_is_expensive(ifname
);
7325 service_order_get(CFDictionaryRef services_info
)
7327 CFArrayRef order
= NULL
;
7328 CFDictionaryRef ipv4_dict
;
7330 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
7331 S_setup_global_ipv4
);
7332 if (ipv4_dict
!= NULL
) {
7333 CFNumberRef ppp_override
;
7336 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
7337 order
= isA_CFArray(order
);
7339 /* get ppp override primary */
7340 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
7341 kSCPropNetPPPOverridePrimary
);
7342 ppp_override
= isA_CFNumber(ppp_override
);
7343 if (ppp_override
!= NULL
) {
7344 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
7346 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
7349 S_ppp_override_primary
= FALSE
;
7355 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
7356 const char * entity
)
7358 boolean_t changed
= FALSE
;
7359 CFStringRef primary
= *primary_p
;
7361 if (new_primary
!= NULL
) {
7362 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
7363 my_log(LOG_INFO
, "%@ is still primary %s", new_primary
, entity
);
7366 my_CFRelease(primary_p
);
7367 *primary_p
= CFRetain(new_primary
);
7368 my_log(LOG_INFO
, "%@ is the new primary %s", new_primary
, entity
);
7372 else if (primary
!= NULL
) {
7373 my_log(LOG_INFO
, "%@ is no longer primary %s", primary
, entity
);
7374 my_CFRelease(primary_p
);
7381 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
7384 if (service_dict_get(serviceID
, entity
) == NULL
) {
7385 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
7387 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
7391 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
7397 #define N_KEYS_VALUES_STATIC 10
7398 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
7401 count
= CFDictionaryGetCount(S_service_state_dict
);
7402 if (count
<= N_KEYS_VALUES_STATIC
) {
7403 keys
= keys_values_buf
;
7405 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
7407 values
= keys
+ count
;
7408 CFDictionaryGetKeysAndValues(S_service_state_dict
,
7409 (const void * *)keys
,
7410 (const void * *)values
);
7412 for (i
= 0; i
< count
; i
++) {
7413 CFDictionaryRef ipdict
= NULL
;
7414 CFStringRef interface
= NULL
;
7415 CFStringRef serviceID
;
7416 CFDictionaryRef service_dict
;
7418 serviceID
= (CFStringRef
)keys
[i
];
7419 service_dict
= (CFDictionaryRef
)values
[i
];
7421 /* check whether service has IPv4 or IPv6 */
7422 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
7423 if (ipdict
== NULL
) {
7424 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
7425 if (ipdict
== NULL
) {
7429 interface
= ipdict_get_ifname(ipdict
);
7430 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
7432 "Found IP service %@ on interface %@",
7434 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
7437 if (keys
!= keys_values_buf
) {
7443 static __inline__
const char *
7444 get_changed_str(CFStringRef serviceID
, CFStringRef entity
,
7445 CFDictionaryRef old_dict
)
7447 CFDictionaryRef new_dict
= NULL
;
7449 if (serviceID
!= NULL
) {
7450 new_dict
= service_dict_get(serviceID
, entity
);
7453 if (old_dict
== NULL
) {
7454 if (new_dict
!= NULL
) {
7458 if (new_dict
== NULL
) {
7460 } else if (!CFEqual(old_dict
, new_dict
)) {
7467 #if !TARGET_OS_SIMULATOR
7470 #define MANAGE_IF_ORDER
7471 #define MANAGE_IF_IOCTL
7472 #endif /* SIOCSIFORDER */
7474 #ifdef SIOCSIFNETSIGNATURE
7475 #define MANAGE_IF_SIGNATURE
7476 #define MANAGE_IF_IOCTL
7477 #endif /* SIOCSIFNETSIGNATURE */
7479 #ifdef MANAGE_IF_IOCTL
7481 inet_dgram_socket(void)
7485 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
7487 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
7492 #endif /* MANAGE_IF_IOCTL */
7494 #ifdef MANAGE_IF_ORDER
7496 interface_order_changed(nwi_state_t old_state
, nwi_state_t new_state
)
7498 if (old_state
== NULL
&& new_state
== NULL
) {
7499 // Both are NULL, nothing changed
7503 if (old_state
== NULL
|| new_state
== NULL
) {
7504 // One is NULL, something changed
7508 if (old_state
->if_list_count
!= new_state
->if_list_count
) {
7509 // Count is different, something changed
7513 if (new_state
->if_list_count
== 0) {
7514 // Count is same and 0, nothing changed
7519 nwi_ifindex_t
*old_scan
;
7520 nwi_ifindex_t
*new_scan
;
7521 for (i
= 0, old_scan
= nwi_state_if_list(old_state
), new_scan
= nwi_state_if_list(new_state
);
7522 i
< new_state
->if_list_count
; i
++, old_scan
++, new_scan
++) {
7523 if (strcmp(old_state
->ifstate_list
[*old_scan
].ifname
, new_state
->ifstate_list
[*new_scan
].ifname
) != 0) {
7524 // Some interface in the list is different, something changed
7529 // Count and contents are the same, nothing changed
7534 update_interface_order(nwi_state_t state
, int sockfd
)
7536 Boolean success
= FALSE
;
7538 // Set interface order into the kernel
7539 struct if_order interface_order
;
7540 interface_order
.ifo_count
= (uint32_t)state
->if_list_count
;
7541 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)calloc((size_t)interface_order
.ifo_count
, sizeof(uint32_t));
7542 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7544 nwi_ifindex_t
*scan
;
7545 for (i
= 0, scan
= nwi_state_if_list(state
);
7546 i
< state
->if_list_count
; i
++, scan
++) {
7547 const char *ifname
= state
->ifstate_list
[*scan
].ifname
;
7548 ((uint32_t *)interface_order
.ifo_ordered_indices
)[i
] = my_if_nametoindex(ifname
);
7551 if (ioctl(sockfd
, SIOCSIFORDER
, &interface_order
) != 0) {
7552 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
));
7554 my_log(LOG_INFO
, "Set kernel interface order for %u interfaces", interface_order
.ifo_count
);
7557 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7558 free((void *)interface_order
.ifo_ordered_indices
);
7559 interface_order
.ifo_ordered_indices
= (mach_vm_address_t
)NULL
;
7564 #endif /* MANAGE_IF_ORDER */
7566 #ifdef MANAGE_IF_SIGNATURE
7568 siocsifnetsignature(int s
, const char * ifname
, int af
,
7569 const uint8_t * signature
, size_t signature_length
)
7571 struct if_nsreq nsreq
;
7573 bzero(&nsreq
, sizeof(nsreq
));
7574 strlcpy(nsreq
.ifnsr_name
, ifname
, sizeof(nsreq
.ifnsr_name
));
7575 nsreq
.ifnsr_family
= af
;
7576 if (signature_length
> 0) {
7577 if (signature_length
> sizeof(nsreq
.ifnsr_data
)) {
7578 signature_length
= sizeof(nsreq
.ifnsr_data
);
7580 nsreq
.ifnsr_len
= signature_length
;
7581 memcpy(nsreq
.ifnsr_data
, signature
, signature_length
);
7583 return (ioctl(s
, SIOCSIFNETSIGNATURE
, &nsreq
));
7587 process_ifstate_difference(nwi_ifstate_t ifstate
, int af
, int sockfd
)
7589 nwi_ifstate_difference_t diff
;
7590 boolean_t set_signature
= FALSE
;
7591 int signature_length
= 0;
7593 diff
= nwi_ifstate_get_difference(ifstate
);
7595 case knwi_ifstate_difference_changed
:
7596 /* set signature for this interface */
7597 set_signature
= TRUE
;
7598 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_SIGNATURE
) != 0) {
7599 signature_length
= sizeof(ifstate
->signature
);
7602 case knwi_ifstate_difference_removed
:
7603 /* remove signature for this interface */
7604 set_signature
= TRUE
;
7609 if (set_signature
) {
7610 if (siocsifnetsignature(sockfd
, ifstate
->ifname
, af
,
7612 signature_length
) < 0) {
7614 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7615 ifstate
->ifname
, ipvx_char(af
),
7620 my_log(LOG_DEBUG
, "IPv%c Network Signature %s %s",
7622 (signature_length
> 0) ? "Set" : "Cleared",
7624 if (signature_length
> 0
7625 && (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7627 char sig_buf
[signature_length
* 3 + 1];
7630 for (i
= 0; i
< signature_length
; i
++) {
7633 snprintf(byte_buf
, sizeof(byte_buf
),
7634 "%02x ", ifstate
->signature
[i
]);
7635 strlcat(sig_buf
, byte_buf
, sizeof(sig_buf
));
7637 my_log(LOG_DEBUG
, "Signature Bytes: %s", sig_buf
);
7645 process_state_differences(nwi_state_t state
, int af
, int sockfd
)
7651 if (af
== AF_INET
) {
7652 count
= state
->ipv4_count
;
7655 count
= state
->ipv6_count
;
7657 for (i
= 0, scan
= nwi_state_ifstate_list(state
, af
);
7658 i
< count
; i
++, scan
++) {
7659 process_ifstate_difference(scan
, af
, sockfd
);
7663 #endif /* MANAGE_IF_SIGNATURE */
7665 #endif /* !TARGET_OS_SIMULATOR */
7668 process_nwi_changes(CFMutableStringRef log_output
,
7669 nwi_state_t changes_state
,
7670 nwi_state_t new_state
,
7671 nwi_state_t old_state
,
7672 boolean_t dns_changed
,
7673 boolean_t dnsinfo_changed
,
7674 CFDictionaryRef old_primary_dns
,
7675 boolean_t proxy_changed
,
7676 CFDictionaryRef old_primary_proxy
,
7677 boolean_t smb_changed
,
7678 CFDictionaryRef old_primary_smb
)
7680 #ifndef MANAGE_IF_ORDER
7681 #pragma unused(new_state)
7682 #pragma unused(old_state)
7683 #endif // !MANAGE_IF_ORDER
7684 #if TARGET_OS_IPHONE
7685 #pragma unused(smb_changed)
7686 #pragma unused(old_primary_smb)
7687 #endif // TARGET_OS_IPHONE
7689 if (changes_state
!= NULL
) {
7690 const sa_family_t af_list
[] = {AF_INET
, AF_INET6
};
7692 #ifdef MANAGE_IF_IOCTL
7693 int sockfd
= inet_dgram_socket();
7694 #endif /* MANAGE_IF_IOCTL */
7696 #ifdef MANAGE_IF_ORDER
7697 if (interface_order_changed(new_state
, old_state
)) {
7698 update_interface_order(new_state
, sockfd
);
7700 #endif /* MANAGE_IF_ORDER */
7702 for (size_t idx
= 0; idx
< countof(af_list
); idx
++) {
7703 int af
= af_list
[idx
];
7704 CFMutableStringRef changes
= NULL
;
7705 CFMutableStringRef primary_str
= NULL
;
7707 #ifdef MANAGE_IF_SIGNATURE
7708 process_state_differences(changes_state
, af
, sockfd
);
7709 #endif /* MANAGE_IF_SIGNATURE */
7710 scan
= nwi_state_get_first_ifstate(changes_state
, af
);
7711 while (scan
!= NULL
) {
7712 const char * changed_str
;
7714 changed_str
= nwi_ifstate_get_diff_str(scan
);
7715 if (changed_str
!= NULL
) {
7717 const char * addr_str
;
7718 char ntopbuf
[INET6_ADDRSTRLEN
];
7720 address
= (void *)nwi_ifstate_get_address(scan
);
7721 addr_str
= inet_ntop(scan
->af
, address
, ntopbuf
,
7723 if (primary_str
== NULL
) {
7724 primary_str
= CFStringCreateMutable(NULL
, 0);
7725 CFStringAppendFormat(primary_str
, NULL
,
7727 nwi_ifstate_get_ifname(scan
),
7728 changed_str
, addr_str
);
7730 if (changes
== NULL
) {
7731 changes
= CFStringCreateMutable(NULL
, 0);
7733 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
7734 nwi_ifstate_get_ifname(scan
));
7735 if (strcmp(changed_str
, "") != 0) {
7736 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
7737 changed_str
, addr_str
);
7741 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
7744 if (primary_str
!= NULL
) {
7745 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
7746 af
== AF_INET
? "v4" : "v6",
7749 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
7750 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
7753 CFStringAppend(log_output
, CFSTR(")"));
7755 my_CFRelease(&primary_str
);
7756 my_CFRelease(&changes
);
7759 #ifdef MANAGE_IF_IOCTL
7763 #endif /* MANAGE_IF_IOCTL */
7766 if (dns_changed
|| dnsinfo_changed
) {
7769 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
7770 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
7771 str
= "*"; // dnsinfo change w/no change to primary
7773 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
7774 } else if (S_primary_dns
!= NULL
) {
7775 CFStringAppend(log_output
, CFSTR(" DNS"));
7778 if (proxy_changed
) {
7781 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
7782 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
7783 } else if (S_primary_proxies
!= NULL
) {
7784 CFStringAppend(log_output
, CFSTR(" Proxy"));
7787 #if !TARGET_OS_IPHONE
7791 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
7792 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
7793 } else if (S_primary_smb
!= NULL
) {
7794 CFStringAppend(log_output
, CFSTR(" SMB"));
7796 #endif // !TARGET_OS_IPHONE
7802 #pragma mark Network changed notification
7804 static dispatch_queue_t
7805 __network_change_queue()
7807 static dispatch_once_t once
;
7808 static dispatch_queue_t q
;
7810 dispatch_once(&once
, ^{
7811 q
= dispatch_queue_create("network change queue", NULL
);
7817 // Note: must run on __network_change_queue()
7819 post_network_change_when_ready()
7823 dispatch_assert_queue(__network_change_queue());
7825 if (S_network_change_needed
== 0) {
7829 if (!S_network_change_timeout
&&
7830 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
7831 // if we [still] need to wait for the DNS configuration
7832 // or network information changes to be ack'd
7834 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
7835 S_dnsinfo_synced
? "DNS" : "!DNS",
7836 S_nwi_synced
? "nwi" : "!nwi");
7840 // cancel any running timer
7841 if (S_network_change_timer
!= NULL
) {
7842 dispatch_source_cancel(S_network_change_timer
);
7843 dispatch_release(S_network_change_timer
);
7844 S_network_change_timer
= NULL
;
7845 S_network_change_timeout
= FALSE
;
7848 // set (and log?) the post time
7850 struct timeval elapsed
;
7853 (void) gettimeofday(&end
, NULL
);
7854 timersub(&end
, &S_network_change_start
, &elapsed
);
7856 #define QUERY_TIME__FMT "%ld.%6.6d"
7857 #define QUERY_TIME__DIV 1
7860 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
7861 S_network_change_timeout
? "timeout" : "delayed",
7863 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
7864 S_network_change_needed
);
7868 /* We are about to post a network change to everyone, get the agents up to date */
7869 #if !TARGET_OS_SIMULATOR
7870 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
7871 /* Setup or Update config agents */
7872 process_AgentMonitor_DNS();
7874 #endif //!TARGET_OS_SIMULATOR
7876 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
7877 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
7878 if (status
!= NOTIFY_STATUS_OK
) {
7880 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%d", status
);
7884 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
7885 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
7886 if (status
!= NOTIFY_STATUS_OK
) {
7888 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%d", status
);
7892 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
7893 #if !TARGET_OS_SIMULATOR
7894 /* Setup or Update config agents */
7895 process_AgentMonitor_Proxy();
7896 #endif //!TARGET_OS_SIMULATOR
7897 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
7898 if (status
!= NOTIFY_STATUS_OK
) {
7900 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%d", status
);
7904 if ((S_network_change_needed
& NETWORK_CHANGE_NAT64
) != 0) {
7905 #if !TARGET_OS_SIMULATOR
7906 // process any NAT64 prefix update requests (and refresh existing prefixes on change)
7907 if ((S_nat64_prefix_requests
!= NULL
) || (S_nat64_prefix_changes
!= NULL
)) {
7908 nat64_configuration_update(S_nat64_prefix_requests
, S_nat64_prefix_changes
);
7909 my_CFRelease(&S_nat64_prefix_requests
);
7910 my_CFRelease(&S_nat64_prefix_changes
);
7912 #endif /* !TARGET_OS_SIMULATOR */
7914 S_network_change_needed
&= ~(NETWORK_CHANGE_NAT64
);
7917 if (S_network_change_needed
!= 0) {
7918 // if more than just a NAT64 prefix change
7919 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
7920 if (status
!= NOTIFY_STATUS_OK
) {
7922 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%d", status
);
7926 S_network_change_needed
= 0;
7930 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
7932 // Note: must run on __network_change_queue()
7934 post_network_change(uint32_t change
)
7936 dispatch_assert_queue(__network_change_queue());
7938 if (S_network_change_needed
== 0) {
7939 // set the start time
7940 (void) gettimeofday(&S_network_change_start
, NULL
);
7943 // indicate that we need to post a change for ...
7944 S_network_change_needed
|= change
;
7946 // cancel any running timer
7947 if (S_network_change_timer
!= NULL
) {
7948 dispatch_source_cancel(S_network_change_timer
);
7949 dispatch_release(S_network_change_timer
);
7950 S_network_change_timer
= NULL
;
7951 S_network_change_timeout
= FALSE
;
7954 // if needed, start new timer
7955 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
7956 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
7959 __network_change_queue());
7960 dispatch_source_set_event_handler(S_network_change_timer
, ^{
7961 os_activity_t activity
;
7963 activity
= os_activity_create("posting delayed network change",
7964 OS_ACTIVITY_CURRENT
,
7965 OS_ACTIVITY_FLAG_DEFAULT
);
7966 os_activity_scope(activity
);
7968 S_network_change_timeout
= TRUE
;
7969 post_network_change_when_ready();
7971 os_release(activity
);
7973 dispatch_source_set_timer(S_network_change_timer
,
7974 dispatch_time(DISPATCH_TIME_NOW
,
7975 TRAILING_EDGE_TIMEOUT_NSEC
), // start
7976 DISPATCH_TIME_FOREVER
, // interval
7977 10 * NSEC_PER_MSEC
); // leeway
7978 dispatch_resume(S_network_change_timer
);
7981 post_network_change_when_ready();
7987 #pragma mark Process network (SCDynamicStore) changes
7990 IPMonitorProcessChanges(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
7991 CFArrayRef if_rank_changes
)
7994 uint32_t changes
= 0;
7995 nwi_state_t changes_state
= NULL
;
7996 boolean_t dns_changed
= FALSE
;
7997 boolean_t dnsinfo_changed
= FALSE
;
7998 boolean_t global_ipv4_changed
= FALSE
;
7999 boolean_t global_ipv6_changed
= FALSE
;
8002 boolean_t nat64_changed
= FALSE
;
8003 CFMutableStringRef network_change_msg
= NULL
;
8005 nwi_state_t old_nwi_state
= NULL
;
8006 CFDictionaryRef old_primary_dns
= NULL
;
8007 CFDictionaryRef old_primary_proxy
= NULL
;
8008 #if !TARGET_OS_IPHONE
8009 CFDictionaryRef old_primary_smb
= NULL
;
8010 #endif // !TARGET_OS_IPHONE
8011 boolean_t proxies_changed
= FALSE
;
8012 boolean_t reachability_changed
= FALSE
;
8013 CFArrayRef service_order
;
8014 CFMutableArrayRef service_changes
= NULL
;
8015 CFDictionaryRef services_info
= NULL
;
8016 #if !TARGET_OS_IPHONE
8017 boolean_t smb_changed
= FALSE
;
8018 #endif // !TARGET_OS_IPHONE
8020 /* populate name/index cache */
8023 if (changed_keys
!= NULL
) {
8024 count
= CFArrayGetCount(changed_keys
);
8025 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8027 "changed keys %@ (%ld)", changed_keys
, count
);
8030 if (if_rank_changes
== NULL
&& count
== 0) {
8034 if (S_primary_dns
!= NULL
) {
8035 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
8036 if (old_primary_dns
!= NULL
) {
8037 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
8041 if (S_primary_proxies
!= NULL
) {
8043 = service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
8044 if (old_primary_proxy
!= NULL
) {
8045 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
8049 #if !TARGET_OS_IPHONE
8050 if (S_primary_smb
!= NULL
) {
8051 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
8052 if (old_primary_smb
!= NULL
) {
8053 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
8056 #endif // !TARGET_OS_IPHONE
8058 keyChangeListInit(&keys
);
8059 service_changes
= CFArrayCreateMutable(NULL
, 0,
8060 &kCFTypeArrayCallBacks
);
8062 for (CFIndex i
= 0; i
< count
; i
++) {
8064 #if !TARGET_OS_SIMULATOR
8065 CFStringRef interface
= NULL
;
8066 #endif /* !TARGET_OS_SIMULATOR */
8068 change
= CFArrayGetValueAtIndex(changed_keys
, i
);
8069 if (CFEqual(change
, S_setup_global_ipv4
)) {
8070 global_ipv4_changed
= TRUE
;
8071 global_ipv6_changed
= TRUE
;
8073 else if (CFEqual(change
, S_multicast_resolvers
)) {
8074 dnsinfo_changed
= TRUE
;
8076 else if (CFEqual(change
, S_private_resolvers
)) {
8077 dnsinfo_changed
= TRUE
;
8079 #if !TARGET_OS_IPHONE
8080 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
8081 dnsinfo_changed
= TRUE
;
8083 #endif /* !TARGET_OS_IPHONE */
8084 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
8085 CFStringRef serviceID
;
8087 serviceID
= parse_component(change
, S_state_service_prefix
);
8089 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8090 CFRelease(serviceID
);
8093 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
8094 CFStringRef serviceID
= parse_component(change
,
8095 S_setup_service_prefix
);
8097 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8098 CFRelease(serviceID
);
8101 for (size_t j
= 0; j
< countof(transientInterfaceEntityNames
); j
++) {
8102 if (CFStringHasSuffix(change
,
8103 *transientInterfaceEntityNames
[j
])) {
8104 reachability_changed
= TRUE
;
8109 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
8110 reachability_changed
= TRUE
;
8113 #if !TARGET_OS_SIMULATOR
8114 else if (is_nat64_prefix_request(change
, &interface
)) {
8115 my_CFSetAddValue_async(__network_change_queue(), &S_nat64_prefix_requests
, interface
);
8116 nat64_changed
= TRUE
;
8118 #endif /* !TARGET_OS_SIMULATOR */
8121 /* determine which serviceIDs are impacted by the interface rank changes */
8122 if (if_rank_changes
!= NULL
) {
8123 n
= CFArrayGetCount(if_rank_changes
);
8124 for (CFIndex i
= 0; i
< n
; i
++) {
8125 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
8127 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8128 my_log(LOG_DEBUG
, "Interface rank changed %@", ifname
);
8130 append_serviceIDs_for_interface(service_changes
, ifname
);
8134 /* grab a snapshot of everything we need */
8135 services_info
= services_info_copy(session
, service_changes
);
8136 assert(services_info
!= NULL
);
8138 /* grab the service order */
8139 service_order
= service_order_get(services_info
);
8140 if (service_order
!= NULL
) {
8141 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8142 my_log(LOG_DEBUG
, "service_order %@ ", service_order
);
8146 /* process protocol (v4, v6, rank, ...) and configuration (dns, proxies, smb, ...) changes */
8147 n
= CFArrayGetCount(service_changes
);
8148 for (CFIndex i
= 0; i
< n
; i
++) {
8150 CFStringRef serviceID
;
8152 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
8153 changes
= service_changed(services_info
, serviceID
);
8154 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
8155 /* if __Service__ (e.g. PrimaryRank) changed */
8156 global_ipv4_changed
= TRUE
;
8157 global_ipv6_changed
= TRUE
;
8160 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
8161 global_ipv4_changed
= TRUE
;
8162 dnsinfo_changed
= TRUE
;
8163 proxies_changed
= TRUE
;
8165 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
8166 global_ipv6_changed
= TRUE
;
8167 dnsinfo_changed
= TRUE
;
8168 proxies_changed
= TRUE
;
8169 nat64_changed
= TRUE
;
8172 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
8173 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
8176 dnsinfo_changed
= TRUE
;
8177 nat64_changed
= TRUE
;
8179 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
8180 proxies_changed
= TRUE
;
8182 #if !TARGET_OS_IPHONE
8183 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
8184 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
8189 if ((changes
& (1 << kEntityTypeTransientStatus
)) != 0
8190 && (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
8191 || service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
)) {
8192 dnsinfo_changed
= TRUE
;
8196 /* ensure S_nwi_state can hold as many services as we have currently */
8197 n_services
= (int)CFDictionaryGetCount(S_service_state_dict
);
8198 old_nwi_state
= nwi_state_make_copy(S_nwi_state
);
8199 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
8201 if (global_ipv4_changed
) {
8202 if (S_ipv4_results
!= NULL
) {
8203 ElectionResultsRelease(S_ipv4_results
);
8206 = ElectionResultsCopy(AF_INET
, service_order
);
8207 ElectionResultsLog(LOG_INFO
, S_ipv4_results
, "IPv4");
8209 if (global_ipv6_changed
) {
8210 if (S_ipv6_results
!= NULL
) {
8211 ElectionResultsRelease(S_ipv6_results
);
8214 = ElectionResultsCopy(AF_INET6
, service_order
);
8215 ElectionResultsLog(LOG_INFO
, S_ipv6_results
, "IPv6");
8217 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
8218 CFStringRef new_primary
;
8219 CFStringRef new_primary_dns
= NULL
;
8220 CFStringRef new_primary_proxies
= NULL
;
8221 #if !TARGET_OS_IPHONE
8222 CFStringRef new_primary_smb
= NULL
;
8223 #endif /* !TARGET_OS_IPHONE */
8224 RouteListUnion new_routelist
;
8225 CandidateRef other_candidate
;
8226 CandidateRef primary_candidate
;
8228 if (S_nwi_state
!= NULL
) {
8229 nwi_state_clear(S_nwi_state
, AF_INET
);
8230 nwi_state_clear(S_nwi_state
, AF_INET6
);
8234 my_log(LOG_DEBUG
, "electing IPv4 primary");
8235 new_routelist
.ptr
= NULL
;
8236 other_candidate
= (S_ipv6_results
!= NULL
) /* get IPv6 primary */
8237 ? S_ipv6_results
->candidates
: NULL
;
8238 primary_candidate
= ElectionResultsGetPrimary(S_ipv4_results
,
8240 S_nwi_state
, AF_INET
,
8241 &new_routelist
.common
,
8243 new_primary
= (primary_candidate
!= NULL
)
8244 ? primary_candidate
->serviceID
: NULL
;
8245 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
8246 update_ipv4(S_primary_ipv4
, new_routelist
.v4
, &keys
);
8249 my_log(LOG_DEBUG
, "electing IPv6 primary");
8250 new_routelist
.ptr
= NULL
;
8251 other_candidate
= primary_candidate
; /* get IPv4 primary */
8252 primary_candidate
= ElectionResultsGetPrimary(S_ipv6_results
,
8254 S_nwi_state
, AF_INET6
,
8255 &new_routelist
.common
,
8257 new_primary
= (primary_candidate
!= NULL
)
8258 ? primary_candidate
->serviceID
: NULL
;
8259 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
8260 update_ipv6(S_primary_ipv6
, new_routelist
.v6
, &keys
);
8262 nwi_state_finalize(S_nwi_state
);
8264 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
8265 /* decide between IPv4 and IPv6 */
8266 if (rank_service_entity(S_ipv4_service_rank_dict
,
8267 S_primary_ipv4
, kSCEntNetDNS
)
8268 <= rank_service_entity(S_ipv6_service_rank_dict
,
8269 S_primary_ipv6
, kSCEntNetDNS
)) {
8270 new_primary_dns
= S_primary_ipv4
;
8273 new_primary_dns
= S_primary_ipv6
;
8275 if (rank_service_entity(S_ipv4_service_rank_dict
,
8276 S_primary_ipv4
, kSCEntNetProxies
)
8277 <= rank_service_entity(S_ipv6_service_rank_dict
,
8278 S_primary_ipv6
, kSCEntNetProxies
)) {
8279 new_primary_proxies
= S_primary_ipv4
;
8282 new_primary_proxies
= S_primary_ipv6
;
8284 #if !TARGET_OS_IPHONE
8285 if (rank_service_entity(S_ipv4_service_rank_dict
,
8286 S_primary_ipv4
, kSCEntNetSMB
)
8287 <= rank_service_entity(S_ipv6_service_rank_dict
,
8288 S_primary_ipv6
, kSCEntNetSMB
)) {
8289 new_primary_smb
= S_primary_ipv4
;
8292 new_primary_smb
= S_primary_ipv6
;
8294 #endif /* !TARGET_OS_IPHONE */
8297 else if (S_primary_ipv6
!= NULL
) {
8298 new_primary_dns
= S_primary_ipv6
;
8299 new_primary_proxies
= S_primary_ipv6
;
8300 #if !TARGET_OS_IPHONE
8301 new_primary_smb
= S_primary_ipv6
;
8302 #endif /* !TARGET_OS_IPHONE */
8304 else if (S_primary_ipv4
!= NULL
) {
8305 new_primary_dns
= S_primary_ipv4
;
8306 new_primary_proxies
= S_primary_ipv4
;
8307 #if !TARGET_OS_IPHONE
8308 new_primary_smb
= S_primary_ipv4
;
8309 #endif /* !TARGET_OS_IPHONE */
8312 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
8314 dnsinfo_changed
= TRUE
;
8316 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
,
8318 proxies_changed
= TRUE
;
8320 #if !TARGET_OS_IPHONE
8321 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
8324 #endif /* !TARGET_OS_IPHONE */
8327 if (!proxies_changed
&& dnsinfo_changed
8328 && ((G_supplemental_proxies_follow_dns
!= NULL
)
8329 && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
8330 proxies_changed
= TRUE
;
8333 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
8335 if (global_ipv4_changed
|| global_ipv6_changed
8336 || dnsinfo_changed
|| reachability_changed
) {
8337 if (S_nwi_state
!= NULL
) {
8338 S_nwi_state
->generation_count
= mach_absolute_time();
8339 if (global_ipv4_changed
|| global_ipv6_changed
8340 || reachability_changed
) {
8341 SCNetworkReachabilityFlags reach_flags_v4
= 0;
8342 SCNetworkReachabilityFlags reach_flags_v6
= 0;
8344 GetReachabilityFlagsFromTransientServices(services_info
,
8348 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
,
8352 /* Update the per-interface generation count */
8353 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
,
8357 if (update_nwi(S_nwi_state
)) {
8358 changes
|= NETWORK_CHANGE_NET
;
8361 * the DNS configuration includes per-resolver configuration
8362 * reachability flags that are based on the nwi state. Let's
8363 * make sure that we check for changes
8365 dnsinfo_changed
= TRUE
;
8369 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
8370 changes
|= NETWORK_CHANGE_DNS
;
8371 dnsinfo_changed
= TRUE
;
8373 dns_changed
= FALSE
;
8376 if (dnsinfo_changed
) {
8377 if (update_dnsinfo(services_info
, S_primary_dns
,
8378 &keys
, service_order
)) {
8379 changes
|= NETWORK_CHANGE_DNS
;
8381 dnsinfo_changed
= FALSE
;
8384 if (proxies_changed
) {
8385 // if proxy change OR supplemental Proxies follow supplemental DNS
8386 if (update_proxies(services_info
, S_primary_proxies
,
8387 &keys
, service_order
)) {
8388 changes
|= NETWORK_CHANGE_PROXY
;
8390 proxies_changed
= FALSE
;
8393 #if !TARGET_OS_IPHONE
8395 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
8396 changes
|= NETWORK_CHANGE_SMB
;
8398 smb_changed
= FALSE
;
8401 #endif /* !TARGET_OS_IPHONE */
8402 if (nat64_changed
) {
8403 changes
|= NETWORK_CHANGE_NAT64
;
8405 my_CFRelease(&service_changes
);
8406 my_CFRelease(&services_info
);
8409 network_change_msg
= CFStringCreateMutable(NULL
, 0);
8410 process_nwi_changes(network_change_msg
,
8419 #if !TARGET_OS_IPHONE
8422 #else // !TARGET_OS_IPHONE
8423 FALSE
, // smb_changed
8424 NULL
// old_primary_smb
8425 #endif // !TARGET_OS_IPHONE
8429 keyChangeListApplyToStore(&keys
, session
);
8430 my_CFRelease(&old_primary_dns
);
8431 my_CFRelease(&old_primary_proxy
);
8432 #if !TARGET_OS_IPHONE
8433 my_CFRelease(&old_primary_smb
);
8434 #endif // !TARGET_OS_IPHONE
8437 dispatch_async(__network_change_queue(), ^{
8438 post_network_change(changes
);
8442 if ((network_change_msg
!= NULL
)
8443 && (CFStringGetLength(network_change_msg
) != 0)) {
8444 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
8445 } else if (keyChangeListActive(&keys
)) {
8446 my_log(LOG_NOTICE
, "network changed");
8447 } else if (nat64_changed
) {
8448 my_log(LOG_NOTICE
, "nat64 update");
8450 my_log(LOG_INFO
, "network event w/no changes");
8453 my_CFRelease(&network_change_msg
);
8455 if (changes_state
!= NULL
) {
8456 nwi_state_free(changes_state
);
8458 if (old_nwi_state
!= NULL
) {
8459 nwi_state_free(old_nwi_state
);
8461 keyChangeListFree(&keys
);
8463 /* release the name/index cache */
8464 my_if_freenameindex();
8470 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8473 #pragma unused(info)
8474 IPMonitorProcessChanges(session
, changed_keys
, NULL
);
8478 #if !TARGET_OS_IPHONE
8479 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_mcx
8481 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_global
8487 static dispatch_queue_t proxy_cb_queue
;
8489 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
8490 _scprefs_observer_watch(PROXY_GLOBAL_OBSERVER_TYPE
,
8491 "com.apple.SystemConfiguration.plist",
8494 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
8495 #if !TARGET_OS_SIMULATOR
8496 /* Setup or Update config agents */
8497 process_AgentMonitor_Proxy();
8498 #endif //!TARGET_OS_SIMULATOR
8499 (void)notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8500 my_log(LOG_INFO
, "Notifying:\n%@",
8501 S_state_global_proxies
);
8506 #include "IPMonitorControlPrefs.h"
8509 prefs_changed(SCPreferencesRef prefs
)
8511 #pragma unused(prefs)
8512 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
8513 S_IPMonitor_debug
= kDebugFlagDefault
;
8514 S_IPMonitor_verbose
= TRUE
;
8515 my_log(LOG_DEBUG
, "Setting logging verbose mode on");
8517 my_log(LOG_DEBUG
, "Setting logging verbose mode off");
8518 S_IPMonitor_debug
= 0;
8519 S_IPMonitor_verbose
= FALSE
;
8524 #if !TARGET_OS_SIMULATOR
8535 struct rt_msghdr
* rtm
;
8536 struct sockaddr_in
*sin
;
8542 mib
[4] = NET_RT_FLAGS
;
8543 mib
[5] = RTF_STATIC
| RTF_DYNAMIC
;
8544 for (i
= 0; i
< 3; i
++) {
8545 if (sysctl(mib
, N_MIB
, NULL
, &needed
, NULL
, 0) < 0) {
8548 if ((buf
= malloc(needed
)) == NULL
) {
8551 if (sysctl(mib
, N_MIB
, buf
, &needed
, NULL
, 0) >= 0) {
8561 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
8564 /* ALIGN: assume kernel provides necessary alignment */
8565 rtm
= (struct rt_msghdr
*)(void *)next
;
8566 sin
= (struct sockaddr_in
*)(rtm
+ 1);
8568 addr
= ntohl(sin
->sin_addr
.s_addr
);
8569 if (IN_LOOPBACK(addr
)) {
8571 "flush_routes: ignoring loopback route");
8574 if (IN_LOCAL_GROUP(addr
)) {
8576 "flush_routes: ignoring multicast route");
8579 rtm
->rtm_type
= RTM_DELETE
;
8580 rtm
->rtm_seq
= ++rtm_seq
;
8581 if (write(s
, rtm
, rtm
->rtm_msglen
) < 0) {
8583 "flush_routes: removing route for "
8584 IP_FORMAT
" failed: %s",
8585 IP_LIST(&sin
->sin_addr
),
8590 "flush_routes: removed route for " IP_FORMAT
,
8591 IP_LIST(&sin
->sin_addr
));
8599 flush_inet_routes(void)
8603 s
= open_routing_socket();
8610 #else /* !TARGET_OS_SIMULATOR */
8613 flush_inet_routes(void)
8617 #endif /* !TARGET_OS_SIMULATOR */
8624 CFMutableArrayRef keys
= NULL
;
8625 CFStringRef pattern
;
8626 CFMutableArrayRef patterns
= NULL
;
8627 CFRunLoopSourceRef rls
= NULL
;
8629 if (S_is_network_boot() != 0) {
8634 flush_inet_routes();
8637 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
8638 IPMonitorNotify
, NULL
);
8639 if (S_session
== NULL
) {
8641 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8642 SCErrorString(SCError()));
8646 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8647 kSCDynamicStoreDomainState
,
8650 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8651 kSCDynamicStoreDomainState
,
8654 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8655 kSCDynamicStoreDomainState
,
8657 S_state_global_proxies
8658 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8659 kSCDynamicStoreDomainState
,
8661 #if !TARGET_OS_IPHONE
8663 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8664 kSCDynamicStoreDomainState
,
8666 #endif /* !TARGET_OS_IPHONE */
8668 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8669 kSCDynamicStoreDomainSetup
,
8671 S_state_service_prefix
8672 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8673 kSCDynamicStoreDomainState
,
8676 S_setup_service_prefix
8677 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8678 kSCDynamicStoreDomainSetup
,
8681 S_service_state_dict
8682 = CFDictionaryCreateMutable(NULL
, 0,
8683 &kCFTypeDictionaryKeyCallBacks
,
8684 &kCFTypeDictionaryValueCallBacks
);
8686 S_ipv4_service_rank_dict
8687 = CFDictionaryCreateMutable(NULL
, 0,
8688 &kCFTypeDictionaryKeyCallBacks
,
8689 &kCFTypeDictionaryValueCallBacks
);
8691 S_ipv6_service_rank_dict
8692 = CFDictionaryCreateMutable(NULL
, 0,
8693 &kCFTypeDictionaryKeyCallBacks
,
8694 &kCFTypeDictionaryValueCallBacks
);
8696 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8697 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8699 /* register for State: and Setup: per-service notifications */
8700 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
8702 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
8703 CFArrayAppendValue(patterns
, pattern
);
8706 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
8707 CFArrayAppendValue(patterns
, pattern
);
8710 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
8711 CFArrayAppendValue(patterns
, pattern
);
8714 /* register for State: per-service PPP/VPN/IPSec status notifications */
8715 add_transient_status_keys(kSCCompAnyRegex
, patterns
);
8717 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
8718 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
8720 /* add notifier for multicast DNS configuration (Bonjour/.local) */
8721 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8722 kSCDynamicStoreDomainState
,
8724 CFSTR(kDNSServiceCompMulticastDNS
));
8725 CFArrayAppendValue(keys
, S_multicast_resolvers
);
8727 /* add notifier for private DNS configuration (Back to My Mac) */
8728 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8729 kSCDynamicStoreDomainState
,
8731 CFSTR(kDNSServiceCompPrivateDNS
));
8732 CFArrayAppendValue(keys
, S_private_resolvers
);
8734 #if !TARGET_OS_SIMULATOR
8735 /* add NAT64 prefix request pattern */
8736 nat64_prefix_request_add_pattern(patterns
);
8737 #endif /* !TARGET_OS_SIMULATOR */
8739 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
8741 "SCDynamicStoreSetNotificationKeys() failed: %s",
8742 SCErrorString(SCError()));
8746 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
8749 "SCDynamicStoreCreateRunLoopSource() failed: %s",
8750 SCErrorString(SCError()));
8754 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
8757 /* initialize dns configuration */
8758 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
8759 #if !TARGET_OS_IPHONE
8761 #endif /* !TARGET_OS_IPHONE */
8762 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
8764 #if !TARGET_OS_IPHONE
8765 /* initialize SMB configuration */
8766 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
8767 #endif /* !TARGET_OS_IPHONE */
8772 my_CFRelease(&keys
);
8773 my_CFRelease(&patterns
);
8781 /* initialize multicast route */
8782 update_ipv4(NULL
, NULL
, NULL
);
8784 #if !TARGET_OS_SIMULATOR
8785 process_AgentMonitor();
8786 #endif // !TARGET_OS_SIMULATOR
8792 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
8796 boolean_t ret
= def
;
8798 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
8800 ret
= CFBooleanGetValue(b
);
8805 #if !TARGET_OS_SIMULATOR
8806 #include "IPMonitorControlServer.h"
8809 InterfaceRankChanged(void * info
)
8811 #pragma unused(info)
8812 os_activity_t activity
;
8813 CFDictionaryRef assertions
= NULL
;
8816 activity
= os_activity_create("processing IPMonitor [rank] change",
8817 OS_ACTIVITY_CURRENT
,
8818 OS_ACTIVITY_FLAG_DEFAULT
);
8819 os_activity_scope(activity
);
8821 changes
= IPMonitorControlServerCopyInterfaceRankInformation(&assertions
);
8822 if (S_if_rank_dict
!= NULL
) {
8823 CFRelease(S_if_rank_dict
);
8825 S_if_rank_dict
= assertions
;
8826 if (changes
!= NULL
) {
8827 IPMonitorProcessChanges(S_session
, NULL
, changes
);
8831 os_release(activity
);
8837 StartIPMonitorControlServer(void)
8839 CFRunLoopSourceContext context
;
8840 CFRunLoopSourceRef rls
;
8842 bzero(&context
, sizeof(context
));
8843 context
.perform
= InterfaceRankChanged
;
8844 rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
8845 if (!IPMonitorControlServerStart(CFRunLoopGetCurrent(),
8847 &S_bundle_logging_verbose
)) {
8848 my_log(LOG_ERR
, "IPMonitorControlServerStart failed");
8851 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
8852 kCFRunLoopDefaultMode
);
8858 #endif /* !TARGET_OS_SIMULATOR */
8862 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
8864 CFDictionaryRef info_dict
;
8866 info_dict
= CFBundleGetInfoDictionary(bundle
);
8868 if (info_dict
!= NULL
) {
8870 = S_get_plist_boolean(info_dict
,
8871 CFSTR("AppendStateArrayToSetupArray"),
8874 if (bundleVerbose
) {
8875 S_IPMonitor_debug
= kDebugFlagDefault
;
8876 S_bundle_logging_verbose
= TRUE
;
8877 S_IPMonitor_verbose
= TRUE
;
8880 /* register to receive changes to the "verbose" flag and read the initial setting */
8881 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
8882 prefs_changed(NULL
);
8884 /* start DNS configuration (dnsinfo) server */
8885 load_DNSConfiguration(bundle
, // bundle
8886 ^(Boolean inSync
) { // syncHandler
8887 dispatch_async(__network_change_queue(), ^{
8888 S_dnsinfo_synced
= inSync
;
8891 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
8892 // all of the DNS service ack's should result
8893 // in a [new] network change being posted
8894 post_network_change(NETWORK_CHANGE_DNS
);
8896 post_network_change_when_ready();
8901 /* start Network Information (nwi) server */
8902 load_NetworkInformation(bundle
, // bundle
8903 ^(Boolean inSync
) { // syncHandler
8904 dispatch_async(__network_change_queue(), ^{
8905 S_nwi_synced
= inSync
;
8906 post_network_change_when_ready();
8910 #if !TARGET_OS_SIMULATOR
8911 /* start IPMonitor Control (InterfaceRank) server */
8912 StartIPMonitorControlServer();
8913 #endif /* !TARGET_OS_IPHONE */
8915 /* initialize DNS configuration */
8916 dns_configuration_init(bundle
);
8918 #if !TARGET_OS_SIMULATOR
8919 /* initialize NAT64 configuration */
8920 nat64_configuration_init(bundle
);
8921 #endif /* !TARGET_OS_SIMULATOR */
8923 /* initialize proxy configuration */
8924 proxy_configuration_init(bundle
);
8928 #if !TARGET_OS_IPHONE
8929 if (S_session
!= NULL
) {
8930 dns_configuration_monitor(S_session
, IPMonitorNotify
);
8932 #endif /* !TARGET_OS_IPHONE */
8934 #if !TARGET_OS_SIMULATOR
8935 load_hostname(TRUE
);
8936 #endif /* !TARGET_OS_SIMULATOR */
8938 #if !TARGET_OS_IPHONE
8939 load_smb_configuration(TRUE
);
8940 #endif /* !TARGET_OS_IPHONE */
8947 #pragma mark Standalone test code
8950 #ifdef TEST_IPMONITOR
8953 main(int argc
, char **argv
)
8957 S_IPMonitor_debug
= kDebugFlag1
;
8959 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
8962 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
8964 S_IPMonitor_debug
= kDebugFlag1
;
8970 #endif /* TEST_IPMONITOR */
8972 #ifdef TEST_ROUTELIST
8977 const char * gateway
;
8978 const char * ifname
;
8983 #ifdef TEST_IPV4_ROUTELIST
8989 const char * router
;
8990 const char * ifname
;
8992 const CFStringRef
* primary_rank
;
8993 struct route
* additional_routes
;
8994 int additional_routes_count
;
8995 struct route
* excluded_routes
;
8996 int excluded_routes_count
;
8997 } IPv4ServiceContents
;
8999 typedef const IPv4ServiceContents
* IPv4ServiceContentsRef
;
9001 struct route loop_routelist
[] = {
9002 { "1.1.1.1", 32, "1.1.1.2", NULL
},
9003 { "1.1.1.2", 32, "1.1.1.3", NULL
},
9004 { "1.1.1.3", 32, "1.1.1.4", NULL
},
9005 { "1.1.1.4", 32, "1.1.1.5", NULL
},
9006 { "1.1.1.5", 32, "1.1.1.6", NULL
},
9007 { "1.1.1.6", 32, "1.1.1.7", NULL
},
9008 { "1.1.1.7", 32, "1.1.1.8", NULL
},
9009 { "1.1.1.8", 32, "1.1.1.9", NULL
},
9010 { "1.1.1.9", 32, "1.1.1.10", NULL
},
9011 { "1.1.1.10", 32, "1.1.1.11", NULL
},
9012 { "1.1.1.11", 32, "1.1.1.1", NULL
},
9015 struct route vpn_routelist
[] = {
9016 { "10.1.3.0", 24, "17.153.46.24", NULL
},
9017 { "10.1.4.0", 24, "17.153.46.24", NULL
},
9018 { "10.1.5.0", 24, "17.153.46.24", NULL
},
9019 { "10.1.6.0", 24, "17.153.46.24", NULL
},
9020 { "10.1.7.0", 24, "17.153.46.24", NULL
},
9021 { "10.16.0.0", 12, "17.153.46.24", NULL
},
9022 { "10.45.0.0", 16, "17.153.46.24", NULL
},
9023 { "10.53.0.0", 16, "17.153.46.24", NULL
},
9024 { "10.70.0.0", 15, "17.153.46.24", NULL
},
9025 { "10.74.0.0", 15, "17.153.46.24", NULL
},
9026 { "10.90.0.0", 15, "17.153.46.24", NULL
},
9027 { "10.91.0.0", 16, "17.153.46.24", NULL
},
9028 { "10.100.0.0", 16, "17.153.46.24", NULL
},
9029 { "10.113.0.0", 16, "17.153.46.24", NULL
},
9030 { "10.128.0.0", 9, "17.153.46.24", NULL
},
9031 { "17.0.0.0", 9, "17.153.46.24", NULL
},
9032 { "17.34.0.0", 16, "17.153.46.24", NULL
},
9033 { "17.112.156.53", 32, "17.153.46.24", NULL
},
9034 { "17.128.0.0", 10, "17.153.46.24", NULL
},
9035 { "17.149.0.121", 32, "17.153.46.24", NULL
},
9036 { "17.149.7.200", 32, "17.153.46.24", NULL
},
9037 { "17.153.46.24", 32, "17.153.46.24", NULL
},
9038 { "17.192.0.0", 12, "17.153.46.24", NULL
},
9039 { "17.208.0.0", 15, "17.153.46.24", NULL
},
9040 { "17.211.0.0", 16, "17.153.46.24", NULL
},
9041 { "17.212.0.0", 14, "17.153.46.24", NULL
},
9042 { "17.216.0.0", 13, "17.153.46.24", NULL
},
9043 { "17.224.0.0", 12, "17.153.46.24", NULL
},
9044 { "17.240.0.0", 16, "17.153.46.24", NULL
},
9045 { "17.241.0.0", 16, "17.153.46.24", NULL
},
9046 { "17.248.0.0", 14, "17.153.46.24", NULL
},
9047 { "17.251.104.200", 32, "17.153.46.24", NULL
},
9048 { "17.252.0.0", 16, "17.153.46.24", NULL
},
9049 { "17.253.0.0", 16, "17.153.46.24", NULL
},
9050 { "17.254.0.0", 16, "17.153.46.24", NULL
},
9051 { "17.255.0.0", 16, "17.153.46.24", NULL
},
9052 { "151.193.141.0", 27, "17.153.46.24", NULL
},
9053 { "172.16.2.0", 24, "17.153.46.24", NULL
},
9054 { "192.35.50.0", 24, "17.153.46.24", NULL
},
9055 { "204.179.20.0", 24, "17.153.46.24", NULL
},
9056 { "206.112.116.0", 24, "17.153.46.24", NULL
},
9059 struct route vpn_routelist_ext
[] = {
9060 { "17.151.63.82", 32, "10.0.0.1", "en0" },
9061 { "17.151.63.81", 32, "17.151.63.81", "en0" },
9062 { "17.151.63.80", 32, NULL
, NULL
},
9063 { "17.1.0.0", 16, NULL
, NULL
},
9064 { "17.2.0.0", 24, NULL
, NULL
},
9065 { "10.0.0.0", 24, NULL
, NULL
},
9069 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
9071 const IPv4ServiceContents en0_10
= {
9072 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9075 const IPv4ServiceContents en0_15
= {
9076 "10.0.0.19", 24, NULL
, "10.0.0.1", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9079 const IPv4ServiceContents en0_30
= {
9080 "10.0.0.11", 24, NULL
, "10.0.0.1", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9083 const IPv4ServiceContents en0_40
= {
9084 "10.0.0.12", 24, NULL
, "10.0.0.1", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9087 const IPv4ServiceContents en0_50
= {
9088 "10.0.0.13", 24, NULL
, "10.0.0.1", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9091 const IPv4ServiceContents en0_110
= {
9092 "192.168.2.10", 24, NULL
, "192.168.2.1", "en0", 110, NULL
, NULL
, 0, NULL
, 0
9095 const IPv4ServiceContents en0_1
= {
9096 "17.202.40.191", 22, NULL
, "17.202.20.1", "en0", 1, NULL
, NULL
, 0, NULL
, 0
9099 const IPv4ServiceContents en1_20
= {
9100 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9103 const IPv4ServiceContents en1_2
= {
9104 "17.202.42.24", 22, NULL
, "17.202.20.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9107 const IPv4ServiceContents en1_125
= {
9108 "192.168.2.20", 24, NULL
, "192.168.2.1", "en1", 125, NULL
, NULL
, 0, NULL
, 0
9111 const IPv4ServiceContents fw0_25
= {
9112 "192.168.2.30", 24, NULL
, "192.168.2.1", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9115 const IPv4ServiceContents fw0_21
= {
9116 "192.168.3.30", 24, NULL
, "192.168.3.1", "fw0", 21, NULL
, NULL
, 0, NULL
, 0
9119 const IPv4ServiceContents ppp0_0_1
= {
9120 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
, NULL
, 0, NULL
, 0
9123 const IPv4ServiceContents utun0
= {
9124 "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
)
9127 const IPv4ServiceContents en0_test6
= {
9128 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 2, NULL
, NULL
, 0, NULL
, 0
9131 const IPv4ServiceContents en1_test6
= {
9132 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 3, NULL
, NULL
, 0, NULL
, 0
9135 const IPv4ServiceContents en2_test6
= {
9136 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9139 const IPv4ServiceContents en0_test7
= {
9140 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 3, NULL
, NULL
, 0, NULL
, 0
9143 const IPv4ServiceContents en1_test7
= {
9144 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9147 const IPv4ServiceContents en2_test7
= {
9148 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9151 const IPv4ServiceContents fw0_test6_and_7
= {
9152 "169.254.11.33", 16, NULL
, NULL
, "fw0", 0x0ffffff, NULL
, NULL
, 0, NULL
, 0
9155 const IPv4ServiceContents en0_10_last
= {
9156 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
, NULL
, 0, NULL
, 0
9159 const IPv4ServiceContents en0_10_never
= {
9160 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9163 const IPv4ServiceContents en1_20_first
= {
9164 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
, NULL
, 0, NULL
, 0
9167 const IPv4ServiceContents en1_20_never
= {
9168 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9171 const IPv4ServiceContents en1_20_other_never
= {
9172 "192.168.2.50", 24, NULL
, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9175 const IPv4ServiceContents en0_linklocal
= {
9176 "169.254.22.44", 16, NULL
, NULL
, "en0", 0xfffff, NULL
, NULL
, 0, NULL
, 0
9179 const IPv4ServiceContents en0_route_loop
= {
9180 "192.168.130.16", 24, NULL
, "192.168.130.1", "en0", 2, NULL
, loop_routelist
, countof(loop_routelist
), NULL
, 0
9185 IPv4ServiceContentsRef test
[];
9186 } IPv4RouteTest
, * IPv4RouteTestRef
;
9188 static IPv4RouteTest test1
= {
9202 static IPv4RouteTest test2
= {
9215 static IPv4RouteTest test3
= {
9232 static IPv4RouteTest test4
= {
9244 static IPv4RouteTest test5
= {
9257 static IPv4RouteTest test6
= {
9268 static IPv4RouteTest test7
= {
9279 static IPv4RouteTest test8
= {
9288 static IPv4RouteTest test9
= {
9298 static IPv4RouteTest test10
= {
9308 static IPv4RouteTest test11
= {
9318 static IPv4RouteTest test12
= {
9327 static IPv4RouteTest test13
= {
9336 static IPv4RouteTest test14
= {
9344 static IPv4RouteTest test15
= {
9352 static IPv4RouteTest test16
= {
9361 static IPv4RouteTest test17
= {
9365 &en1_20_other_never
,
9370 static IPv4RouteTest test18
= {
9378 static IPv4RouteTestRef ipv4_tests
[] = {
9401 ipv4_prefix_length_is_valid(int prefix_length
)
9403 if (prefix_length
< 0 || prefix_length
> IPV4_ROUTE_ALL_BITS_SET
) {
9410 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9413 CFStringRef prop_val
;
9418 prop_val
= CFStringCreateWithCString(NULL
,
9420 kCFStringEncodingASCII
);
9421 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9422 CFRelease(prop_val
);
9427 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9431 CFStringRef prop_val
;
9436 prop_val
= CFStringCreateWithCString(NULL
,
9438 kCFStringEncodingASCII
);
9439 array
= CFArrayCreate(NULL
,
9440 (const void **)&prop_val
, 1,
9441 &kCFTypeArrayCallBacks
);
9442 CFRelease(prop_val
);
9443 CFDictionarySetValue(dict
, prop_name
, array
);
9449 dict_add_ip(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9454 str
= my_CFStringCreateWithInAddr(ip
);
9455 CFDictionarySetValue(dict
, prop_name
, str
);
9461 dict_add_ip_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9467 str
= my_CFStringCreateWithInAddr(ip
);
9468 array
= CFArrayCreate(NULL
,
9469 (const void **)&str
, 1,
9470 &kCFTypeArrayCallBacks
);
9472 CFDictionarySetValue(dict
, prop_name
, array
);
9478 dict_insert_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9479 struct route
* routes
, int routes_count
)
9482 CFMutableArrayRef route_list
;
9483 struct route
* scan
;
9485 if (routes
== NULL
|| routes_count
== 0) {
9488 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9489 &kCFTypeArrayCallBacks
);
9490 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9491 struct in_addr mask
;
9492 CFMutableDictionaryRef route_dict
;
9495 = CFDictionaryCreateMutable(NULL
, 0,
9496 &kCFTypeDictionaryKeyCallBacks
,
9497 &kCFTypeDictionaryValueCallBacks
);
9498 dict_add_string(route_dict
, kSCPropNetIPv4RouteDestinationAddress
,
9500 if (ipv4_prefix_length_is_valid(scan
->prefix_length
)) {
9501 mask
.s_addr
= htonl(prefix_to_mask32(scan
->prefix_length
));
9502 dict_add_ip(route_dict
, kSCPropNetIPv4RouteSubnetMask
, mask
);
9504 dict_add_string(route_dict
, kSCPropNetIPv4RouteGatewayAddress
,
9506 dict_add_string(route_dict
, kSCPropNetIPv4RouteInterfaceName
,
9508 CFArrayAppendValue(route_list
, route_dict
);
9509 CFRelease(route_dict
);
9511 CFDictionarySetValue(dict
, prop_name
, route_list
);
9512 CFRelease(route_list
);
9516 static CFDictionaryRef
9517 make_IPv4_dict(IPv4ServiceContentsRef t
)
9519 CFMutableDictionaryRef dict
;
9521 dict
= CFDictionaryCreateMutable(NULL
, 0,
9522 &kCFTypeDictionaryKeyCallBacks
,
9523 &kCFTypeDictionaryValueCallBacks
);
9524 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
9525 if (ipv4_prefix_length_is_valid(t
->prefix_length
)) {
9526 struct in_addr mask
;
9528 mask
.s_addr
= htonl(prefix_to_mask32(t
->prefix_length
));
9529 dict_add_ip_as_array(dict
, kSCPropNetIPv4SubnetMasks
, mask
);
9531 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
9532 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
9533 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9534 dict_add_string(dict
, kSCPropConfirmedInterfaceName
, t
->ifname
);
9535 dict_insert_routes(dict
, kSCPropNetIPv4AdditionalRoutes
,
9536 t
->additional_routes
, t
->additional_routes_count
);
9537 dict_insert_routes(dict
, kSCPropNetIPv4ExcludedRoutes
,
9538 t
->excluded_routes
, t
->excluded_routes_count
);
9543 kDirectionForwards
= 0,
9544 kDirectionBackwards
= 1
9548 kLogRouteDisabled
= 0,
9549 kLogRouteEnabled
= 1
9552 static IPv4RouteListRef
9553 make_IPv4RouteList_for_test(IPv4RouteListRef list
,
9554 IPv4ServiceContentsRef test
,
9557 CFDictionaryRef dict
;
9560 Rank rank_assertion
= kRankAssertionDefault
;
9561 CFNumberRef rank_assertion_cf
= NULL
;
9562 Boolean rank_assertion_is_set
= FALSE
;
9563 IPv4RouteListRef ret
= NULL
;
9564 IPV4_ROUTES_BUF_DECL(routes
);
9566 dict
= make_IPv4_dict(test
);
9568 fprintf(stderr
, "make_IPv4_dict failed\n");
9571 if (test
->primary_rank
!= NULL
) {
9573 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9574 &rank_assertion_is_set
);
9575 if (rank_assertion_is_set
) {
9577 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9580 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
9582 my_CFRelease(&rank_assertion_cf
);
9584 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
9588 if (rank_assertion
== kRankAssertionScoped
) {
9589 rank_assertion
= kRankAssertionNever
;
9591 rank
= RankMake(test
->rank
, rank_assertion
);
9592 if (log_it
== kLogRouteEnabled
9593 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9596 descr
= IPv4RouteListCopyDescription(r
);
9597 SCPrint(TRUE
, stdout
, CFSTR("Adding %@"), descr
);
9600 ret
= IPv4RouteListAddRouteList(list
, 1, r
, rank
);
9608 static IPv4RouteListRef
9609 make_IPv4RouteList(IPv4ServiceContentsRef
* test
, Direction direction
,
9612 IPv4RouteListRef ret
= NULL
;
9613 IPv4ServiceContentsRef
* scan
;
9615 switch (direction
) {
9616 case kDirectionBackwards
:
9617 for (scan
= test
; *scan
!= NULL
; scan
++) {
9618 /* find the end of the list */
9620 for (scan
--; scan
>= test
; scan
--) {
9621 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9625 case kDirectionForwards
:
9626 for (scan
= test
; *scan
!= NULL
; scan
++) {
9627 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9631 IPv4RouteListFinalize(ret
);
9635 #define EMPHASIS_CHARS "================="
9638 * Function: routelist_build_test
9640 * Runs through the given set of routes first in the forward direction,
9641 * then again backwards. We should end up with exactly the same set of
9642 * routes at the end.
9645 routelist_build_test(IPv4RouteTestRef test
)
9648 boolean_t ret
= FALSE
;
9649 IPv4RouteListRef routes1
;
9650 IPv4RouteListRef routes2
;
9652 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
9653 EMPHASIS_CHARS
"\n",
9656 routes1
= make_IPv4RouteList(test
->test
, kDirectionForwards
,
9658 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9659 if (routes1
!= NULL
) {
9660 descr
= IPv4RouteListCopyDescription(routes1
);
9661 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9665 routes2
= make_IPv4RouteList(test
->test
, kDirectionBackwards
,
9667 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9668 if (routes2
!= NULL
) {
9669 descr
= IPv4RouteListCopyDescription(routes2
);
9670 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9674 if ((routes1
!= NULL
&& routes2
== NULL
)
9675 || (routes1
== NULL
&& routes2
!= NULL
)) {
9676 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
9677 (routes1
!= NULL
) ? "not " : "",
9678 (routes2
!= NULL
) ? "not " : "");
9680 else if (routes1
!= NULL
&& routes2
!= NULL
) {
9681 /* check if they are different */
9682 if (routes1
->count
!= routes2
->count
) {
9683 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
9684 routes1
->count
, routes2
->count
);
9686 else if (bcmp(routes1
, routes2
,
9687 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
9688 fprintf(stderr
, "routes1 and routes2 are different\n");
9691 printf("routes1 and routes2 are the same\n");
9695 if (routes1
!= NULL
) {
9698 if (routes2
!= NULL
) {
9701 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
9702 EMPHASIS_CHARS
"\n",
9703 test
->name
, ret
? "PASSED" : "FAILED");
9708 apply_test(IPv4RouteTestRef old_test
, IPv4RouteTestRef new_test
)
9710 IPv4RouteListRef new_routes
;
9711 IPv4RouteListRef old_routes
;
9713 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
9714 EMPHASIS_CHARS
"\n",
9715 old_test
->name
, new_test
->name
);
9717 old_routes
= make_IPv4RouteList(old_test
->test
, kDirectionForwards
,
9719 new_routes
= make_IPv4RouteList(new_test
->test
, kDirectionForwards
,
9721 if (old_routes
== NULL
) {
9722 printf("No Old Routes\n");
9725 printf("Old routes ('%s') = ", old_test
->name
);
9726 IPv4RouteListPrint(old_routes
);
9729 /* apply the old routes */
9730 IPv4RouteListApply(NULL
, old_routes
, -1);
9732 if (new_routes
== NULL
) {
9733 printf("No New Routes\n");
9736 printf("New Routes ('%s') = ", new_test
->name
);
9737 IPv4RouteListPrint(new_routes
);
9740 /* apply the new routes */
9741 IPv4RouteListApply(old_routes
, new_routes
, -1);
9743 if (old_routes
!= NULL
) {
9746 if (new_routes
!= NULL
) {
9749 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
9750 EMPHASIS_CHARS
"\n",
9751 old_test
->name
, new_test
->name
);
9756 main(int argc
, char **argv
)
9758 IPv4RouteTestRef
* test
;
9761 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
9762 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
9764 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9766 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9767 if (!routelist_build_test(*test
)) {
9768 fprintf(stderr
, "%s failed\n", (*test
)->name
);
9772 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9773 IPv4RouteTestRef
* test2
;
9775 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
9776 apply_test(*test
, *test2
);
9777 apply_test(*test2
, *test
);
9784 printf("\nChecking for leaks\n");
9785 sprintf(cmd
, "leaks %d 2>&1", getpid());
9793 #endif /* TEST_IPV4_ROUTELIST */
9795 #ifdef TEST_IPV6_ROUTELIST
9803 typedef const IPv6Address
* IPv6AddressRef
;
9806 IPv6AddressRef addr
;
9808 const char * router
;
9809 const char * ifname
;
9811 const CFStringRef
* primary_rank
;
9812 struct route
* additional_routes
;
9813 int additional_routes_count
;
9814 struct route
* excluded_routes
;
9815 int excluded_routes_count
;
9816 } IPv6ServiceContents
;
9818 typedef const IPv6ServiceContents
* IPv6ServiceContentsRef
;
9820 struct route loop_routelist
[] = {
9821 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
9822 "2620:149:4:f01:225:ff:fecc:89a2", NULL
},
9823 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
9824 "2620:149:4:f01:225:ff:fecc:89a3", NULL
},
9825 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
9826 "2620:149:4:f01:225:ff:fecc:89a4", NULL
},
9827 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
9828 "2620:149:4:f01:225:ff:fecc:89a5", NULL
},
9829 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
9830 "2620:149:4:f01:225:ff:fecc:89a6", NULL
},
9831 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
9832 "2620:149:4:f01:225:ff:fecc:89a7", NULL
},
9833 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
9834 "2620:149:4:f01:225:ff:fecc:89a8", NULL
},
9835 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
9836 "2620:149:4:f01:225:ff:fecc:89a9", NULL
},
9837 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
9838 "2620:149:4:f01:225:ff:fecc:89aa", NULL
},
9839 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
9840 "2620:149:4:f01:225:ff:fecc:89ab", NULL
},
9841 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
9842 "2620:149:4:f01:225:ff:fecc:89a1", NULL
},
9845 struct route vpn_routelist
[] = {
9846 { "2010:470:1f05:3cb::", 64,
9847 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9848 { "2010:222:3fa5:acb::", 48,
9849 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9850 { "2010:222:3fa5:1234::", 40,
9851 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9852 { "2010:222:3fa5:5678::", 40,
9856 struct route vpn_routelist_ext
[] = {
9857 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL
, NULL
},
9860 struct route en1_routelist_ext
[] = {
9861 { "2020:299:abcd:ef12::", 64, NULL
, NULL
},
9865 static const IPv6Address en0_addr1
[] = {
9866 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL
},
9867 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL
}
9870 static const IPv6Address en0_addr2
[] = {
9871 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL
},
9872 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL
}
9875 static const IPv6Address en0_addr3
[] = {
9876 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL
},
9877 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL
}
9880 static const IPv6Address en0_addr4
[] = {
9881 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL
},
9882 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL
}
9885 static const IPv6Address en0_addr5
[] = {
9886 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL
},
9887 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL
}
9890 static const IPv6Address en0_addr6
[] = {
9891 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL
},
9894 static const IPv6Address en0_lladdr
[] = {
9895 { "fe80::cabc:c8ff:fe96:96af", 64, NULL
}
9898 static const IPv6Address en1_addr
[] = {
9899 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL
},
9900 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL
}
9903 static const IPv6Address utun0_addr
[] = {
9904 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL
},
9907 static const IPv6Address fw0_addr1
[] = {
9908 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL
},
9909 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL
}
9913 * address+address-count
9914 * router ifname pri rank additional-routes+count excluded-routes+count
9917 static const IPv6ServiceContents en0_10
= {
9918 en0_addr1
, countof(en0_addr1
),
9919 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9922 static const IPv6ServiceContents en0_15
= {
9923 en0_addr2
, countof(en0_addr2
),
9924 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9927 static const IPv6ServiceContents en0_30
= {
9928 en0_addr3
, countof(en0_addr3
),
9929 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9932 static const IPv6ServiceContents en0_40
= {
9933 en0_addr4
, countof(en0_addr4
),
9934 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9937 static const IPv6ServiceContents en0_50
= {
9938 en0_addr5
, countof(en0_addr5
),
9939 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9942 static const IPv6ServiceContents en0_10_a
= {
9943 en0_addr6
, countof(en0_addr6
),
9944 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9947 static const IPv6ServiceContents fw0_25
= {
9948 fw0_addr1
, countof(fw0_addr1
),
9949 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9952 static const IPv6ServiceContents en1_20
= {
9953 en1_addr
, countof(en1_addr
),
9954 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9957 static const IPv6ServiceContents en1_10_ext
= {
9958 en1_addr
, countof(en1_addr
),
9959 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL
, NULL
, 0,
9960 en1_routelist_ext
, countof(en1_routelist_ext
)
9963 static const IPv6ServiceContents en0_0_lladdr
= {
9964 en0_lladdr
, countof(en0_lladdr
),
9965 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL
, NULL
, 0, NULL
, 0
9968 static const IPv6ServiceContents en0_loop
= {
9969 en0_addr1
, countof(en0_addr1
),
9970 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
,
9971 loop_routelist
, countof(loop_routelist
), NULL
, 0
9974 static const IPv6ServiceContents utun0
= {
9975 utun0_addr
, countof(utun0_addr
),
9976 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL
,
9977 vpn_routelist
, countof(vpn_routelist
),
9978 vpn_routelist_ext
, countof(vpn_routelist_ext
),
9983 IPv6ServiceContentsRef test
[];
9984 } IPv6RouteTest
, * IPv6RouteTestRef
;
9986 static IPv6RouteTest test1
= {
10000 static IPv6RouteTest test2
= {
10013 static IPv6RouteTest test3
= {
10022 static IPv6RouteTest test4
= {
10031 static IPv6RouteTest test5
= {
10043 static IPv6RouteTestRef ipv6_tests
[] = {
10054 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10057 CFStringRef prop_val
;
10062 prop_val
= CFStringCreateWithCString(NULL
,
10064 kCFStringEncodingASCII
);
10065 CFDictionarySetValue(dict
, prop_name
, prop_val
);
10066 CFRelease(prop_val
);
10071 dict_add_int(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10076 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10077 CFDictionarySetValue(dict
, prop_name
, num
);
10083 dict_insert_v6_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
10084 struct route
* routes
, int routes_count
)
10087 CFMutableArrayRef route_list
;
10088 struct route
* scan
;
10090 if (routes
== NULL
|| routes_count
== 0) {
10093 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
10094 &kCFTypeArrayCallBacks
);
10095 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
10096 CFMutableDictionaryRef route_dict
;
10098 route_dict
= CFDictionaryCreateMutable(NULL
, 0,
10099 &kCFTypeDictionaryKeyCallBacks
,
10100 &kCFTypeDictionaryValueCallBacks
);
10101 dict_add_string(route_dict
, kSCPropNetIPv6RouteDestinationAddress
,
10103 dict_add_int(route_dict
, kSCPropNetIPv6PrefixLength
,
10104 scan
->prefix_length
);
10105 dict_add_string(route_dict
, kSCPropNetIPv6RouteGatewayAddress
,
10107 dict_add_string(route_dict
, kSCPropNetIPv6RouteInterfaceName
,
10109 CFArrayAppendValue(route_list
, route_dict
);
10110 CFRelease(route_dict
);
10112 CFDictionarySetValue(dict
, prop_name
, route_list
);
10113 CFRelease(route_list
);
10118 array_add_string(CFMutableArrayRef array
, const char * c_str
)
10122 str
= CFStringCreateWithCString(NULL
,
10124 kCFStringEncodingUTF8
);
10125 CFArrayAppendValue(array
, str
);
10131 array_add_int(CFMutableArrayRef array
, int int_val
)
10135 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10136 CFArrayAppendValue(array
, num
);
10142 dict_add_ipv6_addressing(CFMutableDictionaryRef dict
,
10143 IPv6AddressRef list
, int list_count
)
10145 CFMutableArrayRef addr
= NULL
;
10146 CFMutableArrayRef dest
= NULL
;
10148 CFMutableArrayRef prefix
= NULL
;
10149 IPv6AddressRef scan
;
10151 if (list
== NULL
|| list_count
== 0) {
10154 for (i
= 0, scan
= list
; i
< list_count
; i
++, scan
++) {
10155 if (scan
->addr
!= NULL
) {
10156 if (addr
== NULL
) {
10157 addr
= CFArrayCreateMutable(NULL
, list_count
,
10158 &kCFTypeArrayCallBacks
);
10160 array_add_string(addr
, scan
->addr
);
10162 if (scan
->prefix_length
>= 0) {
10163 if (prefix
== NULL
) {
10164 prefix
= CFArrayCreateMutable(NULL
, list_count
,
10165 &kCFTypeArrayCallBacks
);
10167 array_add_int(prefix
, scan
->prefix_length
);
10169 if (scan
->dest
!= NULL
) {
10170 if (dest
== NULL
) {
10171 dest
= CFArrayCreateMutable(NULL
, list_count
,
10172 &kCFTypeArrayCallBacks
);
10174 array_add_string(dest
, scan
->dest
);
10177 if (addr
!= NULL
) {
10178 CFDictionarySetValue(dict
, kSCPropNetIPv6Addresses
, addr
);
10181 if (dest
!= NULL
) {
10182 CFDictionarySetValue(dict
, kSCPropNetIPv6DestAddresses
, dest
);
10185 if (prefix
!= NULL
) {
10186 CFDictionarySetValue(dict
, kSCPropNetIPv6PrefixLength
, prefix
);
10192 static CFDictionaryRef
10193 make_IPv6_dict(IPv6ServiceContentsRef t
)
10195 CFMutableDictionaryRef dict
;
10197 dict
= CFDictionaryCreateMutable(NULL
, 0,
10198 &kCFTypeDictionaryKeyCallBacks
,
10199 &kCFTypeDictionaryValueCallBacks
);
10200 dict_add_ipv6_addressing(dict
, t
->addr
, t
->addr_count
);
10201 dict_add_string(dict
, kSCPropNetIPv6Router
, t
->router
);
10202 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
10203 dict_insert_v6_routes(dict
, kSCPropNetIPv6AdditionalRoutes
,
10204 t
->additional_routes
, t
->additional_routes_count
);
10205 dict_insert_v6_routes(dict
, kSCPropNetIPv6ExcludedRoutes
,
10206 t
->excluded_routes
, t
->excluded_routes_count
);
10211 kDirectionForwards
= 0,
10212 kDirectionBackwards
= 1
10216 kLogRouteDisabled
= 0,
10217 kLogRouteEnabled
= 1
10220 static IPv6RouteListRef
10221 make_IPv6RouteList_for_test(IPv6RouteListRef list
,
10222 IPv6ServiceContentsRef test
,
10225 CFDictionaryRef dict
;
10226 IPv6RouteListRef r
;
10228 Rank rank_assertion
= kRankAssertionDefault
;
10229 CFNumberRef rank_assertion_cf
= NULL
;
10230 Boolean rank_assertion_is_set
= FALSE
;
10231 IPv6RouteListRef ret
= NULL
;
10232 IPV6_ROUTES_BUF_DECL(routes
);
10234 dict
= make_IPv6_dict(test
);
10235 if (dict
== NULL
) {
10236 fprintf(stderr
, "make_IPv6_dict failed\n");
10239 if (test
->primary_rank
!= NULL
) {
10241 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
10242 &rank_assertion_is_set
);
10243 if (rank_assertion_is_set
) {
10245 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
10248 r
= IPv6RouteListCreateWithDictionary(routes
, dict
,
10249 rank_assertion_cf
);
10250 my_CFRelease(&rank_assertion_cf
);
10252 fprintf(stderr
, "IPv6RouteListCreateWithDictionary failed\n");
10256 if (rank_assertion
== kRankAssertionScoped
) {
10257 rank_assertion
= kRankAssertionNever
;
10259 rank
= RankMake(test
->rank
, rank_assertion
);
10260 if (log_it
== kLogRouteEnabled
10261 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10264 descr
= IPv6RouteListCopyDescription(r
);
10265 SCPrint(TRUE
, stdout
, CFSTR("Adding %@"), descr
);
10268 ret
= IPv6RouteListAddRouteList(list
, 1, r
, rank
);
10276 static IPv6RouteListRef
10277 make_IPv6RouteList(IPv6ServiceContentsRef
* test
, Direction direction
,
10280 IPv6RouteListRef ret
= NULL
;
10281 IPv6ServiceContentsRef
* scan
;
10283 switch (direction
) {
10284 case kDirectionBackwards
:
10285 for (scan
= test
; *scan
!= NULL
; scan
++) {
10286 /* find the end of the list */
10288 for (scan
--; scan
>= test
; scan
--) {
10289 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10293 case kDirectionForwards
:
10294 for (scan
= test
; *scan
!= NULL
; scan
++) {
10295 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10299 IPv6RouteListFinalize(ret
);
10303 #define EMPHASIS_CHARS "================="
10306 * Function: routelist_build_test
10308 * Runs through the given set of routes first in the forward direction,
10309 * then again backwards. We should end up with exactly the same set of
10310 * routes at the end.
10313 routelist_build_test(IPv6RouteTestRef test
)
10316 boolean_t ret
= FALSE
;
10317 IPv6RouteListRef routes1
;
10318 IPv6RouteListRef routes2
;
10320 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
10321 EMPHASIS_CHARS
"\n",
10324 routes1
= make_IPv6RouteList(test
->test
, kDirectionForwards
,
10326 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10327 if (routes1
!= NULL
) {
10328 descr
= IPv6RouteListCopyDescription(routes1
);
10329 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10333 routes2
= make_IPv6RouteList(test
->test
, kDirectionBackwards
,
10335 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10336 if (routes2
!= NULL
) {
10337 descr
= IPv6RouteListCopyDescription(routes2
);
10338 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10342 if ((routes1
!= NULL
&& routes2
== NULL
)
10343 || (routes1
== NULL
&& routes2
!= NULL
)) {
10344 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
10345 (routes1
!= NULL
) ? "not " : "",
10346 (routes2
!= NULL
) ? "not " : "");
10348 else if (routes1
!= NULL
&& routes2
!= NULL
) {
10349 /* check if they are different */
10350 if (routes1
->count
!= routes2
->count
) {
10351 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
10352 routes1
->count
, routes2
->count
);
10354 else if (bcmp(routes1
, routes2
,
10355 IPv6RouteListComputeSize(routes1
->count
)) != 0) {
10356 fprintf(stderr
, "routes1 and routes2 are different\n");
10359 printf("routes1 and routes2 are the same\n");
10363 if (routes1
!= NULL
) {
10366 if (routes2
!= NULL
) {
10369 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
10370 EMPHASIS_CHARS
"\n",
10371 test
->name
, ret
? "PASSED" : "FAILED");
10376 apply_test(IPv6RouteTestRef old_test
, IPv6RouteTestRef new_test
)
10378 IPv6RouteListRef new_routes
;
10379 IPv6RouteListRef old_routes
;
10381 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
10382 EMPHASIS_CHARS
"\n",
10383 old_test
->name
, new_test
->name
);
10385 old_routes
= make_IPv6RouteList(old_test
->test
, kDirectionForwards
,
10386 kLogRouteDisabled
);
10387 new_routes
= make_IPv6RouteList(new_test
->test
, kDirectionForwards
,
10388 kLogRouteDisabled
);
10389 if (old_routes
== NULL
) {
10390 printf("No Old Routes\n");
10393 printf("Old routes ('%s') = ", old_test
->name
);
10394 IPv6RouteListPrint(old_routes
);
10397 /* apply the old routes */
10398 IPv6RouteListApply(NULL
, old_routes
, -1);
10399 if (new_routes
== NULL
) {
10400 printf("No New Routes\n");
10403 printf("New Routes ('%s') = ", new_test
->name
);
10404 IPv6RouteListPrint(new_routes
);
10407 /* apply the new routes */
10408 IPv6RouteListApply(old_routes
, new_routes
, -1);
10409 if (old_routes
!= NULL
) {
10412 if (new_routes
!= NULL
) {
10415 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10416 EMPHASIS_CHARS
"\n",
10417 old_test
->name
, new_test
->name
);
10422 main(int argc
, char **argv
)
10424 IPv6RouteTestRef
* test
;
10427 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10428 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10430 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10432 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10433 if (!routelist_build_test(*test
)) {
10434 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10438 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10439 IPv6RouteTestRef
* test2
;
10441 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10442 apply_test(*test
, *test2
);
10443 apply_test(*test2
, *test
);
10450 printf("\nChecking for leaks\n");
10451 sprintf(cmd
, "leaks %d 2>&1", getpid());
10459 #endif /* TEST_IPV6_ROUTELIST */
10461 #ifdef TEST_DNS_ORDER
10463 #define kProtocolFlagsIPv4v6 (kProtocolFlagsIPv4 | kProtocolFlagsIPv6)
10465 #define V4_ADDR_LOOP CFSTR("127.0.0.1")
10466 #define V4_ADDR_1 CFSTR("192.168.1.1")
10467 #define V4_ADDR_2 CFSTR("192.168.1.2")
10468 #define V4_ADDR_3 CFSTR("8.8.8.8")
10469 #define V4_ADDR_4 CFSTR("8.8.4.4")
10471 #define V6_ADDR_LOOP CFSTR("::1")
10472 #define V6_ADDR_1 CFSTR("fe80::0123:4567:89ab:cdef%en0")
10473 #define V6_ADDR_2 CFSTR("fd00::2acf:e9ff:fe14:8c59")
10474 #define V6_ADDR_3 CFSTR("2001:4860:4860::8888")
10478 const ProtocolFlags flags
;
10479 const CFStringRef server_addrs
[];
10480 } DNSOrderTest
, * DNSOrderTestRef
;
10482 static DNSOrderTest test0a
= {
10484 kProtocolFlagsIPv4
,
10486 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, NULL
10490 static DNSOrderTest test0b
= {
10492 kProtocolFlagsIPv6
,
10494 V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10498 static DNSOrderTest test1a
= {
10500 kProtocolFlagsIPv4v6
,
10502 V4_ADDR_1
, V6_ADDR_1
, NULL
10506 static DNSOrderTest test2a
= {
10508 kProtocolFlagsIPv4v6
,
10510 V4_ADDR_1
, V6_ADDR_2
, NULL
10514 static DNSOrderTest test3a
= {
10516 kProtocolFlagsIPv4v6
,
10518 V4_ADDR_1
, V6_ADDR_3
, NULL
10522 static DNSOrderTest test1b
= {
10524 kProtocolFlagsIPv4v6
,
10526 V4_ADDR_3
, V6_ADDR_1
, NULL
10530 static DNSOrderTest test2b
= {
10532 kProtocolFlagsIPv4v6
,
10534 V4_ADDR_3
, V6_ADDR_2
, NULL
10538 static DNSOrderTest test3b
= {
10540 kProtocolFlagsIPv4v6
,
10542 V4_ADDR_3
, V6_ADDR_3
, NULL
10546 static DNSOrderTest test1c
= {
10548 kProtocolFlagsIPv4v6
,
10550 V6_ADDR_1
, V4_ADDR_1
, NULL
10554 static DNSOrderTest test2c
= {
10556 kProtocolFlagsIPv4v6
,
10558 V6_ADDR_2
, V4_ADDR_1
, NULL
10562 static DNSOrderTest test3c
= {
10564 kProtocolFlagsIPv4v6
,
10566 V6_ADDR_3
, V4_ADDR_1
, NULL
10570 static DNSOrderTest test1d
= {
10572 kProtocolFlagsIPv4v6
,
10574 V6_ADDR_1
, V4_ADDR_3
, NULL
10578 static DNSOrderTest test2d
= {
10580 kProtocolFlagsIPv4v6
,
10582 V6_ADDR_2
, V4_ADDR_3
, NULL
10586 static DNSOrderTest test3d
= {
10588 kProtocolFlagsIPv4v6
,
10590 V6_ADDR_3
, V4_ADDR_3
, NULL
10594 static DNSOrderTest test4
= {
10596 kProtocolFlagsIPv4v6
,
10598 V4_ADDR_LOOP
, V4_ADDR_3
, V6_ADDR_2
, NULL
10602 static DNSOrderTest test5
= {
10604 kProtocolFlagsIPv4v6
,
10606 V4_ADDR_3
, V6_ADDR_LOOP
, V6_ADDR_2
, NULL
10610 static DNSOrderTest test6
= {
10612 kProtocolFlagsIPv4v6
,
10614 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10618 static DNSOrderTest test7
= {
10620 kProtocolFlagsIPv4v6
,
10622 V4_ADDR_1
, V6_ADDR_1
, V4_ADDR_3
, V6_ADDR_2
, NULL
10626 static DNSOrderTestRef dns_order_tests
[] = {
10628 &test1a
, &test2a
, &test3a
,
10629 &test1b
, &test2b
, &test3b
,
10630 &test1c
, &test2c
, &test3c
,
10631 &test1d
, &test2d
, &test3d
,
10639 #define EMPHASIS_CHARS "================="
10642 apply_order(CFArrayRef servers
, ProtocolFlags flags
)
10644 CFArrayRef ordered_servers
;
10646 ordered_servers
= order_dns_servers(servers
, flags
);
10647 if (ordered_servers
!= NULL
) {
10648 SCPrint(TRUE
, stdout
, CFSTR("After :\n%@\n"), ordered_servers
);
10649 CFRelease(ordered_servers
);
10651 printf("FAIL: No ordered servers\n");
10658 apply_test(DNSOrderTestRef test
)
10660 CFMutableArrayRef servers
;
10662 printf("\n" EMPHASIS_CHARS
"> '%s' Begin <" EMPHASIS_CHARS
"\n\n", test
->name
);
10664 servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
10665 for (int i
= 0; test
->server_addrs
[i
] != NULL
; i
++) {
10666 CFStringRef server_addr
= test
->server_addrs
[i
];
10668 CFArrayAppendValue(servers
, server_addr
);
10671 SCPrint(TRUE
, stdout
, CFSTR("Before :\n%@\n"), servers
);
10673 apply_order(servers
, test
->flags
);
10675 CFRelease(servers
);
10681 main(int argc
, char **argv
)
10684 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10685 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10687 S_IPMonitor_debug
= (uint32
)strtoul(argv
[1], NULL
, 0);
10690 for (DNSOrderTestRef
* test
= dns_order_tests
; *test
!= NULL
; test
++) {
10697 printf("\nChecking for leaks\n");
10698 sprintf(cmd
, "leaks %d 2>&1", getpid());
10707 #endif /* TEST_DNS_ORDER */