2 * Copyright (c) 2000-2016 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 <SystemConfiguration/SystemConfiguration.h>
99 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
100 #include <SystemConfiguration/SCValidation.h>
101 #include <SystemConfiguration/scprefs_observer.h>
102 #include <SystemConfiguration/SCPrivate.h>
103 #include "SCNetworkReachabilityInternal.h"
104 #include "SCNetworkSignaturePrivate.h"
106 #include "dnsinfo_server.h"
108 #include <ppp/PPPControllerPriv.h>
111 #include <dns_sd_private.h>
113 #include <network_information.h>
114 #include "network_state_information_priv.h"
115 #include "network_information_server.h"
116 #include <ppp/ppp_msg.h>
117 #include "ip_plugin.h"
118 #if !TARGET_OS_SIMULATOR
119 #include "set-hostname.h"
120 #endif /* !TARGET_OS_SIMULATOR */
122 #include "dns-configuration.h"
123 #include "proxy-configuration.h"
125 #if !TARGET_OS_SIMULATOR
126 #include "agent-monitor.h"
127 #endif // !TARGET_OS_SIMULATOR
129 #if !TARGET_OS_IPHONE
130 #include "smb-configuration.h"
131 #endif /* !TARGET_OS_IPHONE */
133 #define kLoopbackInterface "lo0"
134 #define EROUTENOTAPPLIED 1001
136 typedef CF_ENUM(uint8_t, ProtocolFlags
) {
137 kProtocolFlagsNone
= 0x0,
138 kProtocolFlagsIPv4
= 0x1,
139 kProtocolFlagsIPv6
= 0x2
143 kDebugFlag1
= 0x00000001,
144 kDebugFlag2
= 0x00000002,
145 kDebugFlag4
= 0x00000004,
146 kDebugFlag8
= 0x00000008,
147 kDebugFlagDefault
= kDebugFlag1
,
148 kDebugFlagAll
= 0xffffffff
151 typedef unsigned int IFIndex
;
158 __private_extern__ os_log_t
161 static os_log_t log
= NULL
;
164 log
= os_log_create("com.apple.SystemConfiguration", "IPMonitor");
172 #pragma mark interface index
175 #ifndef TEST_ROUTELIST
177 #define ROUTELIST_DEBUG(flag, fmt, ...)
179 static struct if_nameindex
* S_if_nameindex_cache
;
181 __private_extern__ IFIndex
182 my_if_nametoindex(const char * ifname
)
185 struct if_nameindex
* scan
;
187 if (S_if_nameindex_cache
== NULL
) {
188 return (if_nametoindex(ifname
));
190 for (scan
= S_if_nameindex_cache
;
191 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
193 if (strcmp(scan
->if_name
, ifname
) == 0) {
194 idx
= scan
->if_index
;
201 __private_extern__
const char *
202 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
204 const char * name
= NULL
;
205 struct if_nameindex
* scan
;
207 if (S_if_nameindex_cache
== NULL
) {
208 return (if_indextoname(idx
, if_name
));
210 for (scan
= S_if_nameindex_cache
;
211 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
213 if (scan
->if_index
== idx
) {
215 strlcpy(if_name
, scan
->if_name
, IFNAMSIZ
);
223 my_if_freenameindex(void)
225 if (S_if_nameindex_cache
!= NULL
) {
226 if_freenameindex(S_if_nameindex_cache
);
227 S_if_nameindex_cache
= NULL
;
233 my_if_nameindex(void)
235 my_if_freenameindex();
236 S_if_nameindex_cache
= if_nameindex();
241 #else /* TEST_ROUTELIST */
243 #define ROUTELIST_DEBUG(flags, format, ...) { if (((S_IPMonitor_debug & (flags)) != 0)) printf((format), ## __VA_ARGS__ ); }
246 static const char * * list
;
247 static int list_count
;
248 static int list_size
;
250 __private_extern__ IFIndex
251 my_if_nametoindex(const char * ifname
)
258 list
= (const char * *)malloc(sizeof(*list
) * list_size
);
259 list
[0] = strdup("");
260 list
[1] = strdup(kLoopbackInterface
);
265 for (i
= 1; i
< list_count
; i
++) {
266 if (strcmp(list
[i
], ifname
) == 0) {
272 if (list_count
== list_size
) {
274 list
= (const char * *)realloc(list
, sizeof(*list
) * list_size
);
276 list
[list_count
] = strdup(ifname
);
283 __private_extern__
const char *
284 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
286 const char * name
= NULL
;
288 if (idx
< list_count
) {
290 strlcpy(if_name
, list
[idx
], IFNAMSIZ
);
296 my_if_nameindex(void)
301 my_if_freenameindex(void)
305 #endif /* TEST_ROUTELIST */
308 my_if_indextoname2(IFIndex ifindex
, char ifname
[IFNAMSIZ
])
313 if (my_if_indextoname(ifindex
, ifname
) == NULL
) {
314 snprintf(ifname
, IFNAMSIZ
, "[%d]", ifindex
);
326 idx
= my_if_nametoindex(kLoopbackInterface
);
336 * Property: kServiceOptionRankAssertion
338 * Key used in the service options dictionary to hold the RankAssertion
339 * derived from the kSCPropNetServicePrimaryRank string.
341 #define kServiceOptionRankAssertion CFSTR("RankAssertion") /* number */
344 * Property: kIPIsCoupled
346 * Used to indicate that the IPv4 and IPv6 services are coupled.
347 * Neither the IPv4 part nor the IPv6 part of a coupled service
348 * may become primary if IPv4 or IPv6 is primary for another interface.
350 * For example, if the service over en3 is "coupled" and has IPv6,
351 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible
352 * to become primary for IPv6.
354 #define kIPIsCoupled CFSTR("IPIsCoupled")
356 #define PPP_PREFIX "ppp"
358 #define IP_FORMAT "%d.%d.%d.%d"
359 #define IP_CH(ip) ((u_char *)(ip))
360 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
362 static SCLoggerRef S_IPMonitor_logger
;
364 static Boolean S_bundle_logging_verbose
;
367 * IPv4 Route management
370 typedef CF_ENUM(uint16_t, RouteFlags
) {
371 kRouteFlagsIsScoped
= 0x0001,
372 kRouteFlagsHasGateway
= 0x0002,
373 kRouteFlagsIsHost
= 0x0004,
374 kRouteFlagsIsNULL
= 0x0008,
375 kRouteFlagsKernelManaged
= 0x0010
378 typedef CF_ENUM(uint16_t, ControlFlags
) {
379 kControlFlagsProcessed
= 0x0001,
380 kControlFlagsAdded
= 0x0002,
383 #define ROUTE_COMMON \
386 IFIndex exclude_ifindex; \
389 ControlFlags control_flags;
395 #define PREFIX_LENGTH_IN_CLASSC 24
396 #define PREFIX_LENGTH_IN_CLASSD 4
402 struct in_addr gateway
;
404 } IPv4Route
, * IPv4RouteRef
;
408 struct in6_addr dest
;
409 struct in6_addr gateway
;
411 } IPv6Route
, * IPv6RouteRef
;
413 typedef CF_ENUM(uint16_t, RouteListFlags
) {
414 kRouteListFlagsExcludeNWI
= 0x0001,
415 kRouteListFlagsHasDefault
= 0x0002,
416 kRouteListFlagsScopedOnly
= 0x0004
419 #define ROUTELIST_COMMON \
422 RouteListFlags flags;
426 } RouteListCommon
, * RouteListRef
;
430 IPv4Route list
[1]; /* variable length */
431 } IPv4RouteList
, * IPv4RouteListRef
;
435 IPv6Route list
[1]; /* variable length */
436 } IPv6RouteList
, * IPv6RouteListRef
;
451 * Election Information
452 * - information about the current best services
460 struct sockaddr_in v4
;
461 struct sockaddr_in6 v6
;
464 typedef struct Candidate
{
465 CFStringRef serviceID
;
468 boolean_t ip_is_coupled
;
469 boolean_t ineligible
;
470 SCNetworkReachabilityFlags reachability_flags
;
472 in_sockaddr vpn_server_addr
;
473 CFStringRef signature
;
474 } Candidate
, * CandidateRef
;
476 typedef struct ElectionResults
{
480 Candidate candidates
[1];
481 } ElectionResults
, * ElectionResultsRef
;
483 static __inline__
size_t
484 ElectionResultsComputeSize(unsigned int n
)
486 return (offsetof(ElectionResults
, candidates
[n
]));
492 * A 32-bit value to encode the relative rank of a service.
494 * The top 8 bits are used to hold the rank assertion (first, default, last,
497 * The bottom 24 bits are used to store the service index (i.e. the
498 * position within the service order array).
500 #define RANK_ASSERTION_MAKE(r) ((Rank)(r) << 24)
501 #define kRankAssertionFirst RANK_ASSERTION_MAKE(0)
502 #define kRankAssertionDefault RANK_ASSERTION_MAKE(1)
503 #define kRankAssertionLast RANK_ASSERTION_MAKE(2)
504 #define kRankAssertionNever RANK_ASSERTION_MAKE(3)
505 #define kRankAssertionScoped RANK_ASSERTION_MAKE(4)
506 #define kRankAssertionMask RANK_ASSERTION_MAKE(0xff)
507 #define RANK_ASSERTION_MASK(r) ((Rank)(r) & kRankAssertionMask)
508 #define RANK_ASSERTION_GET(r) ((Rank)(r) >> 24)
509 #define RANK_INDEX_MAKE(r) ((Rank)(r))
510 #define kRankIndexMask RANK_INDEX_MAKE(0xffffff)
511 #define RANK_INDEX_MASK(r) ((Rank)(r) & kRankIndexMask)
513 static __inline__ Rank
514 RankMake(uint32_t service_index
, Rank primary_rank
)
516 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
520 InterfaceRankGetRankAssertion(CFNumberRef rank_cf
, Boolean
* ret_is_set
)
522 SCNetworkServicePrimaryRank if_rank
;
523 Boolean is_set
= FALSE
;
524 Rank rank
= kRankAssertionDefault
;
527 && CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &if_rank
)
528 && if_rank
!= kSCNetworkServicePrimaryRankDefault
) {
529 if (if_rank
== kSCNetworkServicePrimaryRankFirst
) {
530 rank
= kRankAssertionFirst
;
533 rank
= RANK_ASSERTION_MAKE(if_rank
);
537 if (ret_is_set
!= NULL
) {
538 *ret_is_set
= is_set
;
544 PrimaryRankGetRankAssertion(CFStringRef rank_str
, Boolean
* is_set
)
548 const CFStringRef
* name
;
551 { &kSCValNetServicePrimaryRankFirst
, kRankAssertionFirst
},
552 { &kSCValNetServicePrimaryRankLast
, kRankAssertionLast
},
553 { &kSCValNetServicePrimaryRankNever
, kRankAssertionNever
},
554 { &kSCValNetServicePrimaryRankScoped
, kRankAssertionScoped
}
557 if (rank_str
!= NULL
) {
558 for (i
= 0; i
< countof(values
); i
++) {
559 if (CFEqual(rank_str
, *(values
[i
].name
))) {
560 if (is_set
!= NULL
) {
563 return (values
[i
].rank_assertion
);
567 if (is_set
!= NULL
) {
570 return (kRankAssertionDefault
);
573 /* SCDynamicStore session */
574 static SCDynamicStoreRef S_session
= NULL
;
576 /* debug output flags */
577 static uint32_t S_IPMonitor_debug
= 0;
578 static Boolean S_IPMonitor_verbose
= FALSE
;
580 /* are we netbooted? If so, don't touch the default route */
581 static boolean_t S_netboot
= FALSE
;
583 /* dictionary to hold per-service state: key is the serviceID */
584 static CFMutableDictionaryRef S_service_state_dict
= NULL
;
585 static CFMutableDictionaryRef S_ipv4_service_rank_dict
= NULL
;
586 static CFMutableDictionaryRef S_ipv6_service_rank_dict
= NULL
;
588 /* dictionary to hold per-interface rank information */
589 static CFDictionaryRef S_if_rank_dict
;
591 /* if set, a PPP interface overrides the primary */
592 static boolean_t S_ppp_override_primary
= FALSE
;
594 /* the current primary serviceID's */
595 static CFStringRef S_primary_ipv4
= NULL
;
596 static CFStringRef S_primary_ipv6
= NULL
;
597 static CFStringRef S_primary_dns
= NULL
;
598 static CFStringRef S_primary_proxies
= NULL
;
600 /* the current election results */
601 static ElectionResultsRef S_ipv4_results
;
602 static ElectionResultsRef S_ipv6_results
;
604 static CFStringRef S_state_global_ipv4
= NULL
;
605 static CFStringRef S_state_global_ipv6
= NULL
;
606 static CFStringRef S_state_global_dns
= NULL
;
607 static CFStringRef S_state_global_proxies
= NULL
;
608 static CFStringRef S_state_service_prefix
= NULL
;
609 static CFStringRef S_setup_global_ipv4
= NULL
;
610 static CFStringRef S_setup_service_prefix
= NULL
;
612 static CFStringRef S_multicast_resolvers
= NULL
;
613 static CFStringRef S_private_resolvers
= NULL
;
615 #if !TARGET_OS_SIMULATOR
616 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
617 static IPv6RouteListRef S_ipv6_routelist
= NULL
;
619 #endif /* !TARGET_OS_SIMULATOR */
621 static boolean_t S_append_state
= FALSE
;
623 static CFDictionaryRef S_dns_dict
= NULL
;
625 static Boolean S_dnsinfo_synced
= TRUE
;
627 static nwi_state_t S_nwi_state
= NULL
;
628 static Boolean S_nwi_synced
= TRUE
;
630 static CFDictionaryRef S_proxies_dict
= NULL
;
632 // Note: access should be gated with __network_change_queue()
633 static uint32_t S_network_change_needed
= 0;
634 #define NETWORK_CHANGE_NET 1<<0
635 #define NETWORK_CHANGE_DNS 1<<1
636 #define NETWORK_CHANGE_PROXY 1<<2
637 #if !TARGET_OS_IPHONE
638 #define NETWORK_CHANGE_SMB 1<<3
639 #endif /* !TARGET_OS_IPHONE */
640 static struct timeval S_network_change_start
;
641 static Boolean S_network_change_timeout
= FALSE
;
642 static dispatch_source_t S_network_change_timer
= NULL
;
644 #if !TARGET_OS_IPHONE
645 static CFStringRef S_primary_smb
= NULL
;
646 static CFStringRef S_state_global_smb
= NULL
;
647 static CFDictionaryRef S_smb_dict
= NULL
;
648 #endif /* !TARGET_OS_IPHONE */
650 #if !TARGET_OS_IPHONE
651 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
652 #endif /* !TARGET_OS_IPHONE */
655 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
656 #endif /* KERN_NETBOOT */
659 ** entityType*, GetEntityChanges*
660 ** - definitions for the entity types we handle
667 #if !TARGET_OS_IPHONE
669 #endif /* !TARGET_OS_IPHONE */
671 kEntityTypeTransientStatus
,
672 kEntityTypeServiceOptions
= 31
675 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
676 &kSCEntNetIPv4
, /* 0 */
677 &kSCEntNetIPv6
, /* 1 */
678 &kSCEntNetDNS
, /* 2 */
679 &kSCEntNetProxies
, /* 3 */
680 #if !TARGET_OS_IPHONE
681 &kSCEntNetSMB
, /* 4 */
682 #endif /* !TARGET_OS_IPHONE */
686 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
);
688 static __inline__
char
691 return ((af
== AF_INET
) ? '4' : '6');
694 static __inline__
char
695 ipvx_other_char(int af
)
697 return ((af
== AF_INET
) ? '6' : '4');
701 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
703 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
704 * Routes CFData containing IPv4RouteList/IPv6RouteList
705 * Service dictionary containing kSCEntNetIPv[46] service entity
707 #define kIPDictRoutes CFSTR("Routes") /* data */
708 #define kIPDictService CFSTR("Service") /* dict */
710 static CFDictionaryRef
711 ipdict_create(CFDictionaryRef dict
, CFDataRef routes_data
)
716 keys
[0] = kIPDictService
;
718 keys
[1] = kIPDictRoutes
;
719 values
[1] = routes_data
;
720 return (CFDictionaryCreate(NULL
,
721 (const void * *)keys
,
724 &kCFTypeDictionaryKeyCallBacks
,
725 &kCFTypeDictionaryValueCallBacks
));
729 ipdict_get_routelist(CFDictionaryRef dict
)
731 void * routes_list
= NULL
;
736 routes
= CFDictionaryGetValue(dict
, kIPDictRoutes
);
737 if (routes
!= NULL
) {
738 routes_list
= (void *)CFDataGetBytePtr(routes
);
741 return (routes_list
);
744 static CFDictionaryRef
745 ipdict_get_service(CFDictionaryRef dict
)
747 CFDictionaryRef ip_dict
= NULL
;
750 ip_dict
= CFDictionaryGetValue(dict
, kIPDictService
);
756 ipdict_get_ifname(CFDictionaryRef dict
)
758 CFStringRef ifname
= NULL
;
759 CFDictionaryRef ip_dict
;
761 ip_dict
= ipdict_get_service(dict
);
762 if (ip_dict
!= NULL
) {
763 ifname
= CFDictionaryGetValue(ip_dict
, kSCPropInterfaceName
);
768 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
769 CFDictionaryRef state_dict
,
770 CFDictionaryRef setup_dict
,
771 CFDictionaryRef info
);
772 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
774 static GetEntityChangesFunc get_ipv4_changes
;
775 static GetEntityChangesFunc get_ipv6_changes
;
776 static GetEntityChangesFunc get_dns_changes
;
777 static GetEntityChangesFunc get_proxies_changes
;
778 #if !TARGET_OS_IPHONE
779 static GetEntityChangesFunc get_smb_changes
;
780 #endif /* !TARGET_OS_IPHONE */
782 static __inline__
void
783 my_CFRelease(void * t
)
785 void * * obj
= (void * *)t
;
795 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
798 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
800 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
801 get_ipv4_changes
, /* 0 */
802 get_ipv6_changes
, /* 1 */
803 get_dns_changes
, /* 2 */
804 get_proxies_changes
,/* 3 */
805 #if !TARGET_OS_IPHONE
806 get_smb_changes
, /* 4 */
807 #endif /* !TARGET_OS_IPHONE */
812 ** - mechanism to do an atomic update of the SCDynamicStore
813 ** when the content needs to be changed across multiple functions
816 CFMutableArrayRef notify
;
817 CFMutableArrayRef remove
;
818 CFMutableDictionaryRef set
;
819 } keyChangeList
, * keyChangeListRef
;
822 keyChangeListInit(keyChangeListRef keys
)
824 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
825 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
826 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
827 &kCFTypeDictionaryKeyCallBacks
,
828 &kCFTypeDictionaryValueCallBacks
);
833 keyChangeListFree(keyChangeListRef keys
)
835 my_CFRelease(&keys
->notify
);
836 my_CFRelease(&keys
->remove
);
837 my_CFRelease(&keys
->set
);
842 keyChangeListActive(keyChangeListRef keys
)
844 return ((CFDictionaryGetCount(keys
->set
) > 0) ||
845 (CFArrayGetCount(keys
->remove
) > 0) ||
846 (CFArrayGetCount(keys
->notify
) > 0));
850 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
852 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
857 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
859 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
860 CFDictionaryRemoveValue(keys
->set
, key
);
865 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
867 my_CFArrayRemoveValue(keys
->remove
, key
);
868 CFDictionarySetValue(keys
->set
, key
, value
);
873 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
875 CFArrayRef notify
= keys
->notify
;
876 CFArrayRef remove
= keys
->remove
;
877 CFDictionaryRef set
= keys
->set
;
879 if (CFArrayGetCount(notify
) == 0) {
882 if (CFArrayGetCount(remove
) == 0) {
885 if (CFDictionaryGetCount(set
) == 0) {
888 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
891 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
893 my_log(LOG_DEBUG
, "Setting:\n%@", set
);
895 if (remove
!= NULL
) {
896 my_log(LOG_DEBUG
, "Removing:\n%@", remove
);
898 if (notify
!= NULL
) {
899 my_log(LOG_DEBUG
, "Notifying:\n%@", notify
);
902 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
908 S_nwi_ifstate_dump(nwi_ifstate_t ifstate
, int i
)
910 const char * addr_str
;
912 char ntopbuf
[INET6_ADDRSTRLEN
];
913 char vpn_ntopbuf
[INET6_ADDRSTRLEN
];
914 const struct sockaddr
* vpn_addr
;
916 address
= nwi_ifstate_get_address(ifstate
);
917 addr_str
= inet_ntop(ifstate
->af
, address
, ntopbuf
, sizeof(ntopbuf
));
918 vpn_addr
= nwi_ifstate_get_vpn_server(ifstate
);
919 if (vpn_addr
!= NULL
) {
920 _SC_sockaddr_to_string(nwi_ifstate_get_vpn_server(ifstate
),
922 sizeof(vpn_ntopbuf
));
925 " [%d]: %s%s%s%s rank 0x%x iaddr %s%s%s reach_flags 0x%x",
927 nwi_ifstate_get_diff_str(ifstate
),
928 (ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_DNS
) != 0
930 (ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0
934 (vpn_addr
!= NULL
) ? " vpn_server_addr: " : "",
935 (vpn_addr
!= NULL
) ? vpn_ntopbuf
: "",
936 ifstate
->reach_flags
);
941 S_nwi_state_dump(nwi_state_t state
)
947 my_log(LOG_INFO
, "nwi_state = <none>");
952 "gen=%llu size=%lu #v4=%u #v6=%u "
953 "reach_flags=(v4=0x%x, v6=0x%x) }",
954 state
->generation_count
,
955 nwi_state_size(state
),
958 nwi_state_get_reachability_flags(state
, AF_INET
),
959 nwi_state_get_reachability_flags(state
, AF_INET6
));
960 if (state
->ipv4_count
) {
961 my_log(LOG_INFO
, "IPv4:");
962 for (i
= 0, scan
= nwi_state_ifstate_list(state
, AF_INET
);
963 i
< state
->ipv4_count
; i
++, scan
++) {
964 S_nwi_ifstate_dump(scan
, i
);
967 if (state
->ipv6_count
) {
968 my_log(LOG_INFO
, "IPv6:");
969 for (i
= 0, scan
= nwi_state_ifstate_list(state
, AF_INET6
);
970 i
< state
->ipv6_count
; i
++, scan
++) {
971 S_nwi_ifstate_dump(scan
, i
);
974 if (state
->max_if_count
) {
975 nwi_ifindex_t
* ifindex
;
977 my_log(LOG_INFO
, "%d interfaces:", state
->if_list_count
);
978 for (i
= 0, ifindex
= nwi_state_if_list(state
);
979 i
< state
->if_list_count
;
981 my_log(LOG_INFO
, "%s", state
->ifstate_list
[*ifindex
].ifname
);
995 mib
[1] = KERN_NETBOOT
;
996 len
= sizeof(netboot
);
997 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
1001 static int rtm_seq
= 0;
1003 #if !TARGET_OS_SIMULATOR
1005 open_routing_socket(void)
1009 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
1010 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
1015 static __inline__
int
1016 inet6_dgram_socket(void)
1020 sockfd
= socket(AF_INET6
, SOCK_DGRAM
, 0);
1022 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
1029 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
1031 struct in6_defrouter dr
;
1032 struct sockaddr_in6
* sin6
;
1034 bzero(&dr
, sizeof(dr
));
1036 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
1037 sin6
->sin6_family
= AF_INET6
;
1038 sin6
->sin6_addr
= *addr
;
1040 dr
.if_index
= if_index
;
1041 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
1045 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
1047 struct in6_defrouter dr
;
1048 struct sockaddr_in6
* sin6
;
1050 bzero(&dr
, sizeof(dr
));
1052 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
1053 sin6
->sin6_family
= AF_INET6
;
1054 sin6
->sin6_addr
= *addr
;
1055 dr
.if_index
= if_index
;
1056 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
1059 #endif /* !TARGET_OS_SIMULATOR */
1062 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
1064 CFIndex n
= CFArrayGetCount(arr
);
1066 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
1069 CFArrayAppendValue(arr
, new);
1074 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
1078 i
= CFArrayGetFirstIndexOfValue(arr
,
1079 CFRangeMake(0, CFArrayGetCount(arr
)),
1081 if (i
!= kCFNotFound
) {
1082 CFArrayRemoveValueAtIndex(arr
, i
);
1088 my_CFArrayCreateCombinedArray(CFArrayRef array1
, CFArrayRef array2
)
1090 CFMutableArrayRef combined
;
1092 combined
= CFArrayCreateMutableCopy(NULL
, 0, array1
);
1093 CFArrayAppendArray(combined
,
1095 CFRangeMake(0, CFArrayGetCount(array2
)));
1099 static CFDictionaryRef
1100 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
1102 if (isA_CFDictionary(dict
) == NULL
) {
1105 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
1109 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
1111 if (isA_CFDictionary(dict
) == NULL
) {
1114 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
1118 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, int addr_size
)
1122 if (isA_CFString(str
) == NULL
) {
1128 if (addr_size
< sizeof(struct in_addr
)) {
1133 if (addr_size
< sizeof(struct in6_addr
)) {
1140 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
1141 if (inet_pton(family
, buf
, addr
) == 1) {
1145 bzero(addr
, addr_size
);
1151 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
1153 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
1158 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
1160 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
1164 cfnumber_to_int(CFNumberRef num
, int * int_val
)
1166 if (isA_CFNumber(num
) == NULL
) {
1169 return (CFNumberGetValue(num
, kCFNumberIntType
, int_val
));
1172 static CF_RETURNS_RETAINED CFStringRef
1173 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
1175 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1176 kSCDynamicStoreDomainSetup
,
1181 static CF_RETURNS_RETAINED CFStringRef
1182 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
1184 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1185 kSCDynamicStoreDomainState
,
1191 interface_entity_key_copy(CFStringRef ifname
, CFStringRef entity
)
1193 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1194 kSCDynamicStoreDomainState
,
1199 static CFDictionaryRef
1200 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1203 CFStringRef setup_key
;
1204 CFDictionaryRef setup_dict
;
1206 setup_key
= setup_service_key(serviceID
, entity
);
1207 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
1208 my_CFRelease(&setup_key
);
1209 return (setup_dict
);
1212 static CFDictionaryRef
1213 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1216 CFStringRef state_key
;
1217 CFDictionaryRef state_dict
;
1219 state_key
= state_service_key(serviceID
, entity
);
1220 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
1221 my_CFRelease(&state_key
);
1222 return (state_dict
);
1226 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1230 ip_list
= CFDictionaryGetValue(dict
, prop
);
1231 if (isA_CFArray(ip_list
) != NULL
1232 && CFArrayGetCount(ip_list
) > 0
1233 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1240 dict_get_first_ipv6(CFDictionaryRef dict
, CFStringRef prop
,
1241 struct in6_addr
* ip_p
)
1245 ip_list
= CFDictionaryGetValue(dict
, prop
);
1246 if (isA_CFArray(ip_list
) != NULL
1247 && CFArrayGetCount(ip_list
) > 0
1248 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1255 dict_get_first_int(CFDictionaryRef dict
, CFStringRef prop
,
1260 list
= CFDictionaryGetValue(dict
, prop
);
1261 if (isA_CFArray(list
) != NULL
1262 && CFArrayGetCount(list
) > 0
1263 && cfnumber_to_int(CFArrayGetValueAtIndex(list
, 0), val
)) {
1270 dict_get_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1274 val
= CFDictionaryGetValue(dict
, prop
);
1275 return (cfstring_to_ip(val
, ip_p
));
1279 dict_get_ipv6(CFDictionaryRef dict
, CFStringRef prop
, struct in6_addr
* ip_p
)
1283 val
= CFDictionaryGetValue(dict
, prop
);
1284 return (cfstring_to_ip6(val
, ip_p
));
1288 dict_get_int(CFDictionaryRef dict
, CFStringRef prop
, int * intval
)
1292 val
= CFDictionaryGetValue(dict
, prop
);
1293 return (cfnumber_to_int(val
, intval
));
1297 get_override_primary(CFDictionaryRef dict
)
1301 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
1302 if (isA_CFNumber(override
) != NULL
) {
1305 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
1310 else if (isA_CFBoolean(override
) != NULL
) {
1311 if (CFBooleanGetValue(override
)) {
1323 (*RouteListComputeSize
)(CFIndex n
);
1326 (*RouteIsEqual
)(RouteRef a
, RouteRef b
);
1329 (*RouteApply
)(RouteRef route
, int cmd
, int sockfd
);
1331 typedef const void *
1332 (*RouteGateway
)(RouteRef route
);
1335 (*RouteSetGateway
)(RouteRef route
, const void * address
);
1337 typedef const void *
1338 (*RouteDestination
)(RouteRef route
);
1341 (*RouteSameSubnet
)(RouteRef route
, const void * address
);
1344 (*RouteCopyDescription
)(RouteRef route
);
1347 (*RouteLog
)(int priority
, RouteRef route
, const char * msg
);
1350 RouteListComputeSize list_compute_size
;
1352 RouteIsEqual route_equal
;
1353 RouteApply route_apply
;
1354 RouteGateway route_gateway
;
1355 RouteSetGateway route_set_gateway
;
1356 RouteDestination route_destination
;
1357 RouteSameSubnet route_same_subnet
;
1359 RouteCopyDescription route_copy_description
;
1366 typedef const RouteListInfo
* RouteListInfoRef
;
1369 RouteListInfoRef info
;
1370 RouteListRef old_routes
;
1371 RouteListRef new_routes
;
1374 } RouteListApplyContext
, * RouteListApplyContextRef
;
1378 RouteAddressCompare(RouteListInfoRef info
,
1382 return (memcmp(addr1
, addr2
, info
->address_size
));
1386 RouteCompare(RouteListInfoRef info
,
1387 RouteRef a
, Rank a_rank
,
1388 RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
1391 RouteDestination route_destination
;
1392 RouteCopyDescription route_copy_description
;
1395 route_destination
= info
->route_destination
;
1396 route_copy_description
= info
->route_copy_description
;
1397 cmp
= RouteAddressCompare(info
,
1398 (*route_destination
)(a
),
1399 (*route_destination
)(b
));
1401 cmp
= a
->prefix_length
- b
->prefix_length
;
1403 int index_cmp
= a
->ifindex
- b
->ifindex
;
1405 if (index_cmp
== 0) {
1408 else if ((a
->ifindex
== 0 || b
->ifindex
== 0)
1409 && (a
->flags
& kRouteFlagsIsScoped
) == 0
1410 && (b
->flags
& kRouteFlagsIsScoped
) == 0) {
1412 * Either of the routes specifies no interface and neither
1413 * route is scoped. Claim they are equal to eliminate the
1420 cmp
= RankCompare(a_rank
, b_rank
);
1427 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
1435 else if (cmp
== 0) {
1441 a_str
= (*route_copy_description
)(a
);
1442 b_str
= (*route_copy_description
)(b
);
1443 my_log(LOG_DEBUG
, "%@ rank 0x%x %c %@ rank 0x%x",
1444 a_str
, a_rank
, ch
, b_str
, b_rank
);
1452 RouteListGetRouteAtIndexSimple(RouteListInfoRef info
, RouteListRef routes
,
1455 return ((void *)routes
+ (*info
->list_compute_size
)(where
));
1459 RouteListGetRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1462 if (routes
->count
== 0
1463 || where
>= routes
->count
) {
1466 return (RouteListGetRouteAtIndexSimple(info
, routes
, where
));
1470 RouteListGetFirstRoute(RouteListInfoRef info
, RouteListRef routes
)
1472 return (RouteListGetRouteAtIndexSimple(info
, routes
, 0));
1475 #if !TARGET_OS_SIMULATOR
1477 RouteListRouteIndex(RouteListInfoRef info
, RouteListRef routes
,
1480 return (((void *)route
1481 - (void *)RouteListGetFirstRoute(info
, routes
))
1482 / info
->element_size
);
1484 #endif /* !TARGET_OS_SIMULATOR */
1487 RouteGetNextRoute(RouteListInfoRef info
, RouteRef route
)
1489 return ((RouteRef
)(((void *)route
) + info
->element_size
));
1493 RouteListAddRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1494 RouteRef this_route
, CFIndex where
)
1496 RouteRef insert_route
;
1498 if (where
== kCFNotFound
) {
1499 /* add it to the end */
1501 = RouteListGetRouteAtIndexSimple(info
, routes
, routes
->count
);
1504 /* make space at [where] */
1505 insert_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1507 (void *)insert_route
+ info
->element_size
,
1508 info
->element_size
* (routes
->count
- where
));
1510 /* copy the route */
1511 bcopy(this_route
, insert_route
, info
->element_size
);
1513 return (insert_route
);
1517 RouteListRemoveRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1520 if (routes
->count
== 0
1521 || where
>= routes
->count
) {
1525 if (where
== routes
->count
) {
1526 /* last slot, decrementing gets rid of it */
1529 RouteRef remove_route
;
1531 remove_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1532 bcopy((void *)remove_route
+ info
->element_size
,
1534 info
->element_size
* (routes
->count
- where
));
1540 * Function: RouteListAddRoute
1543 * Add the given route to the list of routes, eliminating lower-ranked
1544 * duplicates on the same interface, and marking any lower ranked duplicates
1545 * on other interfaces with kRouteFlagsIsScoped.
1547 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1550 * Route list updated with the given route, possibly a different pointer,
1551 * due to using realloc'd memory.
1561 RouteListAddRoute(RouteListInfoRef info
,
1562 RouteListRef routes
, int init_size
,
1563 RouteRef this_route
, Rank this_rank
)
1566 RouteRef first_scan
= NULL
;
1569 Scope scope_which
= kScopeNone
;
1570 CFIndex where
= kCFNotFound
;
1572 if (routes
== NULL
) {
1573 size_t alloc_size
= (*info
->list_compute_size
)(init_size
);
1575 routes
= (RouteListRef
)malloc(alloc_size
);
1576 bzero(routes
, sizeof(*routes
));
1577 routes
->size
= init_size
;
1579 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1581 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1583 boolean_t same_dest
;
1585 cmp
= RouteCompare(info
, this_route
, this_rank
, scan
, scan
->rank
,
1587 if (same_dest
&& (first_scan
== NULL
)) {
1591 if (where
== kCFNotFound
) {
1593 && (first_scan
!= NULL
)
1594 && (first_scan
->flags
& kRouteFlagsIsScoped
) == 0) {
1595 if ((scan
->flags
& kRouteFlagsIsScoped
) != 0) {
1596 ROUTELIST_DEBUG(kDebugFlag8
,
1597 "Hit 1: set scope on self\n");
1598 scope_which
= kScopeThis
;
1601 ROUTELIST_DEBUG(kDebugFlag8
,
1602 "Hit 2: set scope on next\n");
1603 scope_which
= kScopeNext
;
1606 /* remember our insertion point, but keep going to find a dup */
1610 else if (cmp
== 0) {
1613 if (where
!= kCFNotFound
1614 && scan
->ifindex
== this_route
->ifindex
1615 && scan
->exclude_ifindex
== 0
1616 && this_route
->exclude_ifindex
== 0) {
1617 /* this route is a duplicate */
1618 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 3: removing [%ld]\n", i
);
1619 RouteListRemoveRouteAtIndex(info
, routes
, i
);
1623 * this_route is "better" than scan if this_route is not excluded
1624 * and scan is excluded or this_route sorts ahead of scan
1626 if (this_route
->exclude_ifindex
== 0
1627 && (scan
->exclude_ifindex
!= 0 || this_rank
< scan
->rank
)) {
1628 IFIndex ifindex
= 0;
1629 boolean_t is_scoped
= FALSE
;
1631 if (scan
->flags
& kRouteFlagsIsScoped
) {
1634 if (this_rank
< scan
->rank
) {
1635 ROUTELIST_DEBUG(kDebugFlag8
,
1636 "Hit 4a: replacing [%ld]"
1637 " rank 0x%x < 0x%x\n",
1638 i
, this_rank
, scan
->rank
);
1641 ROUTELIST_DEBUG(kDebugFlag8
,
1642 "Hit 4b: replacing [%ld] excluded route\n",
1645 if (scan
->ifindex
!= 0) {
1646 ifindex
= scan
->ifindex
;
1648 else if (this_route
->ifindex
!= 0) {
1649 ifindex
= this_route
->ifindex
;
1651 bcopy(this_route
, scan
, info
->element_size
);
1652 scan
->rank
= this_rank
;
1653 scan
->ifindex
= ifindex
;
1654 scan
->exclude_ifindex
= 0;
1656 /* preserve whether route was scoped */
1657 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 5: preserved scope\n");
1658 scan
->flags
|= kRouteFlagsIsScoped
;
1666 if (scope_which
== kScopeNone
) {
1667 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 6: set scope on self\n");
1668 scope_which
= kScopeThis
;
1671 #ifdef TEST_ROUTELIST
1672 else if (where
!= kCFNotFound
) {
1673 /* not possible because we maintain a sorted list */
1675 "Hit 7: moved past routes - can't happen\n");
1679 #endif /* TEST_ROUTELIST */
1683 if (routes
->size
== routes
->count
) {
1685 RouteListRef new_routes
;
1688 /* double the size */
1689 old_size
= routes
->size
;
1690 how_many
= old_size
* 2;
1691 new_routes
= (RouteListRef
)
1692 reallocf(routes
, (*info
->list_compute_size
)(how_many
));
1693 if (new_routes
== NULL
) {
1698 ROUTELIST_DEBUG(kDebugFlag8
, "increasing size from %d to %d\n",
1699 old_size
, how_many
);
1700 new_routes
->size
= how_many
;
1701 routes
= new_routes
;
1704 /* add/insert the new route */
1705 this_route
= RouteListAddRouteAtIndex(info
, routes
, this_route
, where
);
1706 this_route
->rank
= this_rank
;
1708 if (RANK_ASSERTION_MASK(this_rank
) == kRankAssertionNever
) {
1709 flags
|= kRouteFlagsIsScoped
;
1711 switch (scope_which
) {
1713 flags
|= kRouteFlagsIsScoped
;
1716 this_route
= RouteListGetRouteAtIndex(info
, routes
, where
+ 1);
1717 flags
|= kRouteFlagsIsScoped
;
1723 if (this_route
!= NULL
&& flags
!= 0) {
1724 this_route
->flags
|= flags
;
1732 * Function: RouteListAddRouteList
1734 * Invoke RouteListAddRoute for each route in the given list
1735 * 'service_routes' combining them into a combined list 'routes'.
1738 * See RouteListAddRoute for more information.
1741 RouteListAddRouteList(RouteListInfoRef info
,
1742 RouteListRef routes
, int init_size
,
1743 RouteListRef service_routes
, Rank rank
)
1748 for (i
= 0, scan
= RouteListGetFirstRoute(info
, service_routes
);
1749 i
< service_routes
->count
;
1750 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1754 && (service_routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
1755 /* only apply rank to first element of the list (default route) */
1759 this_rank
= RANK_INDEX_MASK(rank
) | RANK_ASSERTION_MASK(scan
->rank
);
1761 routes
= RouteListAddRoute(info
, routes
, init_size
, scan
, this_rank
);
1767 RouteAddInterfaceToDescription(RouteRef r
, CFMutableStringRef str
)
1769 char if_name
[IFNAMSIZ
];
1771 if (my_if_indextoname2(r
->ifindex
, if_name
) != NULL
) {
1772 CFStringAppendFormat(str
, NULL
,
1776 if (my_if_indextoname2(r
->exclude_ifindex
, if_name
) != NULL
) {
1777 CFStringAppendFormat(str
, NULL
,
1785 RouteAddFlagsToDescription(RouteRef r
, CFMutableStringRef str
)
1787 if ((r
->flags
& kRouteFlagsIsNULL
) != 0) {
1788 CFStringAppend(str
, CFSTR(" [null]"));
1791 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
1793 switch (rank_assertion
) {
1794 case kRankAssertionFirst
:
1795 CFStringAppend(str
, CFSTR(" [first]"));
1797 case kRankAssertionLast
:
1798 CFStringAppend(str
, CFSTR(" [last]"));
1800 case kRankAssertionNever
:
1801 CFStringAppend(str
, CFSTR(" [never]"));
1806 if ((r
->flags
& kRouteFlagsKernelManaged
) != 0) {
1807 CFStringAppend(str
, CFSTR(" [kern]"));
1809 if ((r
->flags
& kRouteFlagsIsScoped
) != 0) {
1810 CFStringAppend(str
, CFSTR(" [SCOPED]"));
1816 #if !TARGET_OS_SIMULATOR
1818 RouteListFindRoute(RouteListInfoRef info
, RouteListRef routes
, RouteRef route
)
1821 RouteRef match
= NULL
;
1824 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1826 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1827 if ((*info
->route_equal
)(scan
, route
)) {
1837 kRouteLookupFlagsNone
= 0x0,
1838 kRouteLookupFlagsExcludeInterface
= 0x1
1842 RouteListLookup(RouteListInfoRef info
,
1843 RouteListRef routes
,
1844 const void * address
,
1847 RouteLookupFlags lookup_flags
)
1849 RouteRef best_match
= NULL
;
1853 for (i
= 0, candidate
= RouteListGetFirstRoute(info
, routes
);
1855 i
++, candidate
= RouteGetNextRoute(info
, candidate
)) {
1856 if (candidate
->ifindex
== 0 || candidate
->exclude_ifindex
!= 0) {
1857 /* ignore exclude routes */
1860 if ((lookup_flags
& kRouteLookupFlagsExcludeInterface
) != 0) {
1861 /* exclude interfaces with the same interface index */
1862 if (ifindex
== candidate
->ifindex
) {
1866 else if (ifindex
!= candidate
->ifindex
) {
1869 if ((candidate
->flags
& kRouteFlagsHasGateway
) != 0
1870 && RouteAddressCompare(info
,
1871 (*info
->route_gateway
)(candidate
),
1873 /* skip route whose gateway is the address we're looking for */
1876 if ((candidate
->flags
& kRouteFlagsIsHost
) != 0) {
1877 /* if host route and we're looking for an exact match */
1878 if (n_bits
== info
->all_bits_set
1879 && RouteAddressCompare(info
,
1880 (*info
->route_destination
)(candidate
),
1882 /* found exact match */
1883 best_match
= candidate
;
1889 /* verify that address is on the same subnet */
1890 if ((*info
->route_same_subnet
)(candidate
, address
) == FALSE
) {
1891 /* different subnet */
1895 if (candidate
->prefix_length
== n_bits
) {
1897 best_match
= candidate
;
1900 if (candidate
->prefix_length
> n_bits
) {
1901 /* matched too many bits */
1904 if (best_match
== NULL
1905 || candidate
->prefix_length
> best_match
->prefix_length
) {
1906 best_match
= candidate
;
1909 return (best_match
);
1914 * Function: RouteProcess
1916 * Function to process adding or removing the specified route.
1917 * In the case of adding, that may involve first processing the gateway
1918 * route (recursively).
1921 RouteProcess(RouteRef route
,
1923 RouteListApplyContextRef context
)
1925 RouteLog route_log
= context
->info
->route_log
;
1926 RouteApply route_apply
= context
->info
->route_apply
;
1927 RouteGateway route_gateway
= context
->info
->route_gateway
;
1931 case kRouteCommandAdd
:
1932 if ((route
->control_flags
& kControlFlagsProcessed
) != 0) {
1933 return ((route
->control_flags
& kControlFlagsAdded
) != 0);
1935 route
->control_flags
|= kControlFlagsProcessed
;
1936 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
1938 RouteRef gateway_route
;
1941 = RouteListLookup(context
->info
,
1942 context
->new_routes
,
1943 (*route_gateway
)(route
),
1944 context
->info
->all_bits_set
,
1946 kRouteLookupFlagsNone
);
1947 if (gateway_route
== NULL
) {
1948 (*route_log
)(LOG_NOTICE
, route
, "no gateway route");
1951 #define MAX_RECURSE_DEPTH 10
1952 /* avoid infinite recursion */
1953 if (context
->depth
== MAX_RECURSE_DEPTH
) {
1954 (*route_log
)(LOG_NOTICE
, route
, "routing loop detected, not adding");
1957 /* recurse to add gateway route */
1959 added
= RouteProcess(gateway_route
,
1964 (*route_log
)(LOG_NOTICE
, route
, "failed to add");
1969 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1970 if (retval
== EEXIST
) {
1971 /* delete and add again */
1972 (void)(*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
1973 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1978 "failed to add route, %s:",
1980 (*route_log
)(LOG_NOTICE
, route
, NULL
);
1983 case EROUTENOTAPPLIED
:
1984 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1988 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
1989 snprintf(buf
, sizeof(buf
), "%sAdd new[%ld]",
1991 RouteListRouteIndex(context
->info
,
1992 context
->new_routes
,
1994 (*route_log
)(LOG_DEBUG
, route
, buf
);
1996 route
->control_flags
|= kControlFlagsAdded
;
2000 case kRouteCommandRemove
:
2001 retval
= (*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
2005 case EROUTENOTAPPLIED
:
2006 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2010 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
2011 snprintf(buf
, sizeof(buf
), "%sRemove old[%ld]%s",
2013 RouteListRouteIndex(context
->info
,
2014 context
->old_routes
,
2016 (retval
== ESRCH
) ? "(ESRCH)" : "");
2017 (*route_log
)(LOG_DEBUG
, route
, buf
);
2022 "failed to remove route, %s",
2024 (*route_log
)(LOG_NOTICE
, route
, NULL
);
2035 RouteListApply(RouteListInfoRef info
,
2036 RouteListRef old_routes
, RouteListRef new_routes
,
2039 RouteListApplyContext context
;
2043 if (old_routes
== new_routes
&& old_routes
== NULL
) {
2044 /* both old and new are NULL, so there's nothing to do */
2047 bzero(&context
, sizeof(context
));
2048 context
.old_routes
= old_routes
;
2049 context
.new_routes
= new_routes
;
2050 context
.sockfd
= sockfd
;
2051 context
.info
= info
;
2052 if (old_routes
!= NULL
) {
2053 for (i
= 0, scan
= RouteListGetFirstRoute(info
, old_routes
);
2054 i
< old_routes
->count
;
2055 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2056 RouteRef new_route
= NULL
;
2058 if (new_routes
!= NULL
) {
2059 new_route
= RouteListFindRoute(info
, new_routes
, scan
);
2061 if (new_route
== NULL
) {
2062 if ((scan
->control_flags
& kControlFlagsAdded
) != 0) {
2063 RouteProcess(scan
, kRouteCommandRemove
, &context
);
2068 if (new_routes
!= NULL
) {
2069 if (old_routes
!= NULL
) {
2070 /* preserve the control flags from any old routes */
2071 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2072 i
< new_routes
->count
;
2073 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2074 RouteRef old_route
= NULL
;
2076 old_route
= RouteListFindRoute(info
, old_routes
, scan
);
2077 if (old_route
!= NULL
) {
2078 /* preserve the control state in the new route */
2079 scan
->control_flags
= old_route
->control_flags
;
2083 /* add any routes that need to be added */
2084 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2085 i
< new_routes
->count
;
2086 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2087 if ((scan
->control_flags
& kControlFlagsProcessed
) != 0) {
2090 RouteProcess(scan
, kRouteCommandAdd
, &context
);
2096 * Function: RouteListFinalize
2098 * Look for excluded routes. If the excluded route does not have an assigned
2099 * interface, search for a route that *does not* go over the excluded
2102 * If the excluded route does have an assigned interface, search for a route
2103 * that *does* go over the assigned interface.
2105 * Set the gateway on the excluded route to match the gateway of the found
2109 RouteListFinalize(RouteListInfoRef info
, RouteListRef routes
)
2114 if (routes
== NULL
) {
2117 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
2119 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2122 RouteLookupFlags flags
;
2124 if (scan
->exclude_ifindex
== 0) {
2127 if (scan
->ifindex
== 0) {
2128 ifindex
= scan
->exclude_ifindex
;
2129 flags
= kRouteLookupFlagsExcludeInterface
;
2132 ifindex
= scan
->ifindex
;
2133 flags
= kRouteLookupFlagsNone
;
2135 route
= RouteListLookup(info
, routes
,
2136 (*info
->route_destination
)(scan
),
2137 scan
->prefix_length
, ifindex
, flags
);
2138 if (route
== NULL
) {
2139 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
, "can't resolve excluded route");
2142 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
2143 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)scan
, "Excluded route");
2144 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)route
, "Resolved to");
2146 scan
->ifindex
= route
->ifindex
;
2147 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2148 (*info
->route_set_gateway
)(scan
, (*info
->route_gateway
)(route
));
2149 scan
->flags
|= kRouteFlagsHasGateway
;
2150 if (scan
->prefix_length
== info
->all_bits_set
) {
2151 scan
->flags
|= kRouteFlagsIsHost
;
2155 /* routes directly to interface */
2156 scan
->flags
&= ~(kRouteFlagsHasGateway
| kRouteFlagsIsHost
);
2162 #endif /* !TARGET_OS_SIMULATOR */
2168 #define IPV4_ROUTE_ALL_BITS_SET 32
2170 static __inline__
struct in_addr
2171 subnet_addr(struct in_addr addr
, struct in_addr mask
)
2175 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
2180 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
2182 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
2183 CFStringAppendFormat(str
, NULL
,
2184 CFSTR("Host " IP_FORMAT
),
2188 CFStringAppendFormat(str
, NULL
,
2189 CFSTR("Net " IP_FORMAT
),
2191 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
2194 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
2195 CFStringAppendFormat(str
, NULL
,
2196 CFSTR(" Gate " IP_FORMAT
),
2197 IP_LIST(&r
->gateway
));
2199 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
2200 if (r
->ifa
.s_addr
!= 0) {
2201 CFStringAppendFormat(str
, NULL
,
2202 CFSTR(" Ifa " IP_FORMAT
),
2205 RouteAddFlagsToDescription((RouteRef
)r
, str
);
2210 IPv4RouteCopyDescription(RouteRef r
)
2212 CFMutableStringRef str
;
2214 str
= CFStringCreateMutable(NULL
, 0);
2215 IPv4RouteCopyDescriptionWithString((IPv4RouteRef
)r
, str
);
2219 #ifdef TEST_IPV4_ROUTELIST
2220 static CFMutableStringRef
2221 IPv4RouteListCopyDescription(IPv4RouteListRef routes
);
2224 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2226 CFStringRef str
= IPv4RouteCopyDescription(route
);
2229 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2232 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
2238 static __inline__
void
2239 IPv4RouteListPrint(IPv4RouteListRef routes
)
2241 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2243 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2248 #else /* TEST_IPV4_ROUTELIST */
2250 static __inline__
void
2251 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2253 CFStringRef str
= IPv4RouteCopyDescription(route
);
2256 my_log(level
, "%@", str
);
2259 my_log(level
, "%s: %@", msg
, str
);
2265 #endif /* TEST_IPV4_ROUTELIST */
2268 IPv4RouteIsEqual(RouteRef r_scan
, RouteRef r_route
)
2270 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2271 IPv4RouteRef scan
= (IPv4RouteRef
)r_scan
;
2273 return ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
2274 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
2275 && (scan
->ifindex
== route
->ifindex
)
2276 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
2277 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
2278 && (scan
->flags
== route
->flags
));
2281 static CFMutableStringRef
2282 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
2286 CFMutableStringRef str
;
2288 str
= CFStringCreateMutable(NULL
, 0);
2289 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
2291 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
2292 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
2293 IPv4RouteCopyDescriptionWithString(r
, str
);
2295 CFStringAppend(str
, CFSTR("\n}"));
2300 IPv4RouteListComputeSize(CFIndex n
)
2302 return (offsetof(IPv4RouteList
, list
[n
]));
2306 count_prefix_bits_set(uint32_t n
)
2309 const static int8_t bits
[16] = {
2328 for (count
= 0; n
!= 0; n
>>= 4) {
2329 int nbits
= bits
[n
& 0x0f];
2340 prefix_to_mask32(unsigned int prefix_length
)
2342 if (prefix_length
> 32 || prefix_length
== 0) {
2345 return (0xffffffff << (32 - prefix_length
));
2349 mask_get_prefix_length(struct in_addr mask
)
2353 count
= count_prefix_bits_set(mask
.s_addr
);
2357 val
= prefix_to_mask32(count
);
2358 if (ntohl(mask
.s_addr
) != val
) {
2359 /* expected mask based on prefix length doesn't match */
2367 IPv4RouteSetPrefixLength(IPv4RouteRef route
)
2371 length
= mask_get_prefix_length(route
->mask
);
2375 route
->prefix_length
= length
;
2380 IPv4RouteGateway(RouteRef r_route
)
2382 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2383 return (&route
->gateway
);
2387 IPv4RouteSetGateway(RouteRef r_route
, const void * address
)
2389 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2391 route
->gateway
= *((struct in_addr
*)address
);
2396 IPv4RouteDestination(RouteRef r_route
)
2398 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2399 return (&route
->dest
);
2403 IPv4RouteSameSubnet(RouteRef r_route
, const void * addr
)
2405 const struct in_addr
* address
;
2406 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2408 address
= (const struct in_addr
*)addr
;
2409 return ((address
->s_addr
& route
->mask
.s_addr
) == route
->dest
.s_addr
);
2413 * Define: ROUTE_MSG_ADDRS_SPACE
2415 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2416 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2417 * someone changes the code and doesn't think to modify this.
2419 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2420 + 2 * sizeof(struct sockaddr_dl) \
2423 struct rt_msghdr hdr
;
2424 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2428 * Function: IPv4RouteApply
2430 * Add or remove the specified route to/from the kernel routing table.
2433 IPv4RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
2437 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2440 struct sockaddr_in
* in_p
;
2441 struct sockaddr_dl
* dl_p
;
2445 if (S_netboot
&& route
->dest
.s_addr
== 0) {
2446 /* don't touch the default route */
2447 return (EROUTENOTAPPLIED
);
2449 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
2450 return (EROUTENOTAPPLIED
);
2452 if (route
->ifindex
== 0) {
2454 IP_FORMAT
" no interface specified, ignoring",
2455 IP_LIST(&route
->dest
));
2459 #ifdef TEST_IPV4_ROUTELIST
2461 #else /* TEST_IPV4_ROUTELIST */
2463 #endif /* TEST_IPV4_ROUTELIST */
2465 memset(&rtmsg
, 0, sizeof(rtmsg
));
2466 rtmsg
.hdr
.rtm_type
= cmd
;
2467 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2468 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2469 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
2470 if (route
->ifa
.s_addr
!= 0) {
2471 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
2473 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2474 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
2475 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
2478 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
2479 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
2480 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
2483 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2484 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
2486 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
2487 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
2488 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2491 rtaddr
.ptr
= rtmsg
.addrs
;
2494 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2495 rtaddr
.in_p
->sin_family
= AF_INET
;
2496 rtaddr
.in_p
->sin_addr
= route
->dest
;
2497 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2500 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2501 /* gateway is an IP address */
2502 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2503 rtaddr
.in_p
->sin_family
= AF_INET
;
2504 rtaddr
.in_p
->sin_addr
= route
->gateway
;
2505 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2508 /* gateway is the interface itself */
2509 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2510 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2511 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2512 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2516 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
2517 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2518 rtaddr
.in_p
->sin_family
= AF_INET
;
2519 rtaddr
.in_p
->sin_addr
= route
->mask
;
2520 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2524 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
2525 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2526 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2527 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2528 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2530 /* interface address */
2531 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
2532 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2533 rtaddr
.in_p
->sin_family
= AF_INET
;
2534 rtaddr
.in_p
->sin_addr
= route
->ifa
;
2535 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2538 /* apply the route */
2539 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
2540 rtmsg
.hdr
.rtm_msglen
= len
;
2541 if (write(sockfd
, &rtmsg
, len
) == -1) {
2547 static const RouteListInfo IPv4RouteListInfo
= {
2548 IPv4RouteListComputeSize
,
2553 IPv4RouteSetGateway
,
2554 IPv4RouteDestination
,
2555 IPv4RouteSameSubnet
,
2557 IPv4RouteCopyDescription
,
2560 sizeof(struct in_addr
),
2561 IPV4_ROUTE_ALL_BITS_SET
2564 #if !TARGET_OS_SIMULATOR
2565 static __inline__
void
2566 IPv4RouteListLog(int level
, IPv4RouteListRef routes
)
2568 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2570 my_log(level
, "%@", str
);
2576 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
2579 RouteListApply(&IPv4RouteListInfo
,
2580 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
2586 IPv4RouteListFinalize(IPv4RouteListRef routes
)
2588 RouteListFinalize(&IPv4RouteListInfo
, (RouteListRef
)routes
);
2591 #endif /* !TARGET_OS_SIMULATOR */
2593 #ifdef TEST_IPV4_ROUTELIST
2594 static IPv4RouteListRef
2595 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
2596 IPv4RouteListRef service_routes
, Rank rank
)
2598 return ((IPv4RouteListRef
)
2599 RouteListAddRouteList(&IPv4RouteListInfo
,
2600 (RouteListRef
)routes
, init_size
,
2601 (RouteListRef
)service_routes
, rank
));
2603 #endif /* TEST_IPV4_ROUTELIST */
2606 plist_get_string(CFDictionaryRef dict
, CFStringRef prop_name
,
2607 char * buf
, int buf_size
)
2611 val
= CFDictionaryGetValue(dict
, prop_name
);
2612 if (isA_CFString(val
) == NULL
) {
2615 if (!CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingUTF8
)) {
2622 struct in_addr addr
;
2625 IFIndex exclude_ifindex
;
2626 IPv4RouteRef
* route_p
;
2629 } AddIPv4RouteContext
, * AddIPv4RouteContextRef
;
2632 AddIPv4Route(const void * value
, void * context
)
2634 AddIPv4RouteContextRef ctx
= (AddIPv4RouteContextRef
)context
;
2635 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
2636 IPv4RouteRef r
= *ctx
->route_p
;
2638 dict
= isA_CFDictionary(dict
);
2640 || !dict_get_ip(dict
, kSCPropNetIPv4RouteDestinationAddress
, &r
->dest
)
2641 || !dict_get_ip(dict
, kSCPropNetIPv4RouteSubnetMask
, &r
->mask
)) {
2642 /* one less route than we expected */
2644 my_log(LOG_NOTICE
, "%s route is not a dictionary",
2648 my_log(LOG_NOTICE
, "%s route is invalid, %@",
2653 if (!IPv4RouteSetPrefixLength(r
)) {
2654 my_log(LOG_NOTICE
, "%s route has invalid subnet mask, %@",
2658 r
->rank
= ctx
->rank
;
2659 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
2660 if (ctx
->ifindex
!= 0) {
2661 r
->ifindex
= ctx
->ifindex
;
2663 if (ctx
->exclude_ifindex
== 0
2664 && dict_get_ip(dict
,
2665 kSCPropNetIPv4RouteGatewayAddress
,
2667 r
->flags
|= kRouteFlagsHasGateway
;
2668 if (r
->prefix_length
== IPV4_ROUTE_ALL_BITS_SET
) {
2669 r
->flags
|= kRouteFlagsIsHost
;
2674 char ifname
[IFNAMSIZ
];
2676 if (plist_get_string(dict
, kSCPropNetIPv4RouteInterfaceName
,
2677 ifname
, sizeof(ifname
)) != NULL
) {
2680 ifindex
= my_if_nametoindex(ifname
);
2683 "%s: interface %s does not exist, %@",
2684 ctx
->descr
, ifname
, dict
);
2687 else if (ifindex
== ctx
->ifindex
) {
2689 "%s: interface %s unexpected, %@",
2690 ctx
->descr
, ifname
, dict
);
2693 r
->ifindex
= ifindex
;
2706 confirm_interface_name(CFDictionaryRef dict
, CFStringRef ifname
)
2708 CFStringRef confirmed_ifname
;
2709 boolean_t confirmed
;
2712 = CFDictionaryGetValue(dict
, kSCPropConfirmedInterfaceName
);
2713 if (isA_CFString(confirmed_ifname
) != NULL
) {
2714 confirmed
= CFEqual(confirmed_ifname
, ifname
);
2723 * Function: IPv4RouteListCreateWithDictionary
2726 * Given the service ipv4 entity dictionary, generate the list of routes.
2727 * Currently, this includes just the default route and subnet route,
2728 * if the service has a subnet mask.
2731 * If the passed in route_list is NULL or too small, this routine
2732 * allocates malloc'd memory to hold the routes.
2734 static IPv4RouteListRef
2735 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
2736 CFDictionaryRef dict
,
2737 CFNumberRef rank_assertion
)
2739 boolean_t add_broadcast_multicast
= FALSE
;
2740 boolean_t add_default
= FALSE
;
2741 boolean_t add_router_subnet
= FALSE
;
2742 boolean_t add_subnet
= FALSE
;
2743 struct in_addr addr
= { 0 };
2744 CFArrayRef additional_routes
= NULL
;
2745 CFIndex additional_routes_count
;
2746 boolean_t allow_additional_routes
= FALSE
;
2747 boolean_t exclude_from_nwi
= FALSE
;
2748 CFArrayRef excluded_routes
= NULL
;
2749 CFIndex excluded_routes_count
;
2750 RouteFlags flags
= 0;
2752 char ifname
[IFNAMSIZ
];
2753 CFStringRef ifname_cf
;
2754 struct in_addr mask
= { 0 };
2756 int prefix_length
= 0;
2757 Rank primary_rank
= kRankAssertionDefault
;
2759 Rank rank
= kRankAssertionDefault
;
2760 struct in_addr router
= { 0 };
2761 boolean_t scoped_only
= FALSE
;
2762 struct in_addr subnet
= { 0 };
2767 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
2768 ifname
, sizeof(ifname
));
2769 if (ifname_cf
== NULL
) {
2772 ifindex
= my_if_nametoindex(ifname
);
2774 /* interface doesn't exist */
2777 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
2778 if (!dict_get_ip(dict
, kSCPropNetIPv4Router
, &router
)) {
2779 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
2781 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
2782 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
2784 subnet
= subnet_addr(addr
, mask
);
2785 prefix_length
= mask_get_prefix_length(mask
);
2786 if (prefix_length
< 0) {
2788 "ignoring bad subnet mask "
2790 IP_LIST(&mask
), ifname
);
2797 if (addr
.s_addr
== 0) {
2798 /* invalid/non-existent address */
2801 if (rank_assertion
!= NULL
) {
2802 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
2805 if (router
.s_addr
== 0) {
2806 /* if no router is configured, demote the rank if necessary */
2807 switch (primary_rank
) {
2808 case kRankAssertionLast
:
2809 case kRankAssertionNever
:
2810 case kRankAssertionScoped
:
2811 /* rank is already demoted */
2814 /* demote to RankLast */
2815 primary_rank
= kRankAssertionLast
;
2821 * If the router address is our address and the subnet mask is
2822 * not 255.255.255.255, assume all routes are local to the interface.
2824 if (addr
.s_addr
== router
.s_addr
2825 && mask
.s_addr
!= INADDR_BROADCAST
) {
2826 ; /* all routes local */
2829 flags
|= kRouteFlagsHasGateway
;
2831 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
2832 primary_rank
= kRankAssertionFirst
;
2836 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
2837 exclude_from_nwi
= TRUE
;
2838 flags
|= kRouteFlagsIsNULL
;
2841 switch (primary_rank
) {
2842 case kRankAssertionScoped
:
2843 /* Scoped means all routes for the service get scoped */
2844 primary_rank
= rank
= kRankAssertionNever
;
2845 flags
|= kRouteFlagsIsScoped
;
2848 case kRankAssertionNever
:
2849 /* Never means just the default route gets scoped */
2850 rank
= kRankAssertionLast
;
2851 flags
|= kRouteFlagsIsScoped
;
2854 rank
= primary_rank
;
2858 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2859 add_router_subnet
= TRUE
;
2863 if (ifindex
!= lo0_ifindex()) {
2864 if (router
.s_addr
!= 0) {
2868 add_broadcast_multicast
= TRUE
;
2871 if (allow_additional_routes
) {
2873 = CFDictionaryGetValue(dict
, kSCPropNetIPv4AdditionalRoutes
);
2874 additional_routes
= isA_CFArray(additional_routes
);
2875 if (additional_routes
!= NULL
) {
2876 additional_routes_count
= CFArrayGetCount(additional_routes
);
2877 n
+= additional_routes_count
;
2880 = CFDictionaryGetValue(dict
, kSCPropNetIPv4ExcludedRoutes
);
2881 excluded_routes
= isA_CFArray(excluded_routes
);
2882 if (excluded_routes
!= NULL
) {
2883 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
2884 n
+= excluded_routes_count
;
2887 if (routes
== NULL
|| routes
->size
< n
) {
2888 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
2889 bzero(routes
, IPv4RouteListComputeSize(n
));
2893 bzero(routes
->list
, sizeof(routes
->list
[0]) * n
);
2896 if (exclude_from_nwi
) {
2897 routes
->flags
|= kRouteListFlagsExcludeNWI
;
2899 else if (scoped_only
) {
2900 routes
->flags
|= kRouteListFlagsScopedOnly
;
2903 /* start at the beginning */
2907 /* add the default route */
2908 routes
->flags
|= kRouteListFlagsHasDefault
;
2909 r
->ifindex
= ifindex
;
2912 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2913 r
->gateway
= router
;
2918 r
->rank
= primary_rank
;
2921 if (add_broadcast_multicast
) {
2922 /* add the broadcast route (rdar://problem/22149738) */
2923 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2924 r
->flags
|= kRouteFlagsIsNULL
;
2926 r
->dest
.s_addr
= INADDR_BROADCAST
;
2927 r
->mask
.s_addr
= INADDR_BROADCAST
;
2928 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2929 r
->ifindex
= ifindex
;
2931 r
->rank
= primary_rank
;
2934 /* add multicast route (rdar://problem/26457121) */
2935 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2936 r
->flags
|= kRouteFlagsIsNULL
;
2938 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
2939 r
->mask
.s_addr
= htonl(IN_CLASSD_NET
);
2940 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSD
;
2941 r
->ifindex
= ifindex
;
2943 r
->rank
= primary_rank
;
2948 /* add the subnet route */
2950 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2951 r
->flags
|= kRouteFlagsIsNULL
;
2953 r
->ifindex
= ifindex
;
2957 r
->prefix_length
= prefix_length
;
2963 /* add the router subnet route */
2964 if (add_router_subnet
) {
2965 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2966 r
->flags
|= kRouteFlagsIsNULL
;
2968 r
->ifindex
= ifindex
;
2971 r
->mask
.s_addr
= INADDR_BROADCAST
;
2972 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2978 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
2979 AddIPv4RouteContext context
;
2981 bzero(&context
, sizeof(context
));
2982 context
.count_p
= &routes
->count
;
2983 context
.route_p
= &r
;
2984 context
.rank
= rank
;
2986 /* additional routes */
2987 if (additional_routes
!= NULL
) {
2988 context
.ifindex
= ifindex
;
2989 context
.addr
= addr
;
2990 context
.descr
= "AdditionalRoutes";
2991 CFArrayApplyFunction(additional_routes
,
2992 CFRangeMake(0, additional_routes_count
),
2993 AddIPv4Route
, &context
);
2995 /* excluded routes */
2996 if (excluded_routes
!= NULL
) {
2997 context
.descr
= "ExcludedRoutes";
2998 /* exclude this interface */
2999 context
.ifindex
= 0;
3000 context
.exclude_ifindex
= ifindex
;
3001 CFArrayApplyFunction(excluded_routes
,
3002 CFRangeMake(0, excluded_routes_count
),
3003 AddIPv4Route
, &context
);
3009 #if !TARGET_OS_SIMULATOR
3010 static IPv4RouteListRef
3011 IPv4RouteListCopyMulticastLoopback(void)
3014 IPv4RouteListRef routes
;
3016 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(1));
3017 bzero(routes
, IPv4RouteListComputeSize(1));
3018 routes
->count
= routes
->size
= 1;
3021 r
->dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
3022 r
->mask
.s_addr
= htonl(IN_CLASSC_NET
);
3023 r
->prefix_length
= PREFIX_LENGTH_IN_CLASSC
;
3024 r
->ifindex
= lo0_ifindex();
3027 #endif /* !TARGET_OS_SIMULATOR */
3032 #define IPV6_ROUTE_ALL_BITS_SET 128
3035 ipv6_prefix_length_is_valid(int prefix_length
)
3037 if (prefix_length
< 0 || prefix_length
> IPV6_ROUTE_ALL_BITS_SET
) {
3044 * from netinet6/in6.c
3047 in6_len2mask(struct in6_addr
* mask
, int len
)
3051 bzero(mask
, sizeof(*mask
));
3052 for (i
= 0; i
< len
/ 8; i
++)
3053 mask
->s6_addr
[i
] = 0xff;
3055 mask
->s6_addr
[i
] = (0xff00 >> (len
% 8)) & 0xff;
3059 in6_maskaddr(struct in6_addr
* addr
, const struct in6_addr
* mask
)
3063 for (i
= 0; i
< sizeof(addr
->s6_addr
); i
++) {
3064 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3070 in6_netaddr(struct in6_addr
* addr
, int len
)
3072 struct in6_addr mask
;
3074 in6_len2mask(&mask
, len
);
3075 in6_maskaddr(addr
, &mask
);
3080 in6_addr_scope_linklocal(struct in6_addr
* addr
, IFIndex ifindex
)
3082 if (IN6_IS_ADDR_LINKLOCAL(addr
)) {
3083 addr
->__u6_addr
.__u6_addr16
[1] = htons(ifindex
);
3089 string_append_in6_addr(CFMutableStringRef str
, const struct in6_addr
* addr
)
3091 char ntopbuf
[INET6_ADDRSTRLEN
];
3093 CFStringAppendCString(str
,
3094 inet_ntop(AF_INET6
, addr
, ntopbuf
, sizeof(ntopbuf
)),
3095 kCFStringEncodingASCII
);
3100 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r
, CFMutableStringRef str
)
3102 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
3103 CFStringAppend(str
, CFSTR("Host "));
3104 string_append_in6_addr(str
, &r
->dest
);
3107 CFStringAppend(str
, CFSTR("Net "));
3108 string_append_in6_addr(str
, &r
->dest
);
3109 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
3112 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
3113 CFStringAppend(str
, CFSTR(" Gate "));
3114 string_append_in6_addr(str
, &r
->gateway
);
3116 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
3117 if (!IN6_ARE_ADDR_EQUAL(&r
->ifa
, &in6addr_any
)) {
3118 CFStringAppend(str
, CFSTR(" Ifa "));
3119 string_append_in6_addr(str
, &r
->ifa
);
3121 RouteAddFlagsToDescription((RouteRef
)r
, str
);
3126 IPv6RouteCopyDescription(RouteRef r
)
3128 CFMutableStringRef str
;
3130 str
= CFStringCreateMutable(NULL
, 0);
3131 IPv6RouteCopyDescriptionWithString((IPv6RouteRef
)r
, str
);
3135 static CFMutableStringRef
3136 IPv6RouteListCopyDescription(IPv6RouteListRef routes
)
3140 CFMutableStringRef str
;
3142 str
= CFStringCreateMutable(NULL
, 0);
3143 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv6RouteList[%d]> = {"),
3145 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
3146 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
3147 IPv6RouteCopyDescriptionWithString(r
, str
);
3149 CFStringAppend(str
, CFSTR("\n}"));
3153 #ifdef TEST_IPV6_ROUTELIST
3156 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3158 CFStringRef str
= IPv6RouteCopyDescription(route
);
3161 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3164 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
3170 static __inline__
void
3171 IPv6RouteListPrint(IPv6RouteListRef routes
)
3173 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3175 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3180 #else /* TEST_IPV6_ROUTELIST */
3182 static __inline__
void
3183 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3185 CFStringRef str
= IPv6RouteCopyDescription(route
);
3188 my_log(level
, "%@", str
);
3191 my_log(level
, "%s: %@", msg
, str
);
3197 #endif /* TEST_IPV6_ROUTELIST */
3200 IPv6RouteListComputeSize(CFIndex n
)
3202 return (offsetof(IPv6RouteList
, list
[n
]));
3207 struct in6_addr
* addr
;
3210 IFIndex exclude_ifindex
;
3211 IPv6RouteRef
* route_p
;
3214 } AddIPv6RouteContext
, * AddIPv6RouteContextRef
;
3217 AddIPv6Route(const void * value
, void * context
)
3219 AddIPv6RouteContextRef ctx
= (AddIPv6RouteContextRef
)context
;
3220 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
3221 IPv6RouteRef r
= *ctx
->route_p
;
3223 dict
= isA_CFDictionary(dict
);
3225 || !dict_get_ipv6(dict
, kSCPropNetIPv6RouteDestinationAddress
, &r
->dest
)
3226 || !dict_get_int(dict
, kSCPropNetIPv6RoutePrefixLength
,
3228 || !ipv6_prefix_length_is_valid(r
->prefix_length
)) {
3229 /* one less route than we expected */
3231 my_log(LOG_NOTICE
, "%s route is not a dictionary",
3235 my_log(LOG_NOTICE
, "%s route is invalid, %@",
3240 r
->rank
= ctx
->rank
;
3241 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
3242 if (ctx
->ifindex
!= 0) {
3243 r
->ifindex
= ctx
->ifindex
;
3244 r
->ifa
= *ctx
->addr
;
3245 if (ctx
->exclude_ifindex
== 0
3246 && dict_get_ipv6(dict
,
3247 kSCPropNetIPv6RouteGatewayAddress
,
3249 r
->flags
|= kRouteFlagsHasGateway
;
3250 if (r
->prefix_length
== IPV6_ROUTE_ALL_BITS_SET
) {
3251 r
->flags
|= kRouteFlagsIsHost
;
3256 char ifname
[IFNAMSIZ
];
3258 if (plist_get_string(dict
, kSCPropNetIPv6RouteInterfaceName
,
3259 ifname
, sizeof(ifname
)) != NULL
) {
3262 ifindex
= my_if_nametoindex(ifname
);
3265 "%s: interface %s does not exist, %@",
3266 ctx
->descr
, ifname
, dict
);
3269 else if (ifindex
== ctx
->ifindex
) {
3271 "%s: interface %s unexpected, %@",
3272 ctx
->descr
, ifname
, dict
);
3275 r
->ifindex
= ifindex
;
3288 * Function: IPv6RouteListCreateWithDictionary
3291 * Given the service IPv6 entity dictionary, generate the list of routes.
3294 * If the passed in route_list is NULL or too small, this routine
3295 * allocates malloc'd memory to hold the routes.
3297 static IPv6RouteListRef
3298 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes
,
3299 CFDictionaryRef dict
,
3300 CFNumberRef rank_assertion
)
3302 boolean_t add_default
= FALSE
;
3303 boolean_t add_prefix
= FALSE
;
3304 struct in6_addr addr
;
3305 CFArrayRef additional_routes
= NULL
;
3306 CFIndex additional_routes_count
;
3307 boolean_t allow_additional_routes
= FALSE
;
3308 boolean_t exclude_from_nwi
= FALSE
;
3309 CFArrayRef excluded_routes
= NULL
;
3310 CFIndex excluded_routes_count
;
3311 RouteFlags flags
= 0;
3313 char ifname
[IFNAMSIZ
];
3314 CFStringRef ifname_cf
;
3316 int prefix_length
= 0;
3317 Rank primary_rank
= kRankAssertionDefault
;
3319 Rank rank
= kRankAssertionDefault
;
3320 struct in6_addr router
= in6addr_any
;
3321 boolean_t scoped_only
= FALSE
;
3326 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
3327 ifname
, sizeof(ifname
));
3328 if (ifname_cf
== NULL
) {
3331 ifindex
= my_if_nametoindex(ifname
);
3333 /* interface doesn't exist */
3336 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
3337 if (!dict_get_ipv6(dict
, kSCPropNetIPv6Router
, &router
)) {
3338 (void)dict_get_first_ipv6(dict
, kSCPropNetIPv6DestAddresses
, &router
);
3340 if (dict_get_first_ipv6(dict
, kSCPropNetIPv6Addresses
, &addr
)) {
3341 if (IN6_IS_ADDR_UNSPECIFIED(&addr
)) {
3344 if (dict_get_first_int(dict
, kSCPropNetIPv6PrefixLength
,
3346 && !IN6_IS_ADDR_LINKLOCAL(&addr
)
3347 && ipv6_prefix_length_is_valid(prefix_length
)) {
3359 if (rank_assertion
!= NULL
) {
3360 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
3363 if (!IN6_IS_ADDR_UNSPECIFIED(&router
)) {
3364 if (ifindex
!= lo0_ifindex()) {
3369 * If the router address is our address and the prefix length is
3370 * not 128, assume all routes are local to the interface.
3372 if (IN6_ARE_ADDR_EQUAL(&router
, &addr
)
3373 && prefix_length
!= IPV6_ROUTE_ALL_BITS_SET
) {
3374 ; /* all routes local */
3377 flags
|= kRouteFlagsHasGateway
;
3379 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
3380 primary_rank
= kRankAssertionFirst
;
3383 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
3384 exclude_from_nwi
= TRUE
;
3385 flags
|= kRouteFlagsIsNULL
;
3388 switch (primary_rank
) {
3389 case kRankAssertionScoped
:
3390 /* Scoped means all routes for the service get scoped */
3391 primary_rank
= rank
= kRankAssertionNever
;
3392 flags
|= kRouteFlagsIsScoped
;
3395 case kRankAssertionNever
:
3396 /* Never means just the default route gets scoped */
3397 rank
= kRankAssertionLast
;
3398 flags
|= kRouteFlagsIsScoped
;
3401 rank
= primary_rank
;
3405 if (allow_additional_routes
) {
3407 = CFDictionaryGetValue(dict
, kSCPropNetIPv6AdditionalRoutes
);
3408 additional_routes
= isA_CFArray(additional_routes
);
3409 if (additional_routes
!= NULL
) {
3410 additional_routes_count
= CFArrayGetCount(additional_routes
);
3411 n
+= additional_routes_count
;
3413 excluded_routes
= CFDictionaryGetValue(dict
,
3414 kSCPropNetIPv6ExcludedRoutes
);
3415 excluded_routes
= isA_CFArray(excluded_routes
);
3416 if (excluded_routes
!= NULL
) {
3417 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
3418 n
+= excluded_routes_count
;
3425 /* need IPv6LL subnet route */
3428 if (routes
== NULL
|| routes
->size
< n
) {
3429 routes
= (IPv6RouteListRef
)malloc(IPv6RouteListComputeSize(n
));
3430 bzero(routes
, IPv6RouteListComputeSize(n
));
3434 bzero(routes
->list
, sizeof(routes
->list
[0]) * n
);
3437 if (exclude_from_nwi
) {
3438 routes
->flags
|= kRouteListFlagsExcludeNWI
;
3440 else if (scoped_only
) {
3441 routes
->flags
|= kRouteListFlagsScopedOnly
;
3444 /* start at the beginning */
3447 /* add the default route */
3448 routes
->flags
|= kRouteListFlagsHasDefault
;
3449 r
->ifindex
= ifindex
;
3452 if ((flags
& kRouteFlagsHasGateway
) != 0) {
3453 r
->gateway
= router
;
3458 r
->rank
= primary_rank
;
3459 r
->flags
|= kRouteFlagsKernelManaged
;
3464 /* add IPv6LL route */
3465 r
->ifindex
= ifindex
;
3466 r
->dest
.s6_addr
[0] = 0xfe;
3467 r
->dest
.s6_addr
[1] = 0x80;
3468 r
->prefix_length
= 64;
3470 r
->flags
|= kRouteFlagsKernelManaged
;
3474 /* add the prefix route(s) */
3476 r
->flags
|= kRouteFlagsKernelManaged
;
3477 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3478 r
->flags
|= kRouteFlagsIsNULL
;
3480 r
->ifindex
= ifindex
;
3483 in6_netaddr(&r
->dest
, prefix_length
);
3484 r
->prefix_length
= prefix_length
;
3490 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3491 AddIPv6RouteContext context
;
3493 bzero(&context
, sizeof(context
));
3494 context
.count_p
= &routes
->count
;
3495 context
.route_p
= &r
;
3496 context
.rank
= rank
;
3498 /* additional routes */
3499 if (additional_routes
!= NULL
) {
3500 context
.ifindex
= ifindex
;
3501 context
.addr
= &addr
;
3502 context
.descr
= "AdditionalRoutes";
3503 CFArrayApplyFunction(additional_routes
,
3504 CFRangeMake(0, additional_routes_count
),
3505 AddIPv6Route
, &context
);
3507 /* excluded routes */
3508 if (excluded_routes
!= NULL
) {
3509 context
.descr
= "ExcludedRoutes";
3510 /* exclude this interface */
3511 context
.ifindex
= 0;
3512 context
.exclude_ifindex
= ifindex
;
3513 context
.addr
= NULL
;
3514 CFArrayApplyFunction(excluded_routes
,
3515 CFRangeMake(0, excluded_routes_count
),
3516 AddIPv6Route
, &context
);
3523 IPv6RouteGateway(RouteRef r_route
)
3525 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3526 return (&route
->gateway
);
3530 IPv6RouteSetGateway(RouteRef r_route
, const void * address
)
3532 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3534 route
->gateway
= *((struct in6_addr
*)address
);
3539 IPv6RouteDestination(RouteRef r_route
)
3541 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3542 return (&route
->dest
);
3545 static __inline__
int
3546 in6_addr_cmp(const struct in6_addr
* a
, const struct in6_addr
* b
)
3548 return (memcmp(a
->s6_addr
, b
->s6_addr
, sizeof(struct in6_addr
)));
3552 IPv6RouteIsEqual(RouteRef r_route1
, RouteRef r_route2
)
3554 IPv6RouteRef route1
= (IPv6RouteRef
)r_route1
;
3555 IPv6RouteRef route2
= (IPv6RouteRef
)r_route2
;
3557 return (route1
->prefix_length
== route2
->prefix_length
3558 && route1
->ifindex
== route2
->ifindex
3559 && route1
->flags
== route2
->flags
3560 && in6_addr_cmp(&route1
->dest
, &route2
->dest
) == 0
3561 && in6_addr_cmp(&route1
->ifa
, &route2
->ifa
) == 0
3562 && in6_addr_cmp(&route1
->gateway
, &route2
->gateway
) == 0);
3566 IPv6RouteSameSubnet(RouteRef r_route
, const void * addr
)
3568 const struct in6_addr
* address
= (const struct in6_addr
*)addr
;
3569 struct in6_addr netaddr
;
3570 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3573 in6_netaddr(&netaddr
, route
->prefix_length
);
3574 return (in6_addr_cmp(&netaddr
, &route
->dest
) == 0);
3578 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3581 struct rt_msghdr hdr
;
3582 char addrs
[V6_ROUTE_MSG_ADDRS_SPACE
];
3586 * Function: IPv6RouteApply
3588 * Add or remove the specified route to/from the kernel routing table.
3591 IPv6RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
3595 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3598 struct sockaddr_in6
* in_p
;
3599 struct sockaddr_dl
* dl_p
;
3603 if ((route
->flags
& kRouteFlagsKernelManaged
) != 0) {
3604 /* the kernel manages this route, don't touch it */
3605 return (EROUTENOTAPPLIED
);
3607 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
3608 return (EROUTENOTAPPLIED
);
3610 if (route
->ifindex
== 0) {
3611 IPv6RouteLog(LOG_NOTICE
, (RouteRef
)route
,
3612 "no interface specified");
3616 #ifdef TEST_IPV6_ROUTELIST
3618 #else /* TEST_IPV6_ROUTELIST */
3620 #endif /* TEST_IPV6_ROUTELIST */
3622 memset(&rtmsg
, 0, sizeof(rtmsg
));
3623 rtmsg
.hdr
.rtm_type
= cmd
;
3624 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3625 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3626 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
3627 if (!IN6_IS_ADDR_UNSPECIFIED(&route
->ifa
)) {
3628 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
3630 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3631 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
3632 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
3635 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
3636 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
3637 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
3640 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
3641 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
3643 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
3644 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
3645 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
3648 rtaddr
.ptr
= rtmsg
.addrs
;
3651 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3652 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3653 rtaddr
.in_p
->sin6_addr
= route
->dest
;
3654 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3655 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3658 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
3659 /* gateway is an IP address */
3660 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3661 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3662 rtaddr
.in_p
->sin6_addr
= route
->gateway
;
3663 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3664 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3667 /* gateway is the interface itself */
3668 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3669 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3670 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3671 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3675 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
3676 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3677 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3678 in6_len2mask(&rtaddr
.in_p
->sin6_addr
, route
->prefix_length
);
3679 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3683 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
3684 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3685 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3686 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3687 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3689 /* interface address */
3690 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
3691 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3692 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3693 rtaddr
.in_p
->sin6_addr
= route
->ifa
;
3694 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3697 /* apply the route */
3698 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
3699 rtmsg
.hdr
.rtm_msglen
= len
;
3700 if (write(sockfd
, &rtmsg
, len
) == -1) {
3706 static const RouteListInfo IPv6RouteListInfo
= {
3707 IPv6RouteListComputeSize
,
3712 IPv6RouteSetGateway
,
3713 IPv6RouteDestination
,
3714 IPv6RouteSameSubnet
,
3716 IPv6RouteCopyDescription
,
3719 sizeof(struct in6_addr
),
3720 IPV6_ROUTE_ALL_BITS_SET
3723 #ifdef TEST_IPV6_ROUTELIST
3724 static IPv6RouteListRef
3725 IPv6RouteListAddRouteList(IPv6RouteListRef routes
, int init_size
,
3726 IPv6RouteListRef service_routes
, Rank rank
)
3728 return ((IPv6RouteListRef
)
3729 RouteListAddRouteList(&IPv6RouteListInfo
,
3730 (RouteListRef
)routes
, init_size
,
3731 (RouteListRef
)service_routes
, rank
));
3733 #endif /* TEST_IPV6_ROUTELIST */
3735 #if !TARGET_OS_SIMULATOR
3736 static __inline__
void
3737 IPv6RouteListLog(int level
, IPv6RouteListRef routes
)
3739 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3741 my_log(level
, "%@", str
);
3747 IPv6RouteListFinalize(IPv6RouteListRef routes
)
3749 RouteListFinalize(&IPv6RouteListInfo
, (RouteListRef
)routes
);
3754 IPv6RouteListApply(IPv6RouteListRef old_routes
, IPv6RouteListRef new_routes
,
3757 RouteListApply(&IPv6RouteListInfo
,
3758 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
3762 #endif /* !TARGET_OS_SIMULATOR */
3765 * Function: parse_component
3767 * Given a string 'key' and a string prefix 'prefix',
3768 * return the next component in the slash '/' separated
3772 * 1. key = "a/b/c" prefix = "a/"
3774 * 2. key = "a/b/c" prefix = "a/b/"
3777 static CF_RETURNS_RETAINED CFStringRef
3778 parse_component(CFStringRef key
, CFStringRef prefix
)
3780 CFMutableStringRef comp
;
3783 if (!CFStringHasPrefix(key
, prefix
)) {
3786 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
3790 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
3791 range
= CFStringFind(comp
, CFSTR("/"), 0);
3792 if (range
.location
== kCFNotFound
) {
3795 range
.length
= CFStringGetLength(comp
) - range
.location
;
3796 CFStringDelete(comp
, range
);
3800 __private_extern__ boolean_t
3801 service_contains_protocol(CFDictionaryRef service
, int af
)
3803 boolean_t contains_protocol
= FALSE
;
3805 RouteListRef routes
;
3806 CFDictionaryRef dict
;
3808 entity
= (af
== AF_INET
) ? kSCEntNetIPv4
: kSCEntNetIPv6
;
3809 dict
= CFDictionaryGetValue(service
, entity
);
3813 routes
= ipdict_get_routelist(dict
);
3814 if (routes
== NULL
) {
3817 if ((routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
3820 contains_protocol
= TRUE
;
3823 return (contains_protocol
);
3827 static CFMutableDictionaryRef
3828 service_dict_copy(CFStringRef serviceID
)
3830 CFDictionaryRef d
= NULL
;
3831 CFMutableDictionaryRef service_dict
;
3833 /* create a modifyable dictionary, a copy or a new one */
3834 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3837 = CFDictionaryCreateMutable(NULL
, 0,
3838 &kCFTypeDictionaryKeyCallBacks
,
3839 &kCFTypeDictionaryValueCallBacks
);
3842 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
3844 return (service_dict
);
3847 __private_extern__ boolean_t
3848 service_is_scoped_only(CFDictionaryRef service_dict
)
3850 nwi_ifstate_t alias
;
3851 CFDictionaryRef dict
;
3852 char ifname
[IFNAMSIZ
];
3853 nwi_ifstate_t ifstate
;
3854 CFStringRef interface
= NULL
;
3856 // get IPv4 (or IPv6) info
3857 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3859 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
3862 // if no connectivity
3867 interface
= ipdict_get_ifname(dict
);
3868 if ((interface
== NULL
) ||
3869 !CFStringGetCString(interface
, ifname
, sizeof(ifname
), kCFStringEncodingUTF8
)) {
3870 // if no interface / interface name
3875 if (S_nwi_state
== NULL
) {
3876 S_nwi_state
= nwi_state_copy();
3880 // get [nwi] interface state
3881 ifstate
= nwi_state_get_ifstate(S_nwi_state
, ifname
);
3882 if (ifstate
== NULL
) {
3885 } else if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3886 // if scoped (i.e. not in list)
3890 // check both both IPv4 and IPv6
3891 alias
= nwi_ifstate_get_alias(ifstate
, ifstate
->af
== AF_INET
? AF_INET6
: AF_INET
);
3892 if (alias
== NULL
) {
3893 // if only one address family
3895 } else if ((alias
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3896 // if scoped (i.e. not in list)
3904 log_service_entity(int level
, CFStringRef serviceID
, CFStringRef entity
,
3905 CFStringRef operation
, CFTypeRef val
)
3907 CFMutableStringRef this_val
= NULL
;
3913 if ((is_ipv4
= CFEqual(entity
, kSCEntNetIPv4
))
3914 || (is_ipv6
= CFEqual(entity
, kSCEntNetIPv6
))) {
3915 RouteListUnion routes
;
3917 routes
.ptr
= ipdict_get_routelist(val
);
3918 if (routes
.ptr
!= NULL
) {
3919 CFDictionaryRef service_dict
= NULL
;
3922 this_val
= IPv4RouteListCopyDescription(routes
.v4
);
3925 this_val
= IPv6RouteListCopyDescription(routes
.v6
);
3927 service_dict
= ipdict_get_service(val
);
3928 if (service_dict
!= NULL
) {
3929 CFStringAppendFormat(this_val
, NULL
,
3930 CFSTR("\n<Service> = %@"),
3938 val
= CFSTR("<none>");
3940 my_log(level
, "serviceID %@ %@ %@ value = %@",
3941 serviceID
, operation
, entity
, val
);
3942 my_CFRelease(&this_val
);
3947 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
3950 boolean_t changed
= FALSE
;
3952 CFMutableDictionaryRef service_dict
;
3954 service_dict
= service_dict_copy(serviceID
);
3955 old_val
= CFDictionaryGetValue(service_dict
, entity
);
3956 if (new_val
== NULL
) {
3957 if (old_val
!= NULL
) {
3958 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3959 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3960 CFSTR("Removed:"), old_val
);
3962 CFDictionaryRemoveValue(service_dict
, entity
);
3967 if (old_val
== NULL
|| !CFEqual(new_val
, old_val
)) {
3968 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3969 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3970 CFSTR("Changed: old"), old_val
);
3971 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3972 CFSTR("Changed: new"), new_val
);
3974 CFDictionarySetValue(service_dict
, entity
, new_val
);
3978 if (CFDictionaryGetCount(service_dict
) == 0) {
3979 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
3982 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
3984 my_CFRelease(&service_dict
);
3988 static CFDictionaryRef
3989 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
3991 CFDictionaryRef service_dict
;
3993 if (S_service_state_dict
== NULL
) {
3996 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3997 if (service_dict
== NULL
) {
4000 return (CFDictionaryGetValue(service_dict
, entity
));
4003 #ifndef kSCPropNetHostname
4004 #define kSCPropNetHostname CFSTR("Hostname")
4009 copy_dhcp_hostname(CFStringRef serviceID
)
4011 CFDictionaryRef dict
= NULL
;
4012 CFStringRef hostname
= NULL
;
4013 CFDictionaryRef service_dict
= NULL
;
4015 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4019 service_dict
= ipdict_get_service(dict
);
4020 if (service_dict
== NULL
) {
4023 hostname
= CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
4024 if (hostname
!= NULL
) {
4030 #if !TARGET_OS_SIMULATOR
4032 static struct in6_addr
*
4033 ipv6_service_get_router(CFDictionaryRef service
,
4034 IFIndex
* ifindex_p
, CFStringRef
* ifname_p
)
4036 IPv6RouteListRef routes
;
4037 struct in6_addr
* router
= NULL
;
4039 routes
= ipdict_get_routelist(service
);
4041 && (routes
->flags
& kRouteListFlagsExcludeNWI
) == 0
4042 && (routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
4043 router
= &routes
->list
[0].gateway
;
4044 if (*ifindex_p
== 0) {
4045 *ifindex_p
= routes
->list
[0].ifindex
;
4047 if (*ifname_p
== NULL
) {
4048 *ifname_p
= ipdict_get_ifname(service
);
4055 ipv6_service_update_router(CFStringRef serviceID
, CFDictionaryRef new_service
)
4057 IFIndex ifindex
= 0;
4058 CFStringRef ifname
= NULL
;
4059 char ntopbuf
[INET6_ADDRSTRLEN
];
4060 CFDictionaryRef old_service
;
4061 struct in6_addr
* old_router
;
4062 struct in6_addr
* new_router
;
4065 old_service
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4066 old_router
= ipv6_service_get_router(old_service
, &ifindex
, &ifname
);
4067 new_router
= ipv6_service_get_router(new_service
, &ifindex
, &ifname
);
4068 if (ifname
== NULL
|| ifindex
== 0) {
4071 s
= inet6_dgram_socket();
4075 /* remove the old router if it was defined */
4076 if (old_router
!= NULL
4077 && (new_router
== NULL
4078 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4079 if (siocdrdel_in6(s
, ifindex
, old_router
) < 0) {
4080 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4081 "siocdrdel_in6(%@, %s) failed: %s",
4083 inet_ntop(AF_INET6
, old_router
,
4084 ntopbuf
, sizeof(ntopbuf
)),
4089 "%@ removed default route %s",
4091 inet_ntop(AF_INET6
, old_router
, ntopbuf
, sizeof(ntopbuf
)));
4094 /* add the new router if it is defined */
4095 if (new_router
!= NULL
4096 && (old_router
== NULL
4097 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4098 if (siocdradd_in6(s
, ifindex
, new_router
, 0) < 0) {
4099 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4100 "siocdradd_in6(%@, %s) failed: %s",
4102 inet_ntop(AF_INET6
, new_router
,
4103 ntopbuf
, sizeof(ntopbuf
)),
4108 "%@ added default route %s",
4110 inet_ntop(AF_INET6
, new_router
, ntopbuf
, sizeof(ntopbuf
)));
4118 #endif /* !TARGET_OS_SIMULATOR */
4120 #define ALLOW_EMPTY_STRING 0x1
4122 static CF_RETURNS_RETAINED CFTypeRef
4123 sanitize_prop(CFTypeRef val
, uint32_t flags
)
4126 if (isA_CFString(val
)) {
4127 CFMutableStringRef str
;
4129 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
4130 CFStringTrimWhitespace(str
);
4131 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
4145 merge_array_prop(CFMutableDictionaryRef dict
,
4147 CFDictionaryRef state_dict
,
4148 CFDictionaryRef setup_dict
,
4152 CFMutableArrayRef merge_prop
;
4153 CFArrayRef setup_prop
= NULL
;
4154 CFArrayRef state_prop
= NULL
;
4156 if (setup_dict
!= NULL
) {
4157 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
4159 if (state_dict
!= NULL
) {
4160 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
4163 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
4167 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4168 if (setup_prop
!= NULL
) {
4172 n
= CFArrayGetCount(setup_prop
);
4173 for (i
= 0; i
< n
; i
++) {
4176 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
4177 val
= sanitize_prop(val
, flags
);
4179 CFArrayAppendValue(merge_prop
, val
);
4184 if (state_prop
!= NULL
4185 && (setup_prop
== NULL
|| S_append_state
)) {
4188 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
4190 n
= CFArrayGetCount(state_prop
);
4191 for (i
= 0; i
< n
; i
++) {
4194 val
= CFArrayGetValueAtIndex(state_prop
, i
);
4195 val
= sanitize_prop(val
, flags
);
4197 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
4198 CFArrayAppendValue(merge_prop
, val
);
4204 if (CFArrayGetCount(merge_prop
) > 0) {
4205 CFDictionarySetValue(dict
, key
, merge_prop
);
4207 CFRelease(merge_prop
);
4212 pick_prop(CFMutableDictionaryRef dict
,
4214 CFDictionaryRef state_dict
,
4215 CFDictionaryRef setup_dict
,
4218 CFTypeRef val
= NULL
;
4220 if (setup_dict
!= NULL
) {
4221 val
= CFDictionaryGetValue(setup_dict
, key
);
4222 val
= sanitize_prop(val
, flags
);
4224 if (val
== NULL
&& state_dict
!= NULL
) {
4225 val
= CFDictionaryGetValue(state_dict
, key
);
4226 val
= sanitize_prop(val
, flags
);
4229 CFDictionarySetValue(dict
, key
, val
);
4237 ** GetEntityChangesFunc functions
4239 #define IPV4_ROUTES_N_STATIC 5
4240 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4241 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4245 #define IPV4_ROUTES_BUF_DECL(routes) \
4246 IPv4RouteListRef routes; \
4247 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4249 routes = (IPv4RouteListRef)(void *)routes_buf; \
4250 routes->size = IPV4_ROUTES_N_STATIC; \
4251 routes->count = 0; \
4255 IPv4RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4258 CFDataRef routes_data
;
4259 IPV4_ROUTES_BUF_DECL(routes
);
4261 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4263 routes_data
= CFDataCreate(NULL
,
4265 IPv4RouteListComputeSize(r
->count
));
4273 return (routes_data
);
4275 #define IPV6_ROUTES_N_STATIC 3
4276 #define IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4277 (roundup(IPv6RouteListComputeSize(IPV6_ROUTES_N_STATIC), \
4281 #define IPV6_ROUTES_BUF_DECL(routes) \
4282 IPv6RouteListRef routes; \
4283 uint32_t routes_buf[IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4285 routes = (IPv6RouteListRef)(void *)routes_buf; \
4286 routes->size = IPV6_ROUTES_N_STATIC; \
4287 routes->count = 0; \
4291 IPv6RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4294 CFDataRef routes_data
;
4295 IPV6_ROUTES_BUF_DECL(routes
);
4297 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4299 routes_data
= CFDataCreate(NULL
,
4301 IPv6RouteListComputeSize(r
->count
));
4309 return (routes_data
);
4312 static CFDictionaryRef
4313 IPDictCreate(int af
, CFDictionaryRef state_dict
, CFDictionaryRef setup_dict
,
4314 CFNumberRef rank_assertion
)
4316 CFDictionaryRef aggregated_dict
= NULL
;
4317 CFDictionaryRef dict
;
4318 CFMutableDictionaryRef modified_dict
= NULL
;
4319 CFDataRef routes_data
;
4322 if (dict
!= NULL
&& setup_dict
!= NULL
) {
4323 /* look for keys in Setup: that override/merge with State: */
4324 CFArrayRef additional_routes
;
4327 CFStringRef router_prop
;
4328 CFStringRef route_list_prop
;
4333 router_prop
= kSCPropNetIPv4Router
;
4334 route_list_prop
= kSCPropNetIPv4AdditionalRoutes
;
4338 router_prop
= kSCPropNetIPv6Router
;
4339 route_list_prop
= kSCPropNetIPv6AdditionalRoutes
;
4342 router
= CFDictionaryGetValue(setup_dict
, router_prop
);
4344 && !cfstring_to_ipvx(af
, router
, &router_ip
, sizeof(router_ip
))) {
4348 /* AdditionalRoutes */
4350 = CFDictionaryGetValue(setup_dict
, route_list_prop
);
4351 additional_routes
= isA_CFArray(additional_routes
);
4353 if (router
!= NULL
|| additional_routes
!= NULL
) {
4354 modified_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
4355 if (router
!= NULL
) {
4356 CFDictionarySetValue(modified_dict
,
4360 if (additional_routes
!= NULL
) {
4361 CFArrayRef combined_routes
= NULL
;
4362 CFArrayRef state_routes
;
4365 = CFDictionaryGetValue(state_dict
,
4367 if (isA_CFArray(state_routes
) != NULL
) {
4369 = my_CFArrayCreateCombinedArray(additional_routes
,
4371 additional_routes
= combined_routes
;
4373 CFDictionarySetValue(modified_dict
,
4376 if (combined_routes
!= NULL
) {
4377 CFRelease(combined_routes
);
4380 dict
= modified_dict
;
4385 routes_data
= IPv4RouteListDataCreate(dict
, rank_assertion
);
4389 routes_data
= IPv6RouteListDataCreate(dict
, rank_assertion
);
4392 if (routes_data
!= NULL
) {
4393 aggregated_dict
= ipdict_create(dict
, routes_data
);
4394 CFRelease(routes_data
);
4396 if (modified_dict
!= NULL
) {
4397 CFRelease(modified_dict
);
4399 return (aggregated_dict
);
4403 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4404 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4406 CFDictionaryRef dict
= NULL
;
4407 boolean_t changed
= FALSE
;
4408 CFNumberRef rank_assertion
= NULL
;
4409 CFDictionaryRef service_options
;
4411 if (state_dict
== NULL
) {
4414 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4415 if (service_options
!= NULL
) {
4417 = CFDictionaryGetValue(service_options
,
4418 kServiceOptionRankAssertion
);
4420 dict
= IPDictCreate(AF_INET
, state_dict
, setup_dict
, rank_assertion
);
4423 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, dict
);
4425 /* clean up the rank too */
4426 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
4428 my_CFRelease(&dict
);
4433 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4434 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4436 CFDictionaryRef dict
= NULL
;
4437 boolean_t changed
= FALSE
;
4438 CFNumberRef rank_assertion
= NULL
;
4439 CFDictionaryRef service_options
;
4441 if (state_dict
== NULL
) {
4444 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4445 if (service_options
!= NULL
) {
4447 = CFDictionaryGetValue(service_options
,
4448 kServiceOptionRankAssertion
);
4450 dict
= IPDictCreate(AF_INET6
, state_dict
, setup_dict
, rank_assertion
);
4453 #if !TARGET_OS_SIMULATOR
4454 ipv6_service_update_router(serviceID
, dict
);
4455 #endif /* !TARGET_OS_SIMULATOR */
4456 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, dict
);
4458 /* clean up the rank too */
4459 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
4461 my_CFRelease(&dict
);
4467 __private_extern__ CFDictionaryRef
4468 ipv4_dict_create(CFDictionaryRef state_dict
)
4470 return (IPDictCreate(AF_INET
, state_dict
, NULL
, NULL
));
4473 __private_extern__ CFDictionaryRef
4474 ipv6_dict_create(CFDictionaryRef state_dict
)
4476 return (IPDictCreate(AF_INET6
, state_dict
, NULL
, NULL
));
4479 #endif /* TEST_DNS */
4482 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
4483 CFMutableArrayRef out_servers
, CFStringRef interface
)
4488 count
= CFArrayGetCount(in_servers
);
4489 for (i
= 0; i
< count
; i
++) {
4491 struct in6_addr ipv6_addr
;
4492 struct in_addr ip_addr
;
4494 addr
= CFArrayGetValueAtIndex(in_servers
, i
);
4495 assert(addr
!= NULL
);
4497 if (cfstring_to_ip(addr
, &ip_addr
)) {
4499 if ((active_protos
& kProtocolFlagsIPv4
) == 0
4500 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
4502 "no IPv4 connectivity, "
4503 "ignoring DNS server address " IP_FORMAT
,
4510 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
4512 if ((active_protos
& kProtocolFlagsIPv6
) == 0
4513 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
4514 char ntopbuf
[INET6_ADDRSTRLEN
];
4517 "no IPv6 connectivity, "
4518 "ignoring DNS server address %s",
4519 inet_ntop(AF_INET6
, &ipv6_addr
,
4520 ntopbuf
, sizeof(ntopbuf
)));
4524 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
4525 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
4526 && (interface
!= NULL
)
4527 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
4528 // append interface name to IPv6 link local address
4529 addr
= CFStringCreateWithFormat(NULL
, NULL
,
4538 /* bad IP address */
4539 my_log(LOG_NOTICE
, "ignoring bad DNS server address '%@'", addr
);
4543 /* DNS server is valid and one we want */
4544 CFArrayAppendValue(out_servers
, addr
);
4550 static CF_RETURNS_RETAINED CFArrayRef
4551 order_dns_servers(CFArrayRef servers
, ProtocolFlags active_protos
)
4553 Boolean favor_v4
= FALSE
;
4554 CFMutableArrayRef ordered_servers
;
4555 ProtocolFlags proto_last
= kProtocolFlagsIPv4
;
4556 struct sockaddr_in v4_dns1
= { .sin_family
= AF_INET
,
4557 .sin_len
= sizeof(struct sockaddr_in
) };
4559 struct sockaddr_in6 v6_dns1
= { .sin6_family
= AF_INET6
,
4560 .sin6_len
= sizeof(struct sockaddr_in6
),
4561 .sin6_scope_id
= 0 };
4564 if (((active_protos
& kProtocolFlagsIPv4
) == 0) ||
4565 ((active_protos
& kProtocolFlagsIPv6
) == 0)) {
4566 /* only one protocol */
4567 #ifdef TEST_DNS_ORDER
4568 printf("only one protocol\n");
4569 #endif // TEST_DNS_ORDER
4570 return CFRetain(servers
);
4573 ordered_servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4574 for (CFIndex i
= 0, n
= CFArrayGetCount(servers
); i
< n
; i
++) {
4576 struct in6_addr ia6
;
4577 ProtocolFlags proto
;
4580 server
= CFArrayGetValueAtIndex(servers
, i
);
4581 if (cfstring_to_ip(server
, &ia
)) {
4582 proto
= kProtocolFlagsIPv4
;
4584 v4_dns1
.sin_addr
= ia
;
4586 } else if (cfstring_to_ip6(server
, &ia6
)) {
4587 proto
= kProtocolFlagsIPv6
;
4589 bcopy(&ia6
, &v6_dns1
.sin6_addr
, sizeof(ia6
));
4592 CFRelease(ordered_servers
);
4593 return CFRetain(servers
);
4596 if ((i
> 0) && (proto
!= proto_last
)) {
4597 /* if the protocol of the server addresses changed */
4598 if (((proto
== kProtocolFlagsIPv4
) && (v4_n
== 1)) ||
4599 ((proto
== kProtocolFlagsIPv6
) && (v6_n
== 1))) {
4600 /* if we now have the 1st server address of another protocol */
4601 favor_v4
= (sa_dst_compare_no_stats((struct sockaddr
*)&v4_dns1
,
4602 (struct sockaddr
*)&v6_dns1
,
4604 #ifdef TEST_DNS_ORDER
4605 char v4_buf
[INET_ADDRSTRLEN
];
4606 char v6_buf
[INET6_ADDRSTRLEN
];
4607 printf("comparing %s vs %s, favoring %s\n",
4608 inet_ntop(v4_dns1
.sin_family
, &v4_dns1
.sin_addr
, v4_buf
, sizeof(v4_buf
)),
4609 inet_ntop(v6_dns1
.sin6_family
, &v6_dns1
.sin6_addr
, v6_buf
, sizeof(v6_buf
)),
4610 favor_v4
? "v4" : "v6");
4611 #endif // TEST_DNS_ORDER
4613 /* if the server addresses array is randomly mixed */
4614 #ifdef TEST_DNS_ORDER
4615 printf("v4/v6 not ordered\n");
4616 #endif // TEST_DNS_ORDER
4617 CFRelease(ordered_servers
);
4618 return CFRetain(servers
);
4623 if ((proto
== kProtocolFlagsIPv4
) && favor_v4
) {
4624 CFArrayInsertValueAtIndex(ordered_servers
, v4_n
- 1, server
);
4625 } else if ((proto
== kProtocolFlagsIPv6
) && !favor_v4
) {
4626 CFArrayInsertValueAtIndex(ordered_servers
, v6_n
- 1, server
);
4628 CFArrayAppendValue(ordered_servers
, server
);
4632 return ordered_servers
;
4636 merge_dns_servers(CFMutableDictionaryRef new_dict
,
4637 CFArrayRef state_servers
,
4638 CFArrayRef setup_servers
,
4640 Boolean trust_state
,
4641 ProtocolFlags active_protos
,
4642 CFStringRef interface
)
4644 CFMutableArrayRef dns_servers
;
4645 Boolean have_dns_setup
= FALSE
;
4647 if (state_servers
== NULL
&& setup_servers
== NULL
) {
4648 /* no DNS servers */
4651 dns_servers
= CFArrayCreateMutable(NULL
, 0,
4652 &kCFTypeArrayCallBacks
);
4653 if (setup_servers
!= NULL
) {
4654 accumulate_dns_servers(setup_servers
, active_protos
,
4655 dns_servers
, interface
);
4656 if (CFArrayGetCount(dns_servers
) > 0) {
4657 have_dns_setup
= TRUE
;
4660 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
4661 && state_servers
!= NULL
) {
4662 CFArrayRef ordered_servers
;
4664 ordered_servers
= order_dns_servers(state_servers
, active_protos
);
4665 accumulate_dns_servers(ordered_servers
, active_protos
,
4667 CFRelease(ordered_servers
);
4671 * Here, we determine whether or not we want all queries for this DNS
4672 * configuration to be bound to the associated network interface.
4674 * For dynamically derived network configurations (i.e. from State:)
4675 * this would be the preferred option using the argument "Hey, the
4676 * server told us to use these servers on this network so let's not
4679 * But, when a DNS configuration has been provided by the user/admin
4680 * via the Network pref pane (i.e. from Setup:) we opt to not force
4681 * binding of the outbound queries. The simplest example why we take
4682 * this stance is with a multi-homing configuration. Consider a system
4683 * with one network service associated with "en0" and a second service
4684 * associated with "en1". The "en0" service has been set higher in
4685 * the network service order so it would be primary but the user/admin
4686 * wants the DNS queries to go to a server only accessible via "en1".
4687 * Without this exception we would take the DNS server addresses from
4688 * the Network pref pane (for "en0") and have the queries bound to
4689 * "en0" where they'd never reach their intended destination (via
4690 * "en1"). So, our exception to the rule is that we will not bind
4691 * user/admin configurations to any specific network interface.
4693 * We also add an exception to the "follow the dynamically derived
4694 * network configuration" path for on-the-fly (no Setup: content)
4697 * But, we add an exception to the exception to support our own
4698 * VPN code. Here, we look for a "ServiceID" property in the DNS
4699 * entity. If present, and if it matches, then we extend our
4700 * trust even when there is no Setup: content.
4702 if (CFArrayGetCount(dns_servers
) != 0) {
4703 CFDictionarySetValue(new_dict
,
4704 kSCPropNetDNSServerAddresses
, dns_servers
);
4705 if ((have_setup
&& !have_dns_setup
) || (!have_setup
&& trust_state
)) {
4706 // if this is a "setup"+"state" service with only "state" DNS content (i.e. no
4707 // setup override) or this is a TRUSTED "state"-only service
4708 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
4712 my_CFRelease(&dns_servers
);
4718 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4719 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4721 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4722 boolean_t changed
= FALSE
;
4724 Boolean have_setup
= FALSE
;
4725 CFStringRef interface
= NULL
;
4726 CFDictionaryRef ipv4
;
4727 CFDictionaryRef ipv6
;
4734 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
4735 { kSCPropNetDNSSortList
, 0, FALSE
},
4736 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4737 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
4739 CFMutableDictionaryRef new_dict
= NULL
;
4740 const CFStringRef pick_list
[] = {
4741 kSCPropNetDNSDomainName
,
4742 kSCPropNetDNSOptions
,
4743 kSCPropNetDNSSearchOrder
,
4744 kSCPropNetDNSServerPort
,
4745 kSCPropNetDNSServerTimeout
,
4746 kSCPropNetDNSServiceIdentifier
,
4747 kSCPropNetDNSSupplementalMatchDomainsNoSearch
,
4749 Boolean trust_state
= FALSE
;
4751 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4752 /* there is no DNS content */
4756 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4758 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
4761 active_protos
|= kProtocolFlagsIPv4
;
4762 interface
= ipdict_get_ifname(ipv4
);
4765 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4768 && (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
)
4772 active_protos
|= kProtocolFlagsIPv6
;
4773 if (interface
== NULL
) {
4774 interface
= ipdict_get_ifname(ipv6
);
4779 if (active_protos
== kProtocolFlagsNone
) {
4780 /* there is no IPv4 nor IPv6 */
4781 if (state_dict
== NULL
) {
4782 /* ... and no DNS content that we care about */
4788 if (state_dict
!= NULL
) {
4789 CFStringRef state_serviceID
= NULL
;
4791 if (CFDictionaryGetValueIfPresent(state_dict
,
4792 kSCPropNetDNSConfirmedServiceID
,
4793 (const void **)&state_serviceID
) &&
4794 isA_CFString(state_serviceID
) &&
4795 CFEqual(serviceID
, state_serviceID
)) {
4800 /* merge DNS configuration */
4801 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
4802 &kCFTypeDictionaryKeyCallBacks
,
4803 &kCFTypeDictionaryValueCallBacks
);
4805 if (active_protos
== kProtocolFlagsNone
) {
4806 merge_dns_servers(new_dict
,
4807 my_CFDictionaryGetArray(state_dict
,
4808 kSCPropNetDNSServerAddresses
),
4812 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
4816 merge_dns_servers(new_dict
,
4817 my_CFDictionaryGetArray(state_dict
,
4818 kSCPropNetDNSServerAddresses
),
4819 my_CFDictionaryGetArray(setup_dict
,
4820 kSCPropNetDNSServerAddresses
),
4827 for (i
= 0; i
< countof(merge_list
); i
++) {
4828 merge_array_prop(new_dict
,
4832 merge_list
[i
].flags
,
4833 merge_list
[i
].append
);
4836 for (i
= 0; i
< countof(pick_list
); i
++) {
4844 if (active_protos
== kProtocolFlagsNone
) {
4845 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
4846 if (CFDictionaryContainsKey(new_dict
,
4847 kSCPropNetDNSSupplementalMatchDomains
)) {
4848 /* only keep State: supplemental */
4849 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
4850 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
4851 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
4852 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
4854 if ((interface
== NULL
) && (setup_dict
== NULL
) && (state_dict
!= NULL
)) {
4856 * for supplemental-only configurations, add any scoped (or
4857 * wild-card "*") interface
4859 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4861 } else if (CFDictionaryContainsKey(new_dict
, kSCPropNetDNSServiceIdentifier
) &&
4862 (interface
== NULL
) &&
4863 (state_dict
!= NULL
)) {
4864 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4870 if (CFDictionaryGetCount(new_dict
) == 0) {
4871 my_CFRelease(&new_dict
);
4875 if (interface
!= NULL
) {
4876 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
4879 if (S_append_state
) {
4881 * ensure any specified domain name (e.g. the domain returned by
4882 * a DHCP server) is in the search list.
4884 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
4885 if (isA_CFString(domain
)) {
4888 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
4889 if (isA_CFArray(search
) &&
4890 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
4891 CFMutableArrayRef new_search
;
4893 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
4894 CFArrayAppendValue(new_search
, domain
);
4895 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
4896 my_CFRelease(&new_search
);
4902 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
4903 my_CFRelease(&new_dict
);
4908 merge_dict(const void *key
, const void *value
, void *context
)
4910 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
4912 CFDictionarySetValue(dict
, key
, value
);
4916 #define PROXY_AUTO_DISCOVERY_URL 252
4918 static CF_RETURNS_RETAINED CFStringRef
4919 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
4921 CFStringRef urlString
= NULL
;
4923 if (dhcp_options
!= NULL
) {
4926 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
4929 const UInt8
*urlBytes
;
4932 urlBytes
= CFDataGetBytePtr(data
);
4933 urlLen
= CFDataGetLength(data
);
4934 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
4935 // remove trailing NUL
4943 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
4945 urlString
= CFURLGetString(url
);
4946 if (urlString
!= NULL
) {
4947 CFRetain(urlString
);
4957 static CF_RETURNS_RETAINED CFStringRef
4961 CFStringRef urlString
= NULL
;
4963 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
4965 urlString
= CFURLGetString(url
);
4966 if (urlString
!= NULL
) {
4967 CFRetain(urlString
);
4976 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4977 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4979 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4980 boolean_t changed
= FALSE
;
4981 CFStringRef interface
= NULL
;
4982 CFDictionaryRef ipv4
;
4983 CFDictionaryRef ipv6
;
4984 CFMutableDictionaryRef new_dict
= NULL
;
4990 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4991 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
4994 CFStringRef key1
; /* an "enable" key */
4998 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
4999 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
5000 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
5001 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
5002 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
5003 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
5004 { kSCPropNetProxiesProxyAutoConfigEnable
,
5005 kSCPropNetProxiesProxyAutoConfigURLString
,
5006 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
5007 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5012 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
5013 /* there is no proxy content */
5016 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
5017 if (ipdict_get_routelist(ipv4
) != NULL
) {
5018 active_protos
|= kProtocolFlagsIPv4
;
5019 interface
= ipdict_get_ifname(ipv4
);
5021 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
5022 if (ipdict_get_routelist(ipv6
) != NULL
) {
5023 active_protos
|= kProtocolFlagsIPv6
;
5024 if (interface
== NULL
) {
5025 interface
= ipdict_get_ifname(ipv6
);
5028 if (active_protos
== kProtocolFlagsNone
) {
5029 /* there is no IPv4 nor IPv6 */
5030 if (state_dict
== NULL
) {
5031 /* ... and no proxy content that we care about */
5037 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
5039 CFMutableDictionaryRef setup_copy
;
5042 * Merge the per-service "Setup:" and "State:" proxy information with
5043 * the "Setup:" information always taking precedence. Additionally,
5044 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
5045 * Port) is defined than all of the values for that group will be
5046 * used. That is, we don't allow mixing some of the values from
5047 * the "Setup:" keys and others from the "State:" keys.
5049 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5050 for (i
= 0; i
< countof(merge_list
); i
++) {
5051 merge_array_prop(new_dict
,
5055 merge_list
[i
].flags
,
5056 merge_list
[i
].append
);
5059 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5060 for (i
= 0; i
< countof(pick_list
); i
++) {
5061 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
5063 * if a "Setup:" enabled key has been provided than we want to
5064 * ignore all of the "State:" keys
5066 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
5067 if (pick_list
[i
].key2
!= NULL
) {
5068 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
5070 if (pick_list
[i
].key3
!= NULL
) {
5071 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
5073 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
5074 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
5075 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
5077 * if a "Setup:" enabled key has not been provided and we have
5078 * some" "State:" keys than we remove all of of "Setup:" keys
5080 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
5081 if (pick_list
[i
].key2
!= NULL
) {
5082 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
5084 if (pick_list
[i
].key3
!= NULL
) {
5085 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
5090 /* merge the "Setup:" keys */
5091 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
5092 CFRelease(setup_copy
);
5094 else if (setup_dict
!= NULL
) {
5095 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
5097 else if (state_dict
!= NULL
) {
5098 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
5101 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
5102 CFRelease(new_dict
);
5106 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
5107 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
5111 if (new_dict
!= NULL
) {
5112 CFDictionaryRef dhcp_options
;
5114 CFNumberRef wpad
= NULL
;
5115 int wpadEnabled
= 0;
5116 CFStringRef wpadURL
= NULL
;
5118 if (CFDictionaryGetValueIfPresent(new_dict
,
5119 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5120 (const void **)&num
) &&
5121 isA_CFNumber(num
)) {
5122 /* if we have a WPAD key */
5124 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
5125 /* if we don't like the enabled key/value */
5133 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
5134 if (!isA_CFNumber(num
) ||
5135 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
5136 /* if we don't like the enabled key/value */
5143 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
5144 if (pacURL
!= NULL
) {
5145 if (!isA_CFString(pacURL
) || (CFStringGetLength(pacURL
) == 0)) {
5146 /* if we don't like the PAC URL */
5152 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
5153 if (!isA_CFString(pacJS
) || (CFStringGetLength(pacJS
) == 0)) {
5154 /* if we don't have (or like) the PAC JavaScript */
5162 * we already have a PAC URL so disable WPAD.
5169 * if WPAD is enabled and we don't already have a PAC URL then
5170 * we check for a DHCP provided URL. If not available, we use
5171 * a PAC URL pointing to a well-known file (wpad.dat) on a
5172 * well-known host (wpad.<domain>).
5174 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
5175 wpadURL
= wpadURL_dhcp(dhcp_options
);
5176 if (wpadURL
== NULL
) {
5177 wpadURL
= wpadURL_dns();
5179 if (wpadURL
== NULL
) {
5180 wpadEnabled
= 0; /* if we don't have a WPAD URL */
5185 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
5186 CFDictionarySetValue(new_dict
,
5187 kSCPropNetProxiesProxyAutoConfigEnable
,
5190 CFDictionarySetValue(new_dict
,
5191 kSCPropNetProxiesProxyAutoConfigURLString
,
5198 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
5199 CFDictionarySetValue(new_dict
,
5200 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5207 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
5208 my_CFRelease(&new_dict
);
5212 #if !TARGET_OS_IPHONE
5214 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5215 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5217 boolean_t changed
= FALSE
;
5219 CFMutableDictionaryRef new_dict
= NULL
;
5220 const CFStringRef pick_list
[] = {
5221 kSCPropNetSMBNetBIOSName
,
5222 kSCPropNetSMBNetBIOSNodeType
,
5223 #ifdef ADD_NETBIOS_SCOPE
5224 kSCPropNetSMBNetBIOSScope
,
5225 #endif // ADD_NETBIOS_SCOPE
5226 kSCPropNetSMBWorkgroup
,
5229 if (state_dict
== NULL
&& setup_dict
== NULL
) {
5230 /* there is no SMB */
5233 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
5234 && service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
) {
5235 /* there is no IPv4 or IPv6 */
5239 /* merge SMB configuration */
5240 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5241 &kCFTypeDictionaryKeyCallBacks
,
5242 &kCFTypeDictionaryValueCallBacks
);
5243 merge_array_prop(new_dict
,
5244 kSCPropNetSMBWINSAddresses
,
5249 for (i
= 0; i
< countof(pick_list
); i
++) {
5257 if (CFDictionaryGetCount(new_dict
) == 0) {
5258 my_CFRelease(&new_dict
);
5263 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
5264 my_CFRelease(&new_dict
);
5267 #endif /* !TARGET_OS_IPHONE */
5270 services_info_get_interface(CFDictionaryRef services_info
,
5271 CFStringRef serviceID
)
5273 CFStringRef interface
= NULL
;
5274 CFDictionaryRef ipv4_dict
;
5276 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
5278 if (ipv4_dict
!= NULL
) {
5279 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
5282 CFDictionaryRef ipv6_dict
;
5284 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
5286 if (ipv6_dict
!= NULL
) {
5287 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5294 static const struct {
5295 const CFStringRef
* entityName
;
5296 const CFStringRef
* statusKey
;
5297 } transientServiceInfo
[] = {
5298 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
5299 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
5300 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
5304 get_transient_status_changes(CFStringRef serviceID
,
5305 CFDictionaryRef services_info
)
5307 boolean_t changed
= FALSE
;
5310 for (i
= 0; i
< countof(transientServiceInfo
); i
++) {
5311 CFDictionaryRef dict
;
5312 CFNumberRef status
= NULL
;
5313 CFMutableDictionaryRef ts_dict
= NULL
;
5315 dict
= get_service_state_entity(services_info
, serviceID
,
5316 *transientServiceInfo
[i
].entityName
);
5319 status
= CFDictionaryGetValue(dict
,
5320 *transientServiceInfo
[i
].statusKey
);
5323 if (isA_CFNumber(status
) != NULL
) {
5324 ts_dict
= CFDictionaryCreateMutable(NULL
,
5326 &kCFTypeDictionaryKeyCallBacks
,
5327 &kCFTypeDictionaryValueCallBacks
);
5328 CFDictionaryAddValue(ts_dict
,
5329 *transientServiceInfo
[i
].statusKey
,
5333 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
,
5338 if (ts_dict
!= NULL
) {
5346 if_dict_is_expensive(CFDictionaryRef if_dict
)
5348 boolean_t is_expensive
= FALSE
;
5350 if (isA_CFDictionary(if_dict
) != NULL
) {
5351 CFBooleanRef expensive
;
5352 expensive
= CFDictionaryGetValue(if_dict
, kSCPropNetLinkExpensive
);
5353 if (isA_CFBoolean(expensive
) != NULL
5354 && CFBooleanGetValue(expensive
)) {
5355 is_expensive
= TRUE
;
5358 return is_expensive
;
5362 service_is_expensive(CFStringRef serviceID
, CFDictionaryRef services_info
)
5365 boolean_t is_expensive
= FALSE
;
5367 ifname
= services_info_get_interface(services_info
, serviceID
);
5368 if (ifname
!= NULL
) {
5369 CFDictionaryRef if_dict
;
5372 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5373 if_dict
= CFDictionaryGetValue(services_info
, key
);
5375 is_expensive
= if_dict_is_expensive(if_dict
);
5377 return (is_expensive
);
5381 interface_is_expensive(CFStringRef ifname
)
5383 boolean_t is_expensive
= FALSE
;
5385 if (ifname
!= NULL
) {
5386 CFDictionaryRef if_dict
;
5389 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5390 if_dict
= SCDynamicStoreCopyValue(S_session
, key
);
5392 if (if_dict
!= NULL
) {
5393 is_expensive
= if_dict_is_expensive(if_dict
);
5397 return (is_expensive
);
5401 service_rank_entity_get_index(CFDictionaryRef dict
, CFStringRef serviceID
,
5402 CFStringRef which
, uint32_t * ret_val
)
5404 CFNumberRef service_index
= NULL
;
5407 service_index
= CFDictionaryGetValue(dict
,
5408 kSCPropNetServiceServiceIndex
);
5409 service_index
= isA_CFNumber(service_index
);
5411 if (service_index
!= NULL
) {
5414 if (!CFNumberGetValue(service_index
, kCFNumberSInt32Type
,
5416 || index_val
<= 0) {
5417 /* ServiceIndex must be >= 1 */
5419 "%@%@ ServiceIndex %@ is invalid, ignoring",
5420 which
, serviceID
, service_index
);
5421 service_index
= NULL
;
5423 else if (ret_val
!= NULL
) {
5424 *ret_val
= (uint32_t)index_val
;
5427 return (service_index
);
5431 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
5432 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
5434 boolean_t changed
= FALSE
;
5435 boolean_t ip_is_coupled
= FALSE
;
5436 CFMutableDictionaryRef new_dict
= NULL
;
5437 Rank rank_assertion
= kRankAssertionDefault
;
5438 Boolean rank_assertion_is_set
= FALSE
;
5439 CFStringRef setup_rank
= NULL
;
5440 CFStringRef state_rank
= NULL
;
5441 CFNumberRef service_index
= NULL
;
5444 if (setup_options
!= NULL
) {
5445 CFBooleanRef coupled
;
5448 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
5449 setup_rank
= isA_CFString(setup_rank
);
5450 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
5451 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5452 ip_is_coupled
= TRUE
;
5455 = service_rank_entity_get_index(setup_options
,
5457 kSCDynamicStoreDomainSetup
,
5460 if (state_options
!= NULL
) {
5461 CFBooleanRef coupled
;
5464 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
5465 state_rank
= isA_CFString(state_rank
);
5466 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
5467 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5468 ip_is_coupled
= TRUE
;
5470 if (service_index
== NULL
) {
5472 = service_rank_entity_get_index(state_options
,
5474 kSCDynamicStoreDomainState
,
5479 if (!ip_is_coupled
) {
5480 ip_is_coupled
= service_is_expensive(serviceID
, services_info
);
5482 if (setup_rank
!= NULL
|| state_rank
!= NULL
) {
5483 /* rank assertion is set on the service */
5484 Rank setup_assertion
;
5485 Boolean setup_assertion_is_set
= FALSE
;
5486 Rank state_assertion
;
5487 Boolean state_assertion_is_set
= FALSE
;
5489 setup_assertion
= PrimaryRankGetRankAssertion(setup_rank
,
5490 &setup_assertion_is_set
);
5491 state_assertion
= PrimaryRankGetRankAssertion(state_rank
,
5492 &state_assertion_is_set
);
5493 if (setup_assertion_is_set
&& state_assertion_is_set
) {
5494 if (setup_assertion
> state_assertion
) {
5495 rank_assertion
= setup_assertion
;
5498 rank_assertion
= state_assertion
;
5500 rank_assertion_is_set
= TRUE
;
5502 else if (setup_assertion_is_set
) {
5503 rank_assertion
= setup_assertion
;
5504 rank_assertion_is_set
= TRUE
;
5506 else if (state_assertion_is_set
) {
5507 rank_assertion
= state_assertion
;
5508 rank_assertion_is_set
= TRUE
;
5512 if (!rank_assertion_is_set
) {
5513 /* check for a rank assertion on the interface */
5514 CFStringRef interface
;
5516 interface
= services_info_get_interface(services_info
, serviceID
);
5517 if (interface
!= NULL
) {
5518 CFNumberRef if_rank
= NULL
;
5520 if (S_if_rank_dict
!= NULL
) {
5521 if_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
5524 = InterfaceRankGetRankAssertion(if_rank
,
5525 &rank_assertion_is_set
);
5527 "serviceID %@ interface %@ rank = %@",
5530 (if_rank
!= NULL
) ? (CFTypeRef
)if_rank
: (CFTypeRef
)CFSTR("Not set)"));
5535 if (service_index
!= NULL
|| rank_assertion_is_set
|| ip_is_coupled
) {
5536 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5537 &kCFTypeDictionaryKeyCallBacks
,
5538 &kCFTypeDictionaryValueCallBacks
);
5539 if (rank_assertion_is_set
) {
5540 CFNumberRef new_rank
;
5542 new_rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
,
5543 (const void *)&rank_assertion
);
5544 CFDictionarySetValue(new_dict
, kServiceOptionRankAssertion
,
5546 CFRelease(new_rank
);
5548 if (ip_is_coupled
) {
5549 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
5551 if (service_index
!= NULL
) {
5552 CFDictionarySetValue(new_dict
, kSCPropNetServiceServiceIndex
,
5556 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
5557 my_CFRelease(&new_dict
);
5562 add_service_keys(CFStringRef serviceID
,
5563 CFMutableArrayRef keys
, CFMutableArrayRef patterns
)
5568 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
5572 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5573 key
= setup_service_key(serviceID
, *entityTypeNames
[i
]);
5574 CFArrayAppendValue(keys
, key
);
5576 key
= state_service_key(serviceID
, *entityTypeNames
[i
]);
5577 CFArrayAppendValue(keys
, key
);
5581 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
5582 CFArrayAppendValue(patterns
, key
);
5585 key
= setup_service_key(serviceID
, NULL
);
5586 CFArrayAppendValue(patterns
, key
);
5588 key
= state_service_key(serviceID
, NULL
);
5589 CFArrayAppendValue(patterns
, key
);
5596 add_transient_status_keys(CFStringRef service_id
, CFMutableArrayRef patterns
)
5600 for (i
= 0; i
< countof(transientServiceInfo
); i
++) {
5601 CFStringRef pattern
;
5603 pattern
= state_service_key(service_id
,
5604 *transientServiceInfo
[i
].entityName
);
5605 CFArrayAppendValue(patterns
, pattern
);
5612 static const CFStringRef
*reachabilitySetupKeys
[] = {
5614 &kSCEntNetInterface
,
5621 add_reachability_patterns(CFMutableArrayRef patterns
)
5625 for (i
= 0; i
< countof(reachabilitySetupKeys
); i
++) {
5626 CFStringRef pattern
;
5627 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
5628 CFArrayAppendValue(patterns
, pattern
);
5635 add_vpn_pattern(CFMutableArrayRef patterns
)
5637 CFStringRef pattern
;
5639 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
5640 CFArrayAppendValue(patterns
, pattern
);
5645 add_interface_link_pattern(CFMutableArrayRef patterns
)
5647 CFStringRef pattern
;
5649 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetLink
);
5650 CFArrayAppendValue(patterns
, pattern
);
5654 static CFDictionaryRef
5655 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
5658 CFMutableArrayRef get_keys
;
5659 CFMutableArrayRef get_patterns
;
5660 CFDictionaryRef info
;
5663 count
= CFArrayGetCount(service_list
);
5664 get_keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5665 get_patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5667 CFArrayAppendValue(get_keys
, S_setup_global_ipv4
);
5668 CFArrayAppendValue(get_keys
, S_multicast_resolvers
);
5669 CFArrayAppendValue(get_keys
, S_private_resolvers
);
5671 for (s
= 0; s
< count
; s
++) {
5672 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
5674 add_service_keys(serviceID
, get_keys
, get_patterns
);
5675 add_transient_status_keys(serviceID
, get_keys
);
5678 add_reachability_patterns(get_patterns
);
5680 add_vpn_pattern(get_patterns
);
5682 add_interface_link_pattern(get_patterns
);
5684 info
= SCDynamicStoreCopyMultiple(session
, get_keys
, get_patterns
);
5685 my_CFRelease(&get_keys
);
5686 my_CFRelease(&get_patterns
);
5690 #if !TARGET_OS_SIMULATOR
5693 set_ipv6_default_interface(IFIndex ifindex
)
5695 struct in6_ndifreq ndifreq
;
5697 boolean_t success
= FALSE
;
5699 bzero((char *)&ndifreq
, sizeof(ndifreq
));
5700 strlcpy(ndifreq
.ifname
, kLoopbackInterface
, sizeof(ndifreq
.ifname
));
5702 ndifreq
.ifindex
= ifindex
;
5705 ndifreq
.ifindex
= lo0_ifindex();
5707 sock
= inet6_dgram_socket();
5711 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
5713 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5724 #endif /* !TARGET_OS_SIMULATOR */
5726 #if !TARGET_OS_IPHONE
5727 static __inline__
void
5730 (void)unlink(VAR_RUN_RESOLV_CONF
);
5734 set_dns(CFArrayRef val_search_domains
,
5735 CFStringRef val_domain_name
,
5736 CFArrayRef val_servers
,
5737 CFArrayRef val_sortlist
)
5739 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
5741 /* publish new resolv.conf */
5746 SCPrint(TRUE
, f
, CFSTR("#\n"));
5747 SCPrint(TRUE
, f
, CFSTR("# Mac OS X Notice\n"));
5748 SCPrint(TRUE
, f
, CFSTR("#\n"));
5749 SCPrint(TRUE
, f
, CFSTR("# This file is not used by the host name and address resolution\n"));
5750 SCPrint(TRUE
, f
, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
5751 SCPrint(TRUE
, f
, CFSTR("# this Mac OS X system.\n"));
5752 SCPrint(TRUE
, f
, CFSTR("#\n"));
5753 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
5754 SCPrint(TRUE
, f
, CFSTR("#\n"));
5756 if (isA_CFArray(val_search_domains
)) {
5757 SCPrint(TRUE
, f
, CFSTR("search"));
5758 n
= CFArrayGetCount(val_search_domains
);
5759 for (i
= 0; i
< n
; i
++) {
5762 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
5763 if (isA_CFString(domain
)) {
5764 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
5767 SCPrint(TRUE
, f
, CFSTR("\n"));
5769 else if (isA_CFString(val_domain_name
)) {
5770 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
5773 if (isA_CFArray(val_servers
)) {
5774 n
= CFArrayGetCount(val_servers
);
5775 for (i
= 0; i
< n
; i
++) {
5776 CFStringRef nameserver
;
5778 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
5779 if (isA_CFString(nameserver
)) {
5780 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
5785 if (isA_CFArray(val_sortlist
)) {
5786 SCPrint(TRUE
, f
, CFSTR("sortlist"));
5787 n
= CFArrayGetCount(val_sortlist
);
5788 for (i
= 0; i
< n
; i
++) {
5789 CFStringRef address
;
5791 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
5792 if (isA_CFString(address
)) {
5793 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
5796 SCPrint(TRUE
, f
, CFSTR("\n"));
5800 (void)rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
5804 #endif /* !TARGET_OS_IPHONE */
5807 service_get_ip_is_coupled(CFStringRef serviceID
)
5809 CFDictionaryRef dict
;
5810 boolean_t ip_is_coupled
= FALSE
;
5812 dict
= service_dict_get(serviceID
, kSCEntNetService
);
5814 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
5815 ip_is_coupled
= TRUE
;
5818 return (ip_is_coupled
);
5822 my_CFStringCreateWithInAddr(struct in_addr ip
)
5826 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(IP_FORMAT
), IP_LIST(&ip
));
5831 my_CFStringCreateWithIn6Addr(const struct in6_addr
* ip
)
5833 char ntopbuf
[INET6_ADDRSTRLEN
];
5835 (void)inet_ntop(AF_INET6
, ip
, ntopbuf
, sizeof(ntopbuf
));
5836 return (CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), ntopbuf
));
5840 * Function: update_ipv4
5842 * Update the IPv4 configuration based on the latest information.
5843 * Publish the State:/Network/Global/IPv4 information, and update the
5844 * IPv4 routing table.
5847 update_ipv4(CFStringRef primary
,
5848 IPv4RouteListRef new_routelist
,
5849 keyChangeListRef keys
)
5851 #if !TARGET_OS_SIMULATOR
5853 #endif /* !TARGET_OS_SIMULATOR */
5856 if (new_routelist
!= NULL
&& primary
!= NULL
) {
5857 const char * ifn_p
= NULL
;
5858 char ifname
[IFNAMSIZ
];
5860 CFMutableDictionaryRef dict
= NULL
;
5862 dict
= CFDictionaryCreateMutable(NULL
, 0,
5863 &kCFTypeDictionaryKeyCallBacks
,
5864 &kCFTypeDictionaryValueCallBacks
);
5865 /* the first entry is the default route */
5866 r
= new_routelist
->list
;
5867 if (r
->gateway
.s_addr
!= 0) {
5870 str
= my_CFStringCreateWithInAddr(r
->gateway
);
5871 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, str
);
5874 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
5875 if (ifn_p
!= NULL
) {
5876 CFStringRef ifname_cf
;
5878 ifname_cf
= CFStringCreateWithCString(NULL
,
5880 kCFStringEncodingASCII
);
5881 if (ifname_cf
!= NULL
) {
5882 CFDictionarySetValue(dict
,
5883 kSCDynamicStorePropNetPrimaryInterface
,
5885 CFRelease(ifname_cf
);
5888 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
5890 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
5894 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
5898 #if !TARGET_OS_SIMULATOR
5899 sockfd
= open_routing_socket();
5901 /* go through routelist and bind any unbound routes */
5902 if (new_routelist
!= NULL
) {
5903 IPv4RouteListFinalize(new_routelist
);
5906 /* provide a routelist with just loopback multicast */
5907 new_routelist
= IPv4RouteListCopyMulticastLoopback();
5909 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5910 if (S_ipv4_routelist
== NULL
) {
5911 my_log(LOG_DEBUG
, "Old Routes = <none>");
5914 my_log(LOG_DEBUG
, "Old Routes = ");
5915 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
5917 if (new_routelist
== NULL
) {
5918 my_log(LOG_DEBUG
, "New Routes = <none>");
5921 my_log(LOG_DEBUG
, "New Routes = ");
5922 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
5925 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
, sockfd
);
5928 if (S_ipv4_routelist
!= NULL
) {
5929 free(S_ipv4_routelist
);
5931 S_ipv4_routelist
= new_routelist
;
5932 #else /* !TARGET_OS_SIMULATOR */
5933 if (new_routelist
!= NULL
) {
5934 free(new_routelist
);
5936 #endif /* !TARGET_OS_SIMULATOR */
5942 * Function: update_ipv6
5944 * Update the IPv6 configuration based on the latest information.
5945 * Publish the State:/Network/Global/IPv6 information, and update the
5946 * IPv6 routing table.
5949 update_ipv6(CFStringRef primary
,
5950 IPv6RouteListRef new_routelist
,
5951 keyChangeListRef keys
)
5953 #if !TARGET_OS_SIMULATOR
5955 #endif /* !TARGET_OS_SIMULATOR */
5958 if (new_routelist
!= NULL
&& primary
!= NULL
) {
5959 const char * ifn_p
= NULL
;
5960 char ifname
[IFNAMSIZ
];
5962 CFMutableDictionaryRef dict
= NULL
;
5964 dict
= CFDictionaryCreateMutable(NULL
, 0,
5965 &kCFTypeDictionaryKeyCallBacks
,
5966 &kCFTypeDictionaryValueCallBacks
);
5967 /* the first entry is the default route */
5968 r
= new_routelist
->list
;
5969 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
5972 router
= my_CFStringCreateWithIn6Addr(&r
->gateway
);
5973 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
, router
);
5976 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
5977 if (ifn_p
!= NULL
) {
5978 CFStringRef ifname_cf
;
5980 ifname_cf
= CFStringCreateWithCString(NULL
,
5982 kCFStringEncodingASCII
);
5983 if (ifname_cf
!= NULL
) {
5984 CFDictionarySetValue(dict
,
5985 kSCDynamicStorePropNetPrimaryInterface
,
5987 CFRelease(ifname_cf
);
5990 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
5992 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
5994 #if !TARGET_OS_SIMULATOR
5995 set_ipv6_default_interface(r
->ifindex
);
5996 #endif /* !TARGET_OS_SIMULATOR */
5999 #if !TARGET_OS_SIMULATOR
6000 set_ipv6_default_interface(0);
6001 #endif /* !TARGET_OS_SIMULATOR */
6002 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
6006 #if !TARGET_OS_SIMULATOR
6007 sockfd
= open_routing_socket();
6009 /* go through routelist and bind any unbound routes */
6010 IPv6RouteListFinalize(new_routelist
);
6011 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6012 if (S_ipv6_routelist
== NULL
) {
6013 my_log(LOG_DEBUG
, "Old Routes = <none>");
6016 my_log(LOG_DEBUG
, "Old Routes = ");
6017 IPv6RouteListLog(LOG_DEBUG
, S_ipv6_routelist
);
6019 if (new_routelist
== NULL
) {
6020 my_log(LOG_DEBUG
, "New Routes = <none>");
6023 my_log(LOG_DEBUG
, "New Routes = ");
6024 IPv6RouteListLog(LOG_DEBUG
, new_routelist
);
6027 IPv6RouteListApply(S_ipv6_routelist
, new_routelist
, sockfd
);
6030 if (S_ipv6_routelist
!= NULL
) {
6031 free(S_ipv6_routelist
);
6033 S_ipv6_routelist
= new_routelist
;
6034 #else /* !TARGET_OS_SIMULATOR */
6035 if (new_routelist
!= NULL
) {
6036 free(new_routelist
);
6038 #endif /* !TARGET_OS_SIMULATOR */
6044 update_dns(CFDictionaryRef services_info
,
6045 CFStringRef primary
,
6046 keyChangeListRef keys
)
6048 Boolean changed
= FALSE
;
6049 CFDictionaryRef dict
= NULL
;
6051 if (primary
!= NULL
) {
6052 CFDictionaryRef service_dict
;
6054 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6055 if (service_dict
!= NULL
) {
6056 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6060 if (!_SC_CFEqual(S_dns_dict
, dict
)) {
6062 #if !TARGET_OS_IPHONE
6064 #endif /* !TARGET_OS_IPHONE */
6065 keyChangeListRemoveValue(keys
, S_state_global_dns
);
6067 CFMutableDictionaryRef new_dict
;
6069 #if !TARGET_OS_IPHONE
6070 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
6071 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
6072 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
6073 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
6074 #endif /* !TARGET_OS_IPHONE */
6075 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
6076 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
6077 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
6078 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
6079 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
6080 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
6081 CFRelease(new_dict
);
6086 if (dict
!= NULL
) CFRetain(dict
);
6087 if (S_dns_dict
!= NULL
) CFRelease(S_dns_dict
);
6094 update_dnsinfo(CFDictionaryRef services_info
,
6095 CFStringRef primary
,
6096 keyChangeListRef keys
,
6097 CFArrayRef service_order
)
6100 CFDictionaryRef dict
= NULL
;
6101 CFArrayRef multicastResolvers
;
6102 CFArrayRef privateResolvers
;
6104 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
6105 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
6107 if (primary
!= NULL
) {
6108 CFDictionaryRef service_dict
;
6110 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6111 if (service_dict
!= NULL
) {
6112 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
6116 changed
= dns_configuration_set(dict
,
6117 S_service_state_dict
,
6122 keyChangeListNotifyKey(keys
, S_state_global_dns
);
6128 update_nwi(nwi_state_t state
)
6130 unsigned char signature
[CC_SHA1_DIGEST_LENGTH
];
6131 static unsigned char signature_last
[CC_SHA1_DIGEST_LENGTH
];
6133 _nwi_state_compute_sha1_hash(state
, signature
);
6134 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
6135 my_log(LOG_DEBUG
, "Not updating network information");
6139 // save [new] signature
6140 bcopy(signature
, signature_last
, sizeof(signature
));
6142 // save [new] configuration
6143 my_log(LOG_INFO
, "Updating network information");
6144 S_nwi_state_dump(state
);
6146 if (!_nwi_state_store(state
)) {
6147 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
6154 update_proxies(CFDictionaryRef services_info
,
6155 CFStringRef primary
,
6156 keyChangeListRef keys
,
6157 CFArrayRef service_order
)
6159 Boolean changed
= FALSE
;
6160 CFDictionaryRef dict
= NULL
;
6161 CFDictionaryRef new_dict
;
6163 if (primary
!= NULL
) {
6164 CFDictionaryRef service_dict
;
6166 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6167 if (service_dict
!= NULL
) {
6168 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
6172 new_dict
= proxy_configuration_update(dict
,
6173 S_service_state_dict
,
6176 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
6177 if (new_dict
== NULL
) {
6178 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
6180 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
6185 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
6186 S_proxies_dict
= new_dict
;
6191 #if !TARGET_OS_IPHONE
6193 update_smb(CFDictionaryRef services_info
,
6194 CFStringRef primary
,
6195 keyChangeListRef keys
)
6197 Boolean changed
= FALSE
;
6198 CFDictionaryRef dict
= NULL
;
6200 if (primary
!= NULL
) {
6201 CFDictionaryRef service_dict
;
6203 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6204 if (service_dict
!= NULL
) {
6205 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
6209 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
6211 keyChangeListRemoveValue(keys
, S_state_global_smb
);
6213 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
6218 if (dict
!= NULL
) CFRetain(dict
);
6219 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
6224 #endif /* !TARGET_OS_IPHONE */
6227 get_service_index(CFDictionaryRef rank_entity
,
6228 CFArrayRef order
, CFIndex n_order
, CFStringRef serviceID
)
6231 Rank rank
= kRankIndexMask
;
6232 CFNumberRef service_index
;
6235 = service_rank_entity_get_index(rank_entity
,
6239 if (service_index
!= NULL
) {
6240 /* ServiceIndex specified in service entity */
6243 "%@ specifies ServiceIndex %@, effective index is %d",
6244 serviceID
, service_index
, rank
);
6246 else if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
6247 for (i
= 0; i
< n_order
; i
++) {
6248 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
6253 if (CFEqual(serviceID
, s
)) {
6263 ** Service election:
6266 * Function: rank_dict_get_service_rank
6268 * Retrieve the service rank in the given dictionary.
6271 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
6274 Rank rank_val
= kRankAssertionDefault
;
6276 rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
6277 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
6279 if (!CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
)) {
6280 /* if we don't like the rank value */
6281 rank_val
= kRankAssertionDefault
;
6289 * Function: rank_dict_set_service_rank
6291 * Save the results of ranking the service so we can look it up later without
6292 * repeating all of the ranking code.
6295 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
6296 CFStringRef serviceID
, Rank rank_val
)
6300 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
6302 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
6308 static const CFStringRef
*transientInterfaceEntityNames
[] = {
6314 CollectTransientServices(const void * key
,
6319 CFStringRef service
= key
;
6320 CFMutableArrayRef vif_setup_keys
= context
;
6322 /* This service is either a vpn type service or a comm center service */
6323 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
6327 for (i
= 0; i
< countof(transientInterfaceEntityNames
); i
++) {
6328 if (CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
6329 CFArrayAppendValue(vif_setup_keys
, service
);
6338 static SCNetworkReachabilityFlags
6339 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
6340 CFStringRef service_id
,
6342 CFStringRef vpn_setup_key
)
6345 CFDictionaryRef dict
;
6346 SCNetworkReachabilityFlags flags
= 0;
6349 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6350 kSCDynamicStoreDomainSetup
,
6352 kSCEntNetInterface
);
6353 dict
= CFDictionaryGetValue(services_info
, key
);
6356 if (isA_CFDictionary(dict
)
6357 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
6359 flags
= (kSCNetworkReachabilityFlagsReachable
6360 | kSCNetworkReachabilityFlagsTransientConnection
6361 | kSCNetworkReachabilityFlagsConnectionRequired
);
6363 if (CFEqual(entity
, kSCEntNetPPP
)) {
6365 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
6367 if (!isA_CFDictionary(p_dict
)) {
6371 // get PPP dial-on-traffic status
6372 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
6373 if (isA_CFNumber(num
)) {
6376 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
6378 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6388 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
6390 Boolean ret
= def_value
;
6395 val
= CFDictionaryGetValue(dict
, key
);
6396 if (isA_CFBoolean(val
) != NULL
) {
6397 ret
= CFBooleanGetValue(val
);
6405 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
6406 SCNetworkReachabilityFlags
*reach_flags_v4
,
6407 SCNetworkReachabilityFlags
*reach_flags_v6
)
6411 CFMutableArrayRef vif_setup_keys
;
6413 vif_setup_keys
= CFArrayCreateMutable(NULL
,
6415 &kCFTypeArrayCallBacks
);
6416 CFDictionaryApplyFunction(services_info
, CollectTransientServices
,
6418 count
= CFArrayGetCount(vif_setup_keys
);
6419 for (i
= 0; i
< count
; i
++) {
6420 CFArrayRef components
= NULL
;
6422 CFStringRef service_id
;
6423 CFStringRef vif_setup_key
;
6425 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
6428 * setup key in the following format:
6429 * Setup:/Network/Service/<Service ID>/<Entity>
6431 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
6433 if (CFArrayGetCount(components
) != 5) {
6434 // invalid Setup key encountered
6438 /* service id is the 3rd element */
6439 service_id
= CFArrayGetValueAtIndex(components
, 3);
6441 /* entity id is the 4th element */
6442 entity
= CFArrayGetValueAtIndex(components
, 4);
6445 if (CFEqual(entity
, kSCEntNetPPP
)) {
6446 SCNetworkReachabilityFlags flags
;
6449 flags
= GetReachabilityFlagsFromVPN(services_info
,
6454 /* Check for the v4 reachability flags */
6455 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6456 kSCDynamicStoreDomainSetup
,
6460 if (CFDictionaryContainsKey(services_info
, key
)) {
6461 *reach_flags_v4
|= flags
;
6462 my_log(LOG_DEBUG
, "Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
6467 /* Check for the v6 reachability flags */
6468 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6469 kSCDynamicStoreDomainSetup
,
6473 if (CFDictionaryContainsKey(services_info
, key
)) {
6474 *reach_flags_v6
|= flags
;
6475 my_log(LOG_DEBUG
, "Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
6480 if (components
!= NULL
) {
6481 CFRelease(components
);
6487 if (components
!= NULL
) {
6488 CFRelease(components
);
6492 CFRelease(vif_setup_keys
);
6496 static SCNetworkReachabilityFlags
6497 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
6499 SCNetworkReachabilityFlags flags
= 0;
6501 if (CFEqual(entity
, kSCEntNetPPP
)) {
6504 /* if we're really UP and RUNNING */
6507 /* if we're effectively UP and RUNNING */
6510 /* if we're not connected at all */
6511 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6513 case PPP_STATERESERVED
:
6514 // if we're not connected at all
6515 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6518 /* if we're in the process of [dis]connecting */
6519 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6523 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
6525 case IPSEC_RUNNING
:
6526 /* if we're really UP and RUNNING */
6529 /* if we're not connected at all */
6530 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6533 /* if we're in the process of [dis]connecting */
6534 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6538 else if (CFEqual(entity
, kSCEntNetVPN
)) {
6541 /* if we're really UP and RUNNING */
6546 case VPN_UNLOADING
:
6547 /* if we're not connected at all */
6548 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6551 /* if we're in the process of [dis]connecting */
6552 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6560 VPNAttributesGet(CFStringRef service_id
,
6561 CFDictionaryRef services_info
,
6562 SCNetworkReachabilityFlags
*flags
,
6563 CFStringRef
*server_address
,
6567 CFDictionaryRef entity_dict
;
6569 CFDictionaryRef p_state
= NULL
;
6571 CFStringRef transient_entity
= NULL
;
6573 if (af
== AF_INET
) {
6574 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv4
);
6577 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv6
);
6579 entity_dict
= ipdict_get_service(entity_dict
);
6580 if (entity_dict
== NULL
) {
6584 for (i
= 0; i
< countof(transientServiceInfo
); i
++) {
6585 CFStringRef entity
= *transientServiceInfo
[i
].entityName
;
6587 p_state
= service_dict_get(service_id
, entity
);
6589 /* ensure that this is a VPN Type service */
6590 if (isA_CFDictionary(p_state
)) {
6591 transient_entity
= entity
;
6596 /* Did we find a vpn type service? If not, we are done.*/
6597 if (transient_entity
== NULL
) {
6601 *flags
|= (kSCNetworkReachabilityFlagsReachable
6602 | kSCNetworkReachabilityFlagsTransientConnection
);
6604 /* Get the Server Address */
6605 if (server_address
!= NULL
) {
6606 *server_address
= CFDictionaryGetValue(entity_dict
,
6607 CFSTR("ServerAddress"));
6608 *server_address
= isA_CFString(*server_address
);
6609 if (*server_address
!= NULL
) {
6610 CFRetain(*server_address
);
6615 if (!CFDictionaryGetValueIfPresent(p_state
,
6616 kSCPropNetVPNStatus
, // IPSecStatus, PPPStatus, VPNStatus
6617 (const void **)&num
) ||
6618 !isA_CFNumber(num
) ||
6619 !CFNumberGetValue(num
, kCFNumberIntType
, &status
)) {
6623 *flags
|= GetReachFlagsFromStatus(transient_entity
, status
);
6624 if (CFEqual(transient_entity
, kSCEntNetPPP
)) {
6626 CFDictionaryRef p_setup
;
6629 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6630 kSCDynamicStoreDomainSetup
,
6633 p_setup
= CFDictionaryGetValue(services_info
, key
);
6636 /* get dial-on-traffic status */
6637 if (isA_CFDictionary(p_setup
) &&
6638 CFDictionaryGetValueIfPresent(p_setup
,
6639 kSCPropNetPPPDialOnDemand
,
6640 (const void **)&num
) &&
6641 isA_CFNumber(num
) &&
6642 CFNumberGetValue(num
, kCFNumberIntType
, &ppp_demand
) &&
6643 (ppp_demand
!= 0)) {
6644 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6645 if (status
== PPP_IDLE
) {
6646 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
6654 typedef struct ElectionInfo
{
6660 ElectionResultsRef results
;
6661 CFMutableDictionaryRef rank_dict
;
6662 } ElectionInfo
, * ElectionInfoRef
;
6664 typedef CFDictionaryApplierFunction ElectionFuncRef
;
6667 CandidateRelease(CandidateRef candidate
)
6669 my_CFRelease(&candidate
->serviceID
);
6670 my_CFRelease(&candidate
->if_name
);
6671 my_CFRelease(&candidate
->signature
);
6676 CandidateCopy(CandidateRef dest
, CandidateRef src
)
6679 if (dest
->serviceID
) {
6680 CFRetain(dest
->serviceID
);
6682 if (dest
->if_name
) {
6683 CFRetain(dest
->if_name
);
6685 if(dest
->signature
) {
6686 CFRetain(dest
->signature
);
6691 static ElectionResultsRef
6692 ElectionResultsAlloc(int af
, int size
)
6694 ElectionResultsRef results
;
6696 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
6699 results
->size
= size
;
6704 ElectionResultsRelease(ElectionResultsRef results
)
6709 for (i
= 0, scan
= results
->candidates
;
6712 CandidateRelease(scan
);
6719 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
6724 if (results
== NULL
) {
6725 my_log(level
, "%s: no candidates", prefix
);
6728 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
6729 for (i
= 0, scan
= results
->candidates
;
6732 char ntopbuf
[INET6_ADDRSTRLEN
];
6734 (void)inet_ntop(results
->af
, &scan
->addr
, ntopbuf
, sizeof(ntopbuf
));
6735 my_log(level
, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6736 i
, scan
->if_name
, scan
->serviceID
, ntopbuf
, scan
->rank
,
6737 scan
->ineligible
? " [ineligible]" : "");
6743 * Function: ElectionResultsAddCandidate
6745 * Add the candidate into the election results. Find the insertion point
6746 * by comparing the rank of the candidate with existing entries.
6749 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
6754 if (results
->count
== results
->size
) {
6755 /* this should not happen */
6756 my_log(LOG_NOTICE
, "can't fit another candidate");
6760 /* find the insertion point */
6761 where
= kCFNotFound
;
6762 for (i
= 0; i
< results
->count
; i
++) {
6763 CandidateRef this_candidate
= results
->candidates
+ i
;
6765 if (candidate
->rank
< this_candidate
->rank
) {
6770 /* add it to the end */
6771 if (where
== kCFNotFound
) {
6772 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
6776 /* slide existing entries over */
6777 for (i
= results
->count
; i
> where
; i
--) {
6778 results
->candidates
[i
] = results
->candidates
[i
- 1];
6780 /* insert element */
6781 CandidateCopy(results
->candidates
+ where
, candidate
);
6787 elect_ip(const void * key
, const void * value
, void * context
);
6790 * Function: ElectionResultsCopy
6792 * Visit all of the services and invoke the protocol-specific election
6793 * function. Return the results of the election.
6795 static ElectionResultsRef
6796 ElectionResultsCopy(int af
, CFArrayRef order
)
6801 count
= (int)CFDictionaryGetCount(S_service_state_dict
);
6806 if (af
== AF_INET
) {
6807 info
.entity
= kSCEntNetIPv4
;
6808 info
.rank_dict
= S_ipv4_service_rank_dict
;
6811 info
.entity
= kSCEntNetIPv6
;
6812 info
.rank_dict
= S_ipv6_service_rank_dict
;
6814 info
.results
= ElectionResultsAlloc(af
, count
);
6815 info
.n_services
= count
;
6817 if (order
!= NULL
) {
6818 info
.n_order
= CFArrayGetCount(order
);
6823 CFDictionaryApplyFunction(S_service_state_dict
, elect_ip
, (void *)&info
);
6824 if (info
.results
->count
== 0) {
6825 ElectionResultsRelease(info
.results
);
6826 info
.results
= NULL
;
6828 return (info
.results
);
6832 * Function: ElectionResultsCandidateNeedsDemotion
6834 * Check whether the given candidate requires demotion. A candidate
6835 * might need to be demoted if its IPv4 and IPv6 services must be coupled
6836 * but a higher ranked service has IPv4 or IPv6.
6839 ElectionResultsCandidateNeedsDemotion(ElectionResultsRef other_results
,
6840 CandidateRef candidate
)
6842 CandidateRef other_candidate
;
6843 Boolean ret
= FALSE
;
6845 if (other_results
== NULL
6846 || !candidate
->ip_is_coupled
6847 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
6850 other_candidate
= other_results
->candidates
;
6851 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
6852 /* they are over the same interface, no need to demote */
6855 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
6856 /* avoid creating a feedback loop */
6859 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
6860 /* the other candidate isn't eligible to become primary, ignore */
6863 if (candidate
->rank
< other_candidate
->rank
) {
6864 /* we're higher ranked than the other candidate, ignore */
6876 get_signature_sha1(CFStringRef signature
,
6877 unsigned char * sha1
)
6880 CFDataRef signature_data
;
6882 signature_data
= CFStringCreateExternalRepresentation(NULL
,
6884 kCFStringEncodingUTF8
,
6888 CC_SHA1_Update(&ctx
,
6889 CFDataGetBytePtr(signature_data
),
6890 (CC_LONG
)CFDataGetLength(signature_data
));
6891 CC_SHA1_Final(sha1
, &ctx
);
6893 CFRelease(signature_data
);
6900 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
6901 CandidateRef candidate
, Boolean not_in_list
,
6902 Boolean not_in_iflist
)
6905 char ifname
[IFNAMSIZ
];
6906 nwi_ifstate_t ifstate
;
6908 if (nwi_state
== NULL
) {
6913 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
6914 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
6916 if (not_in_iflist
) {
6917 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST
;
6919 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
6920 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
6922 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
6923 kCFStringEncodingASCII
);
6924 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
6925 char ntopbuf
[INET6_ADDRSTRLEN
];
6927 (void)inet_ntop(af
, &candidate
->addr
, ntopbuf
, sizeof(ntopbuf
));
6929 "Adding IPv%c [%s] %s "
6930 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
6931 ipvx_char(af
), ifname
, ntopbuf
,
6932 flags
, candidate
->rank
, candidate
->reachability_flags
);
6934 ifstate
= nwi_state_add_ifstate(nwi_state
, ifname
, af
, flags
,
6936 (void *)&candidate
->addr
,
6937 (void *)&candidate
->vpn_server_addr
,
6938 candidate
->reachability_flags
);
6939 if (ifstate
!= NULL
&& candidate
->signature
) {
6940 uint8_t hash
[CC_SHA1_DIGEST_LENGTH
];
6942 get_signature_sha1(candidate
->signature
, hash
);
6943 nwi_ifstate_set_signature(ifstate
, hash
);
6950 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
6952 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
6953 CFStringRef vpn_server_address
= NULL
;
6955 VPNAttributesGet(candidate
->serviceID
,
6958 &vpn_server_address
,
6961 candidate
->reachability_flags
= flags
;
6963 if (vpn_server_address
== NULL
) {
6964 bzero(&candidate
->vpn_server_addr
, sizeof(candidate
->vpn_server_addr
));
6968 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
),
6969 kCFStringEncodingASCII
);
6970 _SC_string_to_sockaddr(buf
,
6972 (void *)&candidate
->vpn_server_addr
,
6973 sizeof(candidate
->vpn_server_addr
));
6975 CFRelease(vpn_server_address
);
6980 * Function: ElectionResultsCopyPrimary
6982 * Use the results of the current protocol and the other protocol to
6983 * determine which service should become primary.
6985 * At the same time, generate the IPv4/IPv6 routing table and
6986 * the nwi_state for the protocol.
6989 ElectionResultsCopyPrimary(ElectionResultsRef results
,
6990 ElectionResultsRef other_results
,
6991 nwi_state_t nwi_state
, int af
,
6992 RouteListRef
* ret_routes
,
6993 CFDictionaryRef services_info
)
6995 CFStringRef primary
= NULL
;
6996 Boolean primary_is_null
= FALSE
;
6997 RouteListRef routes
= NULL
;
6999 if (results
!= NULL
) {
7000 CandidateRef deferred
[results
->count
];
7002 CFStringRef entity_name
;
7005 RouteListInfoRef info
;
7010 entity_name
= kSCEntNetIPv4
;
7011 info
= &IPv4RouteListInfo
;
7012 initial_size
= results
->count
* IPV4_ROUTES_N_STATIC
;
7016 entity_name
= kSCEntNetIPv6
;
7017 info
= &IPv6RouteListInfo
;
7018 initial_size
= results
->count
* IPV6_ROUTES_N_STATIC
;
7022 for (i
= 0, scan
= results
->candidates
;
7025 Boolean is_primary
= FALSE
;
7026 CFDictionaryRef service_dict
;
7027 RouteListRef service_routes
;
7028 Boolean skip
= FALSE
;
7030 if (!scan
->ineligible
7032 && RANK_ASSERTION_MASK(scan
->rank
) != kRankAssertionNever
) {
7033 if (ElectionResultsCandidateNeedsDemotion(other_results
,
7035 /* demote the service */
7037 "IPv%c over %@ demoted: not primary for IPv%c",
7038 ipvx_char(af
), scan
->if_name
, ipvx_other_char(af
));
7039 deferred
[deferred_count
++] = scan
;
7043 primary
= CFRetain(scan
->serviceID
);
7047 /* contribute to the routing table */
7048 service_dict
= service_dict_get(scan
->serviceID
, entity_name
);
7049 service_routes
= ipdict_get_routelist(service_dict
);
7050 if (service_routes
!= NULL
) {
7051 Rank rank
= scan
->rank
;
7054 /* routes are RankNever to prevent becoming primary */
7055 rank
= RankMake(rank
, kRankAssertionNever
);
7057 routes
= RouteListAddRouteList(info
, routes
, initial_size
,
7058 service_routes
, rank
);
7059 if ((service_routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
7067 /* if we're skipping the primary, it's NULL */
7069 primary_is_null
= TRUE
;
7072 else if (!scan
->ineligible
) {
7073 Boolean not_in_iflist
;
7075 add_reachability_flags_to_candidate(scan
, services_info
, af
);
7077 = (service_routes
->flags
& kRouteListFlagsScopedOnly
) != 0;
7078 add_candidate_to_nwi_state(nwi_state
, af
, scan
,
7083 for (i
= 0; i
< deferred_count
; i
++) {
7084 CandidateRef candidate
= deferred
[i
];
7086 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
7087 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, TRUE
, FALSE
);
7090 if (ret_routes
!= NULL
) {
7091 *ret_routes
= routes
;
7093 else if (routes
!= NULL
) {
7096 if (primary_is_null
) {
7097 my_CFRelease(&primary
);
7105 service_dict_get_signature(CFDictionaryRef service_dict
)
7109 ifname
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7110 if (isA_CFString(ifname
) == NULL
7111 || !confirm_interface_name(service_dict
, ifname
)) {
7114 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
7118 * Function: elect_ip
7120 * Evaluate the service and determine what rank the service should have.
7121 * If it's a suitable candidate, add it to the election results.
7124 elect_ip(const void * key
, const void * value
, void * context
)
7126 CFDictionaryRef all_entities_dict
= (CFDictionaryRef
)value
;
7127 Candidate candidate
;
7129 ElectionInfoRef elect_info
;
7130 CFStringRef if_name
;
7131 CFDictionaryRef ipdict
;
7133 CFDictionaryRef rank_entity
;
7134 RouteListUnion routelist
;
7135 CFDictionaryRef service_dict
;
7137 elect_info
= (ElectionInfoRef
)context
;
7138 ipdict
= CFDictionaryGetValue(all_entities_dict
, elect_info
->entity
);
7139 if (ipdict
!= NULL
) {
7140 routelist
.ptr
= ipdict_get_routelist(ipdict
);
7141 service_dict
= ipdict_get_service(ipdict
);
7144 routelist
.ptr
= NULL
;
7146 if (routelist
.ptr
== NULL
|| service_dict
== NULL
) {
7147 /* no connectivity */
7150 if_name
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
7151 if (if_name
== NULL
) {
7152 /* need an interface name */
7155 if (CFEqual(if_name
, CFSTR(kLoopbackInterface
))) {
7156 /* don't process loopback */
7159 bzero(&candidate
, sizeof(candidate
));
7160 candidate
.serviceID
= (CFStringRef
)key
;
7161 if ((routelist
.common
->flags
& kRouteListFlagsHasDefault
) == 0) {
7162 /* no default route means it's ineligible to become primary */
7163 candidate
.ineligible
= TRUE
;
7165 rank_entity
= CFDictionaryGetValue(all_entities_dict
, kSCEntNetService
);
7166 candidate
.rank
= get_service_index(rank_entity
,
7167 elect_info
->order
, elect_info
->n_order
,
7168 candidate
.serviceID
);
7169 if (elect_info
->af
== AF_INET
) {
7170 default_rank
= routelist
.v4
->list
->rank
;
7171 candidate
.addr
.v4
= routelist
.v4
->list
->ifa
;
7174 default_rank
= routelist
.v6
->list
->rank
;
7175 candidate
.addr
.v6
= routelist
.v6
->list
->ifa
;
7177 primary_rank
= RANK_ASSERTION_MASK(default_rank
);
7178 if (S_ppp_override_primary
) {
7181 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
7182 kCFStringEncodingASCII
)
7183 && (strncmp(PPP_PREFIX
, ifn
, sizeof(PPP_PREFIX
) - 1) == 0)) {
7184 /* PPP override: make ppp* look the best */
7185 primary_rank
= kRankAssertionFirst
;
7188 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
7189 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
7190 candidate
.if_name
= if_name
;
7191 rank_dict_set_service_rank(elect_info
->rank_dict
,
7192 candidate
.serviceID
, candidate
.rank
);
7193 candidate
.signature
= service_dict_get_signature(service_dict
);
7194 ElectionResultsAddCandidate(elect_info
->results
, &candidate
);
7200 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
7202 uint32_t changed
= 0;
7205 /* update service options first (e.g. rank) */
7206 if (get_rank_changes(serviceID
,
7207 get_service_state_entity(services_info
, serviceID
,
7209 get_service_setup_entity(services_info
, serviceID
,
7212 changed
|= (1 << kEntityTypeServiceOptions
);
7214 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7215 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
7216 GetEntityChangesFuncRef func
= entityChangeFunc
[i
];
7217 if ((*func
)(serviceID
,
7218 get_service_state_entity(services_info
, serviceID
,
7219 *entityTypeNames
[i
]),
7220 get_service_setup_entity(services_info
, serviceID
,
7221 *entityTypeNames
[i
]),
7223 changed
|= (1 << i
);
7227 if (get_transient_status_changes(serviceID
, services_info
)) {
7228 changed
|= (1 << kEntityTypeTransientStatus
);
7235 serviceID_get_ifname(CFStringRef serviceID
)
7237 CFDictionaryRef entity_dict
;
7238 CFStringRef ifname
= NULL
;
7240 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
7241 if (entity_dict
== NULL
) {
7242 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
7244 if (entity_dict
!= NULL
) {
7245 ifname
= ipdict_get_ifname(entity_dict
);
7250 __private_extern__ boolean_t
7251 check_if_service_expensive(CFStringRef serviceID
)
7254 ifname
= serviceID_get_ifname(serviceID
);
7256 return interface_is_expensive(ifname
);
7260 service_order_get(CFDictionaryRef services_info
)
7262 CFArrayRef order
= NULL
;
7263 CFDictionaryRef ipv4_dict
;
7265 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
7266 S_setup_global_ipv4
);
7267 if (ipv4_dict
!= NULL
) {
7268 CFNumberRef ppp_override
;
7271 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
7272 order
= isA_CFArray(order
);
7274 /* get ppp override primary */
7275 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
7276 kSCPropNetPPPOverridePrimary
);
7277 ppp_override
= isA_CFNumber(ppp_override
);
7278 if (ppp_override
!= NULL
) {
7279 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
7281 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
7284 S_ppp_override_primary
= FALSE
;
7290 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
7291 const char * entity
)
7293 boolean_t changed
= FALSE
;
7294 CFStringRef primary
= *primary_p
;
7296 if (new_primary
!= NULL
) {
7297 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
7298 my_log(LOG_INFO
, "%@ is still primary %s", new_primary
, entity
);
7301 my_CFRelease(primary_p
);
7302 *primary_p
= CFRetain(new_primary
);
7303 my_log(LOG_INFO
, "%@ is the new primary %s", new_primary
, entity
);
7307 else if (primary
!= NULL
) {
7308 my_log(LOG_INFO
, "%@ is no longer primary %s", primary
, entity
);
7309 my_CFRelease(primary_p
);
7316 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
7319 if (service_dict_get(serviceID
, entity
) == NULL
) {
7320 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
7322 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
7326 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
7332 #define N_KEYS_VALUES_STATIC 10
7333 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
7336 count
= CFDictionaryGetCount(S_service_state_dict
);
7337 if (count
<= N_KEYS_VALUES_STATIC
) {
7338 keys
= keys_values_buf
;
7340 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
7342 values
= keys
+ count
;
7343 CFDictionaryGetKeysAndValues(S_service_state_dict
,
7344 (const void * *)keys
,
7345 (const void * *)values
);
7347 for (i
= 0; i
< count
; i
++) {
7348 CFDictionaryRef ipdict
= NULL
;
7349 CFStringRef interface
= NULL
;
7350 CFStringRef serviceID
;
7351 CFDictionaryRef service_dict
;
7353 serviceID
= (CFStringRef
)keys
[i
];
7354 service_dict
= (CFDictionaryRef
)values
[i
];
7356 /* check whether service has IPv4 or IPv6 */
7357 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
7358 if (ipdict
== NULL
) {
7359 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
7360 if (ipdict
== NULL
) {
7364 interface
= ipdict_get_ifname(ipdict
);
7365 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
7367 "Found IP service %@ on interface %@",
7369 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
7372 if (keys
!= keys_values_buf
) {
7378 static __inline__
const char *
7379 get_changed_str(CFStringRef serviceID
, CFStringRef entity
,
7380 CFDictionaryRef old_dict
)
7382 CFDictionaryRef new_dict
= NULL
;
7384 if (serviceID
!= NULL
) {
7385 new_dict
= service_dict_get(serviceID
, entity
);
7388 if (old_dict
== NULL
) {
7389 if (new_dict
!= NULL
) {
7393 if (new_dict
== NULL
) {
7395 } else if (!CFEqual(old_dict
, new_dict
)) {
7402 #if !TARGET_OS_SIMULATOR
7405 #define MANAGE_IF_ORDER
7406 #define MANAGE_IF_IOCTL
7407 #endif /* SIOCSIFORDER */
7409 #ifdef SIOCSIFNETSIGNATURE
7410 #define MANAGE_IF_SIGNATURE
7411 #define MANAGE_IF_IOCTL
7412 #endif /* SIOCSIFNETSIGNATURE */
7414 #ifdef MANAGE_IF_IOCTL
7416 inet_dgram_socket(void)
7420 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
7422 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
7427 #endif /* MANAGE_IF_IOCTL */
7429 #ifdef MANAGE_IF_ORDER
7431 interface_order_changed(nwi_state_t old_state
, nwi_state_t new_state
)
7433 if (old_state
== NULL
&& new_state
== NULL
) {
7434 // Both are NULL, nothing changed
7438 if (old_state
== NULL
|| new_state
== NULL
) {
7439 // One is NULL, something changed
7443 if (old_state
->if_list_count
!= new_state
->if_list_count
) {
7444 // Count is different, something changed
7448 if (new_state
->if_list_count
== 0) {
7449 // Count is same and 0, nothing changed
7454 nwi_ifindex_t
*old_scan
;
7455 nwi_ifindex_t
*new_scan
;
7456 for (i
= 0, old_scan
= nwi_state_if_list(old_state
), new_scan
= nwi_state_if_list(new_state
);
7457 i
< new_state
->if_list_count
; i
++, old_scan
++, new_scan
++) {
7458 if (strcmp(old_state
->ifstate_list
[*old_scan
].ifname
, new_state
->ifstate_list
[*new_scan
].ifname
) != 0) {
7459 // Some interface in the list is different, something changed
7464 // Count and contents are the same, nothing changed
7469 update_interface_order(nwi_state_t state
, int sockfd
)
7471 Boolean success
= FALSE
;
7473 // Set interface order into the kernel
7474 struct if_order interface_order
;
7475 interface_order
.ifo_count
= (uint32_t)state
->if_list_count
;
7476 interface_order
.ifo_ordered_indices
= calloc((size_t)interface_order
.ifo_count
, sizeof(uint32_t));
7477 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7479 nwi_ifindex_t
*scan
;
7480 for (i
= 0, scan
= nwi_state_if_list(state
);
7481 i
< state
->if_list_count
; i
++, scan
++) {
7482 const char *ifname
= state
->ifstate_list
[*scan
].ifname
;
7483 ((uint32_t *)interface_order
.ifo_ordered_indices
)[i
] = my_if_nametoindex(ifname
);
7486 if (ioctl(sockfd
, SIOCSIFORDER
, &interface_order
) != 0) {
7487 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
));
7489 my_log(LOG_INFO
, "Set kernel interface order for %u interfaces", interface_order
.ifo_count
);
7492 if (((uint32_t *)interface_order
.ifo_ordered_indices
) != NULL
) {
7493 free(interface_order
.ifo_ordered_indices
);
7494 interface_order
.ifo_ordered_indices
= NULL
;
7499 #endif /* MANAGE_IF_ORDER */
7501 #ifdef MANAGE_IF_SIGNATURE
7503 siocsifnetsignature(int s
, const char * ifname
, int af
,
7504 const uint8_t * signature
, int signature_length
)
7506 struct if_nsreq nsreq
;
7508 bzero(&nsreq
, sizeof(nsreq
));
7509 strlcpy(nsreq
.ifnsr_name
, ifname
, sizeof(nsreq
.ifnsr_name
));
7510 nsreq
.ifnsr_family
= af
;
7511 if (signature_length
> 0) {
7512 if (signature_length
> sizeof(nsreq
.ifnsr_data
)) {
7513 signature_length
= sizeof(nsreq
.ifnsr_data
);
7515 nsreq
.ifnsr_len
= signature_length
;
7516 memcpy(nsreq
.ifnsr_data
, signature
, signature_length
);
7518 return (ioctl(s
, SIOCSIFNETSIGNATURE
, &nsreq
));
7522 process_ifstate_difference(nwi_ifstate_t ifstate
, int af
, int sockfd
)
7524 nwi_ifstate_difference_t diff
;
7525 boolean_t set_signature
= FALSE
;
7526 int signature_length
= 0;
7528 diff
= nwi_ifstate_get_difference(ifstate
);
7530 case knwi_ifstate_difference_changed
:
7531 /* set signature for this interface */
7532 set_signature
= TRUE
;
7533 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_SIGNATURE
) != 0) {
7534 signature_length
= sizeof(ifstate
->signature
);
7537 case knwi_ifstate_difference_removed
:
7538 /* remove signature for this interface */
7539 set_signature
= TRUE
;
7544 if (set_signature
) {
7545 if (siocsifnetsignature(sockfd
, ifstate
->ifname
, af
,
7547 signature_length
) < 0) {
7549 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7550 ifstate
->ifname
, ipvx_char(af
),
7555 my_log(LOG_DEBUG
, "IPv%c Network Signature %s %s",
7557 (signature_length
> 0) ? "Set" : "Cleared",
7559 if (signature_length
> 0
7560 && (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7562 char sig_buf
[signature_length
* 3 + 1];
7565 for (i
= 0; i
< signature_length
; i
++) {
7568 snprintf(byte_buf
, sizeof(byte_buf
),
7569 "%02x ", ifstate
->signature
[i
]);
7570 strlcat(sig_buf
, byte_buf
, sizeof(sig_buf
));
7572 my_log(LOG_DEBUG
, "Signature Bytes: %s", sig_buf
);
7580 process_state_differences(nwi_state_t state
, int af
, int sockfd
)
7586 if (af
== AF_INET
) {
7587 count
= state
->ipv4_count
;
7590 count
= state
->ipv6_count
;
7592 for (i
= 0, scan
= nwi_state_ifstate_list(state
, af
);
7593 i
< count
; i
++, scan
++) {
7594 process_ifstate_difference(scan
, af
, sockfd
);
7598 #endif /* MANAGE_IF_SIGNATURE */
7600 #endif /* !TARGET_OS_SIMULATOR */
7603 process_nwi_changes(CFMutableStringRef log_output
,
7604 nwi_state_t changes_state
,
7605 nwi_state_t new_state
,
7606 nwi_state_t old_state
,
7607 boolean_t dns_changed
,
7608 boolean_t dnsinfo_changed
,
7609 CFDictionaryRef old_primary_dns
,
7610 boolean_t proxy_changed
,
7611 CFDictionaryRef old_primary_proxy
,
7612 boolean_t smb_changed
,
7613 CFDictionaryRef old_primary_smb
)
7617 if (changes_state
!= NULL
) {
7618 const sa_family_t af_list
[] = {AF_INET
, AF_INET6
};
7620 #ifdef MANAGE_IF_IOCTL
7621 int sockfd
= inet_dgram_socket();
7622 #endif /* MANAGE_IF_IOCTL */
7624 #ifdef MANAGE_IF_ORDER
7625 if (interface_order_changed(new_state
, old_state
)) {
7626 update_interface_order(new_state
, sockfd
);
7628 #endif /* MANAGE_IF_ORDER */
7630 for (idx
= 0; idx
< countof(af_list
); idx
++) {
7631 int af
= af_list
[idx
];
7632 CFMutableStringRef changes
= NULL
;
7633 CFMutableStringRef primary_str
= NULL
;
7635 #ifdef MANAGE_IF_SIGNATURE
7636 process_state_differences(changes_state
, af
, sockfd
);
7637 #endif /* MANAGE_IF_SIGNATURE */
7638 scan
= nwi_state_get_first_ifstate(changes_state
, af
);
7639 while (scan
!= NULL
) {
7640 const char * changed_str
;
7642 changed_str
= nwi_ifstate_get_diff_str(scan
);
7643 if (changed_str
!= NULL
) {
7645 const char * addr_str
;
7646 char ntopbuf
[INET6_ADDRSTRLEN
];
7648 address
= (void *)nwi_ifstate_get_address(scan
);
7649 addr_str
= inet_ntop(scan
->af
, address
, ntopbuf
,
7651 if (primary_str
== NULL
) {
7652 primary_str
= CFStringCreateMutable(NULL
, 0);
7653 CFStringAppendFormat(primary_str
, NULL
,
7655 nwi_ifstate_get_ifname(scan
),
7656 changed_str
, addr_str
);
7658 if (changes
== NULL
) {
7659 changes
= CFStringCreateMutable(NULL
, 0);
7661 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
7662 nwi_ifstate_get_ifname(scan
));
7663 if (strcmp(changed_str
, "") != 0) {
7664 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
7665 changed_str
, addr_str
);
7669 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
7672 if (primary_str
!= NULL
) {
7673 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
7674 af
== AF_INET
? "v4" : "v6",
7677 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
7678 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
7681 CFStringAppend(log_output
, CFSTR(")"));
7683 my_CFRelease(&primary_str
);
7684 my_CFRelease(&changes
);
7687 #ifdef MANAGE_IF_IOCTL
7691 #endif /* MANAGE_IF_IOCTL */
7694 if (dns_changed
|| dnsinfo_changed
) {
7697 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
7698 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
7699 str
= "*"; // dnsinfo change w/no change to primary
7701 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
7702 } else if (S_primary_dns
!= NULL
) {
7703 CFStringAppend(log_output
, CFSTR(" DNS"));
7706 if (proxy_changed
) {
7709 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
7710 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
7711 } else if (S_primary_proxies
!= NULL
) {
7712 CFStringAppend(log_output
, CFSTR(" Proxy"));
7715 #if !TARGET_OS_IPHONE
7719 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
7720 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
7721 } else if (S_primary_smb
!= NULL
) {
7722 CFStringAppend(log_output
, CFSTR(" SMB"));
7724 #endif // !TARGET_OS_IPHONE
7730 #pragma mark Network changed notification
7732 static dispatch_queue_t
7733 __network_change_queue()
7735 static dispatch_once_t once
;
7736 static dispatch_queue_t q
;
7738 dispatch_once(&once
, ^{
7739 q
= dispatch_queue_create("network change queue", NULL
);
7745 // Note: must run on __network_change_queue()
7747 post_network_change_when_ready()
7751 if (S_network_change_needed
== 0) {
7755 if (!S_network_change_timeout
&&
7756 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
7757 // if we [still] need to wait for the DNS configuration
7758 // or network information changes to be ack'd
7760 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
7761 S_dnsinfo_synced
? "DNS" : "!DNS",
7762 S_nwi_synced
? "nwi" : "!nwi");
7766 // cancel any running timer
7767 if (S_network_change_timer
!= NULL
) {
7768 dispatch_source_cancel(S_network_change_timer
);
7769 dispatch_release(S_network_change_timer
);
7770 S_network_change_timer
= NULL
;
7771 S_network_change_timeout
= FALSE
;
7774 // set (and log?) the post time
7776 struct timeval elapsed
;
7779 (void) gettimeofday(&end
, NULL
);
7780 timersub(&end
, &S_network_change_start
, &elapsed
);
7782 #define QUERY_TIME__FMT "%ld.%6.6d"
7783 #define QUERY_TIME__DIV 1
7786 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
7787 S_network_change_timeout
? "timeout" : "delayed",
7789 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
7790 S_network_change_needed
);
7794 /* We are about to post a network change to everyone, get the agents up to date */
7796 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
7797 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
7798 if (status
!= NOTIFY_STATUS_OK
) {
7800 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%d", status
);
7804 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
7807 #if !TARGET_OS_SIMULATOR
7808 /* Setup or Update config agents */
7809 process_AgentMonitor_DNS();
7810 #endif //!TARGET_OS_SIMULATOR
7811 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
7812 if (status
!= NOTIFY_STATUS_OK
) {
7814 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%d", status
);
7818 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
7820 #if !TARGET_OS_SIMULATOR
7821 /* Setup or Update config agents */
7822 process_AgentMonitor_Proxy();
7823 #endif //!TARGET_OS_SIMULATOR
7824 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
7825 if (status
!= NOTIFY_STATUS_OK
) {
7827 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%d", status
);
7831 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
7832 if (status
!= NOTIFY_STATUS_OK
) {
7834 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%d", status
);
7837 S_network_change_needed
= 0;
7841 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
7843 // Note: must run on __network_change_queue()
7845 post_network_change(uint32_t change
)
7847 if (S_network_change_needed
== 0) {
7848 // set the start time
7849 (void) gettimeofday(&S_network_change_start
, NULL
);
7852 // indicate that we need to post a change for ...
7853 S_network_change_needed
|= change
;
7855 // cancel any running timer
7856 if (S_network_change_timer
!= NULL
) {
7857 dispatch_source_cancel(S_network_change_timer
);
7858 dispatch_release(S_network_change_timer
);
7859 S_network_change_timer
= NULL
;
7860 S_network_change_timeout
= FALSE
;
7863 // if needed, start new timer
7864 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
7865 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
7868 __network_change_queue());
7869 dispatch_source_set_event_handler(S_network_change_timer
, ^{
7870 os_activity_t activity
;
7872 activity
= os_activity_create("posting delayed network change",
7873 OS_ACTIVITY_CURRENT
,
7874 OS_ACTIVITY_FLAG_DEFAULT
);
7875 os_activity_scope(activity
);
7877 S_network_change_timeout
= TRUE
;
7878 post_network_change_when_ready();
7880 os_release(activity
);
7882 dispatch_source_set_timer(S_network_change_timer
,
7883 dispatch_time(DISPATCH_TIME_NOW
,
7884 TRAILING_EDGE_TIMEOUT_NSEC
), // start
7885 DISPATCH_TIME_FOREVER
, // interval
7886 10 * NSEC_PER_MSEC
); // leeway
7887 dispatch_resume(S_network_change_timer
);
7890 post_network_change_when_ready();
7896 #pragma mark Process network (SCDynamicStore) changes
7899 IPMonitorProcessChanges(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
7900 CFArrayRef if_rank_changes
)
7903 uint32_t changes
= 0;
7904 nwi_state_t changes_state
= NULL
;
7905 boolean_t dns_changed
= FALSE
;
7906 boolean_t dnsinfo_changed
= FALSE
;
7907 boolean_t global_ipv4_changed
= FALSE
;
7908 boolean_t global_ipv6_changed
= FALSE
;
7912 CFMutableStringRef network_change_msg
= NULL
;
7914 nwi_state_t old_nwi_state
= NULL
;
7915 CFDictionaryRef old_primary_dns
= NULL
;
7916 CFDictionaryRef old_primary_proxy
= NULL
;
7917 #if !TARGET_OS_IPHONE
7918 CFDictionaryRef old_primary_smb
= NULL
;
7919 #endif // !TARGET_OS_IPHONE
7920 boolean_t proxies_changed
= FALSE
;
7921 boolean_t reachability_changed
= FALSE
;
7922 CFArrayRef service_order
;
7923 CFMutableArrayRef service_changes
= NULL
;
7924 CFDictionaryRef services_info
= NULL
;
7925 #if !TARGET_OS_IPHONE
7926 boolean_t smb_changed
= FALSE
;
7927 #endif // !TARGET_OS_IPHONE
7929 /* populate name/index cache */
7932 if (changed_keys
!= NULL
) {
7933 count
= CFArrayGetCount(changed_keys
);
7934 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7936 "changed keys %@ (%ld)", changed_keys
, count
);
7939 if (if_rank_changes
== NULL
&& count
== 0) {
7943 if (S_primary_dns
!= NULL
) {
7944 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
7945 if (old_primary_dns
!= NULL
) {
7946 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
7950 if (S_primary_proxies
!= NULL
) {
7952 = service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
7953 if (old_primary_proxy
!= NULL
) {
7954 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
7958 #if !TARGET_OS_IPHONE
7959 if (S_primary_smb
!= NULL
) {
7960 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
7961 if (old_primary_smb
!= NULL
) {
7962 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
7965 #endif // !TARGET_OS_IPHONE
7967 keyChangeListInit(&keys
);
7968 service_changes
= CFArrayCreateMutable(NULL
, 0,
7969 &kCFTypeArrayCallBacks
);
7971 for (i
= 0; i
< count
; i
++) {
7972 CFStringRef change
= CFArrayGetValueAtIndex(changed_keys
, i
);
7973 if (CFEqual(change
, S_setup_global_ipv4
)) {
7974 global_ipv4_changed
= TRUE
;
7975 global_ipv6_changed
= TRUE
;
7977 else if (CFEqual(change
, S_multicast_resolvers
)) {
7978 dnsinfo_changed
= TRUE
;
7980 else if (CFEqual(change
, S_private_resolvers
)) {
7981 dnsinfo_changed
= TRUE
;
7983 #if !TARGET_OS_IPHONE
7984 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
7985 dnsinfo_changed
= TRUE
;
7987 #endif /* !TARGET_OS_IPHONE */
7988 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
7989 CFStringRef serviceID
;
7991 serviceID
= parse_component(change
, S_state_service_prefix
);
7993 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
7994 CFRelease(serviceID
);
7997 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
8000 CFStringRef serviceID
= parse_component(change
,
8001 S_setup_service_prefix
);
8003 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
8004 CFRelease(serviceID
);
8007 for (j
= 0; j
< countof(transientInterfaceEntityNames
); j
++) {
8008 if (CFStringHasSuffix(change
,
8009 *transientInterfaceEntityNames
[j
])) {
8010 reachability_changed
= TRUE
;
8015 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
8016 reachability_changed
= TRUE
;
8021 /* determine which serviceIDs are impacted by the interface rank changes */
8022 if (if_rank_changes
!= NULL
) {
8023 n
= CFArrayGetCount(if_rank_changes
);
8024 for (i
= 0; i
< n
; i
++) {
8025 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
8027 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8028 my_log(LOG_DEBUG
, "Interface rank changed %@", ifname
);
8030 append_serviceIDs_for_interface(service_changes
, ifname
);
8034 /* grab a snapshot of everything we need */
8035 services_info
= services_info_copy(session
, service_changes
);
8036 service_order
= service_order_get(services_info
);
8037 if (service_order
!= NULL
) {
8038 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
8039 my_log(LOG_DEBUG
, "service_order %@ ", service_order
);
8043 n
= CFArrayGetCount(service_changes
);
8044 for (i
= 0; i
< n
; i
++) {
8046 CFStringRef serviceID
;
8048 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
8049 changes
= service_changed(services_info
, serviceID
);
8050 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
8051 /* if __Service__ (e.g. PrimaryRank) changed */
8052 global_ipv4_changed
= TRUE
;
8053 global_ipv6_changed
= TRUE
;
8056 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
8057 global_ipv4_changed
= TRUE
;
8058 dnsinfo_changed
= TRUE
;
8059 proxies_changed
= TRUE
;
8061 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
8062 global_ipv6_changed
= TRUE
;
8063 dnsinfo_changed
= TRUE
;
8064 proxies_changed
= TRUE
;
8067 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
8068 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
8071 dnsinfo_changed
= TRUE
;
8073 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
8074 proxies_changed
= TRUE
;
8076 #if !TARGET_OS_IPHONE
8077 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
8078 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
8083 if ((changes
& (1 << kEntityTypeTransientStatus
)) != 0
8084 && (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
8085 || service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
)) {
8086 dnsinfo_changed
= TRUE
;
8090 /* ensure S_nwi_state can hold as many services as we have currently */
8091 n_services
= (int)CFDictionaryGetCount(S_service_state_dict
);
8092 old_nwi_state
= nwi_state_make_copy(S_nwi_state
);
8093 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
8095 if (global_ipv4_changed
) {
8096 if (S_ipv4_results
!= NULL
) {
8097 ElectionResultsRelease(S_ipv4_results
);
8100 = ElectionResultsCopy(AF_INET
, service_order
);
8101 ElectionResultsLog(LOG_INFO
, S_ipv4_results
, "IPv4");
8103 if (global_ipv6_changed
) {
8104 if (S_ipv6_results
!= NULL
) {
8105 ElectionResultsRelease(S_ipv6_results
);
8108 = ElectionResultsCopy(AF_INET6
, service_order
);
8109 ElectionResultsLog(LOG_INFO
, S_ipv6_results
, "IPv6");
8111 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
8112 CFStringRef new_primary
;
8113 CFStringRef new_primary_dns
= NULL
;
8114 CFStringRef new_primary_proxies
= NULL
;
8115 #if !TARGET_OS_IPHONE
8116 CFStringRef new_primary_smb
= NULL
;
8117 #endif /* !TARGET_OS_IPHONE */
8118 RouteListUnion new_routelist
;
8120 if (S_nwi_state
!= NULL
) {
8121 nwi_state_clear(S_nwi_state
, AF_INET
);
8122 nwi_state_clear(S_nwi_state
, AF_INET6
);
8126 my_log(LOG_DEBUG
, "electing IPv4 primary");
8127 new_routelist
.ptr
= NULL
;
8128 new_primary
= ElectionResultsCopyPrimary(S_ipv4_results
,
8130 S_nwi_state
, AF_INET
,
8131 &new_routelist
.common
,
8133 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
8134 update_ipv4(S_primary_ipv4
, new_routelist
.v4
, &keys
);
8135 my_CFRelease(&new_primary
);
8138 my_log(LOG_DEBUG
, "electing IPv6 primary");
8139 new_routelist
.ptr
= NULL
;
8140 new_primary
= ElectionResultsCopyPrimary(S_ipv6_results
,
8142 S_nwi_state
, AF_INET6
,
8143 &new_routelist
.common
,
8145 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
8146 update_ipv6(S_primary_ipv6
, new_routelist
.v6
, &keys
);
8147 my_CFRelease(&new_primary
);
8149 nwi_state_finalize(S_nwi_state
);
8151 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
8152 /* decide between IPv4 and IPv6 */
8153 if (rank_service_entity(S_ipv4_service_rank_dict
,
8154 S_primary_ipv4
, kSCEntNetDNS
)
8155 <= rank_service_entity(S_ipv6_service_rank_dict
,
8156 S_primary_ipv6
, kSCEntNetDNS
)) {
8157 new_primary_dns
= S_primary_ipv4
;
8160 new_primary_dns
= S_primary_ipv6
;
8162 if (rank_service_entity(S_ipv4_service_rank_dict
,
8163 S_primary_ipv4
, kSCEntNetProxies
)
8164 <= rank_service_entity(S_ipv6_service_rank_dict
,
8165 S_primary_ipv6
, kSCEntNetProxies
)) {
8166 new_primary_proxies
= S_primary_ipv4
;
8169 new_primary_proxies
= S_primary_ipv6
;
8171 #if !TARGET_OS_IPHONE
8172 if (rank_service_entity(S_ipv4_service_rank_dict
,
8173 S_primary_ipv4
, kSCEntNetSMB
)
8174 <= rank_service_entity(S_ipv6_service_rank_dict
,
8175 S_primary_ipv6
, kSCEntNetSMB
)) {
8176 new_primary_smb
= S_primary_ipv4
;
8179 new_primary_smb
= S_primary_ipv6
;
8181 #endif /* !TARGET_OS_IPHONE */
8184 else if (S_primary_ipv6
!= NULL
) {
8185 new_primary_dns
= S_primary_ipv6
;
8186 new_primary_proxies
= S_primary_ipv6
;
8187 #if !TARGET_OS_IPHONE
8188 new_primary_smb
= S_primary_ipv6
;
8189 #endif /* !TARGET_OS_IPHONE */
8191 else if (S_primary_ipv4
!= NULL
) {
8192 new_primary_dns
= S_primary_ipv4
;
8193 new_primary_proxies
= S_primary_ipv4
;
8194 #if !TARGET_OS_IPHONE
8195 new_primary_smb
= S_primary_ipv4
;
8196 #endif /* !TARGET_OS_IPHONE */
8199 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
8201 dnsinfo_changed
= TRUE
;
8203 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
,
8205 proxies_changed
= TRUE
;
8207 #if !TARGET_OS_IPHONE
8208 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
8211 #endif /* !TARGET_OS_IPHONE */
8214 if (!proxies_changed
&& dnsinfo_changed
8215 && ((G_supplemental_proxies_follow_dns
!= NULL
)
8216 && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
8217 proxies_changed
= TRUE
;
8220 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
8222 if (global_ipv4_changed
|| global_ipv6_changed
8223 || dnsinfo_changed
|| reachability_changed
) {
8224 if (S_nwi_state
!= NULL
) {
8225 S_nwi_state
->generation_count
= mach_absolute_time();
8226 if (global_ipv4_changed
|| global_ipv6_changed
8227 || reachability_changed
) {
8228 SCNetworkReachabilityFlags reach_flags_v4
= 0;
8229 SCNetworkReachabilityFlags reach_flags_v6
= 0;
8231 GetReachabilityFlagsFromTransientServices(services_info
,
8235 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
,
8239 /* Update the per-interface generation count */
8240 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
,
8244 if (update_nwi(S_nwi_state
)) {
8245 changes
|= NETWORK_CHANGE_NET
;
8248 * the DNS configuration includes per-resolver configuration
8249 * reachability flags that are based on the nwi state. Let's
8250 * make sure that we check for changes
8252 dnsinfo_changed
= TRUE
;
8256 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
8257 changes
|= NETWORK_CHANGE_DNS
;
8258 dnsinfo_changed
= TRUE
;
8260 dns_changed
= FALSE
;
8263 if (dnsinfo_changed
) {
8264 if (update_dnsinfo(services_info
, S_primary_dns
,
8265 &keys
, service_order
)) {
8266 changes
|= NETWORK_CHANGE_DNS
;
8268 dnsinfo_changed
= FALSE
;
8271 if (proxies_changed
) {
8272 // if proxy change OR supplemental Proxies follow supplemental DNS
8273 if (update_proxies(services_info
, S_primary_proxies
,
8274 &keys
, service_order
)) {
8275 changes
|= NETWORK_CHANGE_PROXY
;
8277 proxies_changed
= FALSE
;
8280 #if !TARGET_OS_IPHONE
8282 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
8283 changes
|= NETWORK_CHANGE_SMB
;
8285 smb_changed
= FALSE
;
8288 #endif /* !TARGET_OS_IPHONE */
8289 my_CFRelease(&service_changes
);
8290 my_CFRelease(&services_info
);
8293 network_change_msg
= CFStringCreateMutable(NULL
, 0);
8294 process_nwi_changes(network_change_msg
,
8303 #if !TARGET_OS_IPHONE
8306 #else // !TARGET_OS_IPHONE
8307 FALSE
, // smb_changed
8308 NULL
// old_primary_smb
8309 #endif // !TARGET_OS_IPHONE
8313 keyChangeListApplyToStore(&keys
, session
);
8314 my_CFRelease(&old_primary_dns
);
8315 my_CFRelease(&old_primary_proxy
);
8316 #if !TARGET_OS_IPHONE
8317 my_CFRelease(&old_primary_smb
);
8318 #endif // !TARGET_OS_IPHONE
8321 dispatch_async(__network_change_queue(), ^{
8322 post_network_change(changes
);
8326 if ((network_change_msg
!= NULL
)
8327 && (CFStringGetLength(network_change_msg
) != 0)) {
8328 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
8329 } else if (keyChangeListActive(&keys
)) {
8330 my_log(LOG_NOTICE
, "network changed");
8332 my_log(LOG_INFO
, "network event w/no changes");
8335 my_CFRelease(&network_change_msg
);
8337 if (changes_state
!= NULL
) {
8338 nwi_state_free(changes_state
);
8340 if (old_nwi_state
!= NULL
) {
8341 nwi_state_free(old_nwi_state
);
8343 keyChangeListFree(&keys
);
8345 /* release the name/index cache */
8346 my_if_freenameindex();
8352 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8355 IPMonitorProcessChanges(session
, changed_keys
, NULL
);
8359 #if !TARGET_OS_IPHONE
8360 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_mcx
8362 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_global
8368 static dispatch_queue_t proxy_cb_queue
;
8370 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
8371 _scprefs_observer_watch(PROXY_GLOBAL_OBSERVER_TYPE
,
8372 "com.apple.SystemConfiguration.plist",
8375 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
8376 #if !TARGET_OS_SIMULATOR
8377 /* Setup or Update config agents */
8378 process_AgentMonitor_Proxy();
8379 #endif //!TARGET_OS_SIMULATOR
8380 (void)notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8381 my_log(LOG_INFO
, "Notifying:\n%@",
8382 S_state_global_proxies
);
8387 #include "IPMonitorControlPrefs.h"
8390 prefs_changed(__unused SCPreferencesRef prefs
)
8392 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
8393 S_IPMonitor_debug
= kDebugFlagDefault
;
8394 S_IPMonitor_verbose
= TRUE
;
8395 SCLoggerSetFlags(S_IPMonitor_logger
, kSCLoggerFlagsFile
| kSCLoggerFlagsDefault
);
8396 my_log(LOG_DEBUG
, "Setting logging verbose mode on");
8398 my_log(LOG_DEBUG
, "Setting logging verbose mode off");
8399 S_IPMonitor_debug
= 0;
8400 S_IPMonitor_verbose
= FALSE
;
8401 SCLoggerSetFlags(S_IPMonitor_logger
, kSCLoggerFlagsDefault
);
8406 #define LOGGER_ID CFSTR("com.apple.networking.IPMonitor")
8410 S_IPMonitor_logger
= SCLoggerCreate(LOGGER_ID
);
8416 #if !TARGET_OS_SIMULATOR
8427 struct rt_msghdr
* rtm
;
8428 struct sockaddr_in
*sin
;
8434 mib
[4] = NET_RT_FLAGS
;
8435 mib
[5] = RTF_STATIC
| RTF_DYNAMIC
;
8436 for (i
= 0; i
< 3; i
++) {
8437 if (sysctl(mib
, N_MIB
, NULL
, &needed
, NULL
, 0) < 0) {
8440 if ((buf
= malloc(needed
)) == NULL
) {
8443 if (sysctl(mib
, N_MIB
, buf
, &needed
, NULL
, 0) >= 0) {
8453 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
8456 /* ALIGN: assume kernel provides necessary alignment */
8457 rtm
= (struct rt_msghdr
*)(void *)next
;
8458 sin
= (struct sockaddr_in
*)(rtm
+ 1);
8460 addr
= ntohl(sin
->sin_addr
.s_addr
);
8461 if (IN_LOOPBACK(addr
)) {
8463 "flush_routes: ignoring loopback route");
8466 if (IN_LOCAL_GROUP(addr
)) {
8468 "flush_routes: ignoring multicast route");
8471 rtm
->rtm_type
= RTM_DELETE
;
8472 rtm
->rtm_seq
= ++rtm_seq
;
8473 if (write(s
, rtm
, rtm
->rtm_msglen
) < 0) {
8475 "flush_routes: removing route for "
8476 IP_FORMAT
" failed: %s",
8477 IP_LIST(&sin
->sin_addr
),
8482 "flush_routes: removed route for " IP_FORMAT
,
8483 IP_LIST(&sin
->sin_addr
));
8491 flush_inet_routes(void)
8495 s
= open_routing_socket();
8502 #else /* !TARGET_OS_SIMULATOR */
8505 flush_inet_routes(void)
8509 #endif /* !TARGET_OS_SIMULATOR */
8516 CFMutableArrayRef keys
= NULL
;
8517 CFStringRef pattern
;
8518 CFMutableArrayRef patterns
= NULL
;
8519 CFRunLoopSourceRef rls
= NULL
;
8521 if (S_is_network_boot() != 0) {
8526 flush_inet_routes();
8529 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
8530 IPMonitorNotify
, NULL
);
8531 if (S_session
== NULL
) {
8533 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8534 SCErrorString(SCError()));
8538 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8539 kSCDynamicStoreDomainState
,
8542 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8543 kSCDynamicStoreDomainState
,
8546 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8547 kSCDynamicStoreDomainState
,
8549 S_state_global_proxies
8550 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8551 kSCDynamicStoreDomainState
,
8553 #if !TARGET_OS_IPHONE
8555 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8556 kSCDynamicStoreDomainState
,
8558 #endif /* !TARGET_OS_IPHONE */
8560 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8561 kSCDynamicStoreDomainSetup
,
8563 S_state_service_prefix
8564 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8565 kSCDynamicStoreDomainState
,
8568 S_setup_service_prefix
8569 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8570 kSCDynamicStoreDomainSetup
,
8573 S_service_state_dict
8574 = CFDictionaryCreateMutable(NULL
, 0,
8575 &kCFTypeDictionaryKeyCallBacks
,
8576 &kCFTypeDictionaryValueCallBacks
);
8578 S_ipv4_service_rank_dict
8579 = CFDictionaryCreateMutable(NULL
, 0,
8580 &kCFTypeDictionaryKeyCallBacks
,
8581 &kCFTypeDictionaryValueCallBacks
);
8583 S_ipv6_service_rank_dict
8584 = CFDictionaryCreateMutable(NULL
, 0,
8585 &kCFTypeDictionaryKeyCallBacks
,
8586 &kCFTypeDictionaryValueCallBacks
);
8588 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8589 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8591 /* register for State: and Setup: per-service notifications */
8592 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
8594 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
8595 CFArrayAppendValue(patterns
, pattern
);
8598 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
8599 CFArrayAppendValue(patterns
, pattern
);
8602 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
8603 CFArrayAppendValue(patterns
, pattern
);
8606 /* register for State: per-service PPP/VPN/IPSec status notifications */
8607 add_transient_status_keys(kSCCompAnyRegex
, patterns
);
8609 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
8610 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
8612 /* add notifier for multicast DNS configuration (Bonjour/.local) */
8613 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8614 kSCDynamicStoreDomainState
,
8616 CFSTR(kDNSServiceCompMulticastDNS
));
8617 CFArrayAppendValue(keys
, S_multicast_resolvers
);
8619 /* add notifier for private DNS configuration (Back to My Mac) */
8620 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8621 kSCDynamicStoreDomainState
,
8623 CFSTR(kDNSServiceCompPrivateDNS
));
8624 CFArrayAppendValue(keys
, S_private_resolvers
);
8626 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
8628 "SCDynamicStoreSetNotificationKeys() failed: %s",
8629 SCErrorString(SCError()));
8633 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
8636 "SCDynamicStoreCreateRunLoopSource() failed: %s",
8637 SCErrorString(SCError()));
8641 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
8644 /* initialize dns configuration */
8645 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
8646 #if !TARGET_OS_IPHONE
8648 #endif /* !TARGET_OS_IPHONE */
8649 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
8651 #if !TARGET_OS_IPHONE
8652 /* initialize SMB configuration */
8653 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
8654 #endif /* !TARGET_OS_IPHONE */
8659 my_CFRelease(&keys
);
8660 my_CFRelease(&patterns
);
8668 /* initialize multicast route */
8669 update_ipv4(NULL
, NULL
, NULL
);
8671 #if !TARGET_OS_SIMULATOR
8672 process_AgentMonitor();
8673 #endif // !TARGET_OS_SIMULATOR
8679 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
8683 boolean_t ret
= def
;
8685 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
8687 ret
= CFBooleanGetValue(b
);
8692 #if !TARGET_OS_SIMULATOR
8693 #include "IPMonitorControlServer.h"
8696 InterfaceRankChanged(void * info
)
8698 os_activity_t activity
;
8699 CFDictionaryRef assertions
= NULL
;
8702 activity
= os_activity_create("processing IPMonitor [rank] change",
8703 OS_ACTIVITY_CURRENT
,
8704 OS_ACTIVITY_FLAG_DEFAULT
);
8705 os_activity_scope(activity
);
8707 changes
= IPMonitorControlServerCopyInterfaceRankInformation(&assertions
);
8708 if (S_if_rank_dict
!= NULL
) {
8709 CFRelease(S_if_rank_dict
);
8711 S_if_rank_dict
= assertions
;
8712 if (changes
!= NULL
) {
8713 IPMonitorProcessChanges(S_session
, NULL
, changes
);
8717 os_release(activity
);
8723 StartIPMonitorControlServer(void)
8725 CFRunLoopSourceContext context
;
8726 CFRunLoopSourceRef rls
;
8728 bzero(&context
, sizeof(context
));
8729 context
.perform
= InterfaceRankChanged
;
8730 rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
8731 if (!IPMonitorControlServerStart(CFRunLoopGetCurrent(),
8733 &S_bundle_logging_verbose
)) {
8734 my_log(LOG_ERR
, "IPMonitorControlServerStart failed");
8737 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
8738 kCFRunLoopDefaultMode
);
8744 #endif /* !TARGET_OS_SIMULATOR */
8748 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
8750 CFDictionaryRef info_dict
;
8752 info_dict
= CFBundleGetInfoDictionary(bundle
);
8754 if (info_dict
!= NULL
) {
8756 = S_get_plist_boolean(info_dict
,
8757 CFSTR("AppendStateArrayToSetupArray"),
8760 if (bundleVerbose
) {
8761 S_IPMonitor_debug
= kDebugFlagDefault
;
8762 S_bundle_logging_verbose
= TRUE
;
8763 S_IPMonitor_verbose
= TRUE
;
8768 /* register to receive changes to verbose and read the initial setting */
8769 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
8770 prefs_changed(NULL
);
8772 load_DNSConfiguration(bundle
, // bundle
8773 ^(Boolean inSync
) { // syncHandler
8774 dispatch_async(__network_change_queue(), ^{
8775 S_dnsinfo_synced
= inSync
;
8778 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
8779 // all of the DNS service ack's should result
8780 // in a [new] network change being posted
8781 post_network_change(NETWORK_CHANGE_DNS
);
8783 post_network_change_when_ready();
8788 load_NetworkInformation(bundle
, // bundle
8789 ^(Boolean inSync
) { // syncHandler
8790 dispatch_async(__network_change_queue(), ^{
8791 S_nwi_synced
= inSync
;
8792 post_network_change_when_ready();
8795 #if !TARGET_OS_SIMULATOR
8796 StartIPMonitorControlServer();
8797 #endif /* !TARGET_OS_IPHONE */
8799 dns_configuration_init(bundle
);
8801 proxy_configuration_init(bundle
);
8805 #if !TARGET_OS_IPHONE
8806 if (S_session
!= NULL
) {
8807 dns_configuration_monitor(S_session
, IPMonitorNotify
);
8809 #endif /* !TARGET_OS_IPHONE */
8811 #if !TARGET_OS_SIMULATOR
8812 load_hostname(TRUE
);
8813 #endif /* !TARGET_OS_SIMULATOR */
8815 #if !TARGET_OS_IPHONE
8816 load_smb_configuration(TRUE
);
8817 #endif /* !TARGET_OS_IPHONE */
8824 #pragma mark Standalone test code
8827 #ifdef TEST_IPMONITOR
8830 main(int argc
, char **argv
)
8834 S_IPMonitor_debug
= kDebugFlag1
;
8836 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
8839 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
8841 S_IPMonitor_debug
= kDebugFlag1
;
8847 #endif /* TEST_IPMONITOR */
8849 #ifdef TEST_ROUTELIST
8854 const char * gateway
;
8855 const char * ifname
;
8860 #ifdef TEST_IPV4_ROUTELIST
8866 const char * router
;
8867 const char * ifname
;
8869 const CFStringRef
* primary_rank
;
8870 struct route
* additional_routes
;
8871 int additional_routes_count
;
8872 struct route
* excluded_routes
;
8873 int excluded_routes_count
;
8874 } IPv4ServiceContents
;
8876 typedef const IPv4ServiceContents
* IPv4ServiceContentsRef
;
8878 struct route loop_routelist
[] = {
8879 { "1.1.1.1", 32, "1.1.1.2", NULL
},
8880 { "1.1.1.2", 32, "1.1.1.3", NULL
},
8881 { "1.1.1.3", 32, "1.1.1.4", NULL
},
8882 { "1.1.1.4", 32, "1.1.1.5", NULL
},
8883 { "1.1.1.5", 32, "1.1.1.6", NULL
},
8884 { "1.1.1.6", 32, "1.1.1.7", NULL
},
8885 { "1.1.1.7", 32, "1.1.1.8", NULL
},
8886 { "1.1.1.8", 32, "1.1.1.9", NULL
},
8887 { "1.1.1.9", 32, "1.1.1.10", NULL
},
8888 { "1.1.1.10", 32, "1.1.1.11", NULL
},
8889 { "1.1.1.11", 32, "1.1.1.1", NULL
},
8892 struct route vpn_routelist
[] = {
8893 { "10.1.3.0", 24, "17.153.46.24", NULL
},
8894 { "10.1.4.0", 24, "17.153.46.24", NULL
},
8895 { "10.1.5.0", 24, "17.153.46.24", NULL
},
8896 { "10.1.6.0", 24, "17.153.46.24", NULL
},
8897 { "10.1.7.0", 24, "17.153.46.24", NULL
},
8898 { "10.16.0.0", 12, "17.153.46.24", NULL
},
8899 { "10.45.0.0", 16, "17.153.46.24", NULL
},
8900 { "10.53.0.0", 16, "17.153.46.24", NULL
},
8901 { "10.70.0.0", 15, "17.153.46.24", NULL
},
8902 { "10.74.0.0", 15, "17.153.46.24", NULL
},
8903 { "10.90.0.0", 15, "17.153.46.24", NULL
},
8904 { "10.91.0.0", 16, "17.153.46.24", NULL
},
8905 { "10.100.0.0", 16, "17.153.46.24", NULL
},
8906 { "10.113.0.0", 16, "17.153.46.24", NULL
},
8907 { "10.128.0.0", 9, "17.153.46.24", NULL
},
8908 { "17.0.0.0", 9, "17.153.46.24", NULL
},
8909 { "17.34.0.0", 16, "17.153.46.24", NULL
},
8910 { "17.112.156.53", 32, "17.153.46.24", NULL
},
8911 { "17.128.0.0", 10, "17.153.46.24", NULL
},
8912 { "17.149.0.121", 32, "17.153.46.24", NULL
},
8913 { "17.149.7.200", 32, "17.153.46.24", NULL
},
8914 { "17.153.46.24", 32, "17.153.46.24", NULL
},
8915 { "17.192.0.0", 12, "17.153.46.24", NULL
},
8916 { "17.208.0.0", 15, "17.153.46.24", NULL
},
8917 { "17.211.0.0", 16, "17.153.46.24", NULL
},
8918 { "17.212.0.0", 14, "17.153.46.24", NULL
},
8919 { "17.216.0.0", 13, "17.153.46.24", NULL
},
8920 { "17.224.0.0", 12, "17.153.46.24", NULL
},
8921 { "17.240.0.0", 16, "17.153.46.24", NULL
},
8922 { "17.241.0.0", 16, "17.153.46.24", NULL
},
8923 { "17.248.0.0", 14, "17.153.46.24", NULL
},
8924 { "17.251.104.200", 32, "17.153.46.24", NULL
},
8925 { "17.252.0.0", 16, "17.153.46.24", NULL
},
8926 { "17.253.0.0", 16, "17.153.46.24", NULL
},
8927 { "17.254.0.0", 16, "17.153.46.24", NULL
},
8928 { "17.255.0.0", 16, "17.153.46.24", NULL
},
8929 { "151.193.141.0", 27, "17.153.46.24", NULL
},
8930 { "172.16.2.0", 24, "17.153.46.24", NULL
},
8931 { "192.35.50.0", 24, "17.153.46.24", NULL
},
8932 { "204.179.20.0", 24, "17.153.46.24", NULL
},
8933 { "206.112.116.0", 24, "17.153.46.24", NULL
},
8936 struct route vpn_routelist_ext
[] = {
8937 { "17.151.63.82", 32, "10.0.0.1", "en0" },
8938 { "17.151.63.81", 32, "17.151.63.81", "en0" },
8939 { "17.151.63.80", 32, NULL
, NULL
},
8940 { "17.1.0.0", 16, NULL
, NULL
},
8941 { "17.2.0.0", 24, NULL
, NULL
},
8942 { "10.0.0.0", 24, NULL
, NULL
},
8946 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
8948 const IPv4ServiceContents en0_10
= {
8949 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, NULL
, NULL
, 0, NULL
, 0
8952 const IPv4ServiceContents en0_15
= {
8953 "10.0.0.19", 24, NULL
, "10.0.0.1", "en0", 15, NULL
, NULL
, 0, NULL
, 0
8956 const IPv4ServiceContents en0_30
= {
8957 "10.0.0.11", 24, NULL
, "10.0.0.1", "en0", 30, NULL
, NULL
, 0, NULL
, 0
8960 const IPv4ServiceContents en0_40
= {
8961 "10.0.0.12", 24, NULL
, "10.0.0.1", "en0", 40, NULL
, NULL
, 0, NULL
, 0
8964 const IPv4ServiceContents en0_50
= {
8965 "10.0.0.13", 24, NULL
, "10.0.0.1", "en0", 50, NULL
, NULL
, 0, NULL
, 0
8968 const IPv4ServiceContents en0_110
= {
8969 "192.168.2.10", 24, NULL
, "192.168.2.1", "en0", 110, NULL
, NULL
, 0, NULL
, 0
8972 const IPv4ServiceContents en0_1
= {
8973 "17.202.40.191", 22, NULL
, "17.202.20.1", "en0", 1, NULL
, NULL
, 0, NULL
, 0
8976 const IPv4ServiceContents en1_20
= {
8977 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, NULL
, NULL
, 0, NULL
, 0
8980 const IPv4ServiceContents en1_2
= {
8981 "17.202.42.24", 22, NULL
, "17.202.20.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
8984 const IPv4ServiceContents en1_125
= {
8985 "192.168.2.20", 24, NULL
, "192.168.2.1", "en1", 125, NULL
, NULL
, 0, NULL
, 0
8988 const IPv4ServiceContents fw0_25
= {
8989 "192.168.2.30", 24, NULL
, "192.168.2.1", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
8992 const IPv4ServiceContents fw0_21
= {
8993 "192.168.3.30", 24, NULL
, "192.168.3.1", "fw0", 21, NULL
, NULL
, 0, NULL
, 0
8996 const IPv4ServiceContents ppp0_0_1
= {
8997 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
, NULL
, 0, NULL
, 0
9000 const IPv4ServiceContents utun0
= {
9001 "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
)
9004 const IPv4ServiceContents en0_test6
= {
9005 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 2, NULL
, NULL
, 0, NULL
, 0
9008 const IPv4ServiceContents en1_test6
= {
9009 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 3, NULL
, NULL
, 0, NULL
, 0
9012 const IPv4ServiceContents en2_test6
= {
9013 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9016 const IPv4ServiceContents en0_test7
= {
9017 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 3, NULL
, NULL
, 0, NULL
, 0
9020 const IPv4ServiceContents en1_test7
= {
9021 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
9024 const IPv4ServiceContents en2_test7
= {
9025 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
9028 const IPv4ServiceContents fw0_test6_and_7
= {
9029 "169.254.11.33", 16, NULL
, NULL
, "fw0", 0x0ffffff, NULL
, NULL
, 0, NULL
, 0
9032 const IPv4ServiceContents en0_10_last
= {
9033 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
, NULL
, 0, NULL
, 0
9036 const IPv4ServiceContents en0_10_never
= {
9037 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9040 const IPv4ServiceContents en1_20_first
= {
9041 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
, NULL
, 0, NULL
, 0
9044 const IPv4ServiceContents en1_20_never
= {
9045 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9048 const IPv4ServiceContents en1_20_other_never
= {
9049 "192.168.2.50", 24, NULL
, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
9052 const IPv4ServiceContents en0_linklocal
= {
9053 "169.254.22.44", 16, NULL
, NULL
, "en0", 0xfffff, NULL
, NULL
, 0, NULL
, 0
9056 const IPv4ServiceContents en0_route_loop
= {
9057 "192.168.130.16", 24, NULL
, "192.168.130.1", "en0", 2, NULL
, loop_routelist
, countof(loop_routelist
), NULL
, 0
9062 IPv4ServiceContentsRef test
[];
9063 } IPv4RouteTest
, * IPv4RouteTestRef
;
9065 static IPv4RouteTest test1
= {
9079 static IPv4RouteTest test2
= {
9092 static IPv4RouteTest test3
= {
9109 static IPv4RouteTest test4
= {
9121 static IPv4RouteTest test5
= {
9134 static IPv4RouteTest test6
= {
9145 static IPv4RouteTest test7
= {
9156 static IPv4RouteTest test8
= {
9165 static IPv4RouteTest test9
= {
9175 static IPv4RouteTest test10
= {
9185 static IPv4RouteTest test11
= {
9195 static IPv4RouteTest test12
= {
9204 static IPv4RouteTest test13
= {
9213 static IPv4RouteTest test14
= {
9221 static IPv4RouteTest test15
= {
9229 static IPv4RouteTest test16
= {
9238 static IPv4RouteTest test17
= {
9242 &en1_20_other_never
,
9247 static IPv4RouteTest test18
= {
9255 static IPv4RouteTestRef ipv4_tests
[] = {
9278 ipv4_prefix_length_is_valid(int prefix_length
)
9280 if (prefix_length
< 0 || prefix_length
> IPV4_ROUTE_ALL_BITS_SET
) {
9287 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9290 CFStringRef prop_val
;
9295 prop_val
= CFStringCreateWithCString(NULL
,
9297 kCFStringEncodingASCII
);
9298 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9299 CFRelease(prop_val
);
9304 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9308 CFStringRef prop_val
;
9313 prop_val
= CFStringCreateWithCString(NULL
,
9315 kCFStringEncodingASCII
);
9316 array
= CFArrayCreate(NULL
,
9317 (const void **)&prop_val
, 1,
9318 &kCFTypeArrayCallBacks
);
9319 CFRelease(prop_val
);
9320 CFDictionarySetValue(dict
, prop_name
, array
);
9326 dict_add_ip(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9331 str
= my_CFStringCreateWithInAddr(ip
);
9332 CFDictionarySetValue(dict
, prop_name
, str
);
9338 dict_add_ip_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9344 str
= my_CFStringCreateWithInAddr(ip
);
9345 array
= CFArrayCreate(NULL
,
9346 (const void **)&str
, 1,
9347 &kCFTypeArrayCallBacks
);
9349 CFDictionarySetValue(dict
, prop_name
, array
);
9355 dict_insert_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9356 struct route
* routes
, int routes_count
)
9359 CFMutableArrayRef route_list
;
9360 struct route
* scan
;
9362 if (routes
== NULL
|| routes_count
== 0) {
9365 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9366 &kCFTypeArrayCallBacks
);
9367 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9368 struct in_addr mask
;
9369 CFMutableDictionaryRef route_dict
;
9372 = CFDictionaryCreateMutable(NULL
, 0,
9373 &kCFTypeDictionaryKeyCallBacks
,
9374 &kCFTypeDictionaryValueCallBacks
);
9375 dict_add_string(route_dict
, kSCPropNetIPv4RouteDestinationAddress
,
9377 if (ipv4_prefix_length_is_valid(scan
->prefix_length
)) {
9378 mask
.s_addr
= htonl(prefix_to_mask32(scan
->prefix_length
));
9379 dict_add_ip(route_dict
, kSCPropNetIPv4RouteSubnetMask
, mask
);
9381 dict_add_string(route_dict
, kSCPropNetIPv4RouteGatewayAddress
,
9383 dict_add_string(route_dict
, kSCPropNetIPv4RouteInterfaceName
,
9385 CFArrayAppendValue(route_list
, route_dict
);
9386 CFRelease(route_dict
);
9388 CFDictionarySetValue(dict
, prop_name
, route_list
);
9389 CFRelease(route_list
);
9393 static CFDictionaryRef
9394 make_IPv4_dict(IPv4ServiceContentsRef t
)
9396 CFMutableDictionaryRef dict
;
9398 dict
= CFDictionaryCreateMutable(NULL
, 0,
9399 &kCFTypeDictionaryKeyCallBacks
,
9400 &kCFTypeDictionaryValueCallBacks
);
9401 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
9402 if (ipv4_prefix_length_is_valid(t
->prefix_length
)) {
9403 struct in_addr mask
;
9405 mask
.s_addr
= htonl(prefix_to_mask32(t
->prefix_length
));
9406 dict_add_ip_as_array(dict
, kSCPropNetIPv4SubnetMasks
, mask
);
9408 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
9409 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
9410 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9411 dict_add_string(dict
, kSCPropConfirmedInterfaceName
, t
->ifname
);
9412 dict_insert_routes(dict
, kSCPropNetIPv4AdditionalRoutes
,
9413 t
->additional_routes
, t
->additional_routes_count
);
9414 dict_insert_routes(dict
, kSCPropNetIPv4ExcludedRoutes
,
9415 t
->excluded_routes
, t
->excluded_routes_count
);
9420 kDirectionForwards
= 0,
9421 kDirectionBackwards
= 1
9425 kLogRouteDisabled
= 0,
9426 kLogRouteEnabled
= 1
9429 static IPv4RouteListRef
9430 make_IPv4RouteList_for_test(IPv4RouteListRef list
,
9431 IPv4ServiceContentsRef test
,
9434 CFDictionaryRef dict
;
9437 Rank rank_assertion
= kRankAssertionDefault
;
9438 CFNumberRef rank_assertion_cf
= NULL
;
9439 Boolean rank_assertion_is_set
= FALSE
;
9440 IPv4RouteListRef ret
= NULL
;
9441 IPV4_ROUTES_BUF_DECL(routes
);
9443 dict
= make_IPv4_dict(test
);
9445 fprintf(stderr
, "make_IPv4_dict failed\n");
9448 if (test
->primary_rank
!= NULL
) {
9450 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9451 &rank_assertion_is_set
);
9452 if (rank_assertion_is_set
) {
9454 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9457 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
9459 my_CFRelease(&rank_assertion_cf
);
9461 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
9465 if (rank_assertion
== kRankAssertionScoped
) {
9466 rank_assertion
= kRankAssertionNever
;
9468 rank
= RankMake(test
->rank
, rank_assertion
);
9469 if (log_it
== kLogRouteEnabled
9470 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9473 descr
= IPv4RouteListCopyDescription(r
);
9474 SCPrint(TRUE
, stdout
, CFSTR("Adding %@"), descr
);
9477 ret
= IPv4RouteListAddRouteList(list
, 1, r
, rank
);
9485 static IPv4RouteListRef
9486 make_IPv4RouteList(IPv4ServiceContentsRef
* test
, Direction direction
,
9489 IPv4RouteListRef ret
= NULL
;
9490 IPv4ServiceContentsRef
* scan
;
9492 switch (direction
) {
9493 case kDirectionBackwards
:
9494 for (scan
= test
; *scan
!= NULL
; scan
++) {
9495 /* find the end of the list */
9497 for (scan
--; scan
>= test
; scan
--) {
9498 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9502 case kDirectionForwards
:
9503 for (scan
= test
; *scan
!= NULL
; scan
++) {
9504 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9508 IPv4RouteListFinalize(ret
);
9512 #define EMPHASIS_CHARS "================="
9515 * Function: routelist_build_test
9517 * Runs through the given set of routes first in the forward direction,
9518 * then again backwards. We should end up with exactly the same set of
9519 * routes at the end.
9522 routelist_build_test(IPv4RouteTestRef test
)
9525 boolean_t ret
= FALSE
;
9526 IPv4RouteListRef routes1
;
9527 IPv4RouteListRef routes2
;
9529 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
9530 EMPHASIS_CHARS
"\n",
9533 routes1
= make_IPv4RouteList(test
->test
, kDirectionForwards
,
9535 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9536 if (routes1
!= NULL
) {
9537 descr
= IPv4RouteListCopyDescription(routes1
);
9538 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9542 routes2
= make_IPv4RouteList(test
->test
, kDirectionBackwards
,
9544 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9545 if (routes2
!= NULL
) {
9546 descr
= IPv4RouteListCopyDescription(routes2
);
9547 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9551 if ((routes1
!= NULL
&& routes2
== NULL
)
9552 || (routes1
== NULL
&& routes2
!= NULL
)) {
9553 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
9554 (routes1
!= NULL
) ? "not " : "",
9555 (routes2
!= NULL
) ? "not " : "");
9557 else if (routes1
!= NULL
&& routes2
!= NULL
) {
9558 /* check if they are different */
9559 if (routes1
->count
!= routes2
->count
) {
9560 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
9561 routes1
->count
, routes2
->count
);
9563 else if (bcmp(routes1
, routes2
,
9564 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
9565 fprintf(stderr
, "routes1 and routes2 are different\n");
9568 printf("routes1 and routes2 are the same\n");
9572 if (routes1
!= NULL
) {
9575 if (routes2
!= NULL
) {
9578 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
9579 EMPHASIS_CHARS
"\n",
9580 test
->name
, ret
? "PASSED" : "FAILED");
9585 apply_test(IPv4RouteTestRef old_test
, IPv4RouteTestRef new_test
)
9587 IPv4RouteListRef new_routes
;
9588 IPv4RouteListRef old_routes
;
9590 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
9591 EMPHASIS_CHARS
"\n",
9592 old_test
->name
, new_test
->name
);
9594 old_routes
= make_IPv4RouteList(old_test
->test
, kDirectionForwards
,
9596 new_routes
= make_IPv4RouteList(new_test
->test
, kDirectionForwards
,
9598 if (old_routes
== NULL
) {
9599 printf("No Old Routes\n");
9602 printf("Old routes ('%s') = ", old_test
->name
);
9603 IPv4RouteListPrint(old_routes
);
9606 /* apply the old routes */
9607 IPv4RouteListApply(NULL
, old_routes
, -1);
9609 if (new_routes
== NULL
) {
9610 printf("No New Routes\n");
9613 printf("New Routes ('%s') = ", new_test
->name
);
9614 IPv4RouteListPrint(new_routes
);
9617 /* apply the new routes */
9618 IPv4RouteListApply(old_routes
, new_routes
, -1);
9620 if (old_routes
!= NULL
) {
9623 if (new_routes
!= NULL
) {
9626 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
9627 EMPHASIS_CHARS
"\n",
9628 old_test
->name
, new_test
->name
);
9633 main(int argc
, char **argv
)
9635 IPv4RouteTestRef
* test
;
9638 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
9639 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
9641 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9643 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9644 if (!routelist_build_test(*test
)) {
9645 fprintf(stderr
, "%s failed\n", (*test
)->name
);
9649 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9650 IPv4RouteTestRef
* test2
;
9652 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
9653 apply_test(*test
, *test2
);
9654 apply_test(*test2
, *test
);
9661 printf("\nChecking for leaks\n");
9662 sprintf(cmd
, "leaks %d 2>&1", getpid());
9670 #endif /* TEST_IPV4_ROUTELIST */
9672 #ifdef TEST_IPV6_ROUTELIST
9680 typedef const IPv6Address
* IPv6AddressRef
;
9683 IPv6AddressRef addr
;
9685 const char * router
;
9686 const char * ifname
;
9688 const CFStringRef
* primary_rank
;
9689 struct route
* additional_routes
;
9690 int additional_routes_count
;
9691 struct route
* excluded_routes
;
9692 int excluded_routes_count
;
9693 } IPv6ServiceContents
;
9695 typedef const IPv6ServiceContents
* IPv6ServiceContentsRef
;
9697 struct route loop_routelist
[] = {
9698 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
9699 "2620:149:4:f01:225:ff:fecc:89a2", NULL
},
9700 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
9701 "2620:149:4:f01:225:ff:fecc:89a3", NULL
},
9702 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
9703 "2620:149:4:f01:225:ff:fecc:89a4", NULL
},
9704 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
9705 "2620:149:4:f01:225:ff:fecc:89a5", NULL
},
9706 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
9707 "2620:149:4:f01:225:ff:fecc:89a6", NULL
},
9708 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
9709 "2620:149:4:f01:225:ff:fecc:89a7", NULL
},
9710 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
9711 "2620:149:4:f01:225:ff:fecc:89a8", NULL
},
9712 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
9713 "2620:149:4:f01:225:ff:fecc:89a9", NULL
},
9714 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
9715 "2620:149:4:f01:225:ff:fecc:89aa", NULL
},
9716 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
9717 "2620:149:4:f01:225:ff:fecc:89ab", NULL
},
9718 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
9719 "2620:149:4:f01:225:ff:fecc:89a1", NULL
},
9722 struct route vpn_routelist
[] = {
9723 { "2010:470:1f05:3cb::", 64,
9724 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9725 { "2010:222:3fa5:acb::", 48,
9726 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9727 { "2010:222:3fa5:1234::", 40,
9728 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9729 { "2010:222:3fa5:5678::", 40,
9733 struct route vpn_routelist_ext
[] = {
9734 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL
, NULL
},
9737 struct route en1_routelist_ext
[] = {
9738 { "2020:299:abcd:ef12::", 64, NULL
, NULL
},
9742 static const IPv6Address en0_addr1
[] = {
9743 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL
},
9744 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL
}
9747 static const IPv6Address en0_addr2
[] = {
9748 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL
},
9749 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL
}
9752 static const IPv6Address en0_addr3
[] = {
9753 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL
},
9754 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL
}
9757 static const IPv6Address en0_addr4
[] = {
9758 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL
},
9759 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL
}
9762 static const IPv6Address en0_addr5
[] = {
9763 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL
},
9764 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL
}
9767 static const IPv6Address en0_addr6
[] = {
9768 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL
},
9771 static const IPv6Address en0_lladdr
[] = {
9772 { "fe80::cabc:c8ff:fe96:96af", 64, NULL
}
9775 static const IPv6Address en1_addr
[] = {
9776 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL
},
9777 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL
}
9780 static const IPv6Address utun0_addr
[] = {
9781 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL
},
9784 static const IPv6Address fw0_addr1
[] = {
9785 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL
},
9786 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL
}
9790 * address+address-count
9791 * router ifname pri rank additional-routes+count excluded-routes+count
9794 static const IPv6ServiceContents en0_10
= {
9795 en0_addr1
, countof(en0_addr1
),
9796 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9799 static const IPv6ServiceContents en0_15
= {
9800 en0_addr2
, countof(en0_addr2
),
9801 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9804 static const IPv6ServiceContents en0_30
= {
9805 en0_addr3
, countof(en0_addr3
),
9806 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9809 static const IPv6ServiceContents en0_40
= {
9810 en0_addr4
, countof(en0_addr4
),
9811 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9814 static const IPv6ServiceContents en0_50
= {
9815 en0_addr5
, countof(en0_addr5
),
9816 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9819 static const IPv6ServiceContents en0_10_a
= {
9820 en0_addr6
, countof(en0_addr6
),
9821 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9824 static const IPv6ServiceContents fw0_25
= {
9825 fw0_addr1
, countof(fw0_addr1
),
9826 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9829 static const IPv6ServiceContents en1_20
= {
9830 en1_addr
, countof(en1_addr
),
9831 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9834 static const IPv6ServiceContents en1_10_ext
= {
9835 en1_addr
, countof(en1_addr
),
9836 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL
, NULL
, 0,
9837 en1_routelist_ext
, countof(en1_routelist_ext
)
9840 static const IPv6ServiceContents en0_0_lladdr
= {
9841 en0_lladdr
, countof(en0_lladdr
),
9842 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL
, NULL
, 0, NULL
, 0
9845 static const IPv6ServiceContents en0_loop
= {
9846 en0_addr1
, countof(en0_addr1
),
9847 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
,
9848 loop_routelist
, countof(loop_routelist
), NULL
, 0
9851 static const IPv6ServiceContents utun0
= {
9852 utun0_addr
, countof(utun0_addr
),
9853 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL
,
9854 vpn_routelist
, countof(vpn_routelist
),
9855 vpn_routelist_ext
, countof(vpn_routelist_ext
),
9860 IPv6ServiceContentsRef test
[];
9861 } IPv6RouteTest
, * IPv6RouteTestRef
;
9863 static IPv6RouteTest test1
= {
9877 static IPv6RouteTest test2
= {
9890 static IPv6RouteTest test3
= {
9899 static IPv6RouteTest test4
= {
9908 static IPv6RouteTest test5
= {
9920 static IPv6RouteTestRef ipv6_tests
[] = {
9931 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9934 CFStringRef prop_val
;
9939 prop_val
= CFStringCreateWithCString(NULL
,
9941 kCFStringEncodingASCII
);
9942 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9943 CFRelease(prop_val
);
9948 dict_add_int(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9953 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
9954 CFDictionarySetValue(dict
, prop_name
, num
);
9960 dict_insert_v6_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9961 struct route
* routes
, int routes_count
)
9964 CFMutableArrayRef route_list
;
9965 struct route
* scan
;
9967 if (routes
== NULL
|| routes_count
== 0) {
9970 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9971 &kCFTypeArrayCallBacks
);
9972 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9973 CFMutableDictionaryRef route_dict
;
9975 route_dict
= CFDictionaryCreateMutable(NULL
, 0,
9976 &kCFTypeDictionaryKeyCallBacks
,
9977 &kCFTypeDictionaryValueCallBacks
);
9978 dict_add_string(route_dict
, kSCPropNetIPv6RouteDestinationAddress
,
9980 dict_add_int(route_dict
, kSCPropNetIPv6PrefixLength
,
9981 scan
->prefix_length
);
9982 dict_add_string(route_dict
, kSCPropNetIPv6RouteGatewayAddress
,
9984 dict_add_string(route_dict
, kSCPropNetIPv6RouteInterfaceName
,
9986 CFArrayAppendValue(route_list
, route_dict
);
9987 CFRelease(route_dict
);
9989 CFDictionarySetValue(dict
, prop_name
, route_list
);
9990 CFRelease(route_list
);
9995 array_add_string(CFMutableArrayRef array
, const char * c_str
)
9999 str
= CFStringCreateWithCString(NULL
,
10001 kCFStringEncodingUTF8
);
10002 CFArrayAppendValue(array
, str
);
10008 array_add_int(CFMutableArrayRef array
, int int_val
)
10012 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
10013 CFArrayAppendValue(array
, num
);
10019 dict_add_ipv6_addressing(CFMutableDictionaryRef dict
,
10020 IPv6AddressRef list
, int list_count
)
10022 CFMutableArrayRef addr
= NULL
;
10023 CFMutableArrayRef dest
= NULL
;
10025 CFMutableArrayRef prefix
= NULL
;
10026 IPv6AddressRef scan
;
10028 if (list
== NULL
|| list_count
== 0) {
10031 for (i
= 0, scan
= list
; i
< list_count
; i
++, scan
++) {
10032 if (scan
->addr
!= NULL
) {
10033 if (addr
== NULL
) {
10034 addr
= CFArrayCreateMutable(NULL
, list_count
,
10035 &kCFTypeArrayCallBacks
);
10037 array_add_string(addr
, scan
->addr
);
10039 if (scan
->prefix_length
>= 0) {
10040 if (prefix
== NULL
) {
10041 prefix
= CFArrayCreateMutable(NULL
, list_count
,
10042 &kCFTypeArrayCallBacks
);
10044 array_add_int(prefix
, scan
->prefix_length
);
10046 if (scan
->dest
!= NULL
) {
10047 if (dest
== NULL
) {
10048 dest
= CFArrayCreateMutable(NULL
, list_count
,
10049 &kCFTypeArrayCallBacks
);
10051 array_add_string(dest
, scan
->dest
);
10054 if (addr
!= NULL
) {
10055 CFDictionarySetValue(dict
, kSCPropNetIPv6Addresses
, addr
);
10058 if (dest
!= NULL
) {
10059 CFDictionarySetValue(dict
, kSCPropNetIPv6DestAddresses
, dest
);
10062 if (prefix
!= NULL
) {
10063 CFDictionarySetValue(dict
, kSCPropNetIPv6PrefixLength
, prefix
);
10069 static CFDictionaryRef
10070 make_IPv6_dict(IPv6ServiceContentsRef t
)
10072 CFMutableDictionaryRef dict
;
10074 dict
= CFDictionaryCreateMutable(NULL
, 0,
10075 &kCFTypeDictionaryKeyCallBacks
,
10076 &kCFTypeDictionaryValueCallBacks
);
10077 dict_add_ipv6_addressing(dict
, t
->addr
, t
->addr_count
);
10078 dict_add_string(dict
, kSCPropNetIPv6Router
, t
->router
);
10079 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
10080 dict_insert_v6_routes(dict
, kSCPropNetIPv6AdditionalRoutes
,
10081 t
->additional_routes
, t
->additional_routes_count
);
10082 dict_insert_v6_routes(dict
, kSCPropNetIPv6ExcludedRoutes
,
10083 t
->excluded_routes
, t
->excluded_routes_count
);
10088 kDirectionForwards
= 0,
10089 kDirectionBackwards
= 1
10093 kLogRouteDisabled
= 0,
10094 kLogRouteEnabled
= 1
10097 static IPv6RouteListRef
10098 make_IPv6RouteList_for_test(IPv6RouteListRef list
,
10099 IPv6ServiceContentsRef test
,
10102 CFDictionaryRef dict
;
10103 IPv6RouteListRef r
;
10105 Rank rank_assertion
= kRankAssertionDefault
;
10106 CFNumberRef rank_assertion_cf
= NULL
;
10107 Boolean rank_assertion_is_set
= FALSE
;
10108 IPv6RouteListRef ret
= NULL
;
10109 IPV6_ROUTES_BUF_DECL(routes
);
10111 dict
= make_IPv6_dict(test
);
10112 if (dict
== NULL
) {
10113 fprintf(stderr
, "make_IPv6_dict failed\n");
10116 if (test
->primary_rank
!= NULL
) {
10118 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
10119 &rank_assertion_is_set
);
10120 if (rank_assertion_is_set
) {
10122 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
10125 r
= IPv6RouteListCreateWithDictionary(routes
, dict
,
10126 rank_assertion_cf
);
10127 my_CFRelease(&rank_assertion_cf
);
10129 fprintf(stderr
, "IPv6RouteListCreateWithDictionary failed\n");
10133 if (rank_assertion
== kRankAssertionScoped
) {
10134 rank_assertion
= kRankAssertionNever
;
10136 rank
= RankMake(test
->rank
, rank_assertion
);
10137 if (log_it
== kLogRouteEnabled
10138 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10141 descr
= IPv6RouteListCopyDescription(r
);
10142 SCPrint(TRUE
, stdout
, CFSTR("Adding %@"), descr
);
10145 ret
= IPv6RouteListAddRouteList(list
, 1, r
, rank
);
10153 static IPv6RouteListRef
10154 make_IPv6RouteList(IPv6ServiceContentsRef
* test
, Direction direction
,
10157 IPv6RouteListRef ret
= NULL
;
10158 IPv6ServiceContentsRef
* scan
;
10160 switch (direction
) {
10161 case kDirectionBackwards
:
10162 for (scan
= test
; *scan
!= NULL
; scan
++) {
10163 /* find the end of the list */
10165 for (scan
--; scan
>= test
; scan
--) {
10166 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10170 case kDirectionForwards
:
10171 for (scan
= test
; *scan
!= NULL
; scan
++) {
10172 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
10176 IPv6RouteListFinalize(ret
);
10180 #define EMPHASIS_CHARS "================="
10183 * Function: routelist_build_test
10185 * Runs through the given set of routes first in the forward direction,
10186 * then again backwards. We should end up with exactly the same set of
10187 * routes at the end.
10190 routelist_build_test(IPv6RouteTestRef test
)
10193 boolean_t ret
= FALSE
;
10194 IPv6RouteListRef routes1
;
10195 IPv6RouteListRef routes2
;
10197 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
10198 EMPHASIS_CHARS
"\n",
10201 routes1
= make_IPv6RouteList(test
->test
, kDirectionForwards
,
10203 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10204 if (routes1
!= NULL
) {
10205 descr
= IPv6RouteListCopyDescription(routes1
);
10206 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10210 routes2
= make_IPv6RouteList(test
->test
, kDirectionBackwards
,
10212 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
10213 if (routes2
!= NULL
) {
10214 descr
= IPv6RouteListCopyDescription(routes2
);
10215 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
10219 if ((routes1
!= NULL
&& routes2
== NULL
)
10220 || (routes1
== NULL
&& routes2
!= NULL
)) {
10221 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
10222 (routes1
!= NULL
) ? "not " : "",
10223 (routes2
!= NULL
) ? "not " : "");
10225 else if (routes1
!= NULL
&& routes2
!= NULL
) {
10226 /* check if they are different */
10227 if (routes1
->count
!= routes2
->count
) {
10228 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
10229 routes1
->count
, routes2
->count
);
10231 else if (bcmp(routes1
, routes2
,
10232 IPv6RouteListComputeSize(routes1
->count
)) != 0) {
10233 fprintf(stderr
, "routes1 and routes2 are different\n");
10236 printf("routes1 and routes2 are the same\n");
10240 if (routes1
!= NULL
) {
10243 if (routes2
!= NULL
) {
10246 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
10247 EMPHASIS_CHARS
"\n",
10248 test
->name
, ret
? "PASSED" : "FAILED");
10253 apply_test(IPv6RouteTestRef old_test
, IPv6RouteTestRef new_test
)
10255 IPv6RouteListRef new_routes
;
10256 IPv6RouteListRef old_routes
;
10258 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
10259 EMPHASIS_CHARS
"\n",
10260 old_test
->name
, new_test
->name
);
10262 old_routes
= make_IPv6RouteList(old_test
->test
, kDirectionForwards
,
10263 kLogRouteDisabled
);
10264 new_routes
= make_IPv6RouteList(new_test
->test
, kDirectionForwards
,
10265 kLogRouteDisabled
);
10266 if (old_routes
== NULL
) {
10267 printf("No Old Routes\n");
10270 printf("Old routes ('%s') = ", old_test
->name
);
10271 IPv6RouteListPrint(old_routes
);
10274 /* apply the old routes */
10275 IPv6RouteListApply(NULL
, old_routes
, -1);
10276 if (new_routes
== NULL
) {
10277 printf("No New Routes\n");
10280 printf("New Routes ('%s') = ", new_test
->name
);
10281 IPv6RouteListPrint(new_routes
);
10284 /* apply the new routes */
10285 IPv6RouteListApply(old_routes
, new_routes
, -1);
10286 if (old_routes
!= NULL
) {
10289 if (new_routes
!= NULL
) {
10292 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10293 EMPHASIS_CHARS
"\n",
10294 old_test
->name
, new_test
->name
);
10299 main(int argc
, char **argv
)
10301 IPv6RouteTestRef
* test
;
10304 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10305 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10307 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10309 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10310 if (!routelist_build_test(*test
)) {
10311 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10315 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10316 IPv6RouteTestRef
* test2
;
10318 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10319 apply_test(*test
, *test2
);
10320 apply_test(*test2
, *test
);
10327 printf("\nChecking for leaks\n");
10328 sprintf(cmd
, "leaks %d 2>&1", getpid());
10336 #endif /* TEST_IPV6_ROUTELIST */
10338 #ifdef TEST_DNS_ORDER
10340 #define kProtocolFlagsIPv4v6 (kProtocolFlagsIPv4 | kProtocolFlagsIPv6)
10342 #define V4_ADDR_LOOP CFSTR("127.0.0.1")
10343 #define V4_ADDR_1 CFSTR("192.168.1.1")
10344 #define V4_ADDR_2 CFSTR("192.168.1.2")
10345 #define V4_ADDR_3 CFSTR("8.8.8.8")
10346 #define V4_ADDR_4 CFSTR("8.8.4.4")
10348 #define V6_ADDR_LOOP CFSTR("::1")
10349 #define V6_ADDR_1 CFSTR("fe80::0123:4567:89ab:cdef%en0")
10350 #define V6_ADDR_2 CFSTR("fd00::2acf:e9ff:fe14:8c59")
10351 #define V6_ADDR_3 CFSTR("2001:4860:4860::8888")
10355 const ProtocolFlags flags
;
10356 const CFStringRef server_addrs
[];
10357 } DNSOrderTest
, * DNSOrderTestRef
;
10359 static DNSOrderTest test0a
= {
10361 kProtocolFlagsIPv4
,
10363 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, NULL
10367 static DNSOrderTest test0b
= {
10369 kProtocolFlagsIPv6
,
10371 V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10375 static DNSOrderTest test1a
= {
10377 kProtocolFlagsIPv4v6
,
10379 V4_ADDR_1
, V6_ADDR_1
, NULL
10383 static DNSOrderTest test2a
= {
10385 kProtocolFlagsIPv4v6
,
10387 V4_ADDR_1
, V6_ADDR_2
, NULL
10391 static DNSOrderTest test3a
= {
10393 kProtocolFlagsIPv4v6
,
10395 V4_ADDR_1
, V6_ADDR_3
, NULL
10399 static DNSOrderTest test1b
= {
10401 kProtocolFlagsIPv4v6
,
10403 V4_ADDR_3
, V6_ADDR_1
, NULL
10407 static DNSOrderTest test2b
= {
10409 kProtocolFlagsIPv4v6
,
10411 V4_ADDR_3
, V6_ADDR_2
, NULL
10415 static DNSOrderTest test3b
= {
10417 kProtocolFlagsIPv4v6
,
10419 V4_ADDR_3
, V6_ADDR_3
, NULL
10423 static DNSOrderTest test1c
= {
10425 kProtocolFlagsIPv4v6
,
10427 V6_ADDR_1
, V4_ADDR_1
, NULL
10431 static DNSOrderTest test2c
= {
10433 kProtocolFlagsIPv4v6
,
10435 V6_ADDR_2
, V4_ADDR_1
, NULL
10439 static DNSOrderTest test3c
= {
10441 kProtocolFlagsIPv4v6
,
10443 V6_ADDR_3
, V4_ADDR_1
, NULL
10447 static DNSOrderTest test1d
= {
10449 kProtocolFlagsIPv4v6
,
10451 V6_ADDR_1
, V4_ADDR_3
, NULL
10455 static DNSOrderTest test2d
= {
10457 kProtocolFlagsIPv4v6
,
10459 V6_ADDR_2
, V4_ADDR_3
, NULL
10463 static DNSOrderTest test3d
= {
10465 kProtocolFlagsIPv4v6
,
10467 V6_ADDR_3
, V4_ADDR_3
, NULL
10471 static DNSOrderTest test4
= {
10473 kProtocolFlagsIPv4v6
,
10475 V4_ADDR_LOOP
, V4_ADDR_3
, V6_ADDR_2
, NULL
10479 static DNSOrderTest test5
= {
10481 kProtocolFlagsIPv4v6
,
10483 V4_ADDR_3
, V6_ADDR_LOOP
, V6_ADDR_2
, NULL
10487 static DNSOrderTest test6
= {
10489 kProtocolFlagsIPv4v6
,
10491 V4_ADDR_1
, V4_ADDR_2
, V4_ADDR_3
, V4_ADDR_4
, V6_ADDR_1
, V6_ADDR_2
, V6_ADDR_3
, NULL
10495 static DNSOrderTest test7
= {
10497 kProtocolFlagsIPv4v6
,
10499 V4_ADDR_1
, V6_ADDR_1
, V4_ADDR_3
, V6_ADDR_2
, NULL
10503 static DNSOrderTestRef dns_order_tests
[] = {
10505 &test1a
, &test2a
, &test3a
,
10506 &test1b
, &test2b
, &test3b
,
10507 &test1c
, &test2c
, &test3c
,
10508 &test1d
, &test2d
, &test3d
,
10516 #define EMPHASIS_CHARS "================="
10519 apply_order(CFArrayRef servers
, ProtocolFlags flags
)
10521 CFArrayRef ordered_servers
;
10523 ordered_servers
= order_dns_servers(servers
, flags
);
10524 if (ordered_servers
!= NULL
) {
10525 SCPrint(TRUE
, stdout
, CFSTR("After :\n%@\n"), ordered_servers
);
10526 CFRelease(ordered_servers
);
10528 printf("FAIL: No ordered servers\n");
10535 apply_test(DNSOrderTestRef test
)
10537 CFMutableArrayRef servers
;
10539 printf("\n" EMPHASIS_CHARS
"> '%s' Begin <" EMPHASIS_CHARS
"\n\n", test
->name
);
10541 servers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
10542 for (int i
= 0; test
->server_addrs
[i
] != NULL
; i
++) {
10543 CFStringRef server_addr
= test
->server_addrs
[i
];
10545 CFArrayAppendValue(servers
, server_addr
);
10548 SCPrint(TRUE
, stdout
, CFSTR("Before :\n%@\n"), servers
);
10550 apply_order(servers
, test
->flags
);
10552 CFRelease(servers
);
10558 main(int argc
, char **argv
)
10561 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10562 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10564 S_IPMonitor_debug
= (uint32
)strtoul(argv
[1], NULL
, 0);
10567 for (DNSOrderTestRef
* test
= dns_order_tests
; *test
!= NULL
; test
++) {
10574 printf("\nChecking for leaks\n");
10575 sprintf(cmd
, "leaks %d 2>&1", getpid());
10584 #endif /* TEST_DNS_ORDER */