2 * Copyright (c) 2000-2013 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
75 #include <sys/fcntl.h>
76 #include <sys/ioctl.h>
77 #include <sys/types.h>
78 #include <sys/socket.h>
79 #include <net/route.h>
81 #include <net/if_dl.h>
82 #include <netinet/in.h>
83 #include <netinet/icmp6.h>
84 #include <netinet6/in6_var.h>
85 #include <netinet6/nd6.h>
86 #include <arpa/inet.h>
87 #include <sys/sysctl.h>
90 #include <mach/mach_time.h>
91 #include <dispatch/dispatch.h>
92 #include <CommonCrypto/CommonDigest.h>
94 #include <SystemConfiguration/SystemConfiguration.h>
95 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
96 #include <SystemConfiguration/SCValidation.h>
97 #include <SystemConfiguration/scprefs_observer.h>
98 #include <SystemConfiguration/SCPrivate.h> /* for SCLog() */
99 #include "SCNetworkReachabilityInternal.h"
100 #include "SCNetworkSignaturePrivate.h"
102 #include "dnsinfo_server.h"
104 #if defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS)
105 #include <ppp/PPPControllerPriv.h>
106 #endif // !defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS)
109 #ifndef kDNSServiceCompMulticastDNS
110 #define kDNSServiceCompMulticastDNS "MulticastDNS"
112 #ifndef kDNSServiceCompPrivateDNS
113 #define kDNSServiceCompPrivateDNS "PrivateDNS"
115 #include <network_information.h>
116 #include "network_information_priv.h"
117 #include "network_information_server.h"
118 #include <ppp/ppp_msg.h>
121 kProtocolFlagsNone
= 0x0,
122 kProtocolFlagsIPv4
= 0x1,
123 kProtocolFlagsIPv6
= 0x2
125 typedef uint8_t ProtocolFlags
;
128 kDebugFlag1
= 0x00000001,
129 kDebugFlag2
= 0x00000002,
130 kDebugFlag4
= 0x00000004,
131 kDebugFlag8
= 0x00000008,
132 kDebugFlagDefault
= kDebugFlag1
,
133 kDebugFlagAll
= 0xffffffff
136 #ifdef TEST_IPV4_ROUTELIST
137 #define ROUTELIST_DEBUG(a, f) { if ((S_IPMonitor_debug & (f)) != 0) printf a ;}
139 #define ROUTELIST_DEBUG(a, f)
142 #if !TARGET_IPHONE_SIMULATOR
143 #include "set-hostname.h"
144 #endif /* !TARGET_IPHONE_SIMULATOR */
146 #include "dns-configuration.h"
147 #include "proxy-configuration.h"
149 #if !TARGET_OS_IPHONE
150 #include "smb-configuration.h"
151 #endif /* !TARGET_OS_IPHONE */
154 * Property: kIPIsCoupled
156 * Used to indicate that the IPv4 and IPv6 services are coupled.
157 * Neither the IPv4 part nor the IPv6 part of a coupled service
158 * may become primary if IPv4 or IPv6 is primary for another interface.
160 * For example, if the service over en3 is "coupled" and has IPv6,
161 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible
162 * to become primary for IPv6.
164 #define kIPIsCoupled CFSTR("IPIsCoupled")
166 #define PPP_PREFIX "ppp"
168 #define IP_FORMAT "%d.%d.%d.%d"
169 #define IP_CH(ip) ((u_char *)(ip))
170 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
172 #include "ip_plugin.h"
173 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
174 static SCLoggerRef S_IPMonitor_logger
;
175 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
177 static boolean_t S_bundle_logging_verbose
;
180 * IPv4 Route management
183 typedef uint32_t RouteFlags
;
186 kRouteIsDirectToInterfaceFlag
= 0x00000001,
187 kRouteIsNotSubnetLocalFlag
= 0x00000002,
188 kRouteIsScopedFlag
= 0x00000004,
189 kRouteIsNULLFlag
= 0x00000008
195 struct in_addr gateway
;
196 char ifname
[IFNAMSIZ
];
197 unsigned int ifindex
;
201 } IPv4Route
, *IPv4RouteRef
;
206 boolean_t exclude_from_nwi
;
207 IPv4Route list
[1]; /* variable length */
208 } IPv4RouteList
, *IPv4RouteListRef
;
211 kIPv4RouteListAddRouteCommand
,
212 kIPv4RouteListRemoveRouteCommand
216 * Election Information
217 * - information about the current best services
219 typedef struct Candidate
{
220 CFStringRef serviceID
;
227 boolean_t ip_is_coupled
;
228 SCNetworkReachabilityFlags reachability_flags
;
230 struct sockaddr_in vpn_server_addr4
;
231 struct sockaddr_in6 vpn_server_addr6
;
233 CFStringRef signature
;
234 } Candidate
, * CandidateRef
;
236 typedef struct ElectionResults
{
239 Candidate candidates
[1];
240 } ElectionResults
, * ElectionResultsRef
;
242 static __inline__
unsigned int
243 ElectionResultsComputeSize(unsigned int n
)
245 return (offsetof(ElectionResults
, candidates
[n
]));
251 * A 32-bit value to encode the relative rank of a service.
253 * The top 8 bits are used to hold the rank assertion (first, last
256 * The bottom 24 bits are used to store the service index (i.e. the
257 * position within the service order array).
259 #define RANK_ASSERTION_MAKE(r) ((Rank)(r) << 24)
260 #define kRankAssertionFirst RANK_ASSERTION_MAKE(0)
261 #define kRankAssertionDefault RANK_ASSERTION_MAKE(1)
262 #define kRankAssertionLast RANK_ASSERTION_MAKE(2)
263 #define kRankAssertionNever RANK_ASSERTION_MAKE(3)
264 #define kRankAssertionMask RANK_ASSERTION_MAKE(0xff)
265 #define RANK_ASSERTION_MASK(r) ((Rank)(r) & kRankAssertionMask)
267 #define RANK_INDEX_MAKE(r) ((Rank)(r))
268 #define kRankIndexMask RANK_INDEX_MAKE(0xffffff)
269 #define RANK_INDEX_MASK(r) ((Rank)(r) & kRankIndexMask)
271 static __inline__ Rank
272 RankMake(uint32_t service_index
, Rank primary_rank
)
274 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
277 static __inline__ Rank
278 PrimaryRankGetRankAssertion(CFStringRef primaryRank
)
280 if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankNever
)) {
281 return kRankAssertionNever
;
282 } else if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankFirst
)) {
283 return kRankAssertionFirst
;
284 } else if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankLast
)) {
285 return kRankAssertionLast
;
287 return kRankAssertionDefault
;
290 typedef uint32_t IPv4RouteListApplyCommand
;
292 typedef void IPv4RouteListApplyCallBackFunc(IPv4RouteListApplyCommand cmd
,
293 IPv4RouteRef route
, void * arg
);
294 typedef IPv4RouteListApplyCallBackFunc
* IPv4RouteListApplyCallBackFuncPtr
;
296 /* SCDynamicStore session */
297 static SCDynamicStoreRef S_session
= NULL
;
299 /* debug output flags */
300 static uint32_t S_IPMonitor_debug
= 0;
301 static Boolean S_IPMonitor_verbose
= FALSE
;
303 /* are we netbooted? If so, don't touch the default route */
304 static boolean_t S_netboot
= FALSE
;
306 /* is scoped routing enabled? */
308 static boolean_t S_scopedroute
= FALSE
;
309 static boolean_t S_scopedroute_v6
= FALSE
;
310 #endif /* RTF_IFSCOPE */
312 /* dictionary to hold per-service state: key is the serviceID */
313 static CFMutableDictionaryRef S_service_state_dict
= NULL
;
314 static CFMutableDictionaryRef S_ipv4_service_rank_dict
= NULL
;
315 static CFMutableDictionaryRef S_ipv6_service_rank_dict
= NULL
;
317 /* dictionary to hold per-interface rank information */
318 static CFMutableDictionaryRef S_if_rank_dict
= NULL
;
320 /* if set, a PPP interface overrides the primary */
321 static boolean_t S_ppp_override_primary
= FALSE
;
323 /* the current primary serviceID's */
324 static CFStringRef S_primary_ipv4
= NULL
;
325 static CFStringRef S_primary_ipv6
= NULL
;
326 static CFStringRef S_primary_dns
= NULL
;
327 static CFStringRef S_primary_proxies
= NULL
;
329 /* the current election results */
330 static ElectionResultsRef S_ipv4_results
;
331 static ElectionResultsRef S_ipv6_results
;
333 static CFStringRef S_state_global_ipv4
= NULL
;
334 static CFStringRef S_state_global_ipv6
= NULL
;
335 static CFStringRef S_state_global_dns
= NULL
;
336 static CFStringRef S_state_global_proxies
= NULL
;
337 static CFStringRef S_state_service_prefix
= NULL
;
338 static CFStringRef S_setup_global_ipv4
= NULL
;
339 static CFStringRef S_setup_service_prefix
= NULL
;
341 static CFStringRef S_multicast_resolvers
= NULL
;
342 static CFStringRef S_private_resolvers
= NULL
;
344 #if !TARGET_IPHONE_SIMULATOR
345 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
346 #endif /* !TARGET_IPHONE_SIMULATOR */
348 static const struct in_addr S_ip_zeros
= { 0 };
349 static const struct in6_addr S_ip6_zeros
= IN6ADDR_ANY_INIT
;
351 static boolean_t S_append_state
= FALSE
;
353 static CFDictionaryRef S_dns_dict
= NULL
;
355 static Boolean S_dnsinfo_synced
= TRUE
;
357 static nwi_state_t S_nwi_state
= NULL
;
358 static Boolean S_nwi_synced
= TRUE
;
360 static CFDictionaryRef S_proxies_dict
= NULL
;
362 // Note: access should be gated with __network_change_queue()
363 static uint32_t S_network_change_needed
= 0;
364 #define NETWORK_CHANGE_NET 1<<0
365 #define NETWORK_CHANGE_DNS 1<<1
366 #define NETWORK_CHANGE_PROXY 1<<2
367 #if !TARGET_OS_IPHONE
368 #define NETWORK_CHANGE_SMB 1<<3
369 #endif /* !TARGET_OS_IPHONE */
370 static struct timeval S_network_change_start
;
371 static Boolean S_network_change_timeout
= FALSE
;
372 static dispatch_source_t S_network_change_timer
= NULL
;
374 #if !TARGET_OS_IPHONE
375 static CFStringRef S_primary_smb
= NULL
;
376 static CFStringRef S_state_global_smb
= NULL
;
377 static CFDictionaryRef S_smb_dict
= NULL
;
378 #endif /* !TARGET_OS_IPHONE */
380 #if !TARGET_OS_IPHONE
381 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
382 #endif /* !TARGET_OS_IPHONE */
385 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
386 #endif //KERN_NETBOOT
389 ** entityType*, GetEntityChanges*
390 ** - definitions for the entity types we handle
397 #if !TARGET_OS_IPHONE
399 #endif /* !TARGET_OS_IPHONE */
401 kEntityTypeVPNStatus
,
402 kEntityTypeServiceOptions
= 31
404 typedef uint32_t EntityType
;
406 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
407 &kSCEntNetIPv4
, /* 0 */
408 &kSCEntNetIPv6
, /* 1 */
409 &kSCEntNetDNS
, /* 2 */
410 &kSCEntNetProxies
, /* 3 */
411 #if !TARGET_OS_IPHONE
412 &kSCEntNetSMB
, /* 4 */
413 #endif /* !TARGET_OS_IPHONE */
417 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
);
419 static __inline__
char
422 return ((af
== AF_INET
) ? '4' : '6');
425 static __inline__
char
426 ipvx_other_char(int af
)
428 return ((af
== AF_INET
) ? '6' : '4');
431 static IPv4RouteListRef
432 ipv4_dict_get_routelist(CFDictionaryRef ipv4_dict
)
435 IPv4RouteListRef routes_list
= NULL
;
437 if (isA_CFDictionary(ipv4_dict
) == NULL
) {
441 routes
= CFDictionaryGetValue(ipv4_dict
, kIPv4DictRoutes
);
443 if (routes
!= NULL
) {
444 routes_list
= (IPv4RouteListRef
)(void*)CFDataGetBytePtr(routes
);
446 return (routes_list
);
450 ipv4_dict_get_ifname(CFDictionaryRef ipv4_dict
)
452 CFDictionaryRef ipv4_service_dict
= NULL
;
454 if (isA_CFDictionary(ipv4_dict
) == NULL
) {
458 ipv4_service_dict
= CFDictionaryGetValue(ipv4_dict
,
461 if (isA_CFDictionary(ipv4_service_dict
) == NULL
) {
465 return CFDictionaryGetValue(ipv4_service_dict
, kSCPropInterfaceName
);
468 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
469 CFDictionaryRef state_dict
,
470 CFDictionaryRef setup_dict
,
471 CFDictionaryRef info
);
472 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
474 static GetEntityChangesFunc get_ipv4_changes
;
475 static GetEntityChangesFunc get_ipv6_changes
;
476 static GetEntityChangesFunc get_dns_changes
;
477 static GetEntityChangesFunc get_proxies_changes
;
478 #if !TARGET_OS_IPHONE
479 static GetEntityChangesFunc get_smb_changes
;
480 #endif /* !TARGET_OS_IPHONE */
483 my_CFRelease(void * t
);
486 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
489 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
491 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
492 get_ipv4_changes
, /* 0 */
493 get_ipv6_changes
, /* 1 */
494 get_dns_changes
, /* 2 */
495 get_proxies_changes
,/* 3 */
496 #if !TARGET_OS_IPHONE
497 get_smb_changes
, /* 4 */
498 #endif /* !TARGET_OS_IPHONE */
503 ** - mechanism to do an atomic update of the SCDynamicStore
504 ** when the content needs to be changed across multiple functions
507 CFMutableArrayRef notify
;
508 CFMutableArrayRef remove
;
509 CFMutableDictionaryRef set
;
510 } keyChangeList
, * keyChangeListRef
;
513 my_CFStringCopyComponent(CFStringRef path
, CFStringRef separator
,
514 CFIndex component_index
)
517 CFStringRef component
= NULL
;
519 arr
= CFStringCreateArrayBySeparatingStrings(NULL
, path
, separator
);
523 if (CFArrayGetCount(arr
) <= component_index
) {
526 component
= CFRetain(CFArrayGetValueAtIndex(arr
, component_index
));
534 keyChangeListInit(keyChangeListRef keys
)
536 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
537 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
538 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
539 &kCFTypeDictionaryKeyCallBacks
,
540 &kCFTypeDictionaryValueCallBacks
);
545 keyChangeListFree(keyChangeListRef keys
)
547 my_CFRelease(&keys
->notify
);
548 my_CFRelease(&keys
->remove
);
549 my_CFRelease(&keys
->set
);
554 keyChangeListActive(keyChangeListRef keys
)
556 return ((CFDictionaryGetCount(keys
->set
) > 0) ||
557 (CFArrayGetCount(keys
->remove
) > 0) ||
558 (CFArrayGetCount(keys
->notify
) > 0));
562 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
564 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
569 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
571 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
572 CFDictionaryRemoveValue(keys
->set
, key
);
577 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
579 my_CFArrayRemoveValue(keys
->remove
, key
);
580 CFDictionarySetValue(keys
->set
, key
, value
);
585 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
587 CFArrayRef notify
= keys
->notify
;
588 CFArrayRef remove
= keys
->remove
;
589 CFDictionaryRef set
= keys
->set
;
591 if (CFArrayGetCount(notify
) == 0) {
594 if (CFArrayGetCount(remove
) == 0) {
597 if (CFDictionaryGetCount(set
) == 0) {
600 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
603 if (S_IPMonitor_debug
& kDebugFlag1
) {
605 my_log(LOG_DEBUG
, "IPMonitor: Setting:\n%@", set
);
607 if (remove
!= NULL
) {
608 my_log(LOG_DEBUG
, "IPMonitor: Removing:\n%@", remove
);
610 if (notify
!= NULL
) {
611 my_log(LOG_DEBUG
, "IPMonitor: Notifying:\n%@", notify
);
614 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
620 S_nwi_ifstate_dump(nwi_ifstate_t ifstate
, int i
)
622 const char * addr_str
;
624 char ntopbuf
[INET6_ADDRSTRLEN
];
625 char vpn_ntopbuf
[INET6_ADDRSTRLEN
];
626 const struct sockaddr
* vpn_addr
;
628 address
= nwi_ifstate_get_address(ifstate
);
629 addr_str
= inet_ntop(ifstate
->af
, address
, ntopbuf
, sizeof(ntopbuf
));
630 vpn_addr
= nwi_ifstate_get_vpn_server(ifstate
);
631 if (vpn_addr
!= NULL
) {
632 _SC_sockaddr_to_string(nwi_ifstate_get_vpn_server(ifstate
),
634 sizeof(vpn_ntopbuf
));
637 " [%d]: %s%s%s%s rank 0x%x iaddr: %s%s%s reachability_flags %u",
639 ifstate
->diff_str
!= NULL
? ifstate
->diff_str
: "",
640 (ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_DNS
) != 0
642 (ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0
646 (vpn_addr
!= NULL
) ? " vpn_server_addr: " : "",
647 (vpn_addr
!= NULL
) ? vpn_ntopbuf
: "",
648 ifstate
->reach_flags
);
653 S_nwi_state_dump(nwi_state_t state
)
659 my_log(LOG_DEBUG
, "nwi_state = <none>");
664 "gen = %llu size = %u #ipv4 = %u #ipv6 = %u "
665 "reach_flags_v4 = %u reach_flags_v6 %u }",
666 state
->generation_count
,
670 nwi_state_get_reachability_flags(state
, AF_INET
),
671 nwi_state_get_reachability_flags(state
, AF_INET6
));
672 if (state
->ipv4_count
) {
673 my_log(LOG_DEBUG
, "IPv4:");
674 for (i
= 0, scan
= state
->nwi_ifstates
;
675 i
< state
->ipv4_count
; i
++, scan
++) {
676 S_nwi_ifstate_dump(scan
, i
);
679 if (state
->ipv6_count
) {
680 my_log(LOG_DEBUG
, "IPv6:");
681 for (i
= 0, scan
= state
->nwi_ifstates
+ state
->ipv6_start
;
682 i
< state
->ipv6_count
; i
++, scan
++) {
683 S_nwi_ifstate_dump(scan
, i
);
697 mib
[1] = KERN_NETBOOT
;
698 len
= sizeof(netboot
);
699 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
703 #if !TARGET_IPHONE_SIMULATOR
704 static __inline__
int
707 return (socket(AF_INET6
, SOCK_DGRAM
, 0));
712 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
714 struct in6_defrouter dr
;
715 struct sockaddr_in6
* sin6
;
717 bzero(&dr
, sizeof(dr
));
719 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
720 sin6
->sin6_family
= AF_INET6
;
721 sin6
->sin6_addr
= *addr
;
723 dr
.if_index
= if_index
;
724 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
728 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
730 struct in6_defrouter dr
;
731 struct sockaddr_in6
* sin6
;
733 bzero(&dr
, sizeof(dr
));
735 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
736 sin6
->sin6_family
= AF_INET6
;
737 sin6
->sin6_addr
= *addr
;
738 dr
.if_index
= if_index
;
739 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
741 #endif /* SIOCDRADD_IN6 */
742 #endif /* !TARGET_IPHONE_SIMULATOR */
746 S_is_scoped_routing_enabled()
749 size_t len
= sizeof(scopedroute
);
751 if ((sysctlbyname("net.inet.ip.scopedroute",
754 && (errno
!= ENOENT
)) {
755 my_log(LOG_ERR
, "sysctlbyname() failed: %s", strerror(errno
));
757 return (scopedroute
);
761 S_is_scoped_v6_routing_enabled()
763 int scopedroute_v6
= 0;
764 size_t len
= sizeof(scopedroute_v6
);
766 if ((sysctlbyname("net.inet6.ip6.scopedroute",
767 &scopedroute_v6
, &len
,
769 && (errno
!= ENOENT
)) {
770 my_log(LOG_ERR
, "sysctlbyname() failed: %s", strerror(errno
));
772 return (scopedroute_v6
);
774 #endif /* RTF_IFSCOPE */
777 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
779 CFIndex n
= CFArrayGetCount(arr
);
781 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
784 CFArrayAppendValue(arr
, new);
789 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
793 i
= CFArrayGetFirstIndexOfValue(arr
,
794 CFRangeMake(0, CFArrayGetCount(arr
)),
796 if (i
!= kCFNotFound
) {
797 CFArrayRemoveValueAtIndex(arr
, i
);
803 my_CFRelease(void * t
)
805 void * * obj
= (void * *)t
;
814 static CFDictionaryRef
815 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
817 if (isA_CFDictionary(dict
) == NULL
) {
820 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
824 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
826 if (isA_CFDictionary(dict
) == NULL
) {
829 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
833 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, int addr_size
)
837 if (isA_CFString(str
) == NULL
) {
843 if (addr_size
< sizeof(struct in_addr
)) {
848 if (addr_size
< sizeof(struct in6_addr
)) {
855 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
856 if (inet_pton(family
, buf
, addr
) == 1) {
860 bzero(addr
, addr_size
);
866 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
868 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
873 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
875 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
878 static CF_RETURNS_RETAINED CFStringRef
879 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
881 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
882 kSCDynamicStoreDomainSetup
,
887 static CF_RETURNS_RETAINED CFStringRef
888 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
890 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
891 kSCDynamicStoreDomainState
,
896 static CFDictionaryRef
897 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
900 CFStringRef setup_key
;
901 CFDictionaryRef setup_dict
;
903 setup_key
= setup_service_key(serviceID
, entity
);
904 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
905 my_CFRelease(&setup_key
);
909 static CFDictionaryRef
910 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
913 CFStringRef state_key
;
914 CFDictionaryRef state_dict
;
916 state_key
= state_service_key(serviceID
, entity
);
917 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
918 my_CFRelease(&state_key
);
923 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
927 ip_list
= CFDictionaryGetValue(dict
, prop
);
928 if (isA_CFArray(ip_list
) != NULL
929 && CFArrayGetCount(ip_list
) > 0
930 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
937 get_override_primary(CFDictionaryRef dict
)
941 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
942 if (isA_CFNumber(override
) != NULL
) {
945 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
950 else if (isA_CFBoolean(override
) != NULL
) {
951 if (CFBooleanGetValue(override
)) {
962 static __inline__
struct in_addr
963 subnet_addr(struct in_addr addr
, struct in_addr mask
)
967 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
971 static __inline__
int
972 in_addr_cmp(struct in_addr a
, struct in_addr b
)
974 return (uint32_cmp(ntohl(a
.s_addr
), ntohl(b
.s_addr
)));
977 static __inline__
int
978 RouteFlagsCompare(RouteFlags a
, RouteFlags b
)
980 return (uint32_cmp(a
, b
));
984 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
986 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
988 CFStringAppendFormat(str
, NULL
,
989 CFSTR("Dest " IP_FORMAT
992 " Ifp %s Ifa " IP_FORMAT
),
995 IP_LIST(&r
->gateway
),
996 (r
->ifname
[0] != '\0') ? r
->ifname
: "<none>",
998 if ((r
->flags
& kRouteIsNULLFlag
) != 0) {
999 CFStringAppend(str
, CFSTR(" [null]"));
1002 if ((r
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
1003 CFStringAppend(str
, CFSTR(" [non-local]"));
1005 else if ((r
->flags
& kRouteIsDirectToInterfaceFlag
) != 0) {
1006 CFStringAppend(str
, CFSTR(" [direct]"));
1008 switch (rank_assertion
) {
1009 case kRankAssertionFirst
:
1010 CFStringAppend(str
, CFSTR(" [first]"));
1012 case kRankAssertionLast
:
1013 CFStringAppend(str
, CFSTR(" [last]"));
1015 case kRankAssertionNever
:
1016 CFStringAppend(str
, CFSTR(" [never]"));
1021 if ((r
->flags
& kRouteIsScopedFlag
) != 0) {
1022 CFStringAppend(str
, CFSTR(" [SCOPED]"));
1029 IPv4RouteCopyDescription(IPv4RouteRef r
)
1031 CFMutableStringRef str
;
1033 str
= CFStringCreateMutable(NULL
, 0);
1034 IPv4RouteCopyDescriptionWithString(r
, str
);
1038 static __inline__
void
1039 IPv4RoutePrint(IPv4RouteRef route
)
1041 CFStringRef str
= IPv4RouteCopyDescription(route
);
1043 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
1048 static __inline__
void
1049 IPv4RouteLog(int level
, IPv4RouteRef route
)
1051 CFStringRef str
= IPv4RouteCopyDescription(route
);
1053 my_log(level
, "%@", str
);
1059 IPv4RouteCompare(IPv4RouteRef a
, Rank a_rank
,
1060 IPv4RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
1065 cmp
= in_addr_cmp(a
->dest
, b
->dest
);
1067 cmp
= in_addr_cmp(a
->mask
, b
->mask
);
1069 int name_cmp
= strcmp(a
->ifname
, b
->ifname
);
1071 if (name_cmp
== 0) {
1076 cmp
= RankCompare(a_rank
, b_rank
);
1083 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
1091 else if (cmp
== 0) {
1097 a_str
= IPv4RouteCopyDescription(a
);
1098 b_str
= IPv4RouteCopyDescription(b
);
1099 my_log(LOG_DEBUG
, "%@ rank 0x%x %c %@ rank 0x%x",
1100 a_str
, a_rank
, ch
, b_str
, b_rank
);
1107 static CFMutableStringRef
1108 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
1112 CFMutableStringRef str
;
1114 str
= CFStringCreateMutable(NULL
, 0);
1115 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
1117 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
1118 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
1119 IPv4RouteCopyDescriptionWithString(r
, str
);
1121 CFStringAppend(str
, CFSTR("\n}"));
1125 static __inline__
void
1126 IPv4RouteListPrint(IPv4RouteListRef routes
)
1128 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
1130 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
1135 static __inline__
void
1136 IPv4RouteListLog(int level
, IPv4RouteListRef routes
)
1138 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
1140 my_log(level
, "%@", str
);
1145 static __inline__
unsigned int
1146 IPv4RouteListComputeSize(unsigned int n
)
1148 return (offsetof(IPv4RouteList
, list
[n
]));
1151 #if !TARGET_IPHONE_SIMULATOR
1153 IPv4RouteListFindRoute(IPv4RouteListRef routes
, IPv4RouteRef route
)
1156 IPv4RouteRef scan_result
= NULL
;
1159 for (i
= 0, scan
= routes
->list
; i
< routes
->count
; i
++, scan
++) {
1160 if ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
1161 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
1162 && (strcmp(scan
->ifname
, route
->ifname
) == 0)
1163 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
1164 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
1165 && (scan
->flags
== route
->flags
)) {
1170 return (scan_result
);
1174 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
1175 IPv4RouteListApplyCallBackFuncPtr func
, void * arg
)
1180 if (old_routes
== new_routes
&& old_routes
== NULL
) {
1181 /* both old and new are NULL, so there's nothing to do */
1184 if (old_routes
!= NULL
) {
1185 for (i
= 0, scan
= old_routes
->list
;
1186 i
< old_routes
->count
;
1188 IPv4RouteRef new_route
= NULL
;
1190 if (new_routes
!= NULL
) {
1191 new_route
= IPv4RouteListFindRoute(new_routes
, scan
);
1193 if (new_route
== NULL
) {
1195 (*func
)(kIPv4RouteListRemoveRouteCommand
, scan
, arg
);
1200 if (new_routes
!= NULL
) {
1201 for (i
= 0, scan
= new_routes
->list
;
1202 i
< new_routes
->count
;
1204 IPv4RouteRef old_route
= NULL
;
1206 if (old_routes
!= NULL
) {
1207 old_route
= IPv4RouteListFindRoute(old_routes
, scan
);
1209 if (old_route
== NULL
) {
1211 (*func
)(kIPv4RouteListAddRouteCommand
, scan
, arg
);
1218 #endif /* !TARGET_IPHONE_SIMULATOR */
1221 * Function: IPv4RouteListAddRoute
1224 * Add the given IPv4Route to the list of routes, eliminating lower-ranked
1225 * duplicates on the same interface, and marking any lower ranked duplicates
1226 * on other interfaces with kRouteIsScopedFlag.
1228 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1231 * Route list updated with the given route, possibly a different pointer,
1232 * due to using realloc'd memory.
1241 static IPv4RouteListRef
1242 IPv4RouteListAddRoute(IPv4RouteListRef routes
, int init_size
,
1243 IPv4RouteRef this_route
, Rank this_rank
)
1246 IPv4RouteRef first_scan
= NULL
;
1247 int scope_which
= kScopeNone
;
1251 if (routes
== NULL
) {
1252 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(init_size
));
1253 bzero(routes
, sizeof(*routes
));
1254 routes
->size
= init_size
;
1257 for (i
= 0, scan
= routes
->list
; i
< routes
->count
;
1260 boolean_t same_dest
;
1262 cmp
= IPv4RouteCompare(this_route
, this_rank
, scan
, scan
->rank
, &same_dest
);
1264 if (same_dest
== TRUE
&& first_scan
== NULL
) {
1270 if (same_dest
== TRUE
1271 && (first_scan
->flags
& kRouteIsScopedFlag
) == 0) {
1272 if ((scan
->flags
& kRouteIsScopedFlag
) != 0) {
1273 ROUTELIST_DEBUG(("Hit 1: set scope on self\n"),
1275 scope_which
= kScopeThis
;
1278 ROUTELIST_DEBUG(("Hit 2: set scope on next\n"),
1280 scope_which
= kScopeNext
;
1283 /* remember our insertion point, but keep going to find a dup */
1287 else if (cmp
== 0) {
1290 /* this route is a duplicate */
1291 ROUTELIST_DEBUG(("Hit 3: removing [%d]\n", i
), kDebugFlag8
);
1293 if (i
== routes
->count
) {
1294 /* last slot, decrementing gets rid of it */
1297 bcopy(routes
->list
+ i
+ 1,
1299 sizeof(routes
->list
[0]) * (routes
->count
- i
));
1303 /* resolve conflict using rank */
1304 if (this_rank
< scan
->rank
) {
1305 boolean_t is_scoped
= FALSE
;
1307 if (scan
->flags
& kRouteIsScopedFlag
) {
1310 ROUTELIST_DEBUG(("Hit 4:replacing [%d] rank 0x%x < 0x%x\n",
1313 scan
->rank
), kDebugFlag8
);
1314 *scan
= *this_route
;
1315 scan
->rank
= this_rank
;
1317 /* preserve whether route was scoped */
1318 ROUTELIST_DEBUG(("Hit 5: preserved scope\n"), kDebugFlag8
);
1319 scan
->flags
|= kRouteIsScopedFlag
;
1326 if (same_dest
== TRUE
) {
1327 if (scope_which
== kScopeNone
) {
1328 ROUTELIST_DEBUG(("Hit 10: set scope on self\n"),
1330 scope_which
= kScopeThis
;
1333 #ifdef TEST_IPV4_ROUTELIST
1334 else if (where
!= -1) {
1335 /* not possible because we maintain a sorted list */
1336 ROUTELIST_DEBUG(("Hit 11: moved past routes - can't happen\n"),
1340 #endif /* TEST_IPV4_ROUTELIST */
1343 if (routes
->size
== routes
->count
) {
1345 IPv4RouteListRef new_routes
;
1348 /* double the size */
1349 old_size
= routes
->size
;
1350 how_many
= old_size
* 2;
1351 new_routes
= (IPv4RouteListRef
)
1352 realloc(routes
, IPv4RouteListComputeSize(how_many
));
1353 if (new_routes
== NULL
) {
1357 ROUTELIST_DEBUG(("increasing size from %d to %d\n", old_size
,
1358 how_many
), kDebugFlag8
);
1359 new_routes
->size
= how_many
;
1360 routes
= new_routes
;
1363 /* add it to the end */
1364 where
= routes
->count
;
1367 /* insert it at [where] */
1368 bcopy(routes
->list
+ where
,
1369 routes
->list
+ where
+ 1,
1370 sizeof(routes
->list
[0]) * (routes
->count
- where
));
1372 /* copy the route */
1373 routes
->list
[where
] = *this_route
;
1374 routes
->list
[where
].rank
= this_rank
;
1375 if (RANK_ASSERTION_MASK(this_rank
) == kRankAssertionNever
) {
1376 routes
->list
[where
].flags
|= kRouteIsScopedFlag
;
1380 switch (scope_which
) {
1382 routes
->list
[where
].flags
|= kRouteIsScopedFlag
;
1385 routes
->list
[where
+ 1].flags
|= kRouteIsScopedFlag
;
1397 * Function: IPv4RouteListAddRouteList
1400 * Invoke IPv4RouteListAddRoute for each route in the given list.
1403 * See IPv4RouteListAddRoute for more information.
1405 static IPv4RouteListRef
1406 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
1407 IPv4RouteListRef service_routes
, Rank rank
)
1412 for (i
= 0, scan
= service_routes
->list
;
1413 i
< service_routes
->count
; i
++, scan
++) {
1414 routes
= IPv4RouteListAddRoute(routes
, init_size
, scan
, rank
);
1420 plist_get_cstring(CFDictionaryRef dict
, CFStringRef prop_name
,
1421 char * buf
, int buf_size
)
1425 val
= CFDictionaryGetValue(dict
, prop_name
);
1426 if (isA_CFString(val
) == NULL
) {
1429 if (CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingASCII
)
1437 * Function: IPv4RouteListCreateWithDictionary
1440 * Given the service ipv4 entity dictionary, generate the list of routes.
1441 * Currently, this includes just the default route and subnet route,
1442 * if the service has a subnet mask.
1445 * If the passed in route_list is NULL or too small, this routine
1446 * allocates malloc'd memory to hold the routes.
1448 static IPv4RouteListRef
1449 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
1450 CFDictionaryRef dict
,
1451 CFStringRef primaryRank
)
1453 struct in_addr addr
= { 0 };
1454 boolean_t exclude_from_nwi
= FALSE
;
1455 RouteFlags flags
= 0;
1456 unsigned int ifindex
;
1458 struct in_addr mask
= { 0 };
1460 boolean_t add_default
= FALSE
;
1461 boolean_t add_subnet
= FALSE
;
1463 struct in_addr subnet
= { 0 };
1464 struct in_addr router
= { 0 };
1465 Rank rank
= kRankAssertionDefault
;
1470 if (plist_get_cstring(dict
, kSCPropInterfaceName
, ifn
, sizeof(ifn
))
1474 #ifdef TEST_IPV4_ROUTELIST
1476 #else /* TEST_IPV4_ROUTELIST */
1477 ifindex
= if_nametoindex(ifn
);
1479 /* interface doesn't exist */
1482 #endif /* TEST_IPV4_ROUTELIST */
1483 if (cfstring_to_ip(CFDictionaryGetValue(dict
, kSCPropNetIPv4Router
),
1485 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
1487 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
1488 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
1490 subnet
= subnet_addr(addr
, mask
);
1491 /* ignore link-local subnets, let IPConfiguration handle them for now */
1492 if (ntohl(subnet
.s_addr
) != IN_LINKLOCALNETNUM
) {
1495 } else if (router
.s_addr
== 0) {
1496 exclude_from_nwi
= TRUE
;
1499 if (addr
.s_addr
== 0) {
1500 /* thanks for playing */
1503 if (router
.s_addr
== 0) {
1505 * If no router is configured, demote the rank. If there's already
1506 * a rank assertion that indicates RankNever, use that, otherwise
1509 flags
|= kRouteIsDirectToInterfaceFlag
;
1510 if (primaryRank
!= NULL
1511 && PrimaryRankGetRankAssertion(primaryRank
) == kRankAssertionNever
) {
1512 rank
= kRankAssertionNever
;
1515 rank
= kRankAssertionLast
;
1520 * If the router address is our address and the subnet mask is
1521 * not 255.255.255.255, assume all routes are local to the interface.
1523 if (addr
.s_addr
== router
.s_addr
1524 && mask
.s_addr
!= INADDR_BROADCAST
) {
1525 flags
|= kRouteIsDirectToInterfaceFlag
;
1527 if (primaryRank
!= NULL
) {
1528 rank
= PrimaryRankGetRankAssertion(primaryRank
);
1529 } else if (get_override_primary(dict
)) {
1530 rank
= kRankAssertionFirst
;
1534 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
1535 exclude_from_nwi
= TRUE
;
1536 flags
|= kRouteIsNULLFlag
;
1539 if (rank
== kRankAssertionNever
) {
1540 flags
|= kRouteIsScopedFlag
;
1543 if (add_subnet
&& (flags
& kRouteIsDirectToInterfaceFlag
) == 0
1544 && subnet
.s_addr
!= subnet_addr(router
, mask
).s_addr
) {
1545 flags
|= kRouteIsNotSubnetLocalFlag
;
1548 if (strncmp(ifn
, "lo0", sizeof(ifn
)) != 0) {
1553 if (routes
== NULL
|| routes
->size
< n
) {
1554 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
1557 bzero(routes
, IPv4RouteListComputeSize(n
));
1559 routes
->exclude_from_nwi
= exclude_from_nwi
;
1561 /* start at the beginning */
1565 /* add the default route */
1566 r
->ifindex
= ifindex
;
1567 strlcpy(r
->ifname
, ifn
, sizeof(r
->ifname
));
1570 if ((flags
& kRouteIsDirectToInterfaceFlag
) == 0) {
1571 r
->gateway
= router
;
1580 /* add the subnet route */
1582 if ((flags
& kRouteIsNULLFlag
) != 0) {
1583 r
->flags
|= kRouteIsNULLFlag
;
1585 r
->ifindex
= ifindex
;
1589 strlcpy(r
->ifname
, ifn
, sizeof(r
->ifname
));
1597 * Function: parse_component
1599 * Given a string 'key' and a string prefix 'prefix',
1600 * return the next component in the slash '/' separated
1604 * 1. key = "a/b/c" prefix = "a/"
1606 * 2. key = "a/b/c" prefix = "a/b/"
1609 static CF_RETURNS_RETAINED CFStringRef
1610 parse_component(CFStringRef key
, CFStringRef prefix
)
1612 CFMutableStringRef comp
;
1615 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
1618 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
1622 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
1623 range
= CFStringFind(comp
, CFSTR("/"), 0);
1624 if (range
.location
== kCFNotFound
) {
1627 range
.length
= CFStringGetLength(comp
) - range
.location
;
1628 CFStringDelete(comp
, range
);
1632 static CFMutableDictionaryRef
1633 service_dict_copy(CFStringRef serviceID
)
1635 CFDictionaryRef d
= NULL
;
1636 CFMutableDictionaryRef service_dict
;
1638 /* create a modifyable dictionary, a copy or a new one */
1639 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
1642 = CFDictionaryCreateMutable(NULL
, 0,
1643 &kCFTypeDictionaryKeyCallBacks
,
1644 &kCFTypeDictionaryValueCallBacks
);
1647 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
1649 return (service_dict
);
1653 log_service_entity(int level
, CFStringRef serviceID
, CFStringRef entity
,
1654 CFStringRef operation
, CFTypeRef val
)
1657 CFDataRef route_list
;
1658 CFMutableStringRef this_val
= NULL
;
1660 if (CFEqual(entity
, kSCEntNetIPv4
) && isA_CFDictionary(val
) != NULL
) {
1661 CFDictionaryRef service_dict
= NULL
;
1663 route_list
= CFDictionaryGetValue(val
, kIPv4DictRoutes
);
1664 if (route_list
!= NULL
) {
1665 /* ALIGN: CF should align to at least 8-byte boundaries */
1666 this_val
= IPv4RouteListCopyDescription((IPv4RouteListRef
)
1667 (void *)CFDataGetBytePtr(route_list
));
1670 service_dict
= CFDictionaryGetValue(val
, kIPv4DictService
);
1672 if (service_dict
!= NULL
&& isA_CFDictionary(service_dict
) != NULL
) {
1673 if (this_val
== NULL
) {
1674 this_val
= CFStringCreateMutable(NULL
, 0);
1676 CFStringAppendFormat(this_val
, NULL
, CFSTR("\n <IPv4Dictionary>: %@"), service_dict
);
1681 val
= CFSTR("<none>");
1683 my_log(level
, "IPMonitor: serviceID %@ %@ %@ value = %@",
1684 serviceID
, operation
, entity
, val
);
1685 my_CFRelease(&this_val
);
1690 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
1693 boolean_t changed
= FALSE
;
1695 CFMutableDictionaryRef service_dict
;
1697 service_dict
= service_dict_copy(serviceID
);
1698 old_val
= CFDictionaryGetValue(service_dict
, entity
);
1699 if (new_val
== NULL
) {
1700 if (old_val
!= NULL
) {
1701 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1702 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
1703 CFSTR("Removed:"), old_val
);
1705 CFDictionaryRemoveValue(service_dict
, entity
);
1710 if (old_val
== NULL
|| CFEqual(new_val
, old_val
) == FALSE
) {
1711 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1712 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
1713 CFSTR("Changed: old"), old_val
);
1714 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
1715 CFSTR("Changed: new"), new_val
);
1717 CFDictionarySetValue(service_dict
, entity
, new_val
);
1721 if (CFDictionaryGetCount(service_dict
) == 0) {
1722 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
1725 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
1727 my_CFRelease(&service_dict
);
1731 static CFDictionaryRef
1732 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
1734 CFDictionaryRef service_dict
;
1736 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
1737 if (service_dict
== NULL
) {
1740 return (CFDictionaryGetValue(service_dict
, entity
));
1743 #ifndef kSCPropNetHostname
1744 #define kSCPropNetHostname CFSTR("Hostname")
1749 copy_dhcp_hostname(CFStringRef serviceID
)
1751 CFDictionaryRef dict
= NULL
;
1752 CFStringRef hostname
= NULL
;
1753 CFDictionaryRef service_dict
= NULL
;
1755 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
1757 if (dict
== NULL
|| isA_CFDictionary(dict
) == NULL
) {
1762 CFDictionaryGetValue(dict
, kIPv4DictService
);
1764 if (service_dict
== NULL
1765 || isA_CFDictionary(service_dict
) == NULL
) {
1770 CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
1772 if (hostname
!= NULL
) {
1779 #if !TARGET_IPHONE_SIMULATOR
1781 ipv6_service_update_router(CFStringRef serviceID
, CFDictionaryRef new_val
)
1783 #ifdef SIOCDRADD_IN6
1786 CFStringRef new_router
= NULL
;
1787 char ntopbuf
[INET6_ADDRSTRLEN
];
1788 CFDictionaryRef old_val
= NULL
;
1789 CFStringRef old_router
= NULL
;
1790 struct in6_addr router_ip
;
1794 old_val
= service_dict_get(serviceID
, kSCEntNetIPv6
);
1795 if (old_val
!= NULL
) {
1796 plist_get_cstring(old_val
, kSCPropInterfaceName
, ifn
, sizeof(ifn
));
1797 old_router
= CFDictionaryGetValue(old_val
, kSCPropNetIPv6Router
);
1799 if (ifn
[0] == '\0') {
1801 || plist_get_cstring(new_val
, kSCPropInterfaceName
,
1802 ifn
, sizeof(ifn
)) == FALSE
) {
1803 /* no InterfaceName property, ignore it */
1807 if_index
= if_nametoindex(ifn
);
1808 if (if_index
== 0) {
1811 s
= inet6_dgram_socket();
1814 "IPMonitor: ipv6_service_update_router: socket failed, %s",
1818 if (new_val
!= NULL
) {
1819 new_router
= CFDictionaryGetValue(new_val
, kSCPropNetIPv6Router
);
1821 if (S_dict_get_boolean(old_val
, kIsNULL
, FALSE
) == FALSE
1822 && old_router
!= NULL
1823 && (new_router
== NULL
|| CFEqual(old_router
, new_router
) == FALSE
)) {
1824 /* remove the old Router */
1825 if (cfstring_to_ip6(old_router
, &router_ip
)) {
1826 if (IN6_IS_ADDR_LINKLOCAL(&router_ip
) ||
1827 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip
)) {
1829 router_ip
.__u6_addr
.__u6_addr16
[1] = htons(if_index
);
1831 if (siocdrdel_in6(s
, if_index
, &router_ip
) < 0) {
1832 if (errno
!= EINVAL
) {
1834 "IPMonitor: siocdrdel_in6(%s, %s) failed, %s",
1836 inet_ntop(AF_INET6
, &router_ip
,
1837 ntopbuf
, sizeof(ntopbuf
)),
1841 else if (S_IPMonitor_debug
& kDebugFlag1
) {
1843 "IPMonitor: %s removed default route %s",
1845 inet_ntop(AF_INET6
, &router_ip
,
1846 ntopbuf
, sizeof(ntopbuf
)));
1850 /* add the new Router */
1851 if (S_dict_get_boolean(new_val
, kIsNULL
, FALSE
) == FALSE
1852 && cfstring_to_ip6(new_router
, &router_ip
)) {
1853 if (IN6_IS_ADDR_LINKLOCAL(&router_ip
) ||
1854 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip
)) {
1856 router_ip
.__u6_addr
.__u6_addr16
[1] = htons(if_index
);
1858 if (siocdradd_in6(s
, if_index
, &router_ip
, 0) < 0) {
1859 if (errno
!= EINVAL
) {
1861 "IPMonitor: siocdradd_in6(%s, %s) failed, %s",
1863 inet_ntop(AF_INET6
, &router_ip
,
1864 ntopbuf
, sizeof(ntopbuf
)),
1868 else if (S_IPMonitor_debug
& kDebugFlag1
) {
1870 "IPMonitor: %s added default route %s",
1872 inet_ntop(AF_INET6
, &router_ip
,
1873 ntopbuf
, sizeof(ntopbuf
)));
1879 #endif /* SIOCDRADD_IN6 */
1882 #endif /* !TARGET_IPHONE_SIMULATOR */
1884 #define ALLOW_EMPTY_STRING 0x1
1886 static CF_RETURNS_RETAINED CFTypeRef
1887 sanitize_prop(CFTypeRef val
, uint32_t flags
)
1890 if (isA_CFString(val
)) {
1891 CFMutableStringRef str
;
1893 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
1894 CFStringTrimWhitespace(str
);
1895 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
1909 merge_array_prop(CFMutableDictionaryRef dict
,
1911 CFDictionaryRef state_dict
,
1912 CFDictionaryRef setup_dict
,
1916 CFMutableArrayRef merge_prop
;
1917 CFArrayRef setup_prop
= NULL
;
1918 CFArrayRef state_prop
= NULL
;
1920 if (setup_dict
!= NULL
) {
1921 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
1923 if (state_dict
!= NULL
) {
1924 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
1927 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
1931 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1932 if (setup_prop
!= NULL
) {
1936 n
= CFArrayGetCount(setup_prop
);
1937 for (i
= 0; i
< n
; i
++) {
1940 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
1941 val
= sanitize_prop(val
, flags
);
1943 CFArrayAppendValue(merge_prop
, val
);
1948 if (state_prop
!= NULL
1949 && (setup_prop
== NULL
|| S_append_state
)) {
1952 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
1954 n
= CFArrayGetCount(state_prop
);
1955 for (i
= 0; i
< n
; i
++) {
1958 val
= CFArrayGetValueAtIndex(state_prop
, i
);
1959 val
= sanitize_prop(val
, flags
);
1961 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
1962 CFArrayAppendValue(merge_prop
, val
);
1968 if (CFArrayGetCount(merge_prop
) > 0) {
1969 CFDictionarySetValue(dict
, key
, merge_prop
);
1971 CFRelease(merge_prop
);
1976 pick_prop(CFMutableDictionaryRef dict
,
1978 CFDictionaryRef state_dict
,
1979 CFDictionaryRef setup_dict
,
1982 CFTypeRef val
= NULL
;
1984 if (setup_dict
!= NULL
) {
1985 val
= CFDictionaryGetValue(setup_dict
, key
);
1986 val
= sanitize_prop(val
, flags
);
1988 if (val
== NULL
&& state_dict
!= NULL
) {
1989 val
= CFDictionaryGetValue(state_dict
, key
);
1990 val
= sanitize_prop(val
, flags
);
1993 CFDictionarySetValue(dict
, key
, val
);
2001 ** GetEntityChangesFunc functions
2004 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2005 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2007 CFDictionaryRef aggregated_dict
= NULL
;
2008 boolean_t changed
= FALSE
;
2009 CFMutableDictionaryRef dict
= NULL
;
2010 CFStringRef primaryRank
= NULL
;
2013 IPv4RouteListRef routes
;
2014 /* ALIGN: force align */
2015 uint32_t routes_buf
[roundup(IPv4RouteListComputeSize(R_STATIC
), sizeof(uint32_t))];
2016 CFDataRef routes_data
= NULL
;
2017 CFDictionaryRef service_options
;
2019 if (state_dict
== NULL
) {
2022 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
2023 if (service_options
!= NULL
) {
2024 primaryRank
= CFDictionaryGetValue(service_options
, kSCPropNetServicePrimaryRank
);
2026 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
2027 if (setup_dict
!= NULL
) {
2029 struct in_addr router_ip
;
2031 router
= CFDictionaryGetValue(setup_dict
,
2032 kSCPropNetIPv4Router
);
2034 && cfstring_to_ip(router
, &router_ip
)) {
2035 CFDictionarySetValue(dict
,
2036 kSCPropNetIPv4Router
,
2040 routes
= (IPv4RouteListRef
)(void *)routes_buf
;
2041 routes
->size
= R_STATIC
;
2043 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, primaryRank
);
2045 routes_data
= CFDataCreate(NULL
,
2047 IPv4RouteListComputeSize(r
->count
));
2054 "IPMonitor: %@ invalid IPv4 dictionary = %@",
2059 if (routes_data
!= NULL
) {
2060 CFStringRef keys
[2];
2061 CFTypeRef values
[2];
2063 keys
[0] = kIPv4DictService
;
2065 keys
[1] = kIPv4DictRoutes
;
2066 values
[1] = routes_data
;
2068 aggregated_dict
= CFDictionaryCreate(NULL
,
2071 sizeof(keys
)/sizeof(keys
[0]),
2072 &kCFTypeDictionaryKeyCallBacks
,
2073 &kCFTypeDictionaryValueCallBacks
);
2076 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, aggregated_dict
);
2077 if (routes_data
== NULL
) {
2078 /* clean up the rank too */
2079 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
2081 my_CFRelease(&dict
);
2082 my_CFRelease(&aggregated_dict
);
2083 my_CFRelease(&routes_data
);
2088 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2089 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2091 struct in6_addr addr
;
2093 boolean_t changed
= FALSE
;
2094 CFMutableDictionaryRef dict
= NULL
;
2095 CFDictionaryRef new_dict
= NULL
;
2096 CFStringRef router
= NULL
;
2097 struct in6_addr router_ip
;
2098 boolean_t valid_ip
= FALSE
;
2100 if (state_dict
== NULL
) {
2103 addrs
= isA_CFArray(CFDictionaryGetValue(state_dict
,
2104 kSCPropNetIPv6Addresses
));
2105 if (addrs
!= NULL
&& CFArrayGetCount(addrs
) > 0) {
2106 valid_ip
= cfstring_to_ip6(CFArrayGetValueAtIndex(addrs
, 0), &addr
);
2108 if (valid_ip
== FALSE
) {
2109 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2111 "IPMonitor: %@ has no valid IPv6 address, ignoring",
2116 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
2117 if (setup_dict
!= NULL
) {
2118 router
= CFDictionaryGetValue(setup_dict
,
2119 kSCPropNetIPv6Router
);
2120 if (router
!= NULL
&& cfstring_to_ip6(router
, &router_ip
)) {
2121 CFDictionarySetValue(dict
,
2122 kSCPropNetIPv6Router
,
2127 router
= CFDictionaryGetValue(dict
,
2128 kSCPropNetIPv6Router
);
2130 && cfstring_to_ip6(router
, &router_ip
) == FALSE
) {
2131 CFDictionaryRemoveValue(dict
, kSCPropNetIPv6Router
);
2138 #if !TARGET_IPHONE_SIMULATOR
2139 ipv6_service_update_router(serviceID
, new_dict
);
2140 #endif /* !TARGET_IPHONE_SIMULATOR */
2142 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, new_dict
);
2143 if (new_dict
== NULL
) {
2144 /* clean up the rank too */
2145 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
2147 my_CFRelease(&new_dict
);
2152 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
2153 CFMutableArrayRef out_servers
, CFStringRef interface
)
2158 count
= CFArrayGetCount(in_servers
);
2159 for (i
= 0; i
< count
; i
++) {
2161 struct in6_addr ipv6_addr
;
2162 struct in_addr ip_addr
;
2164 addr
= CFArrayGetValueAtIndex(in_servers
, i
);
2165 assert(addr
!= NULL
);
2167 if (cfstring_to_ip(addr
, &ip_addr
)) {
2169 if ((active_protos
& kProtocolFlagsIPv4
) == 0
2170 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
2171 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2173 "IPMonitor: no IPv4 connectivity, "
2174 "ignoring DNS server address ", IP_FORMAT
,
2182 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
2184 if ((active_protos
& kProtocolFlagsIPv6
) == 0
2185 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
2186 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2187 char ntopbuf
[INET6_ADDRSTRLEN
];
2190 "IPMonitor: no IPv6 connectivity, "
2191 "ignoring DNS server address %s",
2192 inet_ntop(AF_INET6
, &ipv6_addr
,
2193 ntopbuf
, sizeof(ntopbuf
)));
2198 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
2199 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
2200 && (interface
!= NULL
)
2201 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
2202 // append interface name to IPv6 link local address
2203 addr
= CFStringCreateWithFormat(NULL
, NULL
,
2212 /* bad IP address */
2214 "IPMonitor: ignoring bad DNS server address '%@'",
2219 /* DNS server is valid and one we want */
2220 CFArrayAppendValue(out_servers
, addr
);
2227 merge_dns_servers(CFMutableDictionaryRef new_dict
,
2228 CFArrayRef state_servers
,
2229 CFArrayRef setup_servers
,
2231 ProtocolFlags active_protos
,
2232 CFStringRef interface
)
2234 CFMutableArrayRef dns_servers
;
2235 Boolean have_dns_setup
= FALSE
;
2237 if (state_servers
== NULL
&& setup_servers
== NULL
) {
2238 /* no DNS servers */
2241 dns_servers
= CFArrayCreateMutable(NULL
, 0,
2242 &kCFTypeArrayCallBacks
);
2243 if (setup_servers
!= NULL
) {
2244 accumulate_dns_servers(setup_servers
, active_protos
,
2245 dns_servers
, interface
);
2246 if (CFArrayGetCount(dns_servers
) > 0) {
2247 have_dns_setup
= TRUE
;
2250 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
2251 && state_servers
!= NULL
) {
2252 accumulate_dns_servers(state_servers
, active_protos
,
2257 * Here, we determine whether or not we want all queries for this DNS
2258 * configuration to be bound to the associated network interface.
2260 * For dynamically derived network configurations (i.e. from State:)
2261 * this would be the preferred option using the argument "Hey, the
2262 * server told us to use these servers on this network so let's not
2265 * But, when a DNS configuration has been provided by the user/admin
2266 * via the Network pref pane (i.e. from Setup:) we opt to not force
2267 * binding of the outbound queries. The simplest example why we take
2268 * this stance is with a multi-homing configuration. Consider a system
2269 * with one network service associated with "en0" and a second service
2270 * associated with "en1". The "en0" service has been set higher in
2271 * the network service order so it would be primary but the user/admin
2272 * wants the DNS queries to go to a server only accessible via "en1".
2273 * Without this exception we would take the DNS server addresses from
2274 * the Network pref pane (for "en0") and have the queries bound to
2275 * "en0" where they'd never reach their intended destination (via
2276 * "en1"). So, our exception to the rule is that we will not bind
2277 * user/admin configurations to any specific network interface.
2279 * We also add an exception to the "follow the dynamically derived
2280 * network configuration" path for on-the-fly (no Setup: content)
2283 if (CFArrayGetCount(dns_servers
) != 0) {
2284 CFDictionarySetValue(new_dict
,
2285 kSCPropNetDNSServerAddresses
, dns_servers
);
2286 if (have_setup
&& !have_dns_setup
) {
2287 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
2291 my_CFRelease(&dns_servers
);
2297 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2298 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2300 ProtocolFlags active_protos
= kProtocolFlagsNone
;
2301 boolean_t changed
= FALSE
;
2303 Boolean have_setup
= FALSE
;
2304 CFStringRef interface
= NULL
;
2305 CFDictionaryRef ipv4
;
2306 CFDictionaryRef ipv6
;
2313 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
2314 { kSCPropNetDNSSortList
, 0, FALSE
},
2315 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
2316 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
2318 CFMutableDictionaryRef new_dict
= NULL
;
2319 const CFStringRef pick_list
[] = {
2320 kSCPropNetDNSDomainName
,
2321 kSCPropNetDNSOptions
,
2322 kSCPropNetDNSSearchOrder
,
2323 kSCPropNetDNSServerPort
,
2324 kSCPropNetDNSServerTimeout
,
2325 kSCPropNetDNSServiceIdentifier
,
2326 kSCPropNetDNSSupplementalMatchDomainsNoSearch
,
2328 IPv4RouteListRef routes
= NULL
;
2330 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
2331 /* there is no DNS content */
2335 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
2336 routes
= ipv4_dict_get_routelist(ipv4
);
2338 if (routes
!= NULL
) {
2339 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
2343 active_protos
|= kProtocolFlagsIPv4
;
2345 interface
= ipv4_dict_get_ifname(ipv4
);
2348 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
2351 get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
) != NULL
) {
2355 active_protos
|= kProtocolFlagsIPv6
;
2357 if (interface
== NULL
) {
2358 interface
= CFDictionaryGetValue(ipv6
,
2359 kSCPropInterfaceName
);
2364 if (active_protos
== kProtocolFlagsNone
) {
2365 /* there is no IPv4 nor IPv6 */
2366 if (state_dict
== NULL
) {
2367 /* ... and no DNS content that we care about */
2373 /* merge DNS configuration */
2374 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
2375 &kCFTypeDictionaryKeyCallBacks
,
2376 &kCFTypeDictionaryValueCallBacks
);
2378 if (active_protos
== kProtocolFlagsNone
) {
2379 merge_dns_servers(new_dict
,
2380 my_CFDictionaryGetArray(state_dict
,
2381 kSCPropNetDNSServerAddresses
),
2384 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
2388 merge_dns_servers(new_dict
,
2389 my_CFDictionaryGetArray(state_dict
,
2390 kSCPropNetDNSServerAddresses
),
2391 my_CFDictionaryGetArray(setup_dict
,
2392 kSCPropNetDNSServerAddresses
),
2398 for (i
= 0; i
< sizeof(merge_list
)/sizeof(merge_list
[0]); i
++) {
2399 merge_array_prop(new_dict
,
2403 merge_list
[i
].flags
,
2404 merge_list
[i
].append
);
2407 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2415 if (active_protos
== kProtocolFlagsNone
) {
2416 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
2417 if (CFDictionaryContainsKey(new_dict
,
2418 kSCPropNetDNSSupplementalMatchDomains
)) {
2419 /* only keep State: supplemental */
2420 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
2421 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
2422 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
2423 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
2425 if ((interface
== NULL
) && (setup_dict
== NULL
) && (state_dict
!= NULL
)) {
2427 * for supplemental-only configurations, add any scoped (or
2428 * wild-card "*") interface
2430 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
2432 } else if (CFDictionaryContainsKey(new_dict
, kSCPropNetDNSServiceIdentifier
) &&
2433 (interface
== NULL
) &&
2434 (state_dict
!= NULL
)) {
2435 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
2441 if (CFDictionaryGetCount(new_dict
) == 0) {
2442 my_CFRelease(&new_dict
);
2446 if (interface
!= NULL
) {
2447 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
2450 if (S_append_state
) {
2452 * ensure any specified domain name (e.g. the domain returned by
2453 * a DHCP server) is in the search list.
2455 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
2456 if (isA_CFString(domain
)) {
2459 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
2460 if (isA_CFArray(search
) &&
2461 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
2462 CFMutableArrayRef new_search
;
2464 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
2465 CFArrayAppendValue(new_search
, domain
);
2466 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
2467 my_CFRelease(&new_search
);
2473 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
2474 my_CFRelease(&new_dict
);
2479 merge_dict(const void *key
, const void *value
, void *context
)
2481 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
2483 CFDictionarySetValue(dict
, key
, value
);
2487 #define PROXY_AUTO_DISCOVERY_URL 252
2489 static CF_RETURNS_RETAINED CFStringRef
2490 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
2492 CFStringRef urlString
= NULL
;
2494 if (isA_CFDictionary(dhcp_options
)) {
2497 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
2500 const UInt8
*urlBytes
;
2503 urlBytes
= CFDataGetBytePtr(data
);
2504 urlLen
= CFDataGetLength(data
);
2505 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
2506 // remove trailing NUL
2514 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
2516 urlString
= CFURLGetString(url
);
2517 if (urlString
!= NULL
) {
2518 CFRetain(urlString
);
2528 static CF_RETURNS_RETAINED CFStringRef
2532 CFStringRef urlString
= NULL
;
2534 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
2536 urlString
= CFURLGetString(url
);
2537 if (urlString
!= NULL
) {
2538 CFRetain(urlString
);
2547 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2548 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2550 ProtocolFlags active_protos
= kProtocolFlagsNone
;
2551 boolean_t changed
= FALSE
;
2552 CFStringRef interface
= NULL
;
2553 CFDictionaryRef ipv4
;
2554 CFDictionaryRef ipv6
;
2555 CFMutableDictionaryRef new_dict
= NULL
;
2561 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
2562 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
2565 CFStringRef key1
; /* an "enable" key */
2569 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
2570 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
2571 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
2572 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
2573 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
2574 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
2575 { kSCPropNetProxiesProxyAutoConfigEnable
,
2576 kSCPropNetProxiesProxyAutoConfigURLString
,
2577 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
2578 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2582 IPv4RouteListRef routes
= NULL
;
2584 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
2585 /* there is no proxy content */
2589 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
2590 routes
= ipv4_dict_get_routelist(ipv4
);
2592 if (routes
!= NULL
) {
2593 active_protos
|= kProtocolFlagsIPv4
;
2595 interface
= ipv4_dict_get_ifname(ipv4
);
2598 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
2600 active_protos
|= kProtocolFlagsIPv6
;
2602 if (interface
== NULL
) {
2603 interface
= CFDictionaryGetValue(ipv6
,
2604 kSCPropInterfaceName
);
2608 if (active_protos
== kProtocolFlagsNone
) {
2609 /* there is no IPv4 nor IPv6 */
2610 if (state_dict
== NULL
) {
2611 /* ... and no proxy content that we care about */
2617 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
2619 CFMutableDictionaryRef setup_copy
;
2622 * Merge the per-service "Setup:" and "State:" proxy information with
2623 * the "Setup:" information always taking precedence. Additionally,
2624 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
2625 * Port) is defined than all of the values for that group will be
2626 * used. That is, we don't allow mixing some of the values from
2627 * the "Setup:" keys and others from the "State:" keys.
2629 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
2631 for (i
= 0; i
< sizeof(merge_list
)/sizeof(merge_list
[0]); i
++) {
2632 merge_array_prop(new_dict
,
2636 merge_list
[i
].flags
,
2637 merge_list
[i
].append
);
2640 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
2641 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2642 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
2644 * if a "Setup:" enabled key has been provided than we want to
2645 * ignore all of the "State:" keys
2647 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
2648 if (pick_list
[i
].key2
!= NULL
) {
2649 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
2651 if (pick_list
[i
].key3
!= NULL
) {
2652 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
2654 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
2655 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
2656 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
2658 * if a "Setup:" enabled key has not been provided and we have
2659 * some" "State:" keys than we remove all of of "Setup:" keys
2661 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
2662 if (pick_list
[i
].key2
!= NULL
) {
2663 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
2665 if (pick_list
[i
].key3
!= NULL
) {
2666 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
2671 /* merge the "Setup:" keys */
2672 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
2673 CFRelease(setup_copy
);
2675 else if (setup_dict
!= NULL
) {
2676 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
2678 else if (state_dict
!= NULL
) {
2679 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
2682 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
2683 CFRelease(new_dict
);
2687 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
2688 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
2692 if (new_dict
!= NULL
) {
2693 CFDictionaryRef dhcp_options
;
2695 CFNumberRef wpad
= NULL
;
2696 int wpadEnabled
= 0;
2697 CFStringRef wpadURL
= NULL
;
2699 if (CFDictionaryGetValueIfPresent(new_dict
,
2700 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2701 (const void **)&num
) &&
2702 isA_CFNumber(num
)) {
2703 /* if we have a WPAD key */
2705 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
2706 /* if we don't like the enabled key/value */
2714 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
2715 if (!isA_CFNumber(num
) ||
2716 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
2717 /* if we don't like the enabled key/value */
2724 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
2725 if (pacURL
!= NULL
) {
2726 if (!isA_CFString(pacURL
)) {
2727 /* if we don't like the PAC URL */
2733 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
2734 if (!isA_CFString(pacJS
)) {
2735 /* if we don't have (or like) the PAC JavaScript */
2743 * we already have a PAC URL so disable WPAD.
2750 * if WPAD is enabled and we don't already have a PAC URL then
2751 * we check for a DHCP provided URL. If not available, we use
2752 * a PAC URL pointing to a well-known file (wpad.dat) on a
2753 * well-known host (wpad.<domain>).
2755 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
2756 wpadURL
= wpadURL_dhcp(dhcp_options
);
2757 if (wpadURL
== NULL
) {
2758 wpadURL
= wpadURL_dns();
2760 if (wpadURL
== NULL
) {
2761 wpadEnabled
= 0; /* if we don't have a WPAD URL */
2766 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
2767 CFDictionarySetValue(new_dict
,
2768 kSCPropNetProxiesProxyAutoConfigEnable
,
2771 CFDictionarySetValue(new_dict
,
2772 kSCPropNetProxiesProxyAutoConfigURLString
,
2779 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
2780 CFDictionarySetValue(new_dict
,
2781 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2788 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
2789 my_CFRelease(&new_dict
);
2793 #if !TARGET_OS_IPHONE
2795 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2796 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2798 boolean_t changed
= FALSE
;
2800 CFMutableDictionaryRef new_dict
= NULL
;
2801 const CFStringRef pick_list
[] = {
2802 kSCPropNetSMBNetBIOSName
,
2803 kSCPropNetSMBNetBIOSNodeType
,
2804 #ifdef ADD_NETBIOS_SCOPE
2805 kSCPropNetSMBNetBIOSScope
,
2806 #endif // ADD_NETBIOS_SCOPE
2807 kSCPropNetSMBWorkgroup
,
2810 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
) {
2811 /* there is no IPv4 */
2815 if (state_dict
== NULL
&& setup_dict
== NULL
) {
2816 /* there is no SMB */
2820 /* merge SMB configuration */
2821 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
2822 &kCFTypeDictionaryKeyCallBacks
,
2823 &kCFTypeDictionaryValueCallBacks
);
2825 merge_array_prop(new_dict
,
2826 kSCPropNetSMBWINSAddresses
,
2831 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2839 if (CFDictionaryGetCount(new_dict
) == 0) {
2840 my_CFRelease(&new_dict
);
2845 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
2846 my_CFRelease(&new_dict
);
2849 #endif /* !TARGET_OS_IPHONE */
2852 services_info_get_interface(CFDictionaryRef services_info
,
2853 CFStringRef serviceID
)
2855 CFStringRef interface
= NULL
;
2856 CFDictionaryRef ipv4_dict
;
2858 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
2860 if (isA_CFDictionary(ipv4_dict
) != NULL
) {
2861 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
2864 CFDictionaryRef ipv6_dict
;
2866 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
2868 if (isA_CFDictionary(ipv6_dict
) != NULL
) {
2869 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
2877 static const CFStringRef
*statusEntityNames
[] = {
2884 get_transient_service_changes(CFStringRef serviceID
, CFDictionaryRef services_info
)
2886 boolean_t changed
= FALSE
;
2889 static const struct {
2890 const CFStringRef
*entityName
;
2891 const CFStringRef
*statusKey
;
2892 } transientServiceInfo
[] = {
2893 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
2894 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
2895 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
2898 for (i
= 0; i
< sizeof(transientServiceInfo
)/sizeof(transientServiceInfo
[0]); i
++) {
2899 CFDictionaryRef dict
;
2900 CFNumberRef status
= NULL
;
2901 CFMutableDictionaryRef ts_dict
= NULL
;
2903 dict
= get_service_state_entity(services_info
, serviceID
, *transientServiceInfo
[i
].entityName
);
2905 if (isA_CFDictionary(dict
) != NULL
) {
2906 status
= CFDictionaryGetValue(dict
, *transientServiceInfo
[i
].statusKey
);
2909 if (isA_CFNumber(status
) != NULL
) {
2910 ts_dict
= CFDictionaryCreateMutable(NULL
,
2912 &kCFTypeDictionaryKeyCallBacks
,
2913 &kCFTypeDictionaryValueCallBacks
);
2914 CFDictionaryAddValue(ts_dict
,
2915 *transientServiceInfo
[i
].statusKey
,
2919 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
, ts_dict
)) {
2923 if (ts_dict
!= NULL
) {
2931 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
2932 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
2934 boolean_t changed
= FALSE
;
2935 CFBooleanRef ip_is_coupled
= NULL
;
2936 CFMutableDictionaryRef new_dict
= NULL
;
2937 CFStringRef new_rank
= NULL
;
2938 CFStringRef setup_rank
= NULL
;
2939 CFStringRef state_rank
= NULL
;
2943 * Check "PrimaryRank" setting
2945 * Note 1: Rank setting in setup/state option overwrites the
2946 * Rank setting in interface
2947 * Within each rank setting, the following precedence is defined:
2949 * Note 2: Rank Never > Rank Last > Rank First > Rank None
2951 if (isA_CFDictionary(state_options
)) {
2952 CFBooleanRef coupled
;
2955 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
2956 state_rank
= isA_CFString(state_rank
);
2957 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
2958 if (isA_CFBoolean(coupled
) != NULL
) {
2959 ip_is_coupled
= coupled
;
2962 if (isA_CFDictionary(setup_options
)) {
2963 CFBooleanRef coupled
;
2966 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
2967 setup_rank
= isA_CFString(setup_rank
);
2969 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
2970 if (isA_CFBoolean(coupled
) != NULL
) {
2971 ip_is_coupled
= coupled
;
2975 if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankNever
)) ||
2976 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankNever
))) {
2977 new_rank
= kSCValNetServicePrimaryRankNever
;
2979 else if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankLast
)) ||
2980 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankLast
))) {
2981 new_rank
= kSCValNetServicePrimaryRankLast
;
2983 else if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankFirst
)) ||
2984 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankFirst
))) {
2985 new_rank
= kSCValNetServicePrimaryRankFirst
;
2988 /* This corresponds to Note 1 */
2989 if (setup_rank
== NULL
&& state_rank
== NULL
) {
2990 /* Fetch the interface associated with the service */
2991 CFStringRef interface
;
2993 interface
= services_info_get_interface(services_info
, serviceID
);
2995 /* Get the rank on that interface */
2996 if (interface
!= NULL
) {
2997 new_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
2998 if (S_IPMonitor_debug
& kDebugFlag1
) {
3000 "serviceID %@ interface %@ rank = %@",
3001 serviceID
, interface
,
3002 (new_rank
!= NULL
) ? new_rank
: CFSTR("<none>"));
3008 if (ip_is_coupled
!= NULL
&& CFBooleanGetValue(ip_is_coupled
) == FALSE
) {
3009 /* don't bother setting a value if it's the default */
3010 ip_is_coupled
= NULL
;
3012 if (new_rank
!= NULL
|| ip_is_coupled
!= NULL
) {
3013 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
3014 &kCFTypeDictionaryKeyCallBacks
,
3015 &kCFTypeDictionaryValueCallBacks
);
3016 if (new_rank
!= NULL
) {
3017 CFDictionarySetValue(new_dict
, kSCPropNetServicePrimaryRank
,
3020 if (ip_is_coupled
!= NULL
) {
3021 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
3024 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
3025 my_CFRelease(&new_dict
);
3030 if_rank_key_copy(CFStringRef ifname
)
3032 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
3033 kSCDynamicStoreDomainState
,
3039 if_rank_set(CFStringRef ifname
, CFDictionaryRef rank_dict
)
3041 CFStringRef rank
= NULL
;
3043 if (isA_CFDictionary(rank_dict
) != NULL
) {
3044 rank
= CFDictionaryGetValue(rank_dict
, kSCPropNetServicePrimaryRank
);
3045 rank
= isA_CFString(rank
);
3048 /* specific rank is asserted */
3050 if (S_IPMonitor_debug
& kDebugFlag1
) {
3051 my_log(LOG_DEBUG
, "Interface %@ asserted rank %@",
3054 CFDictionarySetValue(S_if_rank_dict
, ifname
, rank
);
3056 if (S_IPMonitor_debug
& kDebugFlag1
) {
3057 my_log(LOG_DEBUG
, "Interface %@ removed rank",
3060 CFDictionaryRemoveValue(S_if_rank_dict
, ifname
);
3066 if_rank_apply(const void * key
, const void * value
, void * context
)
3069 CFDictionaryRef rank_dict
= (CFDictionaryRef
)value
;
3071 /* State:/Network/Interface/<ifname>/Service, <ifname> is at index 3 */
3072 ifname
= my_CFStringCopyComponent(key
, CFSTR("/"), 3);
3073 if (ifname
!= NULL
) {
3074 if_rank_set(ifname
, rank_dict
);
3081 if_rank_dict_init(void)
3083 CFDictionaryRef info
;
3084 CFStringRef pattern
;
3085 CFArrayRef patterns
;
3088 = CFDictionaryCreateMutable(NULL
, 0,
3089 &kCFTypeDictionaryKeyCallBacks
,
3090 &kCFTypeDictionaryValueCallBacks
);
3091 pattern
= if_rank_key_copy(kSCCompAnyRegex
);
3092 patterns
= CFArrayCreate(NULL
,
3093 (const void **)&pattern
, 1,
3094 &kCFTypeArrayCallBacks
);
3096 info
= SCDynamicStoreCopyMultiple(S_session
, NULL
, patterns
);
3097 CFRelease(patterns
);
3099 CFDictionaryApplyFunction(info
, if_rank_apply
, NULL
);
3107 add_service_keys(CFStringRef serviceID
, CFMutableArrayRef keys
, CFMutableArrayRef patterns
)
3112 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
3116 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
3117 key
= setup_service_key(serviceID
, *entityTypeNames
[i
]);
3118 CFArrayAppendValue(keys
, key
);
3120 key
= state_service_key(serviceID
, *entityTypeNames
[i
]);
3121 CFArrayAppendValue(keys
, key
);
3125 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
3126 CFArrayAppendValue(patterns
, key
);
3129 key
= setup_service_key(serviceID
, NULL
);
3130 CFArrayAppendValue(patterns
, key
);
3132 key
= state_service_key(serviceID
, NULL
);
3133 CFArrayAppendValue(patterns
, key
);
3140 add_status_keys(CFStringRef service_id
, CFMutableArrayRef patterns
)
3144 for (i
= 0; i
< sizeof(statusEntityNames
)/sizeof(statusEntityNames
[0]); i
++) {
3145 CFStringRef pattern
;
3147 pattern
= state_service_key(service_id
, *statusEntityNames
[i
]);
3148 CFArrayAppendValue(patterns
, pattern
);
3155 static const CFStringRef
*reachabilitySetupKeys
[] = {
3157 &kSCEntNetInterface
,
3164 add_reachability_keys(CFMutableArrayRef patterns
)
3168 for (i
= 0; i
< sizeof(reachabilitySetupKeys
)/(sizeof(reachabilitySetupKeys
[0])); i
++)
3170 CFStringRef pattern
;
3171 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
3172 CFArrayAppendValue(patterns
, pattern
);
3179 add_vpn_keys(CFMutableArrayRef patterns
)
3181 CFStringRef pattern
;
3183 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
3184 CFArrayAppendValue(patterns
, pattern
);
3189 static CFDictionaryRef
3190 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
,
3191 CFArrayRef if_rank_list
)
3194 CFMutableArrayRef get_keys
;
3195 CFMutableArrayRef get_patterns
;
3197 CFDictionaryRef info
;
3200 count
= CFArrayGetCount(service_list
);
3201 get_keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3202 get_patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3204 CFArrayAppendValue(get_keys
, S_setup_global_ipv4
);
3205 CFArrayAppendValue(get_keys
, S_multicast_resolvers
);
3206 CFArrayAppendValue(get_keys
, S_private_resolvers
);
3208 for (s
= 0; s
< count
; s
++) {
3209 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
3211 add_service_keys(serviceID
, get_keys
, get_patterns
);
3212 add_status_keys(serviceID
, get_keys
);
3215 add_reachability_keys(get_patterns
);
3217 add_vpn_keys(get_patterns
);
3219 if_count
= (if_rank_list
!= NULL
)
3220 ? CFArrayGetCount(if_rank_list
) : 0;
3221 for (s
= 0; s
< if_count
; s
++) {
3222 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_list
, s
);
3225 key
= if_rank_key_copy(ifname
);
3226 CFArrayAppendValue(get_keys
, key
);
3230 info
= SCDynamicStoreCopyMultiple(session
, get_keys
, get_patterns
);
3231 my_CFRelease(&get_keys
);
3232 my_CFRelease(&get_patterns
);
3236 #if !TARGET_IPHONE_SIMULATOR
3237 static int rtm_seq
= 0;
3238 #endif /* !TARGET_IPHONE_SIMULATOR */
3240 #if !TARGET_IPHONE_SIMULATOR
3242 route_open_socket(void)
3246 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
3248 "IPMonitor: route_open_socket: socket failed, %s",
3253 #endif /* !TARGET_IPHONE_SIMULATOR */
3256 * Define: ROUTE_MSG_ADDRS_SPACE
3258 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
3259 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
3260 * someone changes the code and doesn't think to modify this.
3262 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
3263 + 2 * sizeof(struct sockaddr_dl) \
3266 struct rt_msghdr hdr
;
3267 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
3270 #if !TARGET_IPHONE_SIMULATOR
3272 ipv4_route(int sockfd
,
3273 int cmd
, struct in_addr gateway
, struct in_addr netaddr
,
3274 struct in_addr netmask
, char * ifname
, unsigned int ifindex
,
3275 struct in_addr ifa
, RouteFlags flags
)
3277 boolean_t default_route
= (netaddr
.s_addr
== 0);
3282 struct sockaddr_in
* in_p
;
3283 struct sockaddr_dl
* dl_p
;
3287 if (default_route
&& S_netboot
) {
3291 if (ifname
== NULL
) {
3292 /* this should not happen, but rather than crash, return an error */
3294 "IPMonitor: ipv4_route ifname is NULL on network address %s",
3295 inet_ntoa(netaddr
));
3298 if ((flags
& kRouteIsNULLFlag
) != 0) {
3299 my_log(LOG_DEBUG
, "IPMonitor: ignoring route %s on %s",
3300 inet_ntoa(netaddr
), ifname
);
3303 memset(&rtmsg
, 0, sizeof(rtmsg
));
3304 rtmsg
.hdr
.rtm_type
= cmd
;
3305 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3306 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3308 = RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
| RTA_IFP
| RTA_IFA
;
3310 && (flags
& kRouteIsDirectToInterfaceFlag
) == 0) {
3311 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_GATEWAY
| RTF_STATIC
;
3314 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_CLONING
| RTF_STATIC
;
3316 if ((flags
& kRouteIsScopedFlag
) != 0) {
3318 if (!S_scopedroute
) {
3322 /* specifically asked for a scoped route, yet no index supplied */
3324 "IPMonitor: ipv4_route index is 0 on %s-scoped route %s",
3325 ifname
, inet_ntoa(netaddr
));
3328 rtmsg
.hdr
.rtm_index
= ifindex
;
3329 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
3330 #else /* RTF_IFSCOPE */
3332 #endif /* RTF_IFSCOPE */
3335 rtaddr
.ptr
= rtmsg
.addrs
;
3338 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
3339 rtaddr
.in_p
->sin_family
= AF_INET
;
3340 rtaddr
.in_p
->sin_addr
= netaddr
;
3341 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3344 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
3345 /* gateway is an IP address */
3346 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
3347 rtaddr
.in_p
->sin_family
= AF_INET
;
3348 rtaddr
.in_p
->sin_addr
= gateway
;
3349 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3352 /* gateway is the interface itself */
3353 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3354 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3355 rtaddr
.dl_p
->sdl_nlen
= strlen(ifname
);
3356 bcopy(ifname
, rtaddr
.dl_p
->sdl_data
, rtaddr
.dl_p
->sdl_nlen
);
3357 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3361 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
3362 rtaddr
.in_p
->sin_family
= AF_INET
;
3363 rtaddr
.in_p
->sin_addr
= netmask
;
3364 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3366 /* interface name */
3367 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3368 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3369 rtaddr
.dl_p
->sdl_nlen
= strlen(ifname
);
3370 bcopy(ifname
, rtaddr
.dl_p
->sdl_data
, rtaddr
.dl_p
->sdl_nlen
);
3371 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3373 /* interface address */
3374 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
3375 rtaddr
.in_p
->sin_family
= AF_INET
;
3376 rtaddr
.in_p
->sin_addr
= ifa
;
3377 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3379 len
= sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
);
3380 rtmsg
.hdr
.rtm_msglen
= len
;
3381 if (write(sockfd
, &rtmsg
, len
) == -1) {
3386 #endif /* !TARGET_IPHONE_SIMULATOR */
3388 #if !TARGET_IPHONE_SIMULATOR
3390 ipv6_route(int cmd
, struct in6_addr gateway
, struct in6_addr netaddr
,
3391 struct in6_addr netmask
, char * ifname
, boolean_t is_direct
)
3393 boolean_t default_route
;
3395 boolean_t ret
= TRUE
;
3397 struct rt_msghdr hdr
;
3398 struct sockaddr_in6 dst
;
3399 struct sockaddr_in6 gway
;
3400 struct sockaddr_in6 mask
;
3401 struct sockaddr_dl ifp
;
3404 struct in6_addr zeroes
= IN6ADDR_ANY_INIT
;
3406 default_route
= (bcmp(&zeroes
, &netaddr
, sizeof(netaddr
)) == 0);
3408 if ((IN6_IS_ADDR_LINKLOCAL(&gateway
) ||
3409 IN6_IS_ADDR_MC_LINKLOCAL(&gateway
)) &&
3411 unsigned int index
= if_nametoindex(ifname
);
3413 /* add the scope id to the link local address */
3414 gateway
.__u6_addr
.__u6_addr16
[1] = (uint16_t)htons(index
);
3416 sockfd
= route_open_socket();
3420 memset(&rtmsg
, 0, sizeof(rtmsg
));
3421 rtmsg
.hdr
.rtm_type
= cmd
;
3422 if (default_route
) {
3424 /* if router is directly reachable, don't set the gateway flag */
3425 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3428 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_GATEWAY
| RTF_STATIC
;
3432 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_CLONING
| RTF_STATIC
;
3434 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3435 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3436 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
;
3437 rtmsg
.dst
.sin6_len
= sizeof(rtmsg
.dst
);
3438 rtmsg
.dst
.sin6_family
= AF_INET6
;
3439 rtmsg
.dst
.sin6_addr
= netaddr
;
3440 rtmsg
.gway
.sin6_len
= sizeof(rtmsg
.gway
);
3441 rtmsg
.gway
.sin6_family
= AF_INET6
;
3442 rtmsg
.gway
.sin6_addr
= gateway
;
3443 rtmsg
.mask
.sin6_len
= sizeof(rtmsg
.mask
);
3444 rtmsg
.mask
.sin6_family
= AF_INET6
;
3445 rtmsg
.mask
.sin6_addr
= netmask
;
3447 len
= sizeof(rtmsg
);
3449 rtmsg
.ifp
.sdl_len
= sizeof(rtmsg
.ifp
);
3450 rtmsg
.ifp
.sdl_family
= AF_LINK
;
3451 rtmsg
.ifp
.sdl_nlen
= strlen(ifname
);
3452 rtmsg
.hdr
.rtm_addrs
|= RTA_IFP
;
3453 bcopy(ifname
, rtmsg
.ifp
.sdl_data
, rtmsg
.ifp
.sdl_nlen
);
3456 /* no ifp information */
3457 len
-= sizeof(rtmsg
.ifp
);
3459 rtmsg
.hdr
.rtm_msglen
= len
;
3460 if (write(sockfd
, &rtmsg
, len
) == -1) {
3461 if ((cmd
== RTM_ADD
) && (errno
== EEXIST
)) {
3462 /* no sense complaining about a route that already exists */
3464 else if ((cmd
== RTM_DELETE
) && (errno
== ESRCH
)) {
3465 /* no sense complaining about a route that isn't there */
3468 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3470 "IPMonitor ipv6_route: write routing"
3471 " socket failed, %s", strerror(errno
));
3482 ipv6_default_route_delete(void)
3484 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3485 my_log(LOG_DEBUG
, "IPMonitor: IPv6 route delete default");
3487 return (ipv6_route(RTM_DELETE
, S_ip6_zeros
, S_ip6_zeros
, S_ip6_zeros
,
3492 ipv6_default_route_add(struct in6_addr router
, char * ifname
,
3493 boolean_t is_direct
)
3495 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3496 char ntopbuf
[INET6_ADDRSTRLEN
];
3499 "IPMonitor: IPv6 route add default"
3500 " %s interface %s direct %d",
3501 inet_ntop(AF_INET6
, &router
, ntopbuf
, sizeof(ntopbuf
)),
3504 return (ipv6_route(RTM_ADD
, router
, S_ip6_zeros
, S_ip6_zeros
,
3505 ifname
, is_direct
));
3507 #endif /* !TARGET_IPHONE_SIMULATOR */
3510 #if !TARGET_IPHONE_SIMULATOR
3512 multicast_route_delete(int sockfd
)
3514 struct in_addr gateway
= { htonl(INADDR_LOOPBACK
) };
3515 struct in_addr netaddr
= { htonl(INADDR_UNSPEC_GROUP
) };
3516 struct in_addr netmask
= { htonl(IN_CLASSD_NET
) };
3518 return (ipv4_route(sockfd
, RTM_DELETE
, gateway
, netaddr
, netmask
, "lo0", 0,
3523 multicast_route_add(int sockfd
)
3525 struct in_addr gateway
= { htonl(INADDR_LOOPBACK
) };
3526 struct in_addr netaddr
= { htonl(INADDR_UNSPEC_GROUP
) };
3527 struct in_addr netmask
= { htonl(IN_CLASSD_NET
) };
3529 return (ipv4_route(sockfd
, RTM_ADD
, gateway
, netaddr
, netmask
, "lo0", 0,
3532 #endif /* !TARGET_IPHONE_SIMULATOR */
3534 #if !TARGET_IPHONE_SIMULATOR
3537 set_ipv6_default_interface(char * ifname
)
3539 struct in6_ndifreq ndifreq
;
3542 bzero((char *)&ndifreq
, sizeof(ndifreq
));
3543 if (ifname
!= NULL
) {
3544 strlcpy(ndifreq
.ifname
, ifname
, sizeof(ndifreq
.ifname
));
3545 ndifreq
.ifindex
= if_nametoindex(ifname
);
3547 strlcpy(ndifreq
.ifname
, "lo0", sizeof(ndifreq
.ifname
));
3548 ndifreq
.ifindex
= 0;
3551 sock
= inet6_dgram_socket();
3554 "IPMonitor: set_ipv6_default_interface: socket failed, %s",
3558 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
3560 "IPMonitor: set_ipv6_default_interface: ioctl(SIOCSDEFIFACE_IN6) failed, %s",
3566 #endif /* RTF_IFSCOPE */
3569 set_ipv6_router(struct in6_addr
* router
, char * ifname
, boolean_t is_direct
)
3571 /* assign the new default route, ensure local multicast route available */
3572 (void)ipv6_default_route_delete();
3573 if (router
!= NULL
) {
3574 (void)ipv6_default_route_add(*router
, ifname
, is_direct
);
3578 #endif /* !TARGET_IPHONE_SIMULATOR */
3580 #if !TARGET_OS_IPHONE
3581 static __inline__
void
3584 (void)unlink(VAR_RUN_RESOLV_CONF
);
3588 set_dns(CFArrayRef val_search_domains
,
3589 CFStringRef val_domain_name
,
3590 CFArrayRef val_servers
,
3591 CFArrayRef val_sortlist
)
3593 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
3595 /* publish new resolv.conf */
3600 SCPrint(TRUE
, f
, CFSTR("#\n"));
3601 SCPrint(TRUE
, f
, CFSTR("# Mac OS X Notice\n"));
3602 SCPrint(TRUE
, f
, CFSTR("#\n"));
3603 SCPrint(TRUE
, f
, CFSTR("# This file is not used by the host name and address resolution\n"));
3604 SCPrint(TRUE
, f
, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
3605 SCPrint(TRUE
, f
, CFSTR("# this Mac OS X system.\n"));
3606 SCPrint(TRUE
, f
, CFSTR("#\n"));
3607 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
3608 SCPrint(TRUE
, f
, CFSTR("#\n"));
3610 if (isA_CFArray(val_search_domains
)) {
3611 SCPrint(TRUE
, f
, CFSTR("search"));
3612 n
= CFArrayGetCount(val_search_domains
);
3613 for (i
= 0; i
< n
; i
++) {
3616 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
3617 if (isA_CFString(domain
)) {
3618 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
3621 SCPrint(TRUE
, f
, CFSTR("\n"));
3623 else if (isA_CFString(val_domain_name
)) {
3624 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
3627 if (isA_CFArray(val_servers
)) {
3628 n
= CFArrayGetCount(val_servers
);
3629 for (i
= 0; i
< n
; i
++) {
3630 CFStringRef nameserver
;
3632 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
3633 if (isA_CFString(nameserver
)) {
3634 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
3639 if (isA_CFArray(val_sortlist
)) {
3640 SCPrint(TRUE
, f
, CFSTR("sortlist"));
3641 n
= CFArrayGetCount(val_sortlist
);
3642 for (i
= 0; i
< n
; i
++) {
3643 CFStringRef address
;
3645 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
3646 if (isA_CFString(address
)) {
3647 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
3650 SCPrint(TRUE
, f
, CFSTR("\n"));
3654 rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
3658 #endif /* !TARGET_OS_IPHONE */
3660 #if !TARGET_IPHONE_SIMULATOR
3662 router_is_our_ipv6_address(CFStringRef router
, CFArrayRef addr_list
)
3665 CFIndex n
= CFArrayGetCount(addr_list
);
3668 (void)cfstring_to_ip6(router
, &r
);
3669 for (i
= 0; i
< n
; i
++) {
3672 if (cfstring_to_ip6(CFArrayGetValueAtIndex(addr_list
, i
), &ip
)
3673 && bcmp(&r
, &ip
, sizeof(r
)) == 0) {
3679 #endif /* !TARGET_IPHONE_SIMULATOR */
3681 static IPv4RouteListRef
3682 service_dict_get_ipv4_routelist(CFDictionaryRef service_dict
)
3684 CFDictionaryRef dict
;
3686 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3688 return (ipv4_dict_get_routelist(dict
));
3692 service_dict_get_ipv4_ifname(CFDictionaryRef service_dict
)
3694 CFDictionaryRef dict
;
3696 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3697 return (ipv4_dict_get_ifname(dict
));
3701 service_get_ip_is_coupled(CFStringRef serviceID
)
3703 CFDictionaryRef dict
;
3704 boolean_t ip_is_coupled
= FALSE
;
3706 dict
= service_dict_get(serviceID
, kSCEntNetService
);
3708 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
3709 ip_is_coupled
= TRUE
;
3712 return (ip_is_coupled
);
3715 #if !TARGET_IPHONE_SIMULATOR
3717 typedef struct apply_ipv4_route_context
{
3718 IPv4RouteListRef old
;
3719 IPv4RouteListRef
new;
3721 } apply_ipv4_route_context_t
;
3723 /* add/remove a router/32 subnet */
3725 ipv4_route_gateway(int sockfd
, int cmd
, char * ifn_p
,
3726 IPv4RouteRef def_route
)
3728 struct in_addr mask
;
3730 mask
.s_addr
= htonl(INADDR_BROADCAST
);
3731 return (ipv4_route(sockfd
, cmd
, def_route
->ifa
,
3732 def_route
->gateway
, mask
, ifn_p
, def_route
->ifindex
,
3733 def_route
->ifa
, def_route
->flags
));
3737 * Function: apply_ipv4_route
3739 * Callback function that adds/removes the specified route.
3742 apply_ipv4_route(IPv4RouteListApplyCommand cmd
, IPv4RouteRef route
, void * arg
)
3744 apply_ipv4_route_context_t
*context
= (apply_ipv4_route_context_t
*)arg
;
3748 ifn_p
= route
->ifname
;
3750 case kIPv4RouteListAddRouteCommand
:
3751 if ((route
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
3752 retval
= ipv4_route_gateway(context
->sockfd
, RTM_ADD
,
3754 if (retval
== EEXIST
) {
3755 /* delete and add again */
3756 (void)ipv4_route_gateway(context
->sockfd
, RTM_DELETE
,
3758 retval
= ipv4_route_gateway(context
->sockfd
, RTM_ADD
,
3763 "IPMonitor apply_ipv4_route failed to add"
3765 inet_ntoa(route
->gateway
), strerror(retval
));
3767 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3768 my_log(LOG_DEBUG
, "Added IPv4 Route %s/32",
3769 inet_ntoa(route
->gateway
));
3772 retval
= ipv4_route(context
->sockfd
,
3773 RTM_ADD
, route
->gateway
,
3774 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
3775 route
->ifa
, route
->flags
);
3776 if (retval
== EEXIST
) {
3777 /* delete and add again */
3778 (void)ipv4_route(context
->sockfd
,
3779 RTM_DELETE
, route
->gateway
,
3780 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
3781 route
->ifa
, route
->flags
);
3782 retval
= ipv4_route(context
->sockfd
,
3783 RTM_ADD
, route
->gateway
,
3784 route
->dest
, route
->mask
,
3785 ifn_p
, route
->ifindex
,
3786 route
->ifa
, route
->flags
);
3790 "IPMonitor apply_ipv4_route failed to add"
3791 " route, %s:", strerror(retval
));
3792 IPv4RouteLog(LOG_NOTICE
, route
);
3794 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3796 "Added IPv4 route new[%d] = ",
3797 route
- context
->new->list
);
3798 IPv4RouteLog(LOG_DEBUG
, route
);
3801 case kIPv4RouteListRemoveRouteCommand
:
3802 retval
= ipv4_route(context
->sockfd
,
3803 RTM_DELETE
, route
->gateway
,
3804 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
3805 route
->ifa
, route
->flags
);
3807 if (retval
!= ESRCH
) {
3809 "IPMonitor apply_ipv4_route failed to remove"
3810 " route, %s: ", strerror(retval
));
3811 IPv4RouteLog(LOG_NOTICE
, route
);
3814 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3816 "Removed IPv4 route old[%d] = ",
3817 route
- context
->old
->list
);
3818 IPv4RouteLog(LOG_DEBUG
, route
);
3820 if ((route
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
3821 retval
= ipv4_route_gateway(context
->sockfd
, RTM_DELETE
,
3825 "IPMonitor apply_ipv4_route failed to remove"
3826 " %s/32 route, %s: ",
3827 inet_ntoa(route
->gateway
), strerror(retval
));
3829 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3830 my_log(LOG_DEBUG
, "Removed IPv4 Route %s/32",
3831 inet_ntoa(route
->gateway
));
3840 #endif /* !TARGET_IPHONE_SIMULATOR */
3843 * Function: update_ipv4
3846 * Update the IPv4 configuration based on the latest information.
3847 * Publish the State:/Network/Global/IPv4 information, and update the
3848 * IPv4 routing table. IPv4RouteListApply() invokes our callback,
3849 * apply_ipv4_route(), to install/remove the routes.
3852 update_ipv4(CFStringRef primary
,
3853 IPv4RouteListRef new_routelist
,
3854 keyChangeListRef keys
)
3856 #if !TARGET_IPHONE_SIMULATOR
3857 apply_ipv4_route_context_t context
;
3858 #endif /* !TARGET_IPHONE_SIMULATOR */
3861 if (new_routelist
!= NULL
&& primary
!= NULL
) {
3862 char * ifn_p
= NULL
;
3864 CFMutableDictionaryRef dict
= NULL
;
3866 dict
= CFDictionaryCreateMutable(NULL
, 0,
3867 &kCFTypeDictionaryKeyCallBacks
,
3868 &kCFTypeDictionaryValueCallBacks
);
3869 /* the first entry is the default route */
3870 r
= new_routelist
->list
;
3871 if (r
->gateway
.s_addr
!= 0) {
3874 router
= CFStringCreateWithCString(NULL
,
3875 inet_ntoa(r
->gateway
),
3876 kCFStringEncodingASCII
);
3877 if (router
!= NULL
) {
3878 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, router
);
3882 if (r
->ifname
[0] != '\0') {
3885 if (ifn_p
!= NULL
) {
3886 CFStringRef ifname_cf
;
3888 ifname_cf
= CFStringCreateWithCString(NULL
,
3890 kCFStringEncodingASCII
);
3891 if (ifname_cf
!= NULL
) {
3892 CFDictionarySetValue(dict
,
3893 kSCDynamicStorePropNetPrimaryInterface
,
3895 CFRelease(ifname_cf
);
3898 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
3900 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
3904 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
3908 #if !TARGET_IPHONE_SIMULATOR
3909 bzero(&context
, sizeof(context
));
3910 context
.sockfd
= route_open_socket();
3911 if (context
.sockfd
!= -1) {
3912 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3913 if (S_ipv4_routelist
== NULL
) {
3914 my_log(LOG_DEBUG
, "Old Routes = <none>");
3917 my_log(LOG_DEBUG
, "Old Routes = ");
3918 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
3920 if (new_routelist
== NULL
) {
3921 my_log(LOG_DEBUG
, "New Routes = <none>");
3924 my_log(LOG_DEBUG
, "New Routes = ");
3925 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
3928 context
.old
= S_ipv4_routelist
;
3929 context
.new = new_routelist
;
3930 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
,
3931 &apply_ipv4_route
, (void *)&context
);
3932 if (new_routelist
!= NULL
) {
3933 (void)multicast_route_delete(context
.sockfd
);
3936 (void)multicast_route_add(context
.sockfd
);
3938 close(context
.sockfd
);
3940 if (S_ipv4_routelist
!= NULL
) {
3941 free(S_ipv4_routelist
);
3943 S_ipv4_routelist
= new_routelist
;
3944 #endif /* !TARGET_IPHONE_SIMULATOR */
3950 update_ipv6(CFStringRef primary
,
3951 keyChangeListRef keys
)
3953 CFDictionaryRef ipv6_dict
= NULL
;
3955 if (primary
!= NULL
) {
3956 ipv6_dict
= service_dict_get(primary
, kSCEntNetIPv6
);
3958 if (ipv6_dict
!= NULL
) {
3959 #if !TARGET_IPHONE_SIMULATOR
3961 #endif /* !TARGET_IPHONE_SIMULATOR */
3962 CFMutableDictionaryRef dict
= NULL
;
3963 CFStringRef if_name
= NULL
;
3964 #if !TARGET_IPHONE_SIMULATOR
3965 char ifn
[IFNAMSIZ
] = { '\0' };
3966 char * ifn_p
= NULL
;
3967 boolean_t is_direct
= FALSE
;
3968 #endif /* !TARGET_IPHONE_SIMULATOR */
3969 CFStringRef val_router
= NULL
;
3971 dict
= CFDictionaryCreateMutable(NULL
, 0,
3972 &kCFTypeDictionaryKeyCallBacks
,
3973 &kCFTypeDictionaryValueCallBacks
);
3975 #if !TARGET_IPHONE_SIMULATOR
3976 addrs
= CFDictionaryGetValue(ipv6_dict
,
3977 kSCPropNetIPv6Addresses
);
3978 #endif /* !TARGET_IPHONE_SIMULATOR */
3980 val_router
= CFDictionaryGetValue(ipv6_dict
, kSCPropNetIPv6Router
);
3981 if (val_router
!= NULL
) {
3982 #if !TARGET_IPHONE_SIMULATOR
3983 is_direct
= router_is_our_ipv6_address(val_router
, addrs
);
3984 #endif /* !TARGET_IPHONE_SIMULATOR */
3985 /* no router if router is one of our IP addresses */
3986 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
,
3989 #if !TARGET_IPHONE_SIMULATOR
3991 val_router
= CFArrayGetValueAtIndex(addrs
, 0);
3994 #endif /* !TARGET_IPHONE_SIMULATOR */
3995 if_name
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
3997 CFDictionarySetValue(dict
,
3998 kSCDynamicStorePropNetPrimaryInterface
,
4000 #if !TARGET_IPHONE_SIMULATOR
4001 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
4002 kCFStringEncodingASCII
)) {
4005 #endif /* !TARGET_IPHONE_SIMULATOR */
4007 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
4009 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
4012 #if !TARGET_IPHONE_SIMULATOR
4014 if (S_scopedroute_v6
) {
4015 set_ipv6_default_interface(ifn_p
);
4017 #endif /* RTF_IFSCOPE */
4018 { /* route add default ... */
4019 struct in6_addr router
;
4021 (void)cfstring_to_ip6(val_router
, &router
);
4022 set_ipv6_router(&router
, ifn_p
, is_direct
);
4024 #endif /* !TARGET_IPHONE_SIMULATOR */
4027 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
4028 #if !TARGET_IPHONE_SIMULATOR
4030 if (S_scopedroute_v6
) {
4031 set_ipv6_default_interface(NULL
);
4033 #endif /* RTF_IFSCOPE */
4034 { /* route delete default ... */
4035 set_ipv6_router(NULL
, NULL
, FALSE
);
4037 #endif /* !TARGET_IPHONE_SIMULATOR */
4043 update_dns(CFDictionaryRef services_info
,
4044 CFStringRef primary
,
4045 keyChangeListRef keys
)
4047 Boolean changed
= FALSE
;
4048 CFDictionaryRef dict
= NULL
;
4050 if (primary
!= NULL
) {
4051 CFDictionaryRef service_dict
;
4053 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
4054 if (service_dict
!= NULL
) {
4055 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
4059 if (!_SC_CFEqual(S_dns_dict
, dict
)) {
4061 #if !TARGET_OS_IPHONE
4063 #endif /* !TARGET_OS_IPHONE */
4064 keyChangeListRemoveValue(keys
, S_state_global_dns
);
4066 CFMutableDictionaryRef new_dict
;
4068 #if !TARGET_OS_IPHONE
4069 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
4070 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
4071 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
4072 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
4073 #endif /* !TARGET_OS_IPHONE */
4074 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
4075 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
4076 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
4077 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
4078 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
4079 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
4080 CFRelease(new_dict
);
4085 if (dict
!= NULL
) CFRetain(dict
);
4086 if (S_dns_dict
!= NULL
) CFRelease(S_dns_dict
);
4093 update_dnsinfo(CFDictionaryRef services_info
,
4094 CFStringRef primary
,
4095 keyChangeListRef keys
,
4096 CFArrayRef service_order
)
4099 CFDictionaryRef dict
= NULL
;
4100 CFArrayRef multicastResolvers
;
4101 CFArrayRef privateResolvers
;
4103 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
4104 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
4106 if (primary
!= NULL
) {
4107 CFDictionaryRef service_dict
;
4109 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
4110 if (service_dict
!= NULL
) {
4111 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
4115 changed
= dns_configuration_set(dict
,
4116 S_service_state_dict
,
4121 keyChangeListNotifyKey(keys
, S_state_global_dns
);
4127 update_nwi(nwi_state_t state
)
4129 unsigned char signature
[CC_SHA1_DIGEST_LENGTH
];
4130 static unsigned char signature_last
[CC_SHA1_DIGEST_LENGTH
];
4132 _nwi_state_signature(state
, signature
, sizeof(signature
));
4133 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
4137 // save [new] signature
4138 bcopy(signature
, signature_last
, sizeof(signature
));
4140 // save [new] configuration
4141 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4142 my_log(LOG_DEBUG
, "Updating network information");
4143 S_nwi_state_dump(state
);
4145 if (_nwi_state_store(state
) == FALSE
) {
4146 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
4153 update_proxies(CFDictionaryRef services_info
,
4154 CFStringRef primary
,
4155 keyChangeListRef keys
,
4156 CFArrayRef service_order
)
4158 Boolean changed
= FALSE
;
4159 CFDictionaryRef dict
= NULL
;
4160 CFDictionaryRef new_dict
;
4162 if (primary
!= NULL
) {
4163 CFDictionaryRef service_dict
;
4165 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
4166 if (service_dict
!= NULL
) {
4167 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
4171 new_dict
= proxy_configuration_update(dict
,
4172 S_service_state_dict
,
4175 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
4176 if (new_dict
== NULL
) {
4177 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
4179 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
4184 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
4185 S_proxies_dict
= new_dict
;
4190 #if !TARGET_OS_IPHONE
4192 update_smb(CFDictionaryRef services_info
,
4193 CFStringRef primary
,
4194 keyChangeListRef keys
)
4196 Boolean changed
= FALSE
;
4197 CFDictionaryRef dict
= NULL
;
4199 if (primary
!= NULL
) {
4200 CFDictionaryRef service_dict
;
4202 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
4203 if (service_dict
!= NULL
) {
4204 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
4208 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
4210 keyChangeListRemoveValue(keys
, S_state_global_smb
);
4212 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
4217 if (dict
!= NULL
) CFRetain(dict
);
4218 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
4223 #endif /* !TARGET_OS_IPHONE */
4226 get_service_rank(CFArrayRef order
, int n_order
, CFStringRef serviceID
)
4229 Rank rank
= kRankIndexMask
;
4231 if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
4232 for (i
= 0; i
< n_order
; i
++) {
4233 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
4238 if (CFEqual(serviceID
, s
)) {
4248 ** Service election:
4251 * Function: rank_dict_get_service_rank
4253 * Retrieve the service rank in the given dictionary.
4256 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
4259 Rank rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
4261 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
4263 CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
);
4269 * Function: rank_dict_set_service_rank
4271 * Save the results of ranking the service so we can look it up later without
4272 * repeating all of the ranking code.
4275 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
4276 CFStringRef serviceID
, Rank rank_val
)
4280 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
4282 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
4288 static const CFStringRef
*transientInterfaceEntityNames
[] = {
4294 CollectTransientServices(const void * key
,
4299 CFStringRef service
= key
;
4300 CFMutableArrayRef vif_setup_keys
= context
;
4302 /* This service is either a vpn type service or a comm center service */
4303 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
4307 for (i
= 0; i
< sizeof(transientInterfaceEntityNames
)/sizeof(transientInterfaceEntityNames
[0]); i
++) {
4308 if (!CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
4312 CFArrayAppendValue(vif_setup_keys
, service
);
4318 static SCNetworkReachabilityFlags
4319 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
4320 CFStringRef service_id
,
4322 CFStringRef vpn_setup_key
)
4325 CFDictionaryRef dict
;
4326 SCNetworkReachabilityFlags flags
= 0;
4329 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4330 kSCDynamicStoreDomainSetup
,
4332 kSCEntNetInterface
);
4333 dict
= CFDictionaryGetValue(services_info
, key
);
4336 if (isA_CFDictionary(dict
)
4337 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
4339 flags
= (kSCNetworkReachabilityFlagsReachable
4340 | kSCNetworkReachabilityFlagsTransientConnection
4341 | kSCNetworkReachabilityFlagsConnectionRequired
);
4343 if (CFEqual(entity
, kSCEntNetPPP
)) {
4345 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
4347 if (!isA_CFDictionary(p_dict
)) {
4351 // get PPP dial-on-traffic status
4352 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
4353 if (isA_CFNumber(num
)) {
4356 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
4358 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
4368 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
4370 Boolean ret
= def_value
;
4375 val
= CFDictionaryGetValue(dict
, key
);
4376 if (isA_CFBoolean(val
) != NULL
) {
4377 ret
= CFBooleanGetValue(val
);
4385 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
4386 SCNetworkReachabilityFlags
*reach_flags_v4
,
4387 SCNetworkReachabilityFlags
*reach_flags_v6
)
4391 CFMutableArrayRef vif_setup_keys
;
4393 vif_setup_keys
= CFArrayCreateMutable(NULL
,
4395 &kCFTypeArrayCallBacks
);
4397 CFDictionaryApplyFunction(services_info
, CollectTransientServices
, vif_setup_keys
);
4399 count
= CFArrayGetCount(vif_setup_keys
);
4402 my_log(LOG_DEBUG
, "Collected the following VIF Setup Keys: %@", vif_setup_keys
);
4405 for (i
= 0; i
< count
; i
++) {
4406 CFArrayRef components
= NULL
;
4408 CFStringRef service_id
;
4409 CFStringRef vif_setup_key
;
4411 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
4414 * setup key in the following format:
4415 * Setup:/Network/Service/<Service ID>/<Entity>
4417 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
4419 if (CFArrayGetCount(components
) != 5) {
4420 my_log(LOG_ERR
, "Invalid Setup Key encountered: %@", vif_setup_key
);
4424 /* service id is the 3rd element */
4425 service_id
= CFArrayGetValueAtIndex(components
, 3);
4427 /* entity id is the 4th element */
4428 entity
= CFArrayGetValueAtIndex(components
, 4);
4430 my_log(LOG_DEBUG
, "Service %@ is a %@ Entity", service_id
, entity
);
4433 if (CFEqual(entity
, kSCEntNetPPP
)) {
4434 SCNetworkReachabilityFlags flags
;
4437 flags
= GetReachabilityFlagsFromVPN(services_info
,
4442 /* Check for the v4 reachability flags */
4443 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4444 kSCDynamicStoreDomainSetup
,
4448 if (CFDictionaryContainsKey(services_info
, key
)) {
4449 *reach_flags_v4
|= flags
;
4450 my_log(LOG_DEBUG
,"Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
4455 /* Check for the v6 reachability flags */
4456 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4457 kSCDynamicStoreDomainSetup
,
4461 if (CFDictionaryContainsKey(services_info
, key
)) {
4462 *reach_flags_v6
|= flags
;
4463 my_log(LOG_DEBUG
,"Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
4468 if (components
!= NULL
) {
4469 CFRelease(components
);
4475 if (components
!= NULL
) {
4476 CFRelease(components
);
4480 CFRelease(vif_setup_keys
);
4484 static SCNetworkReachabilityFlags
4485 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
4487 SCNetworkReachabilityFlags flags
= 0;
4489 if (CFEqual(entity
, kSCEntNetPPP
)) {
4492 /* if we're really UP and RUNNING */
4495 /* if we're effectively UP and RUNNING */
4498 /* if we're not connected at all */
4499 my_log(LOG_INFO
, "PPP link idle");
4500 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4502 case PPP_STATERESERVED
:
4503 // if we're not connected at all
4504 my_log(LOG_INFO
, "PPP link idle, dial-on-traffic to connect");
4505 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4508 /* if we're in the process of [dis]connecting */
4509 my_log(LOG_INFO
, "PPP link, connection in progress");
4510 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4514 #ifdef HAVE_IPSEC_STATUS
4515 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
4517 case IPSEC_RUNNING
:
4518 /* if we're really UP and RUNNING */
4521 /* if we're not connected at all */
4522 my_log(LOG_INFO
, "IPSec link idle");
4523 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4526 /* if we're in the process of [dis]connecting */
4527 my_log(LOG_INFO
, "IPSec link, connection in progress");
4528 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4532 #endif // HAVE_IPSEC_STATUS
4533 #ifdef HAVE_VPN_STATUS
4534 else if (CFEqual(entity
, kSCEntNetVPN
)) {
4537 /* if we're really UP and RUNNING */
4542 case VPN_UNLOADING
:
4543 /* if we're not connected at all */
4544 my_log(LOG_INFO
, "%s VPN link idle");
4545 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4548 /* if we're in the process of [dis]connecting */
4549 my_log(LOG_INFO
, "VPN link, connection in progress");
4550 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4554 #endif // HAVE_VPN_STATUS
4559 VPNAttributesGet(CFStringRef service_id
,
4560 CFDictionaryRef services_info
,
4561 SCNetworkReachabilityFlags
*flags
,
4562 CFStringRef
*server_address
,
4566 CFDictionaryRef entity_dict
;
4567 boolean_t found
= FALSE
;
4569 CFDictionaryRef p_state
= NULL
;
4572 /* if the IPv[4/6] exist */
4573 entity_dict
= service_dict_get(service_id
, (af
== AF_INET
) ? kSCEntNetIPv4
: kSCEntNetIPv6
);
4574 if (!isA_CFDictionary(entity_dict
)) {
4578 if (af
== AF_INET
) {
4579 entity_dict
= CFDictionaryGetValue(entity_dict
, kIPv4DictService
);
4580 if (!isA_CFDictionary(entity_dict
)) {
4585 for (i
= 0; i
< sizeof(statusEntityNames
)/sizeof(statusEntityNames
[0]); i
++) {
4586 p_state
= service_dict_get(service_id
, *statusEntityNames
[i
]);
4587 /* ensure that this is a VPN Type service */
4588 if (isA_CFDictionary(p_state
)) {
4594 /* Did we find a vpn type service? If not, we are done.*/
4599 *flags
|= (kSCNetworkReachabilityFlagsReachable
| kSCNetworkReachabilityFlagsTransientConnection
);
4601 /* Get the Server Address */
4602 if (server_address
!= NULL
) {
4603 *server_address
= CFDictionaryGetValue(entity_dict
, CFSTR("ServerAddress"));
4604 *server_address
= isA_CFString(*server_address
);
4605 if (*server_address
!= NULL
) {
4606 CFRetain(*server_address
);
4611 if (!CFDictionaryGetValueIfPresent(p_state
,
4612 kSCPropNetVPNStatus
,
4613 (const void **)&num
) ||
4614 !isA_CFNumber(num
) ||
4615 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &status
)) {
4619 *flags
|= GetReachFlagsFromStatus(*statusEntityNames
[i
], status
);
4621 if (CFEqual(*statusEntityNames
[i
], kSCEntNetPPP
)) {
4623 CFDictionaryRef p_setup
;
4626 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4627 kSCDynamicStoreDomainSetup
,
4630 p_setup
= CFDictionaryGetValue(services_info
, key
);
4633 /* get dial-on-traffic status */
4634 if (isA_CFDictionary(p_setup
) &&
4635 CFDictionaryGetValueIfPresent(p_setup
,
4636 kSCPropNetPPPDialOnDemand
,
4637 (const void **)&num
) &&
4638 isA_CFNumber(num
) &&
4639 CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) &&
4640 (ppp_demand
!= 0)) {
4641 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
4642 if (status
== PPP_IDLE
) {
4643 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
4651 typedef struct ElectionInfo
{
4655 ElectionResultsRef results
;
4656 } ElectionInfo
, * ElectionInfoRef
;
4658 typedef CFDictionaryApplierFunction ElectionFuncRef
;
4661 CandidateRelease(CandidateRef candidate
)
4663 my_CFRelease(&candidate
->serviceID
);
4664 my_CFRelease(&candidate
->if_name
);
4665 my_CFRelease(&candidate
->signature
);
4670 CandidateCopy(CandidateRef dest
, CandidateRef src
)
4673 if (dest
->serviceID
) {
4674 CFRetain(dest
->serviceID
);
4676 if (dest
->if_name
) {
4677 CFRetain(dest
->if_name
);
4679 if(dest
->signature
) {
4680 CFRetain(dest
->signature
);
4685 static ElectionResultsRef
4686 ElectionResultsAlloc(int size
)
4688 ElectionResultsRef results
;
4690 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
4692 results
->size
= size
;
4697 ElectionResultsRelease(ElectionResultsRef results
)
4702 for (i
= 0, scan
= results
->candidates
;
4705 CandidateRelease(scan
);
4712 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
4717 if (results
== NULL
) {
4718 my_log(level
, "%s: no candidates", prefix
);
4721 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
4722 for (i
= 0, scan
= results
->candidates
;
4725 my_log(level
, "%d. %@ Rank=0x%x serviceID=%@", i
, scan
->if_name
,
4726 scan
->rank
, scan
->serviceID
);
4732 * Function: ElectionResultsAddCandidate
4734 * Add the candidate into the election results. Find the insertion point
4735 * by comparing the rank of the candidate with existing entries.
4738 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
4743 #define BAD_INDEX (-1)
4744 if (results
->count
== results
->size
) {
4745 /* this should not happen */
4746 my_log(LOG_NOTICE
, "can't fit another candidate");
4750 /* find the insertion point */
4752 for (i
= 0; i
< results
->count
; i
++) {
4753 CandidateRef this_candidate
= results
->candidates
+ i
;
4755 if (candidate
->rank
< this_candidate
->rank
) {
4760 /* add it to the end */
4761 if (where
== BAD_INDEX
) {
4762 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
4766 /* slide existing entries over */
4767 for (i
= results
->count
; i
> where
; i
--) {
4768 results
->candidates
[i
] = results
->candidates
[i
- 1];
4770 /* insert element */
4771 CandidateCopy(results
->candidates
+ where
, candidate
);
4777 * Function: ElectionResultsCopy
4779 * Visit all of the services and invoke the protocol-specific election
4780 * function. Return the results of the election.
4782 static ElectionResultsRef
4783 ElectionResultsCopy(ElectionFuncRef elect_func
, CFArrayRef order
, int n_order
)
4788 count
= CFDictionaryGetCount(S_service_state_dict
);
4792 info
.results
= ElectionResultsAlloc(count
);
4793 info
.n_services
= count
;
4795 info
.n_order
= n_order
;
4796 CFDictionaryApplyFunction(S_service_state_dict
, elect_func
, (void *)&info
);
4797 if (info
.results
->count
== 0) {
4798 ElectionResultsRelease(info
.results
);
4799 info
.results
= NULL
;
4801 return (info
.results
);
4805 * Function: ElectionResultsCandidateNeedsDemotion
4807 * Check whether the given candidate requires demotion. A candidate
4808 * might need to be demoted if its IPv4 and IPv6 services must be coupled
4809 * but a higher ranked service has IPv4 or IPv6.
4812 ElectionResultsCandidateNeedsDemotion(ElectionResultsRef other_results
,
4813 CandidateRef candidate
)
4815 CandidateRef other_candidate
;
4816 Boolean ret
= FALSE
;
4818 if (other_results
== NULL
4819 || candidate
->ip_is_coupled
== FALSE
4820 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
4823 other_candidate
= other_results
->candidates
;
4824 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
4825 /* they are over the same interface, no need to demote */
4828 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
4829 /* avoid creating a feedback loop */
4832 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
4833 /* the other candidate isn't eligible to become primary, ignore */
4836 if (candidate
->rank
< other_candidate
->rank
) {
4837 /* we're higher ranked than the other candidate, ignore */
4849 get_signature_sha1(CFStringRef signature
,
4850 unsigned char * sha1
)
4853 CFDataRef signature_data
;
4855 signature_data
= CFStringCreateExternalRepresentation(NULL
,
4857 kCFStringEncodingUTF8
,
4861 CC_SHA1_Update(&ctx
,
4863 CFDataGetLength(signature_data
));
4864 CC_SHA1_Final(sha1
, &ctx
);
4866 CFRelease(signature_data
);
4873 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
4874 CandidateRef candidate
, Rank rank
)
4877 char ifname
[IFNAMSIZ
];
4878 nwi_ifstate_t ifstate
;
4880 if (nwi_state
== NULL
) {
4884 if (RANK_ASSERTION_MASK(rank
) == kRankAssertionNever
) {
4885 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
4887 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
4888 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
4890 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
4891 kCFStringEncodingASCII
);
4892 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
4894 "Inserting IPv%c [%s] with flags 0x%x primary_rank 0x%x reach_flags %d",
4895 ipvx_char(af
), ifname
, rank
, candidate
->reachability_flags
);
4897 ifstate
= nwi_insert_ifstate(nwi_state
, ifname
, af
, flags
, rank
,
4898 (void *)&candidate
->addr
,
4899 (void *)&candidate
->vpn_server_addr
,
4900 candidate
->reachability_flags
);
4901 if (ifstate
!= NULL
&& candidate
->signature
) {
4902 uint8_t hash
[CC_SHA1_DIGEST_LENGTH
];
4904 get_signature_sha1(candidate
->signature
, hash
);
4905 nwi_ifstate_set_signature(ifstate
, hash
);
4912 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
4914 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
4915 CFStringRef vpn_server_address
= NULL
;
4917 VPNAttributesGet(candidate
->serviceID
,
4920 &vpn_server_address
,
4923 candidate
->reachability_flags
= flags
;
4925 if (vpn_server_address
== NULL
) {
4926 bzero(&candidate
->vpn_server_addr
, sizeof(candidate
->vpn_server_addr
));
4929 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
4931 _SC_string_to_sockaddr(buf
,
4933 (void *)&candidate
->vpn_server_addr
,
4934 sizeof(candidate
->vpn_server_addr
));
4936 CFRelease(vpn_server_address
);
4941 * Function: ElectionResultsCopyPrimary
4943 * Use the results of the current protocol and the other protocol to
4944 * determine which service should become primary.
4946 * At the same time, generate the nwi_state for the protocol.
4948 * For IPv4, also generate the IPv4 routing table.
4951 ElectionResultsCopyPrimary(ElectionResultsRef results
,
4952 ElectionResultsRef other_results
,
4953 nwi_state_t nwi_state
, int af
,
4954 IPv4RouteListRef
* ret_routes
,
4955 CFDictionaryRef services_info
)
4957 CFStringRef primary
= NULL
;
4958 Boolean primary_is_null
= FALSE
;
4959 IPv4RouteListRef routes
= NULL
;
4961 if (nwi_state
!= NULL
) {
4962 nwi_state_clear(nwi_state
, af
);
4964 if (results
!= NULL
) {
4965 CandidateRef deferred
[results
->count
];
4971 for (i
= 0, scan
= results
->candidates
;
4974 Boolean is_primary
= FALSE
;
4975 Rank rank
= scan
->rank
;
4976 Boolean skip
= FALSE
;
4979 && RANK_ASSERTION_MASK(rank
) != kRankAssertionNever
) {
4980 if (ElectionResultsCandidateNeedsDemotion(other_results
,
4982 /* demote to RankNever */
4984 "IPv%c over %@ demoted: not primary for IPv%c",
4985 ipvx_char(af
), scan
->if_name
, ipvx_other_char(af
));
4986 rank
= RankMake(rank
, kRankAssertionNever
);
4987 deferred
[deferred_count
++] = scan
;
4991 primary
= CFRetain(scan
->serviceID
);
4995 if (af
== AF_INET
) {
4996 /* generate the routing table for IPv4 */
4997 CFDictionaryRef service_dict
;
4998 IPv4RouteListRef service_routes
;
5001 = service_dict_get(scan
->serviceID
, kSCEntNetIPv4
);
5002 service_routes
= ipv4_dict_get_routelist(service_dict
);
5003 if (service_routes
!= NULL
) {
5004 routes
= IPv4RouteListAddRouteList(routes
,
5008 if (service_routes
->exclude_from_nwi
) {
5017 /* a NULL service must be excluded from nwi */
5018 CFDictionaryRef ipv6_dict
;
5020 ipv6_dict
= service_dict_get(scan
->serviceID
, kSCEntNetIPv6
);
5022 if (S_dict_get_boolean(ipv6_dict
, kIsNULL
, FALSE
)) {
5027 /* if we're skipping the primary, it's NULL */
5029 primary_is_null
= TRUE
;
5033 if (primary_is_null
) {
5034 /* everything after the primary must be Never */
5035 rank
= RankMake(rank
, kRankAssertionNever
);
5037 add_reachability_flags_to_candidate(scan
, services_info
, af
);
5038 add_candidate_to_nwi_state(nwi_state
, af
, scan
, rank
);
5041 for (i
= 0; i
< deferred_count
; i
++) {
5042 CandidateRef candidate
= deferred
[i
];
5045 /* demote to RankNever */
5046 rank
= RankMake(candidate
->rank
, kRankAssertionNever
);
5047 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
5048 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, rank
);
5051 if (nwi_state
!= NULL
) {
5052 nwi_state_set_last(nwi_state
, af
);
5054 if (ret_routes
!= NULL
) {
5055 *ret_routes
= routes
;
5057 else if (routes
!= NULL
) {
5060 if (primary_is_null
) {
5061 my_CFRelease(&primary
);
5069 service_dict_get_signature(CFDictionaryRef service_dict
)
5071 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
5076 * Function: elect_ipv4
5078 * Evaluate the service and determine what rank the service should have.
5079 * If it's a suitable candidate, add it to the election results.
5082 elect_ipv4(const void * key
, const void * value
, void * context
)
5084 Candidate candidate
;
5085 CFStringRef if_name
;
5086 ElectionInfoRef info
;
5088 CFDictionaryRef service_dict
= (CFDictionaryRef
)value
;
5089 IPv4RouteListRef service_routes
;
5090 CFDictionaryRef v4_dict
;
5091 CFDictionaryRef v4_service_dict
;
5093 service_routes
= service_dict_get_ipv4_routelist(service_dict
);
5094 if (service_routes
== NULL
) {
5095 /* no service routes, no service */
5098 if_name
= service_dict_get_ipv4_ifname(service_dict
);
5099 if (if_name
== NULL
) {
5100 /* need an interface name */
5103 if (CFEqual(if_name
, CFSTR("lo0"))) {
5104 /* don't ever elect loopback */
5107 info
= (ElectionInfoRef
)context
;
5108 bzero(&candidate
, sizeof(candidate
));
5109 candidate
.serviceID
= (CFStringRef
)key
;
5110 candidate
.rank
= get_service_rank(info
->order
, info
->n_order
,
5111 candidate
.serviceID
);
5112 primary_rank
= RANK_ASSERTION_MASK(service_routes
->list
->rank
);
5113 if (S_ppp_override_primary
5114 && (strncmp(PPP_PREFIX
, service_routes
->list
->ifname
,
5115 sizeof(PPP_PREFIX
) - 1) == 0)) {
5116 /* PPP override: make ppp* look the best */
5117 /* Hack: should use interface type, not interface name */
5118 primary_rank
= kRankAssertionFirst
;
5120 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
5121 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
5122 candidate
.if_name
= if_name
;
5123 candidate
.addr
.v4
= service_routes
->list
->ifa
;
5124 rank_dict_set_service_rank(S_ipv4_service_rank_dict
,
5125 candidate
.serviceID
, candidate
.rank
);
5126 v4_dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
5127 v4_service_dict
= CFDictionaryGetValue(v4_dict
, kIPv4DictService
);
5128 candidate
.signature
= service_dict_get_signature(v4_service_dict
);
5129 ElectionResultsAddCandidate(info
->results
, &candidate
);
5135 * Function: elect_ipv6
5137 * Evaluate the service and determine what rank the service should have.
5138 * If it's a suitable candidate, add it to the election results.
5141 elect_ipv6(const void * key
, const void * value
, void * context
)
5144 Candidate candidate
;
5145 CFStringRef if_name
;
5146 ElectionInfoRef info
;
5147 Rank primary_rank
= kRankAssertionDefault
;
5148 CFDictionaryRef ipv6_dict
;
5150 CFDictionaryRef service_dict
= (CFDictionaryRef
)value
;
5151 CFDictionaryRef service_options
;
5154 ipv6_dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
5155 if (ipv6_dict
== NULL
) {
5159 if_name
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5160 if (if_name
== NULL
) {
5161 /* need an interface name */
5164 if (CFEqual(if_name
, CFSTR("lo0"))) {
5165 /* don't ever elect loopback */
5168 router
= CFDictionaryGetValue(ipv6_dict
, kSCPropNetIPv6Router
);
5169 if (router
== NULL
) {
5170 /* don't care about services without a router */
5173 info
= (ElectionInfoRef
)context
;
5174 bzero(&candidate
, sizeof(candidate
));
5175 candidate
.serviceID
= (CFStringRef
)key
;
5176 candidate
.if_name
= if_name
;
5177 addrs
= CFDictionaryGetValue(ipv6_dict
, kSCPropNetIPv6Addresses
);
5178 (void)cfstring_to_ip6(CFArrayGetValueAtIndex(addrs
, 0),
5179 &candidate
.addr
.v6
);
5180 candidate
.rank
= get_service_rank(info
->order
, info
->n_order
,
5181 candidate
.serviceID
);
5183 = service_dict_get(candidate
.serviceID
, kSCEntNetService
);
5184 if (service_options
!= NULL
) {
5185 CFStringRef primaryRankStr
= NULL
;
5187 primaryRankStr
= CFDictionaryGetValue(service_options
,
5188 kSCPropNetServicePrimaryRank
);
5189 if (primaryRankStr
!= NULL
) {
5190 primary_rank
= PrimaryRankGetRankAssertion(primaryRankStr
);
5192 candidate
.ip_is_coupled
5193 = CFDictionaryContainsKey(service_options
, kIPIsCoupled
);
5195 if (primary_rank
!= kRankAssertionNever
) {
5196 if (get_override_primary(ipv6_dict
)) {
5197 primary_rank
= kRankAssertionFirst
;
5199 else if (S_ppp_override_primary
5200 && CFStringHasPrefix(if_name
, CFSTR(PPP_PREFIX
))) {
5201 /* PPP override: make ppp* look the best */
5202 /* Hack: should use interface type, not interface name */
5203 primary_rank
= kRankAssertionFirst
;
5206 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
5207 rank_dict_set_service_rank(S_ipv6_service_rank_dict
,
5208 candidate
.serviceID
, candidate
.rank
);
5209 candidate
.signature
= service_dict_get_signature(ipv6_dict
);
5210 ElectionResultsAddCandidate(info
->results
, &candidate
);
5215 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
5217 uint32_t changed
= 0;
5220 /* update service options first (e.g. rank) */
5221 if (get_rank_changes(serviceID
,
5222 get_service_state_entity(services_info
, serviceID
,
5224 get_service_setup_entity(services_info
, serviceID
,
5227 changed
|= (1 << kEntityTypeServiceOptions
);
5229 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
5230 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5231 GetEntityChangesFuncRef func
= entityChangeFunc
[i
];
5232 if ((*func
)(serviceID
,
5233 get_service_state_entity(services_info
, serviceID
,
5234 *entityTypeNames
[i
]),
5235 get_service_setup_entity(services_info
, serviceID
,
5236 *entityTypeNames
[i
]),
5238 changed
|= (1 << i
);
5242 if (get_transient_service_changes(serviceID
, services_info
)) {
5243 changed
|= (1 << kEntityTypeVPNStatus
);
5250 service_order_get(CFDictionaryRef services_info
)
5252 CFArrayRef order
= NULL
;
5253 CFDictionaryRef ipv4_dict
;
5255 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
5256 S_setup_global_ipv4
);
5257 if (ipv4_dict
!= NULL
) {
5258 CFNumberRef ppp_override
;
5261 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
5262 order
= isA_CFArray(order
);
5264 /* get ppp override primary */
5265 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
5266 kSCPropNetPPPOverridePrimary
);
5267 ppp_override
= isA_CFNumber(ppp_override
);
5268 if (ppp_override
!= NULL
) {
5269 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
5271 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
5274 S_ppp_override_primary
= FALSE
;
5280 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
5281 const char * entity
)
5283 boolean_t changed
= FALSE
;
5284 CFStringRef primary
= *primary_p
;
5286 if (new_primary
!= NULL
) {
5287 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
5288 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5290 "IPMonitor: %@ is still primary %s",
5291 new_primary
, entity
);
5295 my_CFRelease(primary_p
);
5296 *primary_p
= CFRetain(new_primary
);
5297 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5299 "IPMonitor: %@ is the new primary %s",
5300 new_primary
, entity
);
5305 else if (primary
!= NULL
) {
5306 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5308 "IPMonitor: %@ is no longer primary %s",
5311 my_CFRelease(primary_p
);
5318 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
5321 if (service_dict_get(serviceID
, entity
) == NULL
) {
5322 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
5324 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
5328 update_interface_rank(CFDictionaryRef services_info
, CFStringRef ifname
)
5330 CFStringRef if_rank_key
;
5331 CFDictionaryRef rank_dict
;
5333 if_rank_key
= if_rank_key_copy(ifname
);
5334 rank_dict
= CFDictionaryGetValue(services_info
, if_rank_key
);
5335 CFRelease(if_rank_key
);
5336 if_rank_set(ifname
, rank_dict
);
5341 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
5347 #define N_KEYS_VALUES_STATIC 10
5348 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
5351 count
= CFDictionaryGetCount(S_service_state_dict
);
5352 if (count
<= N_KEYS_VALUES_STATIC
) {
5353 keys
= keys_values_buf
;
5355 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
5357 values
= keys
+ count
;
5358 CFDictionaryGetKeysAndValues(S_service_state_dict
,
5359 (const void * *)keys
,
5360 (const void * *)values
);
5362 for (i
= 0; i
< count
; i
++) {
5363 CFDictionaryRef ipv4
= NULL
;
5364 CFStringRef interface
= NULL
;
5365 CFStringRef serviceID
;
5366 CFDictionaryRef service_dict
;
5368 serviceID
= (CFStringRef
)keys
[i
];
5369 service_dict
= (CFDictionaryRef
)values
[i
];
5371 /* check if this is a ipv4 dictionary */
5372 ipv4
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
5374 interface
= ipv4_dict_get_ifname(ipv4
);
5375 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
5376 if (S_IPMonitor_debug
& kDebugFlag1
) {
5378 "Found ipv4 service %@ on interface %@.",
5382 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
5385 CFDictionaryRef proto_dict
;
5387 /* check if this is a ipv6 dictionary */
5388 proto_dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
5389 if (proto_dict
== NULL
) {
5392 interface
= CFDictionaryGetValue(proto_dict
, kSCPropInterfaceName
);
5393 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
5394 if (S_IPMonitor_debug
& kDebugFlag1
) {
5395 my_log(LOG_DEBUG
, "Found ipv6 service %@ on interface %@.",
5399 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
5404 if (keys
!= keys_values_buf
) {
5409 static __inline__
const char *
5410 get_changed_str(CFStringRef serviceID
, CFStringRef entity
, CFDictionaryRef old_dict
)
5412 CFDictionaryRef new_dict
= NULL
;
5414 if (serviceID
!= NULL
) {
5415 new_dict
= service_dict_get(serviceID
, entity
);
5418 if (old_dict
== NULL
) {
5419 if (new_dict
!= NULL
) {
5423 if (new_dict
== NULL
) {
5425 } else if (!CFEqual(old_dict
, new_dict
)) {
5432 static CF_RETURNS_RETAINED CFStringRef
5433 generate_log_changes(nwi_state_t changes_state
,
5434 boolean_t dns_changed
,
5435 boolean_t dnsinfo_changed
,
5436 CFDictionaryRef old_primary_dns
,
5437 boolean_t proxy_changed
,
5438 CFDictionaryRef old_primary_proxy
,
5439 boolean_t smb_changed
,
5440 CFDictionaryRef old_primary_smb
5444 CFMutableStringRef log_output
;
5447 log_output
= CFStringCreateMutable(NULL
, 0);
5449 if (changes_state
!= NULL
) {
5450 for (idx
= 0; idx
< sizeof(nwi_af_list
)/sizeof(nwi_af_list
[0]); idx
++) {
5451 CFMutableStringRef changes
= NULL
;
5452 CFMutableStringRef primary_str
= NULL
;
5454 scan
= nwi_state_get_first_ifstate(changes_state
, nwi_af_list
[idx
]);
5456 while (scan
!= NULL
) {
5457 const char * changed_str
;
5459 changed_str
= nwi_ifstate_get_diff_str(scan
);
5460 if (changed_str
!= NULL
) {
5462 const char * addr_str
;
5463 char ntopbuf
[INET6_ADDRSTRLEN
];
5465 address
= (void *)nwi_ifstate_get_address(scan
);
5466 addr_str
= inet_ntop(scan
->af
, address
,
5467 ntopbuf
, sizeof(ntopbuf
));
5469 if (primary_str
== NULL
) {
5470 primary_str
= CFStringCreateMutable(NULL
, 0);
5471 CFStringAppendFormat(primary_str
, NULL
, CFSTR("%s%s:%s"),
5472 nwi_ifstate_get_ifname(scan
),
5473 changed_str
, addr_str
);
5475 if (changes
== NULL
) {
5476 changes
= CFStringCreateMutable(NULL
, 0);
5478 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
5479 nwi_ifstate_get_ifname(scan
));
5480 if (strcmp(changed_str
, "") != 0) {
5481 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
5482 changed_str
, addr_str
);
5486 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
5489 if (primary_str
!= NULL
) {
5490 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
5491 nwi_af_list
[idx
] == AF_INET
? "v4" : "v6",
5494 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
5495 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
5498 CFStringAppendFormat(log_output
, NULL
, CFSTR(")"));
5500 my_CFRelease(&primary_str
);
5501 my_CFRelease(&changes
);
5506 if (dns_changed
|| dnsinfo_changed
) {
5509 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
5510 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
5511 str
= "*"; // dnsinfo change w/no change to primary
5513 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
5514 } else if (S_primary_dns
!= NULL
) {
5515 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS"));
5518 if (proxy_changed
) {
5521 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
5522 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
5523 } else if (S_primary_proxies
!= NULL
) {
5524 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy"));
5527 #if !TARGET_OS_IPHONE
5531 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
5532 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
5533 } else if (S_primary_smb
!= NULL
) {
5534 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB"));
5536 #endif // !TARGET_OS_IPHONE
5542 #pragma mark Network changed notification
5544 static dispatch_queue_t
5545 __network_change_queue()
5547 static dispatch_once_t once
;
5548 static dispatch_queue_t q
;
5550 dispatch_once(&once
, ^{
5551 q
= dispatch_queue_create("network change queue", NULL
);
5557 // Note: must run on __network_change_queue()
5559 post_network_change_when_ready()
5563 if (S_network_change_needed
== 0) {
5567 if (!S_network_change_timeout
&&
5568 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
5569 // if we [still] need to wait for the DNS configuration
5570 // or network information changes to be ack'd
5572 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5574 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
5575 S_dnsinfo_synced
? "DNS" : "!DNS",
5576 S_nwi_synced
? "nwi" : "!nwi");
5581 // cancel any running timer
5582 if (S_network_change_timer
!= NULL
) {
5583 dispatch_source_cancel(S_network_change_timer
);
5584 dispatch_release(S_network_change_timer
);
5585 S_network_change_timer
= NULL
;
5586 S_network_change_timeout
= FALSE
;
5589 // set (and log?) the post time
5590 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5591 struct timeval elapsed
;
5594 (void) gettimeofday(&end
, NULL
);
5595 timersub(&end
, &S_network_change_start
, &elapsed
);
5597 #define QUERY_TIME__FMT "%d.%6.6d"
5598 #define QUERY_TIME__DIV 1
5601 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
5602 S_network_change_timeout
? "timeout" : "delayed",
5604 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
5605 S_network_change_needed
);
5608 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
5609 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
5610 if (status
!= NOTIFY_STATUS_OK
) {
5612 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%ld", status
);
5616 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
5617 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
5618 if (status
!= NOTIFY_STATUS_OK
) {
5620 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%ld", status
);
5624 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
5625 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
5626 if (status
!= NOTIFY_STATUS_OK
) {
5628 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%ld", status
);
5632 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
5633 if (status
!= NOTIFY_STATUS_OK
) {
5635 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%ld", status
);
5638 S_network_change_needed
= 0;
5642 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
5644 // Note: must run on __network_change_queue()
5646 post_network_change(uint32_t change
)
5648 if (S_network_change_needed
== 0) {
5649 // set the start time
5650 (void) gettimeofday(&S_network_change_start
, NULL
);
5653 // indicate that we need to post a change for ...
5654 S_network_change_needed
|= change
;
5656 // cancel any running timer
5657 if (S_network_change_timer
!= NULL
) {
5658 dispatch_source_cancel(S_network_change_timer
);
5659 dispatch_release(S_network_change_timer
);
5660 S_network_change_timer
= NULL
;
5661 S_network_change_timeout
= FALSE
;
5664 // if needed, start new timer
5665 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
5666 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
5669 __network_change_queue());
5670 dispatch_source_set_event_handler(S_network_change_timer
, ^{
5671 S_network_change_timeout
= TRUE
;
5672 post_network_change_when_ready();
5674 dispatch_source_set_timer(S_network_change_timer
,
5675 dispatch_time(DISPATCH_TIME_NOW
,
5676 TRAILING_EDGE_TIMEOUT_NSEC
), // start
5678 10 * NSEC_PER_MSEC
); // leeway
5679 dispatch_resume(S_network_change_timer
);
5682 post_network_change_when_ready();
5688 #pragma mark Process network (SCDynamicStore) changes
5691 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
5695 uint32_t changes
= 0;
5696 nwi_state_t changes_state
= NULL
;
5697 boolean_t dns_changed
= FALSE
;
5698 boolean_t dnsinfo_changed
= FALSE
;
5699 boolean_t global_ipv4_changed
= FALSE
;
5700 boolean_t global_ipv6_changed
= FALSE
;
5702 CFMutableArrayRef if_rank_changes
= NULL
;
5705 CFStringRef network_change_msg
= NULL
;
5707 int n_service_order
= 0;
5708 nwi_state_t old_nwi_state
= NULL
;
5709 CFDictionaryRef old_primary_dns
= NULL
;
5710 CFDictionaryRef old_primary_proxy
= NULL
;
5711 #if !TARGET_OS_IPHONE
5712 CFDictionaryRef old_primary_smb
= NULL
;
5713 #endif // !TARGET_OS_IPHONE
5714 boolean_t proxies_changed
= FALSE
;
5715 boolean_t reachability_changed
= FALSE
;
5716 CFArrayRef service_order
;
5717 CFMutableArrayRef service_changes
= NULL
;
5718 CFDictionaryRef services_info
= NULL
;
5719 #if !TARGET_OS_IPHONE
5720 boolean_t smb_changed
= FALSE
;
5721 #endif // !TARGET_OS_IPHONE
5723 count
= CFArrayGetCount(changed_keys
);
5728 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5730 "IPMonitor: changes %@ (%d)", changed_keys
, count
);
5733 if (S_primary_dns
!= NULL
) {
5734 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
5735 if (old_primary_dns
!= NULL
) {
5736 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
5740 if (S_primary_proxies
!= NULL
) {
5741 old_primary_proxy
= service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
5742 if (old_primary_proxy
!= NULL
) {
5743 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
5747 #if !TARGET_OS_IPHONE
5748 if (S_primary_smb
!= NULL
) {
5749 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
5750 if (old_primary_smb
!= NULL
) {
5751 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
5754 #endif // !TARGET_OS_IPHONE
5756 keyChangeListInit(&keys
);
5757 service_changes
= CFArrayCreateMutable(NULL
, 0,
5758 &kCFTypeArrayCallBacks
);
5760 for (i
= 0; i
< count
; i
++) {
5761 CFStringRef change
= CFArrayGetValueAtIndex(changed_keys
, i
);
5762 if (CFEqual(change
, S_setup_global_ipv4
)) {
5763 global_ipv4_changed
= TRUE
;
5764 global_ipv6_changed
= TRUE
;
5766 else if (CFEqual(change
, S_multicast_resolvers
)) {
5767 dnsinfo_changed
= TRUE
;
5769 else if (CFEqual(change
, S_private_resolvers
)) {
5770 dnsinfo_changed
= TRUE
;
5772 #if !TARGET_OS_IPHONE
5773 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
5774 dnsinfo_changed
= TRUE
;
5776 #endif /* !TARGET_OS_IPHONE */
5777 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
5779 CFStringRef serviceID
;
5781 for (i
= 0; i
< sizeof(statusEntityNames
)/sizeof(statusEntityNames
[0]); i
++) {
5782 if (CFStringHasSuffix(change
, *statusEntityNames
[i
])) {
5783 dnsinfo_changed
= TRUE
;
5788 serviceID
= parse_component(change
, S_state_service_prefix
);
5790 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
5791 CFRelease(serviceID
);
5794 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
5797 CFStringRef serviceID
= parse_component(change
,
5798 S_setup_service_prefix
);
5800 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
5801 CFRelease(serviceID
);
5804 for (j
= 0; j
< sizeof(transientInterfaceEntityNames
)/sizeof(transientInterfaceEntityNames
[0]); j
++) {
5805 if (CFStringHasSuffix(change
, *transientInterfaceEntityNames
[j
])) {
5806 reachability_changed
= TRUE
;
5811 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
5812 reachability_changed
= TRUE
;
5817 else if (CFStringHasSuffix(change
, kSCEntNetService
)) {
5818 CFStringRef ifname
= my_CFStringCopyComponent(change
, CFSTR("/"), 3);
5820 if (ifname
!= NULL
) {
5821 if (if_rank_changes
== NULL
) {
5822 if_rank_changes
= CFArrayCreateMutable(NULL
, 0,
5823 &kCFTypeArrayCallBacks
);
5825 my_CFArrayAppendUniqueValue(if_rank_changes
, ifname
);
5831 /* determine which serviceIDs are impacted by the interface rank changes */
5832 if (if_rank_changes
!= NULL
) {
5833 n
= CFArrayGetCount(if_rank_changes
);
5834 for (i
= 0; i
< n
; i
++) {
5835 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
5837 if (S_IPMonitor_debug
& kDebugFlag1
) {
5838 my_log(LOG_DEBUG
, "Interface rank changed %@",
5841 append_serviceIDs_for_interface(service_changes
, ifname
);
5845 /* grab a snapshot of everything we need */
5846 services_info
= services_info_copy(session
, service_changes
,
5848 service_order
= service_order_get(services_info
);
5849 if (service_order
!= NULL
) {
5850 n_service_order
= CFArrayGetCount(service_order
);
5851 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5853 "IPMonitor: service_order %@ ", service_order
);
5857 if (if_rank_changes
!= NULL
) {
5858 for (i
= 0; i
< n
; i
++) {
5859 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
5860 update_interface_rank(services_info
, ifname
);
5864 n
= CFArrayGetCount(service_changes
);
5865 for (i
= 0; i
< n
; i
++) {
5867 CFStringRef serviceID
;
5869 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
5870 changes
= service_changed(services_info
, serviceID
);
5871 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
5872 /* if __Service__ (e.g. PrimaryRank) changed */
5873 global_ipv4_changed
= TRUE
;
5874 global_ipv6_changed
= TRUE
;
5877 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
5878 global_ipv4_changed
= TRUE
;
5879 dnsinfo_changed
= TRUE
;
5880 proxies_changed
= TRUE
;
5882 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
5883 global_ipv6_changed
= TRUE
;
5884 dnsinfo_changed
= TRUE
;
5885 proxies_changed
= TRUE
;
5888 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
5889 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
5892 dnsinfo_changed
= TRUE
;
5894 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
5895 proxies_changed
= TRUE
;
5897 #if !TARGET_OS_IPHONE
5898 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
5899 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
5906 if ((changes
& (1 <<kEntityTypeVPNStatus
)) != 0) {
5907 global_ipv4_changed
= TRUE
;
5908 global_ipv6_changed
= TRUE
;
5911 /* ensure S_nwi_state can hold as many services as we have currently */
5912 n_services
= CFDictionaryGetCount(S_service_state_dict
);
5913 old_nwi_state
= nwi_state_copy_priv(S_nwi_state
);
5914 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
5916 if (global_ipv4_changed
) {
5917 if (S_ipv4_results
!= NULL
) {
5918 ElectionResultsRelease(S_ipv4_results
);
5921 = ElectionResultsCopy(elect_ipv4
, service_order
, n_service_order
);
5922 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5923 ElectionResultsLog(LOG_DEBUG
, S_ipv4_results
, "IPv4");
5926 if (global_ipv6_changed
) {
5927 if (S_ipv6_results
!= NULL
) {
5928 ElectionResultsRelease(S_ipv6_results
);
5931 = ElectionResultsCopy(elect_ipv6
, service_order
, n_service_order
);
5932 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5933 ElectionResultsLog(LOG_DEBUG
, S_ipv6_results
, "IPv6");
5936 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
5937 CFStringRef new_primary
;
5938 IPv4RouteListRef new_routelist
= NULL
;
5941 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5943 "IPMonitor: electing IPv4 primary");
5945 new_primary
= ElectionResultsCopyPrimary(S_ipv4_results
,
5947 S_nwi_state
, AF_INET
,
5950 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
5951 update_ipv4(S_primary_ipv4
, new_routelist
, &keys
);
5952 my_CFRelease(&new_primary
);
5955 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5957 "IPMonitor: electing IPv6 primary");
5959 new_primary
= ElectionResultsCopyPrimary(S_ipv6_results
,
5961 S_nwi_state
, AF_INET6
,
5964 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
5965 update_ipv6(S_primary_ipv6
, &keys
);
5966 my_CFRelease(&new_primary
);
5969 if (global_ipv4_changed
|| global_ipv6_changed
) {
5970 CFStringRef new_primary_dns
= NULL
;
5971 CFStringRef new_primary_proxies
= NULL
;
5972 #if !TARGET_OS_IPHONE
5973 CFStringRef new_primary_smb
= NULL
;
5974 #endif /* !TARGET_OS_IPHONE */
5976 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
5977 /* decide between IPv4 and IPv6 */
5978 if (rank_service_entity(S_ipv4_service_rank_dict
,
5979 S_primary_ipv4
, kSCEntNetDNS
)
5980 <= rank_service_entity(S_ipv6_service_rank_dict
,
5981 S_primary_ipv6
, kSCEntNetDNS
)) {
5982 new_primary_dns
= S_primary_ipv4
;
5985 new_primary_dns
= S_primary_ipv6
;
5987 if (rank_service_entity(S_ipv4_service_rank_dict
,
5988 S_primary_ipv4
, kSCEntNetProxies
)
5989 <= rank_service_entity(S_ipv6_service_rank_dict
,
5990 S_primary_ipv6
, kSCEntNetProxies
)) {
5991 new_primary_proxies
= S_primary_ipv4
;
5994 new_primary_proxies
= S_primary_ipv6
;
5996 #if !TARGET_OS_IPHONE
5997 if (rank_service_entity(S_ipv4_service_rank_dict
,
5998 S_primary_ipv4
, kSCEntNetSMB
)
5999 <= rank_service_entity(S_ipv6_service_rank_dict
,
6000 S_primary_ipv6
, kSCEntNetSMB
)) {
6001 new_primary_smb
= S_primary_ipv4
;
6004 new_primary_smb
= S_primary_ipv6
;
6006 #endif /* !TARGET_OS_IPHONE */
6009 else if (S_primary_ipv6
!= NULL
) {
6010 new_primary_dns
= S_primary_ipv6
;
6011 new_primary_proxies
= S_primary_ipv6
;
6012 #if !TARGET_OS_IPHONE
6013 new_primary_smb
= S_primary_ipv6
;
6014 #endif /* !TARGET_OS_IPHONE */
6016 else if (S_primary_ipv4
!= NULL
) {
6017 new_primary_dns
= S_primary_ipv4
;
6018 new_primary_proxies
= S_primary_ipv4
;
6019 #if !TARGET_OS_IPHONE
6020 new_primary_smb
= S_primary_ipv4
;
6021 #endif /* !TARGET_OS_IPHONE */
6024 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
6026 dnsinfo_changed
= TRUE
;
6028 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
, "Proxies")) {
6029 proxies_changed
= TRUE
;
6031 #if !TARGET_OS_IPHONE
6032 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
6035 #endif /* !TARGET_OS_IPHONE */
6038 if (!proxies_changed
&& dnsinfo_changed
&&
6039 ((G_supplemental_proxies_follow_dns
!= NULL
) && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
6040 proxies_changed
= TRUE
;
6043 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
6045 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
|| reachability_changed
) {
6046 if (S_nwi_state
!= NULL
) {
6047 S_nwi_state
->generation_count
= mach_absolute_time();
6048 if (global_ipv4_changed
|| global_ipv6_changed
|| reachability_changed
) {
6049 SCNetworkReachabilityFlags reach_flags_v4
= 0;
6050 SCNetworkReachabilityFlags reach_flags_v6
= 0;
6052 GetReachabilityFlagsFromTransientServices(services_info
,
6056 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
, reach_flags_v6
);
6059 /* Update the per-interface generation count */
6060 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
, changes_state
);
6063 if (update_nwi(S_nwi_state
)) {
6064 changes
|= NETWORK_CHANGE_NET
;
6067 * the DNS configuration includes per-resolver configuration
6068 * reachability flags that are based on the nwi state. Let's
6069 * make sure that we check for changes
6071 dnsinfo_changed
= TRUE
;
6075 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
6076 changes
|= NETWORK_CHANGE_DNS
;
6077 dnsinfo_changed
= TRUE
;
6079 dns_changed
= FALSE
;
6082 if (dnsinfo_changed
) {
6083 if (update_dnsinfo(services_info
, S_primary_dns
, &keys
, service_order
)) {
6084 changes
|= NETWORK_CHANGE_DNS
;
6086 dnsinfo_changed
= FALSE
;
6089 if (proxies_changed
) {
6090 // if proxy change OR supplemental Proxies follow supplemental DNS
6091 if (update_proxies(services_info
, S_primary_proxies
, &keys
, service_order
)) {
6092 changes
|= NETWORK_CHANGE_PROXY
;
6094 proxies_changed
= FALSE
;
6097 #if !TARGET_OS_IPHONE
6099 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
6100 changes
|= NETWORK_CHANGE_SMB
;
6102 smb_changed
= FALSE
;
6105 #endif /* !TARGET_OS_IPHONE */
6106 my_CFRelease(&service_changes
);
6107 my_CFRelease(&services_info
);
6108 my_CFRelease(&if_rank_changes
);
6111 network_change_msg
=
6112 generate_log_changes(changes_state
,
6118 #if !TARGET_OS_IPHONE
6121 #else // !TARGET_OS_IPHONE
6122 FALSE
, // smb_changed
6123 NULL
// old_primary_smb
6124 #endif // !TARGET_OS_IPHONE
6128 keyChangeListApplyToStore(&keys
, session
);
6129 my_CFRelease(&old_primary_dns
);
6130 my_CFRelease(&old_primary_proxy
);
6131 #if !TARGET_OS_IPHONE
6132 my_CFRelease(&old_primary_smb
);
6133 #endif // !TARGET_OS_IPHONE
6136 dispatch_async(__network_change_queue(), ^{
6137 post_network_change(changes
);
6141 if ((network_change_msg
!= NULL
) && (CFStringGetLength(network_change_msg
) != 0)) {
6142 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
6143 } else if (keyChangeListActive(&keys
)) {
6144 my_log(LOG_NOTICE
, "network changed.");
6146 my_log(LOG_DEBUG
, "network event w/no changes");
6149 my_CFRelease(&network_change_msg
);
6151 if (changes_state
!= NULL
) {
6152 nwi_state_release(changes_state
);
6154 if (old_nwi_state
!= NULL
) {
6155 nwi_state_release(old_nwi_state
);
6157 keyChangeListFree(&keys
);
6164 #if !TARGET_OS_IPHONE
6165 const _scprefs_observer_type type
= scprefs_observer_type_mcx
;
6167 const _scprefs_observer_type type
= scprefs_observer_type_global
;
6169 static dispatch_queue_t proxy_cb_queue
;
6171 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
6172 _scprefs_observer_watch(type
,
6173 "com.apple.SystemConfiguration.plist",
6176 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
6177 notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
6178 my_log(LOG_DEBUG
, "IPMonitor: Notifying:\n%@",
6179 S_state_global_proxies
);
6184 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6185 #include "IPMonitorControlPrefs.h"
6187 __private_extern__ SCLoggerRef
6190 return (S_IPMonitor_logger
);
6194 prefs_changed(__unused SCPreferencesRef prefs
)
6196 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
6197 S_IPMonitor_debug
= kDebugFlagDefault
;
6198 S_IPMonitor_verbose
= TRUE
;
6199 SCLoggerSetFlags(S_IPMonitor_logger
, kSCLoggerFlagsFile
| kSCLoggerFlagsDefault
);
6200 my_log(LOG_DEBUG
, "IPMonitor: Setting logging verbose mode on.");
6202 my_log(LOG_DEBUG
, "IPMonitor: Setting logging verbose mode off.");
6203 S_IPMonitor_debug
= 0;
6204 S_IPMonitor_verbose
= FALSE
;
6205 SCLoggerSetFlags(S_IPMonitor_logger
, kSCLoggerFlagsDefault
);
6210 #define LOGGER_ID CFSTR("com.apple.networking.IPMonitor")
6214 if (S_IPMonitor_logger
!= NULL
) {
6217 S_IPMonitor_logger
= SCLoggerCreate(LOGGER_ID
);
6222 #else // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6230 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6236 CFMutableArrayRef keys
= NULL
;
6237 CFStringRef pattern
;
6238 CFMutableArrayRef patterns
= NULL
;
6239 CFRunLoopSourceRef rls
= NULL
;
6241 if (S_is_network_boot() != 0) {
6246 if (S_is_scoped_routing_enabled() != 0) {
6247 S_scopedroute
= TRUE
;
6250 if (S_is_scoped_v6_routing_enabled() != 0) {
6251 S_scopedroute_v6
= TRUE
;
6253 #endif /* RTF_IFSCOPE */
6255 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
6256 IPMonitorNotify
, NULL
);
6257 if (S_session
== NULL
) {
6259 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
6260 SCErrorString(SCError()));
6264 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
6265 kSCDynamicStoreDomainState
,
6268 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
6269 kSCDynamicStoreDomainState
,
6272 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
6273 kSCDynamicStoreDomainState
,
6275 S_state_global_proxies
6276 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
6277 kSCDynamicStoreDomainState
,
6279 #if !TARGET_OS_IPHONE
6281 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
6282 kSCDynamicStoreDomainState
,
6284 #endif /* !TARGET_OS_IPHONE */
6286 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
6287 kSCDynamicStoreDomainSetup
,
6289 S_state_service_prefix
6290 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6291 kSCDynamicStoreDomainState
,
6294 S_setup_service_prefix
6295 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6296 kSCDynamicStoreDomainSetup
,
6299 S_service_state_dict
6300 = CFDictionaryCreateMutable(NULL
, 0,
6301 &kCFTypeDictionaryKeyCallBacks
,
6302 &kCFTypeDictionaryValueCallBacks
);
6304 S_ipv4_service_rank_dict
6305 = CFDictionaryCreateMutable(NULL
, 0,
6306 &kCFTypeDictionaryKeyCallBacks
,
6307 &kCFTypeDictionaryValueCallBacks
);
6309 S_ipv6_service_rank_dict
6310 = CFDictionaryCreateMutable(NULL
, 0,
6311 &kCFTypeDictionaryKeyCallBacks
,
6312 &kCFTypeDictionaryValueCallBacks
);
6314 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
6315 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
6317 /* register for State: and Setup: per-service notifications */
6318 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
6320 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
6321 CFArrayAppendValue(patterns
, pattern
);
6324 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
6325 CFArrayAppendValue(patterns
, pattern
);
6328 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
6329 CFArrayAppendValue(patterns
, pattern
);
6332 /* register for State: per-service PPP/VPN/IPSec status notifications */
6333 add_status_keys(kSCCompAnyRegex
, patterns
);
6335 /* register for interface rank notifications */
6336 pattern
= if_rank_key_copy(kSCCompAnyRegex
);
6337 CFArrayAppendValue(patterns
, pattern
);
6340 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
6341 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
6343 /* add notifier for multicast DNS configuration (Bonjour/.local) */
6344 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
6345 kSCDynamicStoreDomainState
,
6347 CFSTR(kDNSServiceCompMulticastDNS
));
6348 CFArrayAppendValue(keys
, S_multicast_resolvers
);
6350 /* add notifier for private DNS configuration (Back to My Mac) */
6351 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
6352 kSCDynamicStoreDomainState
,
6354 CFSTR(kDNSServiceCompPrivateDNS
));
6355 CFArrayAppendValue(keys
, S_private_resolvers
);
6357 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
6359 "IPMonitor ip_plugin_init "
6360 "SCDynamicStoreSetNotificationKeys failed: %s",
6361 SCErrorString(SCError()));
6365 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
6368 "IPMonitor ip_plugin_init "
6369 "SCDynamicStoreCreateRunLoopSource failed: %s",
6370 SCErrorString(SCError()));
6374 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
6377 /* initialize dns configuration */
6378 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
6379 #if !TARGET_OS_IPHONE
6381 #endif /* !TARGET_OS_IPHONE */
6382 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
6384 #if !TARGET_OS_IPHONE
6385 /* initialize SMB configuration */
6386 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
6387 #endif /* !TARGET_OS_IPHONE */
6389 if_rank_dict_init();
6393 my_CFRelease(&keys
);
6394 my_CFRelease(&patterns
);
6402 /* initialize multicast route */
6403 update_ipv4(NULL
, NULL
, NULL
);
6408 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
6412 boolean_t ret
= def
;
6414 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
6416 ret
= CFBooleanGetValue(b
);
6423 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
6425 CFDictionaryRef info_dict
;
6427 info_dict
= CFBundleGetInfoDictionary(bundle
);
6429 if (info_dict
!= NULL
) {
6431 = S_get_plist_boolean(info_dict
,
6432 CFSTR("AppendStateArrayToSetupArray"),
6435 if (bundleVerbose
) {
6436 S_IPMonitor_debug
= kDebugFlagDefault
;
6437 S_bundle_logging_verbose
= bundleVerbose
;
6438 S_IPMonitor_verbose
= TRUE
;
6443 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6444 /* register to receive changes to verbose and read the initial setting */
6445 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
6446 prefs_changed(NULL
);
6448 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6450 load_DNSConfiguration(bundle
, // bundle
6451 S_IPMonitor_logger
, // SCLogger
6452 &S_IPMonitor_verbose
, // bundleVerbose
6453 ^(Boolean inSync
) { // syncHandler
6454 dispatch_async(__network_change_queue(), ^{
6455 S_dnsinfo_synced
= inSync
;
6458 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
6459 // all of the mDNSResponder ack's should result
6460 // in a [new] network change being posted
6461 post_network_change(NETWORK_CHANGE_DNS
);
6463 post_network_change_when_ready();
6468 load_NetworkInformation(bundle
, // bundle
6469 S_IPMonitor_logger
, // SCLogger
6470 &S_IPMonitor_verbose
, // bundleVerbose
6471 ^(Boolean inSync
) { // syncHandler
6472 dispatch_async(__network_change_queue(), ^{
6473 S_nwi_synced
= inSync
;
6474 post_network_change_when_ready();
6478 dns_configuration_init(bundle
);
6480 proxy_configuration_init(bundle
);
6484 #if !TARGET_OS_IPHONE
6485 if (S_session
!= NULL
) {
6486 dns_configuration_monitor(S_session
, IPMonitorNotify
);
6488 #endif /* !TARGET_OS_IPHONE */
6490 #if !TARGET_IPHONE_SIMULATOR
6491 load_hostname((S_IPMonitor_debug
& kDebugFlag1
) != 0);
6492 #endif /* !TARGET_IPHONE_SIMULATOR */
6494 #if !TARGET_OS_IPHONE
6495 load_smb_configuration((S_IPMonitor_debug
& kDebugFlag1
) != 0);
6496 #endif /* !TARGET_OS_IPHONE */
6503 #pragma mark Standalone test code
6506 #ifdef TEST_IPMONITOR
6508 #include "dns-configuration.c"
6510 #if !TARGET_IPHONE_SIMULATOR
6511 #include "set-hostname.c"
6512 #endif /* !TARGET_IPHONE_SIMULATOR */
6515 main(int argc
, char **argv
)
6519 S_IPMonitor_debug
= kDebugFlag1
;
6521 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
6524 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
6526 S_IPMonitor_debug
= kDebugFlag1
;
6532 #endif /* TEST_IPMONITOR */
6534 #ifdef TEST_IPV4_ROUTELIST
6536 #include "dns-configuration.c"
6538 #if !TARGET_IPHONE_SIMULATOR
6539 #include "set-hostname.c"
6540 #endif /* !TARGET_IPHONE_SIMULATOR */
6542 struct ipv4_service_contents
{
6546 const char * router
;
6547 const char * ifname
;
6549 const CFStringRef
*primaryRank
;
6553 * addr mask dest router ifname pri primaryRank
6555 struct ipv4_service_contents en0_10
= {
6556 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, NULL
6559 struct ipv4_service_contents en0_15
= {
6560 "10.0.0.19", "255.255.255.0", NULL
, "10.0.0.1", "en0", 15, NULL
6563 struct ipv4_service_contents en0_30
= {
6564 "10.0.0.11", "255.255.255.0", NULL
, "10.0.0.1", "en0", 30, NULL
6567 struct ipv4_service_contents en0_40
= {
6568 "10.0.0.12", "255.255.255.0", NULL
, "10.0.0.1", "en0", 40, NULL
6571 struct ipv4_service_contents en0_50
= {
6572 "10.0.0.13", "255.255.255.0", NULL
, "10.0.0.1", "en0", 50, NULL
6575 struct ipv4_service_contents en0_110
= {
6576 "192.168.2.10", "255.255.255.0", NULL
, "192.168.2.1", "en0", 110, NULL
6579 struct ipv4_service_contents en0_1
= {
6580 "17.202.40.191", "255.255.252.0", NULL
, "17.202.20.1", "en0", 1, NULL
6583 struct ipv4_service_contents en1_20
= {
6584 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, NULL
6587 struct ipv4_service_contents en1_2
= {
6588 "17.202.42.24", "255.255.252.0", NULL
, "17.202.20.1", "en1", 2, NULL
6591 struct ipv4_service_contents en1_125
= {
6592 "192.168.2.20", "255.255.255.0", NULL
, "192.168.2.1", "en1", 125, NULL
6595 struct ipv4_service_contents fw0_25
= {
6596 "192.168.2.30", "255.255.255.0", NULL
, "192.168.2.1", "fw0", 25, NULL
6599 struct ipv4_service_contents fw0_21
= {
6600 "192.168.3.30", "255.255.255.0", NULL
, "192.168.3.1", "fw0", 21, NULL
6603 struct ipv4_service_contents ppp0_0_1
= {
6604 "17.219.156.22", NULL
, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
6607 struct ipv4_service_contents en0_test6
= {
6608 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 2, NULL
6610 struct ipv4_service_contents en1_test6
= {
6611 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 3, NULL
6613 struct ipv4_service_contents en2_test6
= {
6614 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
6617 struct ipv4_service_contents en0_test7
= {
6618 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 3, NULL
6620 struct ipv4_service_contents en1_test7
= {
6621 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 2, NULL
6623 struct ipv4_service_contents en2_test7
= {
6624 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
6626 struct ipv4_service_contents fw0_test6_and_7
= {
6627 "169.254.11.33", "255.255.0.0", NULL
, NULL
, "fw0", 0x0ffffff, NULL
6630 struct ipv4_service_contents en0_10_last
= {
6631 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
6634 struct ipv4_service_contents en0_10_never
= {
6635 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
6638 struct ipv4_service_contents en1_20_first
= {
6639 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
6642 struct ipv4_service_contents en1_20_never
= {
6643 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
6646 struct ipv4_service_contents en0_linklocal
= {
6647 "169.254.22.44", "255.255.0.0", NULL
, NULL
, "en0", 0xfffff, NULL
6650 struct ipv4_service_contents
* test1
[] = {
6661 struct ipv4_service_contents
* test2
[] = {
6671 struct ipv4_service_contents
* test3
[] = {
6685 struct ipv4_service_contents
* test4
[] = {
6694 struct ipv4_service_contents
* test5
[] = {
6704 struct ipv4_service_contents
* test6
[] = {
6712 struct ipv4_service_contents
* test7
[] = {
6720 struct ipv4_service_contents
* test8
[] = {
6726 struct ipv4_service_contents
* test9
[] = {
6733 struct ipv4_service_contents
* test10
[] = {
6740 struct ipv4_service_contents
* test11
[] = {
6747 struct ipv4_service_contents
* test12
[] = {
6753 struct ipv4_service_contents
* test13
[] = {
6759 struct ipv4_service_contents
* test14
[] = {
6764 struct ipv4_service_contents
* test15
[] = {
6770 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
6773 CFStringRef prop_val
;
6778 prop_val
= CFStringCreateWithCString(NULL
,
6780 kCFStringEncodingASCII
);
6781 CFDictionarySetValue(dict
, prop_name
, prop_val
);
6782 CFRelease(prop_val
);
6787 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
6791 CFStringRef prop_val
;
6796 prop_val
= CFStringCreateWithCString(NULL
,
6798 kCFStringEncodingASCII
);
6799 array
= CFArrayCreate(NULL
,
6800 (const void **)&prop_val
, 1,
6801 &kCFTypeArrayCallBacks
);
6802 CFRelease(prop_val
);
6803 CFDictionarySetValue(dict
, prop_name
, array
);
6808 static CFDictionaryRef
6809 make_IPv4_dict(struct ipv4_service_contents
* t
)
6811 CFMutableDictionaryRef dict
;
6813 dict
= CFDictionaryCreateMutable(NULL
, 0,
6814 &kCFTypeDictionaryKeyCallBacks
,
6815 &kCFTypeDictionaryValueCallBacks
);
6816 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
6817 dict_add_string_as_array(dict
, kSCPropNetIPv4SubnetMasks
, t
->mask
);
6818 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
6819 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
6820 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
6824 static IPv4RouteListRef
6825 make_IPv4RouteList(struct ipv4_service_contents
* * this_test
)
6828 IPv4RouteListRef routes
;
6829 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
6830 IPv4RouteListRef ret
= NULL
;
6831 struct ipv4_service_contents
* * scan_test
;
6833 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
6834 CFDictionaryRef dict
;
6836 dict
= make_IPv4_dict(*scan_test
);
6838 fprintf(stderr
, "make_IPv4_dict failed\n");
6841 routes
= (IPv4RouteListRef
)routes_buf
;
6842 routes
->size
= R_STATIC
;
6844 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
6845 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
6847 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
6851 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
, kRankAssertionDefault
);
6853 if ((*scan_test
)->primaryRank
!= NULL
) {
6854 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
,
6855 PrimaryRankGetRankAssertion(*(*scan_test
)->primaryRank
));
6858 if ((*scan_test
)->router
== NULL
) {
6859 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
,
6860 PrimaryRankGetRankAssertion(kSCValNetServicePrimaryRankLast
));
6863 ret
= IPv4RouteListAddRouteList(ret
, 1, r
, (*scan_test
)->rank
);
6873 * Function: run_test
6875 * Runs through the given set of routes first in the forward direction,
6876 * then again backwards. We should end up with exactly the same set of
6877 * routes at the end.
6880 run_test(const char * name
, struct ipv4_service_contents
* * this_test
)
6883 boolean_t ret
= FALSE
;
6885 IPv4RouteListRef routes
;
6886 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
6887 IPv4RouteListRef routes1
= NULL
, routes2
= NULL
;
6888 struct ipv4_service_contents
* * scan_test
;
6890 printf("\nStarting test %s\n", name
);
6891 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
6892 CFDictionaryRef dict
;
6894 dict
= make_IPv4_dict(*scan_test
);
6896 fprintf(stderr
, "make_IPv4_dict failed\n");
6899 routes
= (IPv4RouteListRef
)routes_buf
;
6900 routes
->size
= R_STATIC
;
6902 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
6903 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
6905 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
6908 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
6909 descr
= IPv4RouteListCopyDescription(r
);
6910 SCPrint(TRUE
, stdout
, CFSTR("test: Adding %@\n"), descr
);
6914 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
, kRankAssertionDefault
);
6916 if ((*scan_test
)->primaryRank
!= NULL
) {
6917 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
,
6918 PrimaryRankGetRankAssertion(*(*scan_test
)->primaryRank
));
6921 routes1
= IPv4RouteListAddRouteList(routes1
, 1, r
, (*scan_test
)->rank
);
6927 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
6928 if (routes1
!= NULL
) {
6929 descr
= IPv4RouteListCopyDescription(routes1
);
6930 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
6934 for (scan_test
--; scan_test
>= this_test
; scan_test
--) {
6935 CFDictionaryRef dict
;
6937 dict
= make_IPv4_dict(*scan_test
);
6939 fprintf(stderr
, "make_IPv4_dict failed\n");
6942 routes
= (IPv4RouteListRef
)routes_buf
;
6943 routes
->size
= R_STATIC
;
6945 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
6946 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
6948 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
6951 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
6952 descr
= IPv4RouteListCopyDescription(r
);
6953 SCPrint(TRUE
, stdout
, CFSTR("test: Adding %@\n"), descr
);
6956 if ((*scan_test
)->primaryRank
!= NULL
) {
6957 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
,
6958 PrimaryRankGetRankAssertion(*(*scan_test
)->primaryRank
));
6961 routes2
= IPv4RouteListAddRouteList(routes2
, 1, r
, (*scan_test
)->rank
);
6967 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
6968 if (routes2
!= NULL
) {
6969 descr
= IPv4RouteListCopyDescription(routes2
);
6970 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
6974 if ((routes1
!= NULL
&& routes2
== NULL
)
6975 || (routes1
== NULL
&& routes2
!= NULL
)) {
6976 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
6977 (routes1
!= NULL
) ? "not " : "",
6978 (routes2
!= NULL
) ? "not " : "");
6980 else if (routes1
!= NULL
&& routes2
!= NULL
) {
6981 /* check if they are different */
6982 if (routes1
->count
!= routes2
->count
) {
6983 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
6984 routes1
->count
, routes2
->count
);
6986 else if (bcmp(routes1
, routes2
,
6987 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
6988 fprintf(stderr
, "routes1 and routes2 are different\n");
6991 printf("routes1 and routes2 are the same\n");
6995 if (routes1
!= NULL
) {
6998 if (routes2
!= NULL
) {
7004 typedef struct compare_context
{
7005 IPv4RouteListRef old
;
7006 IPv4RouteListRef
new;
7007 } compare_context_t
;
7010 compare_callback(IPv4RouteListApplyCommand cmd
, IPv4RouteRef route
, void * arg
)
7012 compare_context_t
* context
= (compare_context_t
*)arg
;
7015 case kIPv4RouteListAddRouteCommand
:
7016 printf("Add new[%ld] = ", route
- context
->new->list
);
7017 IPv4RoutePrint(route
);
7019 case kIPv4RouteListRemoveRouteCommand
:
7020 printf("Remove old[%ld] = ", route
- context
->old
->list
);
7021 IPv4RoutePrint(route
);
7030 compare_tests(struct ipv4_service_contents
* * old_test
,
7031 struct ipv4_service_contents
* * new_test
)
7033 IPv4RouteListRef new_routes
;
7034 IPv4RouteListRef old_routes
;
7035 compare_context_t context
;
7037 old_routes
= make_IPv4RouteList(old_test
);
7038 new_routes
= make_IPv4RouteList(new_test
);
7040 if (old_routes
== NULL
) {
7041 printf("No Old Routes\n");
7044 printf("Old Routes = ");
7045 IPv4RouteListPrint(old_routes
);
7047 if (new_routes
== NULL
) {
7048 printf("No New Routes\n");
7051 printf("New Routes = ");
7052 IPv4RouteListPrint(new_routes
);
7054 context
.old
= old_routes
;
7055 context
.new = new_routes
;
7056 IPv4RouteListApply(old_routes
, new_routes
, compare_callback
, &context
);
7057 if (old_routes
!= NULL
) {
7060 if (new_routes
!= NULL
) {
7068 main(int argc
, char **argv
)
7071 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
7073 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
7075 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
7078 if (run_test("test1", test1
) == FALSE
) {
7079 fprintf(stderr
, "test1 failed\n");
7082 if (run_test("test2", test2
) == FALSE
) {
7083 fprintf(stderr
, "test2 failed\n");
7086 if (run_test("test3", test4
) == FALSE
) {
7087 fprintf(stderr
, "test3 failed\n");
7090 if (run_test("test4", test4
) == FALSE
) {
7091 fprintf(stderr
, "test4 failed\n");
7094 if (run_test("test5", test5
) == FALSE
) {
7095 fprintf(stderr
, "test5 failed\n");
7098 if (run_test("test15", test15
) == FALSE
) {
7099 fprintf(stderr
, "test15 failed\n");
7104 printf("\nCompare 1 to 2:\n");
7105 compare_tests(test1
, test2
);
7107 printf("\nCompare 2 to 1:\n");
7108 compare_tests(test2
, test1
);
7110 printf("\nCompare 1 to 1:\n");
7111 compare_tests(test1
, test1
);
7113 printf("\nCompare 1 to 3:\n");
7114 compare_tests(test1
, test3
);
7116 printf("\nCompare 3 to 1:\n");
7117 compare_tests(test3
, test1
);
7119 printf("\nCompare 2 to 3:\n");
7120 compare_tests(test2
, test3
);
7122 printf("\nCompare 3 to 2:\n");
7123 compare_tests(test3
, test2
);
7125 printf("\nCompare 3 to 4:\n");
7126 compare_tests(test3
, test4
);
7128 printf("\nCompare 5 to 4:\n");
7129 compare_tests(test5
, test4
);
7131 printf("\nCompare 6 to 7:\n");
7132 compare_tests(test6
, test7
);
7134 printf("\nCompare 7 to 6:\n");
7135 compare_tests(test7
, test6
);
7137 printf("\nCompare 8 to 9:\n");
7138 compare_tests(test8
, test9
);
7140 printf("\nCompare 8 to 10:\n");
7141 compare_tests(test8
, test10
);
7143 printf("\nCompare 8 to 11:\n");
7144 compare_tests(test8
, test11
);
7146 printf("\nCompare 12 to 13:\n");
7147 compare_tests(test12
, test13
);
7149 printf("\nCompare 13 to 14:\n");
7150 compare_tests(test13
, test14
);
7152 printf("\nChecking for leaks\n");
7154 sprintf(cmd
, "leaks %d 2>&1", getpid());
7162 #endif /* TEST_IPV4_ROUTELIST */