2 * Copyright (c) 2000-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 * - decides which interface will be made the "primary" interface,
27 * that is, the one with the default route assigned
31 * Modification History
33 * July 19, 2000 Dieter Siegmund (dieter@apple.com)
36 * November 15, 2000 Dieter Siegmund (dieter@apple.com)
37 * - changed to use new configuration model
39 * March 19, 2001 Dieter Siegmund (dieter@apple.com)
40 * - use service state instead of interface state
42 * July 16, 2001 Allan Nathanson (ajn@apple.com)
43 * - update to public SystemConfiguration.framework APIs
45 * August 28, 2001 Dieter Siegmund (dieter@apple.com)
46 * - specify the interface name when installing the default route
47 * - this ensures that default traffic goes to the highest priority
48 * service when multiple interfaces are configured to be on the same subnet
50 * September 16, 2002 Dieter Siegmund (dieter@apple.com)
51 * - don't elect a link-local service to be primary unless it's the only
52 * one that's available
54 * July 16, 2003 Dieter Siegmund (dieter@apple.com)
55 * - modifications to support IPv6
56 * - don't elect a service to be primary if it doesn't have a default route
58 * July 29, 2003 Dieter Siegmund (dieter@apple.com)
59 * - support installing a default route to a router that's not on our subnet
61 * March 22, 2004 Allan Nathanson (ajn@apple.com)
62 * - create expanded DNS configuration
64 * June 20, 2006 Allan Nathanson (ajn@apple.com)
65 * - add SMB configuration
67 * December 5, 2007 Dieter Siegmund (dieter@apple.com)
68 * - added support for multiple scoped routes
70 * November 13, 2013 Dieter Siegmund (dieter@apple.com)
71 * - added generic IPv4 routing support
78 #include <sys/fcntl.h>
79 #include <sys/ioctl.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <net/route.h>
84 #include <net/if_dl.h>
85 #include <netinet/in.h>
86 #include <netinet/icmp6.h>
87 #include <netinet6/in6_var.h>
88 #include <netinet6/nd6.h>
89 #include <arpa/inet.h>
90 #include <sys/sysctl.h>
93 #include <mach/mach_time.h>
94 #include <dispatch/dispatch.h>
95 #include <CommonCrypto/CommonDigest.h>
97 #include <SystemConfiguration/SystemConfiguration.h>
98 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
99 #include <SystemConfiguration/SCValidation.h>
100 #include <SystemConfiguration/scprefs_observer.h>
101 #include <SystemConfiguration/SCPrivate.h> /* for SCLog() */
102 #include "SCNetworkReachabilityInternal.h"
103 #include "SCNetworkSignaturePrivate.h"
105 #include "dnsinfo_server.h"
107 #include <ppp/PPPControllerPriv.h>
110 #ifndef kDNSServiceCompMulticastDNS
111 #define kDNSServiceCompMulticastDNS "MulticastDNS"
113 #ifndef kDNSServiceCompPrivateDNS
114 #define kDNSServiceCompPrivateDNS "PrivateDNS"
116 #include <network_information.h>
117 #include "network_information_priv.h"
118 #include "network_information_server.h"
119 #include <ppp/ppp_msg.h>
120 #include "ip_plugin.h"
121 #if !TARGET_IPHONE_SIMULATOR
122 #include "set-hostname.h"
123 #endif /* !TARGET_IPHONE_SIMULATOR */
125 #include "dns-configuration.h"
126 #include "proxy-configuration.h"
128 #if !TARGET_OS_IPHONE
129 #include "smb-configuration.h"
130 #endif /* !TARGET_OS_IPHONE */
132 #define kLoopbackInterface "lo0"
133 #define EROUTENOTAPPLIED 1001
135 typedef CF_ENUM(uint8_t, ProtocolFlags
) {
136 kProtocolFlagsNone
= 0x0,
137 kProtocolFlagsIPv4
= 0x1,
138 kProtocolFlagsIPv6
= 0x2
142 kDebugFlag1
= 0x00000001,
143 kDebugFlag2
= 0x00000002,
144 kDebugFlag4
= 0x00000004,
145 kDebugFlag8
= 0x00000008,
146 kDebugFlagDefault
= kDebugFlag1
,
147 kDebugFlagAll
= 0xffffffff
150 typedef unsigned int IFIndex
;
152 #ifndef TEST_ROUTELIST
154 #define ROUTELIST_DEBUG(flag, fmt, ...)
156 static struct if_nameindex
* S_if_nameindex_cache
;
158 __private_extern__ IFIndex
159 my_if_nametoindex(const char * ifname
)
162 struct if_nameindex
* scan
;
164 if (S_if_nameindex_cache
== NULL
) {
165 return (if_nametoindex(ifname
));
167 for (scan
= S_if_nameindex_cache
;
168 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
170 if (strcmp(scan
->if_name
, ifname
) == 0) {
171 idx
= scan
->if_index
;
178 __private_extern__
const char *
179 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
181 const char * name
= NULL
;
182 struct if_nameindex
* scan
;
184 if (S_if_nameindex_cache
== NULL
) {
185 return (if_indextoname(idx
, if_name
));
187 for (scan
= S_if_nameindex_cache
;
188 scan
->if_index
!= 0 && scan
->if_name
!= NULL
;
190 if (scan
->if_index
== idx
) {
192 strlcpy(if_name
, scan
->if_name
, IFNAMSIZ
);
200 my_if_freenameindex(void)
202 if (S_if_nameindex_cache
!= NULL
) {
203 if_freenameindex(S_if_nameindex_cache
);
204 S_if_nameindex_cache
= NULL
;
210 my_if_nameindex(void)
212 my_if_freenameindex();
213 S_if_nameindex_cache
= if_nameindex();
218 #else /* TEST_ROUTELIST */
220 #define ROUTELIST_DEBUG(flags, format, ...) { if (((S_IPMonitor_debug & (flags)) != 0)) printf((format), ## __VA_ARGS__ ); }
223 static const char * * list
;
224 static int list_count
;
225 static int list_size
;
227 __private_extern__ IFIndex
228 my_if_nametoindex(const char * ifname
)
235 list
= (const char * *)malloc(sizeof(*list
) * list_size
);
236 list
[0] = strdup("");
237 list
[1] = strdup(kLoopbackInterface
);
242 for (i
= 1; i
< list_count
; i
++) {
243 if (strcmp(list
[i
], ifname
) == 0) {
249 if (list_count
== list_size
) {
251 list
= (const char * *)realloc(list
, sizeof(*list
) * list_size
);
253 list
[list_count
] = strdup(ifname
);
260 __private_extern__
const char *
261 my_if_indextoname(IFIndex idx
, char if_name
[IFNAMSIZ
])
263 const char * name
= NULL
;
265 if (idx
< list_count
) {
267 strlcpy(if_name
, list
[idx
], IFNAMSIZ
);
273 my_if_nameindex(void)
278 my_if_freenameindex(void)
282 #endif /* TEST_ROUTELIST */
285 my_if_indextoname2(IFIndex ifindex
, char ifname
[IFNAMSIZ
])
290 if (my_if_indextoname(ifindex
, ifname
) == NULL
) {
291 snprintf(ifname
, IFNAMSIZ
, "[%d]", ifindex
);
303 idx
= my_if_nametoindex(kLoopbackInterface
);
310 * Property: kServiceOptionRankAssertion
312 * Key used in the service options dictionary to hold the RankAssertion
313 * derived from the kSCPropNetServicePrimaryRank string.
315 #define kServiceOptionRankAssertion CFSTR("RankAssertion") /* number */
318 * Property: kIPIsCoupled
320 * Used to indicate that the IPv4 and IPv6 services are coupled.
321 * Neither the IPv4 part nor the IPv6 part of a coupled service
322 * may become primary if IPv4 or IPv6 is primary for another interface.
324 * For example, if the service over en3 is "coupled" and has IPv6,
325 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible
326 * to become primary for IPv6.
328 #define kIPIsCoupled CFSTR("IPIsCoupled")
330 #define PPP_PREFIX "ppp"
332 #define IP_FORMAT "%d.%d.%d.%d"
333 #define IP_CH(ip) ((u_char *)(ip))
334 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
336 static SCLoggerRef S_IPMonitor_logger
;
338 static Boolean S_bundle_logging_verbose
;
341 * IPv4 Route management
344 typedef CF_ENUM(uint16_t, RouteFlags
) {
345 kRouteFlagsIsScoped
= 0x0001,
346 kRouteFlagsHasGateway
= 0x0002,
347 kRouteFlagsIsHost
= 0x0004,
348 kRouteFlagsIsNULL
= 0x0008,
349 kRouteFlagsKernelManaged
= 0x0010
352 typedef CF_ENUM(uint16_t, ControlFlags
) {
353 kControlFlagsProcessed
= 0x0001,
354 kControlFlagsAdded
= 0x0002,
357 #define ROUTE_COMMON \
360 IFIndex exclude_ifindex; \
363 ControlFlags control_flags;
373 struct in_addr gateway
;
375 } IPv4Route
, * IPv4RouteRef
;
379 struct in6_addr dest
;
380 struct in6_addr gateway
;
382 } IPv6Route
, * IPv6RouteRef
;
384 typedef CF_ENUM(uint16_t, RouteListFlags
) {
385 kRouteListFlagsExcludeNWI
= 0x0001,
386 kRouteListFlagsHasDefault
= 0x0002,
387 kRouteListFlagsScopedOnly
= 0x0004
390 #define ROUTELIST_COMMON \
393 RouteListFlags flags;
397 } RouteListCommon
, * RouteListRef
;
401 IPv4Route list
[1]; /* variable length */
402 } IPv4RouteList
, * IPv4RouteListRef
;
406 IPv6Route list
[1]; /* variable length */
407 } IPv6RouteList
, * IPv6RouteListRef
;
422 * Election Information
423 * - information about the current best services
431 struct sockaddr_in v4
;
432 struct sockaddr_in6 v6
;
435 typedef struct Candidate
{
436 CFStringRef serviceID
;
439 boolean_t ip_is_coupled
;
440 boolean_t ineligible
;
441 SCNetworkReachabilityFlags reachability_flags
;
443 in_sockaddr vpn_server_addr
;
444 CFStringRef signature
;
445 } Candidate
, * CandidateRef
;
447 typedef struct ElectionResults
{
451 Candidate candidates
[1];
452 } ElectionResults
, * ElectionResultsRef
;
454 static __inline__
size_t
455 ElectionResultsComputeSize(unsigned int n
)
457 return (offsetof(ElectionResults
, candidates
[n
]));
463 * A 32-bit value to encode the relative rank of a service.
465 * The top 8 bits are used to hold the rank assertion (first, default, last,
468 * The bottom 24 bits are used to store the service index (i.e. the
469 * position within the service order array).
471 #define RANK_ASSERTION_MAKE(r) ((Rank)(r) << 24)
472 #define kRankAssertionFirst RANK_ASSERTION_MAKE(0)
473 #define kRankAssertionDefault RANK_ASSERTION_MAKE(1)
474 #define kRankAssertionLast RANK_ASSERTION_MAKE(2)
475 #define kRankAssertionNever RANK_ASSERTION_MAKE(3)
476 #define kRankAssertionScoped RANK_ASSERTION_MAKE(4)
477 #define kRankAssertionMask RANK_ASSERTION_MAKE(0xff)
478 #define RANK_ASSERTION_MASK(r) ((Rank)(r) & kRankAssertionMask)
479 #define RANK_ASSERTION_GET(r) ((Rank)(r) >> 24)
480 #define RANK_INDEX_MAKE(r) ((Rank)(r))
481 #define kRankIndexMask RANK_INDEX_MAKE(0xffffff)
482 #define RANK_INDEX_MASK(r) ((Rank)(r) & kRankIndexMask)
484 static __inline__ Rank
485 RankMake(uint32_t service_index
, Rank primary_rank
)
487 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
491 InterfaceRankGetRankAssertion(CFNumberRef rank_cf
, Boolean
* ret_is_set
)
493 SCNetworkServicePrimaryRank if_rank
;
494 Boolean is_set
= FALSE
;
495 Rank rank
= kRankAssertionDefault
;
498 && CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &if_rank
)
499 && if_rank
!= kSCNetworkServicePrimaryRankDefault
) {
500 if (if_rank
== kSCNetworkServicePrimaryRankFirst
) {
501 rank
= kRankAssertionFirst
;
504 rank
= RANK_ASSERTION_MAKE(if_rank
);
508 if (ret_is_set
!= NULL
) {
509 *ret_is_set
= is_set
;
515 PrimaryRankGetRankAssertion(CFStringRef rank_str
, Boolean
* is_set
)
519 const CFStringRef
* name
;
522 { &kSCValNetServicePrimaryRankFirst
, kRankAssertionFirst
},
523 { &kSCValNetServicePrimaryRankLast
, kRankAssertionLast
},
524 { &kSCValNetServicePrimaryRankNever
, kRankAssertionNever
},
525 { &kSCValNetServicePrimaryRankScoped
, kRankAssertionScoped
}
528 if (rank_str
!= NULL
) {
529 for (i
= 0; i
< countof(values
); i
++) {
530 if (CFEqual(rank_str
, *(values
[i
].name
))) {
531 if (is_set
!= NULL
) {
534 return (values
[i
].rank_assertion
);
538 if (is_set
!= NULL
) {
541 return (kRankAssertionDefault
);
544 /* SCDynamicStore session */
545 static SCDynamicStoreRef S_session
= NULL
;
547 /* debug output flags */
548 static uint32_t S_IPMonitor_debug
= 0;
549 static Boolean S_IPMonitor_verbose
= FALSE
;
551 /* are we netbooted? If so, don't touch the default route */
552 static boolean_t S_netboot
= FALSE
;
554 /* is scoped routing enabled? */
555 static boolean_t S_scopedroute
= FALSE
;
556 static boolean_t S_scopedroute_v6
= FALSE
;
558 /* dictionary to hold per-service state: key is the serviceID */
559 static CFMutableDictionaryRef S_service_state_dict
= NULL
;
560 static CFMutableDictionaryRef S_ipv4_service_rank_dict
= NULL
;
561 static CFMutableDictionaryRef S_ipv6_service_rank_dict
= NULL
;
563 /* dictionary to hold per-interface rank information */
564 static CFDictionaryRef S_if_rank_dict
;
566 /* if set, a PPP interface overrides the primary */
567 static boolean_t S_ppp_override_primary
= FALSE
;
569 /* the current primary serviceID's */
570 static CFStringRef S_primary_ipv4
= NULL
;
571 static CFStringRef S_primary_ipv6
= NULL
;
572 static CFStringRef S_primary_dns
= NULL
;
573 static CFStringRef S_primary_proxies
= NULL
;
575 /* the current election results */
576 static ElectionResultsRef S_ipv4_results
;
577 static ElectionResultsRef S_ipv6_results
;
579 static CFStringRef S_state_global_ipv4
= NULL
;
580 static CFStringRef S_state_global_ipv6
= NULL
;
581 static CFStringRef S_state_global_dns
= NULL
;
582 static CFStringRef S_state_global_proxies
= NULL
;
583 static CFStringRef S_state_service_prefix
= NULL
;
584 static CFStringRef S_setup_global_ipv4
= NULL
;
585 static CFStringRef S_setup_service_prefix
= NULL
;
587 static CFStringRef S_multicast_resolvers
= NULL
;
588 static CFStringRef S_private_resolvers
= NULL
;
590 #if !TARGET_IPHONE_SIMULATOR
591 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
592 static IPv6RouteListRef S_ipv6_routelist
= NULL
;
594 #endif /* !TARGET_IPHONE_SIMULATOR */
596 static boolean_t S_append_state
= FALSE
;
598 static CFDictionaryRef S_dns_dict
= NULL
;
600 static Boolean S_dnsinfo_synced
= TRUE
;
602 static nwi_state_t S_nwi_state
= NULL
;
603 static Boolean S_nwi_synced
= TRUE
;
605 static CFDictionaryRef S_proxies_dict
= NULL
;
607 // Note: access should be gated with __network_change_queue()
608 static uint32_t S_network_change_needed
= 0;
609 #define NETWORK_CHANGE_NET 1<<0
610 #define NETWORK_CHANGE_DNS 1<<1
611 #define NETWORK_CHANGE_PROXY 1<<2
612 #if !TARGET_OS_IPHONE
613 #define NETWORK_CHANGE_SMB 1<<3
614 #endif /* !TARGET_OS_IPHONE */
615 static struct timeval S_network_change_start
;
616 static Boolean S_network_change_timeout
= FALSE
;
617 static dispatch_source_t S_network_change_timer
= NULL
;
619 #if !TARGET_OS_IPHONE
620 static CFStringRef S_primary_smb
= NULL
;
621 static CFStringRef S_state_global_smb
= NULL
;
622 static CFDictionaryRef S_smb_dict
= NULL
;
623 #endif /* !TARGET_OS_IPHONE */
625 #if !TARGET_OS_IPHONE
626 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
627 #endif /* !TARGET_OS_IPHONE */
630 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
631 #endif /* KERN_NETBOOT */
634 ** entityType*, GetEntityChanges*
635 ** - definitions for the entity types we handle
642 #if !TARGET_OS_IPHONE
644 #endif /* !TARGET_OS_IPHONE */
646 kEntityTypeTransientStatus
,
647 kEntityTypeServiceOptions
= 31
650 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
651 &kSCEntNetIPv4
, /* 0 */
652 &kSCEntNetIPv6
, /* 1 */
653 &kSCEntNetDNS
, /* 2 */
654 &kSCEntNetProxies
, /* 3 */
655 #if !TARGET_OS_IPHONE
656 &kSCEntNetSMB
, /* 4 */
657 #endif /* !TARGET_OS_IPHONE */
661 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
);
663 static __inline__
char
666 return ((af
== AF_INET
) ? '4' : '6');
669 static __inline__
char
670 ipvx_other_char(int af
)
672 return ((af
== AF_INET
) ? '6' : '4');
676 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
678 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
679 * Routes CFData containing IPv4RouteList/IPv6RouteList
680 * Service dictionary containing kSCEntNetIPv[46] service entity
682 #define kIPDictRoutes CFSTR("Routes") /* data */
683 #define kIPDictService CFSTR("Service") /* dict */
685 static CFDictionaryRef
686 ipdict_create(CFDictionaryRef dict
, CFDataRef routes_data
)
691 keys
[0] = kIPDictService
;
693 keys
[1] = kIPDictRoutes
;
694 values
[1] = routes_data
;
695 return (CFDictionaryCreate(NULL
,
696 (const void * *)keys
,
699 &kCFTypeDictionaryKeyCallBacks
,
700 &kCFTypeDictionaryValueCallBacks
));
704 ipdict_get_routelist(CFDictionaryRef dict
)
706 void * routes_list
= NULL
;
711 routes
= CFDictionaryGetValue(dict
, kIPDictRoutes
);
712 if (routes
!= NULL
) {
713 routes_list
= (void *)CFDataGetBytePtr(routes
);
716 return (routes_list
);
719 static CFDictionaryRef
720 ipdict_get_service(CFDictionaryRef dict
)
722 CFDictionaryRef ip_dict
= NULL
;
725 ip_dict
= CFDictionaryGetValue(dict
, kIPDictService
);
731 ipdict_get_ifname(CFDictionaryRef dict
)
733 CFStringRef ifname
= NULL
;
734 CFDictionaryRef ip_dict
;
736 ip_dict
= ipdict_get_service(dict
);
737 if (ip_dict
!= NULL
) {
738 ifname
= CFDictionaryGetValue(ip_dict
, kSCPropInterfaceName
);
743 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
744 CFDictionaryRef state_dict
,
745 CFDictionaryRef setup_dict
,
746 CFDictionaryRef info
);
747 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
749 static GetEntityChangesFunc get_ipv4_changes
;
750 static GetEntityChangesFunc get_ipv6_changes
;
751 static GetEntityChangesFunc get_dns_changes
;
752 static GetEntityChangesFunc get_proxies_changes
;
753 #if !TARGET_OS_IPHONE
754 static GetEntityChangesFunc get_smb_changes
;
755 #endif /* !TARGET_OS_IPHONE */
758 my_CFRelease(void * t
);
761 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
764 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
766 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
767 get_ipv4_changes
, /* 0 */
768 get_ipv6_changes
, /* 1 */
769 get_dns_changes
, /* 2 */
770 get_proxies_changes
,/* 3 */
771 #if !TARGET_OS_IPHONE
772 get_smb_changes
, /* 4 */
773 #endif /* !TARGET_OS_IPHONE */
778 ** - mechanism to do an atomic update of the SCDynamicStore
779 ** when the content needs to be changed across multiple functions
782 CFMutableArrayRef notify
;
783 CFMutableArrayRef remove
;
784 CFMutableDictionaryRef set
;
785 } keyChangeList
, * keyChangeListRef
;
788 keyChangeListInit(keyChangeListRef keys
)
790 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
791 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
792 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
793 &kCFTypeDictionaryKeyCallBacks
,
794 &kCFTypeDictionaryValueCallBacks
);
799 keyChangeListFree(keyChangeListRef keys
)
801 my_CFRelease(&keys
->notify
);
802 my_CFRelease(&keys
->remove
);
803 my_CFRelease(&keys
->set
);
808 keyChangeListActive(keyChangeListRef keys
)
810 return ((CFDictionaryGetCount(keys
->set
) > 0) ||
811 (CFArrayGetCount(keys
->remove
) > 0) ||
812 (CFArrayGetCount(keys
->notify
) > 0));
816 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
818 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
823 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
825 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
826 CFDictionaryRemoveValue(keys
->set
, key
);
831 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
833 my_CFArrayRemoveValue(keys
->remove
, key
);
834 CFDictionarySetValue(keys
->set
, key
, value
);
839 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
841 CFArrayRef notify
= keys
->notify
;
842 CFArrayRef remove
= keys
->remove
;
843 CFDictionaryRef set
= keys
->set
;
845 if (CFArrayGetCount(notify
) == 0) {
848 if (CFArrayGetCount(remove
) == 0) {
851 if (CFDictionaryGetCount(set
) == 0) {
854 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
857 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
859 my_log(LOG_DEBUG
, "Setting:\n%@", set
);
861 if (remove
!= NULL
) {
862 my_log(LOG_DEBUG
, "Removing:\n%@", remove
);
864 if (notify
!= NULL
) {
865 my_log(LOG_DEBUG
, "Notifying:\n%@", notify
);
868 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
874 S_nwi_ifstate_dump(nwi_ifstate_t ifstate
, int i
)
876 const char * addr_str
;
878 char ntopbuf
[INET6_ADDRSTRLEN
];
879 char vpn_ntopbuf
[INET6_ADDRSTRLEN
];
880 const struct sockaddr
* vpn_addr
;
882 address
= nwi_ifstate_get_address(ifstate
);
883 addr_str
= inet_ntop(ifstate
->af
, address
, ntopbuf
, sizeof(ntopbuf
));
884 vpn_addr
= nwi_ifstate_get_vpn_server(ifstate
);
885 if (vpn_addr
!= NULL
) {
886 _SC_sockaddr_to_string(nwi_ifstate_get_vpn_server(ifstate
),
888 sizeof(vpn_ntopbuf
));
891 " [%d]: %s%s%s%s rank 0x%x iaddr %s%s%s reach_flags 0x%x",
893 nwi_ifstate_get_diff_str(ifstate
),
894 (ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_DNS
) != 0
896 (ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0
900 (vpn_addr
!= NULL
) ? " vpn_server_addr: " : "",
901 (vpn_addr
!= NULL
) ? vpn_ntopbuf
: "",
902 ifstate
->reach_flags
);
907 S_nwi_state_dump(nwi_state_t state
)
913 my_log(LOG_INFO
, "nwi_state = <none>");
918 "gen=%llu size=%lu #v4=%u #v6=%u "
919 "reach_flags=(v4=0x%x, v6=0x%x) }",
920 state
->generation_count
,
921 nwi_state_size(state
),
924 nwi_state_get_reachability_flags(state
, AF_INET
),
925 nwi_state_get_reachability_flags(state
, AF_INET6
));
926 if (state
->ipv4_count
) {
927 my_log(LOG_INFO
, "IPv4:");
928 for (i
= 0, scan
= nwi_state_ifstate_list(state
, AF_INET
);
929 i
< state
->ipv4_count
; i
++, scan
++) {
930 S_nwi_ifstate_dump(scan
, i
);
933 if (state
->ipv6_count
) {
934 my_log(LOG_INFO
, "IPv6:");
935 for (i
= 0, scan
= nwi_state_ifstate_list(state
, AF_INET6
);
936 i
< state
->ipv6_count
; i
++, scan
++) {
937 S_nwi_ifstate_dump(scan
, i
);
940 if (state
->max_if_count
) {
941 nwi_ifindex_t
* ifindex
;
943 my_log(LOG_INFO
, "%d interfaces:", state
->if_list_count
);
944 for (i
= 0, ifindex
= nwi_state_if_list(state
);
945 i
< state
->if_list_count
;
947 my_log(LOG_INFO
, "%s", state
->ifstate_list
[*ifindex
].ifname
);
961 mib
[1] = KERN_NETBOOT
;
962 len
= sizeof(netboot
);
963 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
967 static int rtm_seq
= 0;
969 #if !TARGET_IPHONE_SIMULATOR
971 open_routing_socket(void)
975 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
976 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
981 static __inline__
int
982 inet6_dgram_socket(void)
986 sockfd
= socket(AF_INET6
, SOCK_DGRAM
, 0);
988 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
995 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
997 struct in6_defrouter dr
;
998 struct sockaddr_in6
* sin6
;
1000 bzero(&dr
, sizeof(dr
));
1002 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
1003 sin6
->sin6_family
= AF_INET6
;
1004 sin6
->sin6_addr
= *addr
;
1006 dr
.if_index
= if_index
;
1007 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
1011 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
1013 struct in6_defrouter dr
;
1014 struct sockaddr_in6
* sin6
;
1016 bzero(&dr
, sizeof(dr
));
1018 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
1019 sin6
->sin6_family
= AF_INET6
;
1020 sin6
->sin6_addr
= *addr
;
1021 dr
.if_index
= if_index
;
1022 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
1025 #endif /* !TARGET_IPHONE_SIMULATOR */
1028 S_is_scoped_routing_enabled()
1030 int scopedroute
= 0;
1031 size_t len
= sizeof(scopedroute
);
1033 if ((sysctlbyname("net.inet.ip.scopedroute",
1036 && (errno
!= ENOENT
)) {
1037 my_log(LOG_ERR
, "sysctlbyname() failed: %s", strerror(errno
));
1039 return (scopedroute
);
1043 S_is_scoped_v6_routing_enabled()
1045 int scopedroute_v6
= 0;
1046 size_t len
= sizeof(scopedroute_v6
);
1048 if ((sysctlbyname("net.inet6.ip6.scopedroute",
1049 &scopedroute_v6
, &len
,
1051 && (errno
!= ENOENT
)) {
1052 my_log(LOG_ERR
, "sysctlbyname() failed: %s", strerror(errno
));
1054 return (scopedroute_v6
);
1058 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
1060 CFIndex n
= CFArrayGetCount(arr
);
1062 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
1065 CFArrayAppendValue(arr
, new);
1070 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
1074 i
= CFArrayGetFirstIndexOfValue(arr
,
1075 CFRangeMake(0, CFArrayGetCount(arr
)),
1077 if (i
!= kCFNotFound
) {
1078 CFArrayRemoveValueAtIndex(arr
, i
);
1084 my_CFArrayCreateCombinedArray(CFArrayRef array1
, CFArrayRef array2
)
1086 CFMutableArrayRef combined
;
1088 combined
= CFArrayCreateMutableCopy(NULL
, 0, array1
);
1089 CFArrayAppendArray(combined
,
1091 CFRangeMake(0, CFArrayGetCount(array2
)));
1096 my_CFRelease(void * t
)
1098 void * * obj
= (void * *)t
;
1107 static CFDictionaryRef
1108 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
1110 if (isA_CFDictionary(dict
) == NULL
) {
1113 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
1117 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
1119 if (isA_CFDictionary(dict
) == NULL
) {
1122 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
1126 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, int addr_size
)
1130 if (isA_CFString(str
) == NULL
) {
1136 if (addr_size
< sizeof(struct in_addr
)) {
1141 if (addr_size
< sizeof(struct in6_addr
)) {
1148 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
1149 if (inet_pton(family
, buf
, addr
) == 1) {
1153 bzero(addr
, addr_size
);
1159 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
1161 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
1166 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
1168 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
1172 cfnumber_to_int(CFNumberRef num
, int * int_val
)
1174 if (isA_CFNumber(num
) == NULL
) {
1177 return (CFNumberGetValue(num
, kCFNumberIntType
, int_val
));
1180 static CF_RETURNS_RETAINED CFStringRef
1181 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
1183 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1184 kSCDynamicStoreDomainSetup
,
1189 static CF_RETURNS_RETAINED CFStringRef
1190 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
1192 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1193 kSCDynamicStoreDomainState
,
1199 interface_entity_key_copy(CFStringRef ifname
, CFStringRef entity
)
1201 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1202 kSCDynamicStoreDomainState
,
1207 static CFDictionaryRef
1208 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1211 CFStringRef setup_key
;
1212 CFDictionaryRef setup_dict
;
1214 setup_key
= setup_service_key(serviceID
, entity
);
1215 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
1216 my_CFRelease(&setup_key
);
1217 return (setup_dict
);
1220 static CFDictionaryRef
1221 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1224 CFStringRef state_key
;
1225 CFDictionaryRef state_dict
;
1227 state_key
= state_service_key(serviceID
, entity
);
1228 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
1229 my_CFRelease(&state_key
);
1230 return (state_dict
);
1234 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1238 ip_list
= CFDictionaryGetValue(dict
, prop
);
1239 if (isA_CFArray(ip_list
) != NULL
1240 && CFArrayGetCount(ip_list
) > 0
1241 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1248 dict_get_first_ipv6(CFDictionaryRef dict
, CFStringRef prop
,
1249 struct in6_addr
* ip_p
)
1253 ip_list
= CFDictionaryGetValue(dict
, prop
);
1254 if (isA_CFArray(ip_list
) != NULL
1255 && CFArrayGetCount(ip_list
) > 0
1256 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1263 dict_get_first_int(CFDictionaryRef dict
, CFStringRef prop
,
1268 list
= CFDictionaryGetValue(dict
, prop
);
1269 if (isA_CFArray(list
) != NULL
1270 && CFArrayGetCount(list
) > 0
1271 && cfnumber_to_int(CFArrayGetValueAtIndex(list
, 0), val
)) {
1278 dict_get_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1282 val
= CFDictionaryGetValue(dict
, prop
);
1283 return (cfstring_to_ip(val
, ip_p
));
1287 dict_get_ipv6(CFDictionaryRef dict
, CFStringRef prop
, struct in6_addr
* ip_p
)
1291 val
= CFDictionaryGetValue(dict
, prop
);
1292 return (cfstring_to_ip6(val
, ip_p
));
1296 dict_get_int(CFDictionaryRef dict
, CFStringRef prop
, int * intval
)
1300 val
= CFDictionaryGetValue(dict
, prop
);
1301 return (cfnumber_to_int(val
, intval
));
1305 get_override_primary(CFDictionaryRef dict
)
1309 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
1310 if (isA_CFNumber(override
) != NULL
) {
1313 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
1318 else if (isA_CFBoolean(override
) != NULL
) {
1319 if (CFBooleanGetValue(override
)) {
1331 (*RouteListComputeSize
)(CFIndex n
);
1334 (*RouteIsEqual
)(RouteRef a
, RouteRef b
);
1337 (*RouteApply
)(RouteRef route
, int cmd
, int sockfd
);
1339 typedef const void *
1340 (*RouteGateway
)(RouteRef route
);
1343 (*RouteSetGateway
)(RouteRef route
, const void * address
);
1345 typedef const void *
1346 (*RouteDestination
)(RouteRef route
);
1349 (*RouteSameSubnet
)(RouteRef route
, const void * address
);
1352 (*RouteCopyDescription
)(RouteRef route
);
1355 (*RouteLog
)(int priority
, RouteRef route
, const char * msg
);
1358 RouteListComputeSize list_compute_size
;
1360 RouteIsEqual route_equal
;
1361 RouteApply route_apply
;
1362 RouteGateway route_gateway
;
1363 RouteSetGateway route_set_gateway
;
1364 RouteDestination route_destination
;
1365 RouteSameSubnet route_same_subnet
;
1367 RouteCopyDescription route_copy_description
;
1374 typedef const RouteListInfo
* RouteListInfoRef
;
1377 RouteListInfoRef info
;
1378 RouteListRef old_routes
;
1379 RouteListRef new_routes
;
1382 } RouteListApplyContext
, * RouteListApplyContextRef
;
1386 RouteAddressCompare(RouteListInfoRef info
,
1390 return (memcmp(addr1
, addr2
, info
->address_size
));
1394 RouteCompare(RouteListInfoRef info
,
1395 RouteRef a
, Rank a_rank
,
1396 RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
1399 RouteDestination route_destination
;
1400 RouteCopyDescription route_copy_description
;
1403 route_destination
= info
->route_destination
;
1404 route_copy_description
= info
->route_copy_description
;
1405 cmp
= RouteAddressCompare(info
,
1406 (*route_destination
)(a
),
1407 (*route_destination
)(b
));
1409 cmp
= a
->prefix_length
- b
->prefix_length
;
1411 int index_cmp
= a
->ifindex
- b
->ifindex
;
1413 if (index_cmp
== 0) {
1416 else if ((a
->ifindex
== 0 || b
->ifindex
== 0)
1417 && (a
->flags
& kRouteFlagsIsScoped
) == 0
1418 && (b
->flags
& kRouteFlagsIsScoped
) == 0) {
1420 * Either of the routes specifies no interface and neither
1421 * route is scoped. Claim they are equal to eliminate the
1428 cmp
= RankCompare(a_rank
, b_rank
);
1435 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
1443 else if (cmp
== 0) {
1449 a_str
= (*route_copy_description
)(a
);
1450 b_str
= (*route_copy_description
)(b
);
1451 my_log(LOG_DEBUG
, "%@ rank 0x%x %c %@ rank 0x%x",
1452 a_str
, a_rank
, ch
, b_str
, b_rank
);
1460 RouteListGetRouteAtIndexSimple(RouteListInfoRef info
, RouteListRef routes
,
1463 return ((void *)routes
+ (*info
->list_compute_size
)(where
));
1467 RouteListGetRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1470 if (routes
->count
== 0
1471 || where
>= routes
->count
) {
1474 return (RouteListGetRouteAtIndexSimple(info
, routes
, where
));
1478 RouteListGetFirstRoute(RouteListInfoRef info
, RouteListRef routes
)
1480 return (RouteListGetRouteAtIndexSimple(info
, routes
, 0));
1483 #if !TARGET_IPHONE_SIMULATOR
1485 RouteListRouteIndex(RouteListInfoRef info
, RouteListRef routes
,
1488 return (((void *)route
1489 - (void *)RouteListGetFirstRoute(info
, routes
))
1490 / info
->element_size
);
1492 #endif /* !TARGET_IPHONE_SIMULATOR */
1495 RouteGetNextRoute(RouteListInfoRef info
, RouteRef route
)
1497 return ((RouteRef
)(((void *)route
) + info
->element_size
));
1501 RouteListAddRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1502 RouteRef this_route
, CFIndex where
)
1504 RouteRef insert_route
;
1506 if (where
== kCFNotFound
) {
1507 /* add it to the end */
1509 = RouteListGetRouteAtIndexSimple(info
, routes
, routes
->count
);
1512 /* make space at [where] */
1513 insert_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1515 (void *)insert_route
+ info
->element_size
,
1516 info
->element_size
* (routes
->count
- where
));
1518 /* copy the route */
1519 bcopy(this_route
, insert_route
, info
->element_size
);
1521 return (insert_route
);
1525 RouteListRemoveRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1528 if (routes
->count
== 0
1529 || where
>= routes
->count
) {
1533 if (where
== routes
->count
) {
1534 /* last slot, decrementing gets rid of it */
1537 RouteRef remove_route
;
1539 remove_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1540 bcopy((void *)remove_route
+ info
->element_size
,
1542 info
->element_size
* (routes
->count
- where
));
1548 * Function: RouteListAddRoute
1551 * Add the given route to the list of routes, eliminating lower-ranked
1552 * duplicates on the same interface, and marking any lower ranked duplicates
1553 * on other interfaces with kRouteFlagsIsScoped.
1555 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1558 * Route list updated with the given route, possibly a different pointer,
1559 * due to using realloc'd memory.
1569 RouteListAddRoute(RouteListInfoRef info
,
1570 RouteListRef routes
, int init_size
,
1571 RouteRef this_route
, Rank this_rank
)
1574 RouteRef first_scan
= NULL
;
1577 Scope scope_which
= kScopeNone
;
1578 CFIndex where
= kCFNotFound
;
1580 if (routes
== NULL
) {
1581 size_t alloc_size
= (*info
->list_compute_size
)(init_size
);
1583 routes
= (RouteListRef
)malloc(alloc_size
);
1584 bzero(routes
, sizeof(*routes
));
1585 routes
->size
= init_size
;
1587 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1589 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1591 boolean_t same_dest
;
1593 cmp
= RouteCompare(info
, this_route
, this_rank
, scan
, scan
->rank
,
1595 if (same_dest
== TRUE
&& first_scan
== NULL
) {
1599 if (where
== kCFNotFound
) {
1600 if (same_dest
== TRUE
1601 && (first_scan
->flags
& kRouteFlagsIsScoped
) == 0) {
1602 if ((scan
->flags
& kRouteFlagsIsScoped
) != 0) {
1603 ROUTELIST_DEBUG(kDebugFlag8
,
1604 "Hit 1: set scope on self\n");
1605 scope_which
= kScopeThis
;
1608 ROUTELIST_DEBUG(kDebugFlag8
,
1609 "Hit 2: set scope on next\n");
1610 scope_which
= kScopeNext
;
1613 /* remember our insertion point, but keep going to find a dup */
1617 else if (cmp
== 0) {
1620 if (where
!= kCFNotFound
1621 && scan
->ifindex
== this_route
->ifindex
1622 && scan
->exclude_ifindex
== 0
1623 && this_route
->exclude_ifindex
== 0) {
1624 /* this route is a duplicate */
1625 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 3: removing [%ld]\n", i
);
1626 RouteListRemoveRouteAtIndex(info
, routes
, i
);
1630 * this_route is "better" than scan if this_route is not excluded
1631 * and scan is excluded or this_route sorts ahead of scan
1633 if (this_route
->exclude_ifindex
== 0
1634 && (scan
->exclude_ifindex
!= 0 || this_rank
< scan
->rank
)) {
1635 IFIndex ifindex
= 0;
1636 boolean_t is_scoped
= FALSE
;
1638 if (scan
->flags
& kRouteFlagsIsScoped
) {
1641 if (this_rank
< scan
->rank
) {
1642 ROUTELIST_DEBUG(kDebugFlag8
,
1643 "Hit 4a: replacing [%ld]"
1644 " rank 0x%x < 0x%x\n",
1645 i
, this_rank
, scan
->rank
);
1648 ROUTELIST_DEBUG(kDebugFlag8
,
1649 "Hit 4b: replacing [%ld] excluded route\n",
1652 if (scan
->ifindex
!= 0) {
1653 ifindex
= scan
->ifindex
;
1655 else if (this_route
->ifindex
!= 0) {
1656 ifindex
= this_route
->ifindex
;
1658 bcopy(this_route
, scan
, info
->element_size
);
1659 scan
->rank
= this_rank
;
1660 scan
->ifindex
= ifindex
;
1661 scan
->exclude_ifindex
= 0;
1663 /* preserve whether route was scoped */
1664 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 5: preserved scope\n");
1665 scan
->flags
|= kRouteFlagsIsScoped
;
1672 if (same_dest
== TRUE
) {
1673 if (scope_which
== kScopeNone
) {
1674 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 6: set scope on self\n");
1675 scope_which
= kScopeThis
;
1678 #ifdef TEST_ROUTELIST
1679 else if (where
!= kCFNotFound
) {
1680 /* not possible because we maintain a sorted list */
1682 "Hit 7: moved past routes - can't happen\n");
1686 #endif /* TEST_ROUTELIST */
1690 if (routes
->size
== routes
->count
) {
1692 RouteListRef new_routes
;
1695 /* double the size */
1696 old_size
= routes
->size
;
1697 how_many
= old_size
* 2;
1698 new_routes
= (RouteListRef
)
1699 reallocf(routes
, (*info
->list_compute_size
)(how_many
));
1700 if (new_routes
== NULL
) {
1705 ROUTELIST_DEBUG(kDebugFlag8
, "increasing size from %d to %d\n",
1706 old_size
, how_many
);
1707 new_routes
->size
= how_many
;
1708 routes
= new_routes
;
1711 /* add/insert the new route */
1712 this_route
= RouteListAddRouteAtIndex(info
, routes
, this_route
, where
);
1713 this_route
->rank
= this_rank
;
1715 if (RANK_ASSERTION_MASK(this_rank
) == kRankAssertionNever
) {
1716 flags
|= kRouteFlagsIsScoped
;
1718 switch (scope_which
) {
1720 flags
|= kRouteFlagsIsScoped
;
1723 this_route
= RouteListGetRouteAtIndex(info
, routes
, where
+ 1);
1724 flags
|= kRouteFlagsIsScoped
;
1730 if (this_route
!= NULL
&& flags
!= 0) {
1731 this_route
->flags
|= flags
;
1739 * Function: RouteListAddRouteList
1741 * Invoke RouteListAddRoute for each route in the given list
1742 * 'service_routes' combining them into a combined list 'routes'.
1745 * See RouteListAddRoute for more information.
1748 RouteListAddRouteList(RouteListInfoRef info
,
1749 RouteListRef routes
, int init_size
,
1750 RouteListRef service_routes
, Rank rank
)
1755 for (i
= 0, scan
= RouteListGetFirstRoute(info
, service_routes
);
1756 i
< service_routes
->count
;
1757 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1761 && (service_routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
1762 /* only apply rank to first element of the list (default route) */
1766 this_rank
= RANK_INDEX_MASK(rank
) | RANK_ASSERTION_MASK(scan
->rank
);
1768 routes
= RouteListAddRoute(info
, routes
, init_size
, scan
, this_rank
);
1774 RouteAddInterfaceToDescription(RouteRef r
, CFMutableStringRef str
)
1776 char if_name
[IFNAMSIZ
];
1778 if (my_if_indextoname2(r
->ifindex
, if_name
) != NULL
) {
1779 CFStringAppendFormat(str
, NULL
,
1783 if (my_if_indextoname2(r
->exclude_ifindex
, if_name
) != NULL
) {
1784 CFStringAppendFormat(str
, NULL
,
1792 RouteAddFlagsToDescription(RouteRef r
, CFMutableStringRef str
)
1794 if ((r
->flags
& kRouteFlagsIsNULL
) != 0) {
1795 CFStringAppend(str
, CFSTR(" [null]"));
1798 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
1800 switch (rank_assertion
) {
1801 case kRankAssertionFirst
:
1802 CFStringAppend(str
, CFSTR(" [first]"));
1804 case kRankAssertionLast
:
1805 CFStringAppend(str
, CFSTR(" [last]"));
1807 case kRankAssertionNever
:
1808 CFStringAppend(str
, CFSTR(" [never]"));
1813 if ((r
->flags
& kRouteFlagsKernelManaged
) != 0) {
1814 CFStringAppend(str
, CFSTR(" [kern]"));
1816 if ((r
->flags
& kRouteFlagsIsScoped
) != 0) {
1817 CFStringAppend(str
, CFSTR(" [SCOPED]"));
1823 #if !TARGET_IPHONE_SIMULATOR
1825 RouteListFindRoute(RouteListInfoRef info
, RouteListRef routes
, RouteRef route
)
1828 RouteRef match
= NULL
;
1831 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1833 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1834 if ((*info
->route_equal
)(scan
, route
)) {
1844 kRouteLookupFlagsNone
= 0x0,
1845 kRouteLookupFlagsExcludeInterface
= 0x1
1849 RouteListLookup(RouteListInfoRef info
,
1850 RouteListRef routes
,
1851 const void * address
,
1854 RouteLookupFlags lookup_flags
)
1856 RouteRef best_match
= NULL
;
1860 for (i
= 0, candidate
= RouteListGetFirstRoute(info
, routes
);
1862 i
++, candidate
= RouteGetNextRoute(info
, candidate
)) {
1863 if (candidate
->ifindex
== 0 || candidate
->exclude_ifindex
!= 0) {
1864 /* ignore exclude routes */
1867 if ((lookup_flags
& kRouteLookupFlagsExcludeInterface
) != 0) {
1868 /* exclude interfaces with the same interface index */
1869 if (ifindex
== candidate
->ifindex
) {
1873 else if (ifindex
!= candidate
->ifindex
) {
1876 if ((candidate
->flags
& kRouteFlagsHasGateway
) != 0
1877 && RouteAddressCompare(info
,
1878 (*info
->route_gateway
)(candidate
),
1880 /* skip route whose gateway is the address we're looking for */
1883 if ((candidate
->flags
& kRouteFlagsIsHost
) != 0) {
1884 /* if host route and we're looking for an exact match */
1885 if (n_bits
== info
->all_bits_set
1886 && RouteAddressCompare(info
,
1887 (*info
->route_destination
)(candidate
),
1889 /* found exact match */
1890 best_match
= candidate
;
1896 /* verify that address is on the same subnet */
1897 if ((*info
->route_same_subnet
)(candidate
, address
) == FALSE
) {
1898 /* different subnet */
1902 if (candidate
->prefix_length
== n_bits
) {
1904 best_match
= candidate
;
1907 if (candidate
->prefix_length
> n_bits
) {
1908 /* matched too many bits */
1911 if (best_match
== NULL
1912 || candidate
->prefix_length
> best_match
->prefix_length
) {
1913 best_match
= candidate
;
1916 return (best_match
);
1921 * Function: RouteProcess
1923 * Function to process adding or removing the specified route.
1924 * In the case of adding, that may involve first processing the gateway
1925 * route (recursively).
1928 RouteProcess(RouteRef route
,
1930 RouteListApplyContextRef context
)
1932 RouteLog route_log
= context
->info
->route_log
;
1933 RouteApply route_apply
= context
->info
->route_apply
;
1934 RouteGateway route_gateway
= context
->info
->route_gateway
;
1938 case kRouteCommandAdd
:
1939 if ((route
->control_flags
& kControlFlagsProcessed
) != 0) {
1940 return ((route
->control_flags
& kControlFlagsAdded
) != 0);
1942 route
->control_flags
|= kControlFlagsProcessed
;
1943 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
1945 RouteRef gateway_route
;
1948 = RouteListLookup(context
->info
,
1949 context
->new_routes
,
1950 (*route_gateway
)(route
),
1951 context
->info
->all_bits_set
,
1953 kRouteLookupFlagsNone
);
1954 if (gateway_route
== NULL
) {
1955 (*route_log
)(LOG_NOTICE
, route
, "no gateway route");
1958 #define MAX_RECURSE_DEPTH 10
1959 /* avoid infinite recursion */
1960 if (context
->depth
== MAX_RECURSE_DEPTH
) {
1961 (*route_log
)(LOG_NOTICE
, route
, "routing loop detected, not adding");
1964 /* recurse to add gateway route */
1966 added
= RouteProcess(gateway_route
,
1970 if (added
== FALSE
) {
1971 (*route_log
)(LOG_NOTICE
, route
, "failed to add");
1976 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1977 if (retval
== EEXIST
) {
1978 /* delete and add again */
1979 (void)(*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
1980 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1985 "failed to add route, %s:",
1987 (*route_log
)(LOG_NOTICE
, route
, NULL
);
1990 case EROUTENOTAPPLIED
:
1991 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1995 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
1996 snprintf(buf
, sizeof(buf
), "%sAdd new[%ld]",
1998 RouteListRouteIndex(context
->info
,
1999 context
->new_routes
,
2001 (*route_log
)(LOG_DEBUG
, route
, buf
);
2003 route
->control_flags
|= kControlFlagsAdded
;
2007 case kRouteCommandRemove
:
2008 retval
= (*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
2012 case EROUTENOTAPPLIED
:
2013 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2017 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
2018 snprintf(buf
, sizeof(buf
), "%sRemove old[%ld]%s",
2020 RouteListRouteIndex(context
->info
,
2021 context
->old_routes
,
2023 (retval
== ESRCH
) ? "(ESRCH)" : "");
2024 (*route_log
)(LOG_DEBUG
, route
, buf
);
2029 "failed to remove route, %s",
2031 (*route_log
)(LOG_NOTICE
, route
, NULL
);
2042 RouteListApply(RouteListInfoRef info
,
2043 RouteListRef old_routes
, RouteListRef new_routes
,
2046 RouteListApplyContext context
;
2050 if (old_routes
== new_routes
&& old_routes
== NULL
) {
2051 /* both old and new are NULL, so there's nothing to do */
2054 bzero(&context
, sizeof(context
));
2055 context
.old_routes
= old_routes
;
2056 context
.new_routes
= new_routes
;
2057 context
.sockfd
= sockfd
;
2058 context
.info
= info
;
2059 if (old_routes
!= NULL
) {
2060 for (i
= 0, scan
= RouteListGetFirstRoute(info
, old_routes
);
2061 i
< old_routes
->count
;
2062 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2063 RouteRef new_route
= NULL
;
2065 if (new_routes
!= NULL
) {
2066 new_route
= RouteListFindRoute(info
, new_routes
, scan
);
2068 if (new_route
== NULL
) {
2069 if ((scan
->control_flags
& kControlFlagsAdded
) != 0) {
2070 RouteProcess(scan
, kRouteCommandRemove
, &context
);
2075 if (new_routes
!= NULL
) {
2076 if (old_routes
!= NULL
) {
2077 /* preserve the control flags from any old routes */
2078 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2079 i
< new_routes
->count
;
2080 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2081 RouteRef old_route
= NULL
;
2083 old_route
= RouteListFindRoute(info
, old_routes
, scan
);
2084 if (old_route
!= NULL
) {
2085 /* preserve the control state in the new route */
2086 scan
->control_flags
= old_route
->control_flags
;
2090 /* add any routes that need to be added */
2091 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2092 i
< new_routes
->count
;
2093 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2094 if ((scan
->control_flags
& kControlFlagsProcessed
) != 0) {
2097 RouteProcess(scan
, kRouteCommandAdd
, &context
);
2103 * Function: RouteListFinalize
2105 * Look for excluded routes. If the excluded route does not have an assigned
2106 * interface, search for a route that *does not* go over the excluded
2109 * If the excluded route does have an assigned interface, search for a route
2110 * that *does* go over the assigned interface.
2112 * Set the gateway on the excluded route to match the gateway of the found
2116 RouteListFinalize(RouteListInfoRef info
, RouteListRef routes
)
2121 if (routes
== NULL
) {
2124 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
2126 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2129 RouteLookupFlags flags
;
2131 if (scan
->exclude_ifindex
== 0) {
2134 if (scan
->ifindex
== 0) {
2135 ifindex
= scan
->exclude_ifindex
;
2136 flags
= kRouteLookupFlagsExcludeInterface
;
2139 ifindex
= scan
->ifindex
;
2140 flags
= kRouteLookupFlagsNone
;
2142 route
= RouteListLookup(info
, routes
,
2143 (*info
->route_destination
)(scan
),
2144 scan
->prefix_length
, ifindex
, flags
);
2145 if (route
== NULL
) {
2146 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
, "can't resolve excluded route");
2149 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
2150 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)scan
, "Excluded route");
2151 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)route
, "Resolved to");
2153 scan
->ifindex
= route
->ifindex
;
2154 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2155 (*info
->route_set_gateway
)(scan
, (*info
->route_gateway
)(route
));
2156 scan
->flags
|= kRouteFlagsHasGateway
;
2157 if (scan
->prefix_length
== info
->all_bits_set
) {
2158 scan
->flags
|= kRouteFlagsIsHost
;
2162 /* routes directly to interface */
2163 scan
->flags
&= ~(kRouteFlagsHasGateway
| kRouteFlagsIsHost
);
2169 #endif /* !TARGET_IPHONE_SIMULATOR */
2175 #define IPV4_ROUTE_ALL_BITS_SET 32
2177 static __inline__
struct in_addr
2178 subnet_addr(struct in_addr addr
, struct in_addr mask
)
2182 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
2187 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
2189 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
2190 CFStringAppendFormat(str
, NULL
,
2191 CFSTR("Host " IP_FORMAT
),
2195 CFStringAppendFormat(str
, NULL
,
2196 CFSTR("Net " IP_FORMAT
),
2198 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
2201 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
2202 CFStringAppendFormat(str
, NULL
,
2203 CFSTR(" Gate " IP_FORMAT
),
2204 IP_LIST(&r
->gateway
));
2206 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
2207 if (r
->ifa
.s_addr
!= 0) {
2208 CFStringAppendFormat(str
, NULL
,
2209 CFSTR(" Ifa " IP_FORMAT
),
2212 RouteAddFlagsToDescription((RouteRef
)r
, str
);
2217 IPv4RouteCopyDescription(RouteRef r
)
2219 CFMutableStringRef str
;
2221 str
= CFStringCreateMutable(NULL
, 0);
2222 IPv4RouteCopyDescriptionWithString((IPv4RouteRef
)r
, str
);
2226 #ifdef TEST_IPV4_ROUTELIST
2227 static CFMutableStringRef
2228 IPv4RouteListCopyDescription(IPv4RouteListRef routes
);
2231 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2233 CFStringRef str
= IPv4RouteCopyDescription(route
);
2236 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2239 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
2245 static __inline__
void
2246 IPv4RouteListPrint(IPv4RouteListRef routes
)
2248 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2250 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2255 #else /* TEST_IPV4_ROUTELIST */
2257 static __inline__
void
2258 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2260 CFStringRef str
= IPv4RouteCopyDescription(route
);
2263 my_log(level
, "%@", str
);
2266 my_log(level
, "%s: %@", msg
, str
);
2272 #endif /* TEST_IPV4_ROUTELIST */
2275 IPv4RouteIsEqual(RouteRef r_scan
, RouteRef r_route
)
2277 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2278 IPv4RouteRef scan
= (IPv4RouteRef
)r_scan
;
2280 return ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
2281 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
2282 && (scan
->ifindex
== route
->ifindex
)
2283 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
2284 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
2285 && (scan
->flags
== route
->flags
));
2288 static CFMutableStringRef
2289 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
2293 CFMutableStringRef str
;
2295 str
= CFStringCreateMutable(NULL
, 0);
2296 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
2298 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
2299 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
2300 IPv4RouteCopyDescriptionWithString(r
, str
);
2302 CFStringAppend(str
, CFSTR("\n}"));
2307 IPv4RouteListComputeSize(CFIndex n
)
2309 return (offsetof(IPv4RouteList
, list
[n
]));
2313 count_prefix_bits_set(uint32_t n
)
2316 const static int8_t bits
[16] = {
2335 for (count
= 0; n
!= 0; n
>>= 4) {
2336 int nbits
= bits
[n
& 0x0f];
2347 prefix_to_mask32(unsigned int prefix_length
)
2349 if (prefix_length
> 32 || prefix_length
== 0) {
2352 return (0xffffffff << (32 - prefix_length
));
2356 mask_get_prefix_length(struct in_addr mask
)
2360 count
= count_prefix_bits_set(mask
.s_addr
);
2364 val
= prefix_to_mask32(count
);
2365 if (ntohl(mask
.s_addr
) != val
) {
2366 /* expected mask based on prefix length doesn't match */
2374 IPv4RouteSetPrefixLength(IPv4RouteRef route
)
2378 length
= mask_get_prefix_length(route
->mask
);
2382 route
->prefix_length
= length
;
2387 IPv4RouteGateway(RouteRef r_route
)
2389 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2390 return (&route
->gateway
);
2394 IPv4RouteSetGateway(RouteRef r_route
, const void * address
)
2396 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2398 route
->gateway
= *((struct in_addr
*)address
);
2403 IPv4RouteDestination(RouteRef r_route
)
2405 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2406 return (&route
->dest
);
2410 IPv4RouteSameSubnet(RouteRef r_route
, const void * addr
)
2412 const struct in_addr
* address
;
2413 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2415 address
= (const struct in_addr
*)addr
;
2416 return ((address
->s_addr
& route
->mask
.s_addr
) == route
->dest
.s_addr
);
2420 * Define: ROUTE_MSG_ADDRS_SPACE
2422 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2423 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2424 * someone changes the code and doesn't think to modify this.
2426 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2427 + 2 * sizeof(struct sockaddr_dl) \
2430 struct rt_msghdr hdr
;
2431 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2435 * Function: IPv4RouteApply
2437 * Add or remove the specified route to/from the kernel routing table.
2440 IPv4RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
2444 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2447 struct sockaddr_in
* in_p
;
2448 struct sockaddr_dl
* dl_p
;
2452 if (S_netboot
&& route
->dest
.s_addr
== 0) {
2453 /* don't touch the default route */
2454 return (EROUTENOTAPPLIED
);
2456 if ((route
->flags
& kRouteFlagsIsScoped
) != 0
2457 && !S_scopedroute
) {
2458 return (EROUTENOTAPPLIED
);
2460 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
2461 return (EROUTENOTAPPLIED
);
2463 if (route
->ifindex
== 0) {
2465 IP_FORMAT
" no interface specified, ignoring",
2466 IP_LIST(&route
->dest
));
2470 #ifdef TEST_IPV4_ROUTELIST
2472 #else /* TEST_IPV4_ROUTELIST */
2474 #endif /* TEST_IPV4_ROUTELIST */
2476 memset(&rtmsg
, 0, sizeof(rtmsg
));
2477 rtmsg
.hdr
.rtm_type
= cmd
;
2478 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2479 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2480 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
2481 if (route
->ifa
.s_addr
!= 0) {
2482 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
2484 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2485 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
2486 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
2489 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
2490 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
2491 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
2494 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2495 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
2497 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
2498 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
2499 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2502 rtaddr
.ptr
= rtmsg
.addrs
;
2505 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2506 rtaddr
.in_p
->sin_family
= AF_INET
;
2507 rtaddr
.in_p
->sin_addr
= route
->dest
;
2508 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2511 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2512 /* gateway is an IP address */
2513 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2514 rtaddr
.in_p
->sin_family
= AF_INET
;
2515 rtaddr
.in_p
->sin_addr
= route
->gateway
;
2516 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2519 /* gateway is the interface itself */
2520 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2521 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2522 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2523 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2527 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
2528 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2529 rtaddr
.in_p
->sin_family
= AF_INET
;
2530 rtaddr
.in_p
->sin_addr
= route
->mask
;
2531 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2535 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
2536 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2537 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2538 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2539 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2541 /* interface address */
2542 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
2543 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2544 rtaddr
.in_p
->sin_family
= AF_INET
;
2545 rtaddr
.in_p
->sin_addr
= route
->ifa
;
2546 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2549 /* apply the route */
2550 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
2551 rtmsg
.hdr
.rtm_msglen
= len
;
2552 if (write(sockfd
, &rtmsg
, len
) == -1) {
2558 static const RouteListInfo IPv4RouteListInfo
= {
2559 IPv4RouteListComputeSize
,
2564 IPv4RouteSetGateway
,
2565 IPv4RouteDestination
,
2566 IPv4RouteSameSubnet
,
2568 IPv4RouteCopyDescription
,
2571 sizeof(struct in_addr
),
2572 IPV4_ROUTE_ALL_BITS_SET
2575 #if !TARGET_IPHONE_SIMULATOR
2576 static __inline__
void
2577 IPv4RouteListLog(int level
, IPv4RouteListRef routes
)
2579 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2581 my_log(level
, "%@", str
);
2587 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
2590 RouteListApply(&IPv4RouteListInfo
,
2591 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
2597 IPv4RouteListFinalize(IPv4RouteListRef routes
)
2599 RouteListFinalize(&IPv4RouteListInfo
, (RouteListRef
)routes
);
2602 #endif /* !TARGET_IPHONE_SIMULATOR */
2604 #ifdef TEST_IPV4_ROUTELIST
2605 static IPv4RouteListRef
2606 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
2607 IPv4RouteListRef service_routes
, Rank rank
)
2609 return ((IPv4RouteListRef
)
2610 RouteListAddRouteList(&IPv4RouteListInfo
,
2611 (RouteListRef
)routes
, init_size
,
2612 (RouteListRef
)service_routes
, rank
));
2614 #endif /* TEST_IPV4_ROUTELIST */
2617 plist_get_string(CFDictionaryRef dict
, CFStringRef prop_name
,
2618 char * buf
, int buf_size
)
2622 val
= CFDictionaryGetValue(dict
, prop_name
);
2623 if (isA_CFString(val
) == NULL
) {
2626 if (CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingUTF8
)
2634 struct in_addr addr
;
2637 IFIndex exclude_ifindex
;
2638 IPv4RouteRef
* route_p
;
2641 } AddIPv4RouteContext
, * AddIPv4RouteContextRef
;
2644 AddIPv4Route(const void * value
, void * context
)
2646 AddIPv4RouteContextRef ctx
= (AddIPv4RouteContextRef
)context
;
2647 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
2648 IPv4RouteRef r
= *ctx
->route_p
;
2650 dict
= isA_CFDictionary(dict
);
2652 || !dict_get_ip(dict
, kSCPropNetIPv4RouteDestinationAddress
, &r
->dest
)
2653 || !dict_get_ip(dict
, kSCPropNetIPv4RouteSubnetMask
, &r
->mask
)) {
2654 /* one less route than we expected */
2656 my_log(LOG_NOTICE
, "%s route is not a dictionary",
2660 my_log(LOG_NOTICE
, "%s route is invalid, %@",
2665 if (IPv4RouteSetPrefixLength(r
) == FALSE
) {
2666 my_log(LOG_NOTICE
, "%s route has invalid subnet mask, %@",
2670 r
->rank
= ctx
->rank
;
2671 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
2672 if (ctx
->ifindex
!= 0) {
2673 r
->ifindex
= ctx
->ifindex
;
2675 if (ctx
->exclude_ifindex
== 0
2676 && dict_get_ip(dict
,
2677 kSCPropNetIPv4RouteGatewayAddress
,
2679 r
->flags
|= kRouteFlagsHasGateway
;
2680 if (r
->prefix_length
== IPV4_ROUTE_ALL_BITS_SET
) {
2681 r
->flags
|= kRouteFlagsIsHost
;
2686 char ifname
[IFNAMSIZ
];
2688 if (plist_get_string(dict
, kSCPropNetIPv4RouteInterfaceName
,
2689 ifname
, sizeof(ifname
)) != NULL
) {
2692 ifindex
= my_if_nametoindex(ifname
);
2695 "%s: interface %s does not exist, %@",
2696 ctx
->descr
, ifname
, dict
);
2699 else if (ifindex
== ctx
->ifindex
) {
2701 "%s: interface %s unexpected, %@",
2702 ctx
->descr
, ifname
, dict
);
2705 r
->ifindex
= ifindex
;
2718 confirm_interface_name(CFDictionaryRef dict
, CFStringRef ifname
)
2720 CFStringRef confirmed_ifname
;
2721 boolean_t confirmed
;
2724 = CFDictionaryGetValue(dict
, kSCPropConfirmedInterfaceName
);
2725 if (isA_CFString(confirmed_ifname
) != NULL
) {
2726 confirmed
= CFEqual(confirmed_ifname
, ifname
);
2735 * Function: IPv4RouteListCreateWithDictionary
2738 * Given the service ipv4 entity dictionary, generate the list of routes.
2739 * Currently, this includes just the default route and subnet route,
2740 * if the service has a subnet mask.
2743 * If the passed in route_list is NULL or too small, this routine
2744 * allocates malloc'd memory to hold the routes.
2746 static IPv4RouteListRef
2747 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
2748 CFDictionaryRef dict
,
2749 CFNumberRef rank_assertion
)
2751 boolean_t add_default
= FALSE
;
2752 boolean_t add_router_subnet
= FALSE
;
2753 boolean_t add_subnet
= FALSE
;
2754 struct in_addr addr
= { 0 };
2755 CFArrayRef additional_routes
= NULL
;
2756 CFIndex additional_routes_count
;
2757 boolean_t allow_additional_routes
= FALSE
;
2758 boolean_t exclude_from_nwi
= FALSE
;
2759 CFArrayRef excluded_routes
= NULL
;
2760 CFIndex excluded_routes_count
;
2761 RouteFlags flags
= 0;
2763 char ifname
[IFNAMSIZ
];
2764 CFStringRef ifname_cf
;
2765 struct in_addr mask
= { 0 };
2767 int prefix_length
= 0;
2768 Rank primary_rank
= kRankAssertionDefault
;
2770 Rank rank
= kRankAssertionDefault
;
2771 struct in_addr router
= { 0 };
2772 boolean_t scoped_only
= FALSE
;
2773 struct in_addr subnet
= { 0 };
2778 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
2779 ifname
, sizeof(ifname
));
2780 if (ifname_cf
== NULL
) {
2783 ifindex
= my_if_nametoindex(ifname
);
2785 /* interface doesn't exist */
2788 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
2789 if (dict_get_ip(dict
, kSCPropNetIPv4Router
, &router
) == FALSE
) {
2790 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
2792 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
2793 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
2795 subnet
= subnet_addr(addr
, mask
);
2796 /* ignore link-local subnets, let IPConfiguration handle them for now */
2797 if (ntohl(subnet
.s_addr
) != IN_LINKLOCALNETNUM
) {
2798 prefix_length
= mask_get_prefix_length(mask
);
2799 if (prefix_length
< 0) {
2801 "ignoring bad subnet mask "
2803 IP_LIST(&mask
), ifname
);
2810 else if (router
.s_addr
== 0) {
2811 exclude_from_nwi
= TRUE
;
2814 if (addr
.s_addr
== 0) {
2815 /* invalid/non-existent address */
2818 if (rank_assertion
!= NULL
) {
2819 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
2822 if (router
.s_addr
== 0) {
2823 /* if no router is configured, demote the rank if necessary */
2824 switch (primary_rank
) {
2825 case kRankAssertionLast
:
2826 case kRankAssertionNever
:
2827 case kRankAssertionScoped
:
2828 /* rank is already demoted */
2831 /* demote to RankLast */
2832 primary_rank
= kRankAssertionLast
;
2838 * If the router address is our address and the subnet mask is
2839 * not 255.255.255.255, assume all routes are local to the interface.
2841 if (addr
.s_addr
== router
.s_addr
2842 && mask
.s_addr
!= INADDR_BROADCAST
) {
2843 ; /* all routes local */
2846 flags
|= kRouteFlagsHasGateway
;
2848 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
2849 primary_rank
= kRankAssertionFirst
;
2853 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
2854 exclude_from_nwi
= TRUE
;
2855 flags
|= kRouteFlagsIsNULL
;
2858 switch (primary_rank
) {
2859 case kRankAssertionScoped
:
2860 /* Scoped means all routes for the service get scoped */
2861 primary_rank
= rank
= kRankAssertionNever
;
2862 flags
|= kRouteFlagsIsScoped
;
2865 case kRankAssertionNever
:
2866 /* Never means just the default route gets scoped */
2867 rank
= kRankAssertionLast
;
2868 flags
|= kRouteFlagsIsScoped
;
2871 rank
= primary_rank
;
2875 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2876 add_router_subnet
= TRUE
;
2880 if (ifindex
!= lo0_ifindex() && router
.s_addr
!= 0) {
2884 if (allow_additional_routes
) {
2886 = CFDictionaryGetValue(dict
, kSCPropNetIPv4AdditionalRoutes
);
2887 additional_routes
= isA_CFArray(additional_routes
);
2888 if (additional_routes
!= NULL
) {
2889 additional_routes_count
= CFArrayGetCount(additional_routes
);
2890 n
+= additional_routes_count
;
2893 = CFDictionaryGetValue(dict
, kSCPropNetIPv4ExcludedRoutes
);
2894 excluded_routes
= isA_CFArray(excluded_routes
);
2895 if (excluded_routes
!= NULL
) {
2896 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
2897 n
+= excluded_routes_count
;
2900 if (routes
== NULL
|| routes
->size
< n
) {
2901 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
2904 bzero(routes
, IPv4RouteListComputeSize(n
));
2906 if (exclude_from_nwi
) {
2907 routes
->flags
|= kRouteListFlagsExcludeNWI
;
2909 else if (scoped_only
) {
2910 routes
->flags
|= kRouteListFlagsScopedOnly
;
2913 /* start at the beginning */
2917 /* add the default route */
2918 routes
->flags
|= kRouteListFlagsHasDefault
;
2919 r
->ifindex
= ifindex
;
2922 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2923 r
->gateway
= router
;
2928 r
->rank
= primary_rank
;
2932 /* add the subnet route */
2934 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2935 r
->flags
|= kRouteFlagsIsNULL
;
2937 r
->ifindex
= ifindex
;
2941 r
->prefix_length
= prefix_length
;
2947 /* add the router subnet route */
2948 if (add_router_subnet
) {
2949 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2950 r
->flags
|= kRouteFlagsIsNULL
;
2952 r
->ifindex
= ifindex
;
2955 r
->mask
.s_addr
= INADDR_BROADCAST
;
2956 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2962 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
2963 AddIPv4RouteContext context
;
2965 bzero(&context
, sizeof(context
));
2966 context
.count_p
= &routes
->count
;
2967 context
.route_p
= &r
;
2968 context
.rank
= rank
;
2970 /* additional routes */
2971 if (additional_routes
!= NULL
) {
2972 context
.ifindex
= ifindex
;
2973 context
.addr
= addr
;
2974 context
.descr
= "AdditionalRoutes";
2975 CFArrayApplyFunction(additional_routes
,
2976 CFRangeMake(0, additional_routes_count
),
2977 AddIPv4Route
, &context
);
2979 /* excluded routes */
2980 if (excluded_routes
!= NULL
) {
2981 context
.descr
= "ExcludedRoutes";
2982 /* exclude this interface */
2983 context
.ifindex
= 0;
2984 context
.exclude_ifindex
= ifindex
;
2985 CFArrayApplyFunction(excluded_routes
,
2986 CFRangeMake(0, excluded_routes_count
),
2987 AddIPv4Route
, &context
);
2996 #define IPV6_ROUTE_ALL_BITS_SET 128
2999 ipv6_prefix_length_is_valid(int prefix_length
)
3001 if (prefix_length
< 0 || prefix_length
> IPV6_ROUTE_ALL_BITS_SET
) {
3008 * from netinet6/in6.c
3011 in6_len2mask(struct in6_addr
* mask
, int len
)
3015 bzero(mask
, sizeof(*mask
));
3016 for (i
= 0; i
< len
/ 8; i
++)
3017 mask
->s6_addr
[i
] = 0xff;
3019 mask
->s6_addr
[i
] = (0xff00 >> (len
% 8)) & 0xff;
3023 in6_maskaddr(struct in6_addr
* addr
, const struct in6_addr
* mask
)
3027 for (i
= 0; i
< sizeof(addr
->s6_addr
); i
++) {
3028 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3034 in6_netaddr(struct in6_addr
* addr
, int len
)
3036 struct in6_addr mask
;
3038 in6_len2mask(&mask
, len
);
3039 in6_maskaddr(addr
, &mask
);
3044 in6_addr_scope_linklocal(struct in6_addr
* addr
, IFIndex ifindex
)
3046 if (IN6_IS_ADDR_LINKLOCAL(addr
)) {
3047 addr
->__u6_addr
.__u6_addr16
[1] = htons(ifindex
);
3053 string_append_in6_addr(CFMutableStringRef str
, const struct in6_addr
* addr
)
3055 char ntopbuf
[INET6_ADDRSTRLEN
];
3057 CFStringAppendCString(str
,
3058 inet_ntop(AF_INET6
, addr
, ntopbuf
, sizeof(ntopbuf
)),
3059 kCFStringEncodingASCII
);
3064 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r
, CFMutableStringRef str
)
3066 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
3067 CFStringAppend(str
, CFSTR("Host "));
3068 string_append_in6_addr(str
, &r
->dest
);
3071 CFStringAppend(str
, CFSTR("Net "));
3072 string_append_in6_addr(str
, &r
->dest
);
3073 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
3076 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
3077 CFStringAppend(str
, CFSTR(" Gate "));
3078 string_append_in6_addr(str
, &r
->gateway
);
3080 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
3081 if (!IN6_ARE_ADDR_EQUAL(&r
->ifa
, &in6addr_any
)) {
3082 CFStringAppend(str
, CFSTR(" Ifa "));
3083 string_append_in6_addr(str
, &r
->ifa
);
3085 RouteAddFlagsToDescription((RouteRef
)r
, str
);
3090 IPv6RouteCopyDescription(RouteRef r
)
3092 CFMutableStringRef str
;
3094 str
= CFStringCreateMutable(NULL
, 0);
3095 IPv6RouteCopyDescriptionWithString((IPv6RouteRef
)r
, str
);
3099 static CFMutableStringRef
3100 IPv6RouteListCopyDescription(IPv6RouteListRef routes
)
3104 CFMutableStringRef str
;
3106 str
= CFStringCreateMutable(NULL
, 0);
3107 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv6RouteList[%d]> = {"),
3109 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
3110 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
3111 IPv6RouteCopyDescriptionWithString(r
, str
);
3113 CFStringAppend(str
, CFSTR("\n}"));
3117 #ifdef TEST_IPV6_ROUTELIST
3120 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3122 CFStringRef str
= IPv6RouteCopyDescription(route
);
3125 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3128 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
3134 static __inline__
void
3135 IPv6RouteListPrint(IPv6RouteListRef routes
)
3137 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3139 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3144 #else /* TEST_IPV6_ROUTELIST */
3146 static __inline__
void
3147 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3149 CFStringRef str
= IPv6RouteCopyDescription(route
);
3152 my_log(level
, "%@", str
);
3155 my_log(level
, "%s: %@", msg
, str
);
3161 #endif /* TEST_IPV6_ROUTELIST */
3164 IPv6RouteListComputeSize(CFIndex n
)
3166 return (offsetof(IPv6RouteList
, list
[n
]));
3171 struct in6_addr
* addr
;
3174 IFIndex exclude_ifindex
;
3175 IPv6RouteRef
* route_p
;
3178 } AddIPv6RouteContext
, * AddIPv6RouteContextRef
;
3181 AddIPv6Route(const void * value
, void * context
)
3183 AddIPv6RouteContextRef ctx
= (AddIPv6RouteContextRef
)context
;
3184 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
3185 IPv6RouteRef r
= *ctx
->route_p
;
3187 dict
= isA_CFDictionary(dict
);
3189 || !dict_get_ipv6(dict
, kSCPropNetIPv6RouteDestinationAddress
, &r
->dest
)
3190 || !dict_get_int(dict
, kSCPropNetIPv6RoutePrefixLength
,
3192 || !ipv6_prefix_length_is_valid(r
->prefix_length
)) {
3193 /* one less route than we expected */
3195 my_log(LOG_NOTICE
, "%s route is not a dictionary",
3199 my_log(LOG_NOTICE
, "%s route is invalid, %@",
3204 r
->rank
= ctx
->rank
;
3205 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
3206 if (ctx
->ifindex
!= 0) {
3207 r
->ifindex
= ctx
->ifindex
;
3208 r
->ifa
= *ctx
->addr
;
3209 if (ctx
->exclude_ifindex
== 0
3210 && dict_get_ipv6(dict
,
3211 kSCPropNetIPv6RouteGatewayAddress
,
3213 r
->flags
|= kRouteFlagsHasGateway
;
3214 if (r
->prefix_length
== IPV6_ROUTE_ALL_BITS_SET
) {
3215 r
->flags
|= kRouteFlagsIsHost
;
3220 char ifname
[IFNAMSIZ
];
3222 if (plist_get_string(dict
, kSCPropNetIPv6RouteInterfaceName
,
3223 ifname
, sizeof(ifname
)) != NULL
) {
3226 ifindex
= my_if_nametoindex(ifname
);
3229 "%s: interface %s does not exist, %@",
3230 ctx
->descr
, ifname
, dict
);
3233 else if (ifindex
== ctx
->ifindex
) {
3235 "%s: interface %s unexpected, %@",
3236 ctx
->descr
, ifname
, dict
);
3239 r
->ifindex
= ifindex
;
3252 * Function: IPv6RouteListCreateWithDictionary
3255 * Given the service IPv6 entity dictionary, generate the list of routes.
3258 * If the passed in route_list is NULL or too small, this routine
3259 * allocates malloc'd memory to hold the routes.
3261 static IPv6RouteListRef
3262 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes
,
3263 CFDictionaryRef dict
,
3264 CFNumberRef rank_assertion
)
3266 boolean_t add_default
= FALSE
;
3267 boolean_t add_prefix
= FALSE
;
3268 struct in6_addr addr
;
3269 CFArrayRef additional_routes
= NULL
;
3270 CFIndex additional_routes_count
;
3271 boolean_t allow_additional_routes
= FALSE
;
3272 boolean_t exclude_from_nwi
= FALSE
;
3273 CFArrayRef excluded_routes
= NULL
;
3274 CFIndex excluded_routes_count
;
3275 RouteFlags flags
= 0;
3277 char ifname
[IFNAMSIZ
];
3278 CFStringRef ifname_cf
;
3280 int prefix_length
= 0;
3281 Rank primary_rank
= kRankAssertionDefault
;
3283 Rank rank
= kRankAssertionDefault
;
3284 struct in6_addr router
= in6addr_any
;
3285 boolean_t scoped_only
= FALSE
;
3290 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
3291 ifname
, sizeof(ifname
));
3292 if (ifname_cf
== NULL
) {
3295 ifindex
= my_if_nametoindex(ifname
);
3297 /* interface doesn't exist */
3300 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
3301 if (dict_get_ipv6(dict
, kSCPropNetIPv6Router
, &router
) == FALSE
) {
3302 (void)dict_get_first_ipv6(dict
, kSCPropNetIPv6DestAddresses
, &router
);
3304 if (dict_get_first_ipv6(dict
, kSCPropNetIPv6Addresses
, &addr
)) {
3305 if (IN6_IS_ADDR_UNSPECIFIED(&addr
)) {
3308 if (dict_get_first_int(dict
, kSCPropNetIPv6PrefixLength
,
3310 && !IN6_IS_ADDR_LINKLOCAL(&addr
)
3311 && ipv6_prefix_length_is_valid(prefix_length
)) {
3323 if (rank_assertion
!= NULL
) {
3324 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
3327 if (!IN6_IS_ADDR_UNSPECIFIED(&router
)) {
3328 if (ifindex
!= lo0_ifindex()) {
3333 * If the router address is our address and the prefix length is
3334 * not 128, assume all routes are local to the interface.
3336 if (IN6_ARE_ADDR_EQUAL(&router
, &addr
)
3337 && prefix_length
!= IPV6_ROUTE_ALL_BITS_SET
) {
3338 ; /* all routes local */
3341 flags
|= kRouteFlagsHasGateway
;
3343 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
3344 primary_rank
= kRankAssertionFirst
;
3347 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
3348 exclude_from_nwi
= TRUE
;
3349 flags
|= kRouteFlagsIsNULL
;
3352 switch (primary_rank
) {
3353 case kRankAssertionScoped
:
3354 /* Scoped means all routes for the service get scoped */
3355 primary_rank
= rank
= kRankAssertionNever
;
3356 flags
|= kRouteFlagsIsScoped
;
3359 case kRankAssertionNever
:
3360 /* Never means just the default route gets scoped */
3361 rank
= kRankAssertionLast
;
3362 flags
|= kRouteFlagsIsScoped
;
3365 rank
= primary_rank
;
3369 if (allow_additional_routes
) {
3371 = CFDictionaryGetValue(dict
, kSCPropNetIPv6AdditionalRoutes
);
3372 additional_routes
= isA_CFArray(additional_routes
);
3373 if (additional_routes
!= NULL
) {
3374 additional_routes_count
= CFArrayGetCount(additional_routes
);
3375 n
+= additional_routes_count
;
3377 excluded_routes
= CFDictionaryGetValue(dict
,
3378 kSCPropNetIPv6ExcludedRoutes
);
3379 excluded_routes
= isA_CFArray(excluded_routes
);
3380 if (excluded_routes
!= NULL
) {
3381 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
3382 n
+= excluded_routes_count
;
3389 /* need IPv6LL subnet route */
3392 if (routes
== NULL
|| routes
->size
< n
) {
3393 routes
= (IPv6RouteListRef
)malloc(IPv6RouteListComputeSize(n
));
3396 bzero(routes
, IPv6RouteListComputeSize(n
));
3398 if (exclude_from_nwi
) {
3399 routes
->flags
|= kRouteListFlagsExcludeNWI
;
3401 else if (scoped_only
) {
3402 routes
->flags
|= kRouteListFlagsScopedOnly
;
3405 /* start at the beginning */
3408 /* add the default route */
3409 routes
->flags
|= kRouteListFlagsHasDefault
;
3410 r
->ifindex
= ifindex
;
3413 if ((flags
& kRouteFlagsHasGateway
) != 0) {
3414 r
->gateway
= router
;
3419 r
->rank
= primary_rank
;
3420 if (S_scopedroute_v6
) {
3421 r
->flags
|= kRouteFlagsKernelManaged
;
3427 /* add IPv6LL route */
3428 r
->ifindex
= ifindex
;
3429 r
->dest
.s6_addr
[0] = 0xfe;
3430 r
->dest
.s6_addr
[1] = 0x80;
3431 r
->prefix_length
= 64;
3433 r
->flags
|= kRouteFlagsKernelManaged
;
3437 /* add the prefix route(s) */
3439 r
->flags
|= kRouteFlagsKernelManaged
;
3440 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3441 r
->flags
|= kRouteFlagsIsNULL
;
3443 r
->ifindex
= ifindex
;
3446 in6_netaddr(&r
->dest
, prefix_length
);
3447 r
->prefix_length
= prefix_length
;
3453 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3454 AddIPv6RouteContext context
;
3456 bzero(&context
, sizeof(context
));
3457 context
.count_p
= &routes
->count
;
3458 context
.route_p
= &r
;
3459 context
.rank
= rank
;
3461 /* additional routes */
3462 if (additional_routes
!= NULL
) {
3463 context
.ifindex
= ifindex
;
3464 context
.addr
= &addr
;
3465 context
.descr
= "AdditionalRoutes";
3466 CFArrayApplyFunction(additional_routes
,
3467 CFRangeMake(0, additional_routes_count
),
3468 AddIPv6Route
, &context
);
3470 /* excluded routes */
3471 if (excluded_routes
!= NULL
) {
3472 context
.descr
= "ExcludedRoutes";
3473 /* exclude this interface */
3474 context
.ifindex
= 0;
3475 context
.exclude_ifindex
= ifindex
;
3476 context
.addr
= NULL
;
3477 CFArrayApplyFunction(excluded_routes
,
3478 CFRangeMake(0, excluded_routes_count
),
3479 AddIPv6Route
, &context
);
3486 IPv6RouteGateway(RouteRef r_route
)
3488 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3489 return (&route
->gateway
);
3493 IPv6RouteSetGateway(RouteRef r_route
, const void * address
)
3495 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3497 route
->gateway
= *((struct in6_addr
*)address
);
3502 IPv6RouteDestination(RouteRef r_route
)
3504 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3505 return (&route
->dest
);
3508 static __inline__
int
3509 in6_addr_cmp(const struct in6_addr
* a
, const struct in6_addr
* b
)
3511 return (memcmp(a
->s6_addr
, b
->s6_addr
, sizeof(struct in6_addr
)));
3515 IPv6RouteIsEqual(RouteRef r_route1
, RouteRef r_route2
)
3517 IPv6RouteRef route1
= (IPv6RouteRef
)r_route1
;
3518 IPv6RouteRef route2
= (IPv6RouteRef
)r_route2
;
3520 return (route1
->prefix_length
== route2
->prefix_length
3521 && route1
->ifindex
== route2
->ifindex
3522 && route1
->flags
== route2
->flags
3523 && in6_addr_cmp(&route1
->dest
, &route2
->dest
) == 0
3524 && in6_addr_cmp(&route1
->ifa
, &route2
->ifa
) == 0
3525 && in6_addr_cmp(&route1
->gateway
, &route2
->gateway
) == 0);
3529 IPv6RouteSameSubnet(RouteRef r_route
, const void * addr
)
3531 const struct in6_addr
* address
= (const struct in6_addr
*)addr
;
3532 struct in6_addr netaddr
;
3533 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3536 in6_netaddr(&netaddr
, route
->prefix_length
);
3537 return (in6_addr_cmp(&netaddr
, &route
->dest
) == 0);
3541 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3544 struct rt_msghdr hdr
;
3545 char addrs
[V6_ROUTE_MSG_ADDRS_SPACE
];
3549 * Function: IPv6RouteApply
3551 * Add or remove the specified route to/from the kernel routing table.
3554 IPv6RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
3558 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3561 struct sockaddr_in6
* in_p
;
3562 struct sockaddr_dl
* dl_p
;
3566 if ((route
->flags
& kRouteFlagsIsScoped
) != 0
3567 && !S_scopedroute_v6
) {
3568 return (EROUTENOTAPPLIED
);
3570 if ((route
->flags
& kRouteFlagsKernelManaged
) != 0) {
3571 /* the kernel manages this route, don't touch it */
3572 return (EROUTENOTAPPLIED
);
3574 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
3575 return (EROUTENOTAPPLIED
);
3577 if (route
->ifindex
== 0) {
3578 IPv6RouteLog(LOG_NOTICE
, (RouteRef
)route
,
3579 "no interface specified");
3583 #ifdef TEST_IPV6_ROUTELIST
3585 #else /* TEST_IPV6_ROUTELIST */
3587 #endif /* TEST_IPV6_ROUTELIST */
3589 memset(&rtmsg
, 0, sizeof(rtmsg
));
3590 rtmsg
.hdr
.rtm_type
= cmd
;
3591 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3592 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3593 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
3594 if (!IN6_IS_ADDR_UNSPECIFIED(&route
->ifa
)) {
3595 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
3597 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3598 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
3599 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
3602 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
3603 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
3604 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
3607 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
3608 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
3610 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
3611 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
3612 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
3615 rtaddr
.ptr
= rtmsg
.addrs
;
3618 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3619 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3620 rtaddr
.in_p
->sin6_addr
= route
->dest
;
3621 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3622 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3625 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
3626 /* gateway is an IP address */
3627 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3628 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3629 rtaddr
.in_p
->sin6_addr
= route
->gateway
;
3630 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3631 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3634 /* gateway is the interface itself */
3635 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3636 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3637 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3638 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3642 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
3643 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3644 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3645 in6_len2mask(&rtaddr
.in_p
->sin6_addr
, route
->prefix_length
);
3646 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3650 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
3651 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3652 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3653 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3654 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3656 /* interface address */
3657 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
3658 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3659 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3660 rtaddr
.in_p
->sin6_addr
= route
->ifa
;
3661 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3664 /* apply the route */
3665 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
3666 rtmsg
.hdr
.rtm_msglen
= len
;
3667 if (write(sockfd
, &rtmsg
, len
) == -1) {
3673 static const RouteListInfo IPv6RouteListInfo
= {
3674 IPv6RouteListComputeSize
,
3679 IPv6RouteSetGateway
,
3680 IPv6RouteDestination
,
3681 IPv6RouteSameSubnet
,
3683 IPv6RouteCopyDescription
,
3686 sizeof(struct in6_addr
),
3687 IPV6_ROUTE_ALL_BITS_SET
3690 #ifdef TEST_IPV6_ROUTELIST
3691 static IPv6RouteListRef
3692 IPv6RouteListAddRouteList(IPv6RouteListRef routes
, int init_size
,
3693 IPv6RouteListRef service_routes
, Rank rank
)
3695 return ((IPv6RouteListRef
)
3696 RouteListAddRouteList(&IPv6RouteListInfo
,
3697 (RouteListRef
)routes
, init_size
,
3698 (RouteListRef
)service_routes
, rank
));
3700 #endif /* TEST_IPV6_ROUTELIST */
3702 #if !TARGET_IPHONE_SIMULATOR
3703 static __inline__
void
3704 IPv6RouteListLog(int level
, IPv6RouteListRef routes
)
3706 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3708 my_log(level
, "%@", str
);
3714 IPv6RouteListFinalize(IPv6RouteListRef routes
)
3716 RouteListFinalize(&IPv6RouteListInfo
, (RouteListRef
)routes
);
3721 IPv6RouteListApply(IPv6RouteListRef old_routes
, IPv6RouteListRef new_routes
,
3724 RouteListApply(&IPv6RouteListInfo
,
3725 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
3729 #endif /* !TARGET_IPHONE_SIMULATOR */
3732 * Function: parse_component
3734 * Given a string 'key' and a string prefix 'prefix',
3735 * return the next component in the slash '/' separated
3739 * 1. key = "a/b/c" prefix = "a/"
3741 * 2. key = "a/b/c" prefix = "a/b/"
3744 static CF_RETURNS_RETAINED CFStringRef
3745 parse_component(CFStringRef key
, CFStringRef prefix
)
3747 CFMutableStringRef comp
;
3750 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
3753 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
3757 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
3758 range
= CFStringFind(comp
, CFSTR("/"), 0);
3759 if (range
.location
== kCFNotFound
) {
3762 range
.length
= CFStringGetLength(comp
) - range
.location
;
3763 CFStringDelete(comp
, range
);
3767 __private_extern__ boolean_t
3768 service_contains_protocol(CFDictionaryRef service
, int af
)
3770 boolean_t contains_protocol
= FALSE
;
3772 RouteListRef routes
;
3773 CFDictionaryRef dict
;
3775 entity
= (af
== AF_INET
) ? kSCEntNetIPv4
: kSCEntNetIPv6
;
3776 dict
= CFDictionaryGetValue(service
, entity
);
3780 routes
= ipdict_get_routelist(dict
);
3781 if (routes
== NULL
) {
3784 if ((routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
3787 contains_protocol
= TRUE
;
3790 return (contains_protocol
);
3794 static CFMutableDictionaryRef
3795 service_dict_copy(CFStringRef serviceID
)
3797 CFDictionaryRef d
= NULL
;
3798 CFMutableDictionaryRef service_dict
;
3800 /* create a modifyable dictionary, a copy or a new one */
3801 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3804 = CFDictionaryCreateMutable(NULL
, 0,
3805 &kCFTypeDictionaryKeyCallBacks
,
3806 &kCFTypeDictionaryValueCallBacks
);
3809 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
3811 return (service_dict
);
3814 __private_extern__ boolean_t
3815 service_is_scoped_only(CFDictionaryRef service_dict
)
3817 nwi_ifstate_t alias
;
3818 CFDictionaryRef dict
;
3819 char ifname
[IFNAMSIZ
];
3820 nwi_ifstate_t ifstate
;
3821 CFStringRef interface
= NULL
;
3823 // get IPv4 (or IPv6) info
3824 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3826 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
3829 // if no connectivity
3834 interface
= ipdict_get_ifname(dict
);
3835 if ((interface
== NULL
) ||
3836 !CFStringGetCString(interface
, ifname
, sizeof(ifname
), kCFStringEncodingUTF8
)) {
3837 // if no interface / interface name
3842 if (S_nwi_state
== NULL
) {
3843 S_nwi_state
= nwi_state_copy();
3847 // get [nwi] interface state
3848 ifstate
= nwi_state_get_ifstate(S_nwi_state
, ifname
);
3849 if (ifstate
== NULL
) {
3852 } else if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3853 // if scoped (i.e. not in list)
3857 // check both both IPv4 and IPv6
3858 alias
= nwi_ifstate_get_alias(ifstate
, ifstate
->af
== AF_INET
? AF_INET6
: AF_INET
);
3859 if (alias
== NULL
) {
3860 // if only one address family
3862 } else if ((alias
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0) {
3863 // if scoped (i.e. not in list)
3871 log_service_entity(int level
, CFStringRef serviceID
, CFStringRef entity
,
3872 CFStringRef operation
, CFTypeRef val
)
3874 CFMutableStringRef this_val
= NULL
;
3880 if ((is_ipv4
= CFEqual(entity
, kSCEntNetIPv4
))
3881 || (is_ipv6
= CFEqual(entity
, kSCEntNetIPv6
))) {
3882 RouteListUnion routes
;
3884 routes
.ptr
= ipdict_get_routelist(val
);
3885 if (routes
.ptr
!= NULL
) {
3886 CFDictionaryRef service_dict
= NULL
;
3889 this_val
= IPv4RouteListCopyDescription(routes
.v4
);
3892 this_val
= IPv6RouteListCopyDescription(routes
.v6
);
3894 service_dict
= ipdict_get_service(val
);
3895 if (service_dict
!= NULL
) {
3896 CFStringAppendFormat(this_val
, NULL
,
3897 CFSTR("\n<Service> = %@"),
3905 val
= CFSTR("<none>");
3907 my_log(level
, "serviceID %@ %@ %@ value = %@",
3908 serviceID
, operation
, entity
, val
);
3909 my_CFRelease(&this_val
);
3914 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
3917 boolean_t changed
= FALSE
;
3919 CFMutableDictionaryRef service_dict
;
3921 service_dict
= service_dict_copy(serviceID
);
3922 old_val
= CFDictionaryGetValue(service_dict
, entity
);
3923 if (new_val
== NULL
) {
3924 if (old_val
!= NULL
) {
3925 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3926 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3927 CFSTR("Removed:"), old_val
);
3929 CFDictionaryRemoveValue(service_dict
, entity
);
3934 if (old_val
== NULL
|| CFEqual(new_val
, old_val
) == FALSE
) {
3935 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3936 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3937 CFSTR("Changed: old"), old_val
);
3938 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3939 CFSTR("Changed: new"), new_val
);
3941 CFDictionarySetValue(service_dict
, entity
, new_val
);
3945 if (CFDictionaryGetCount(service_dict
) == 0) {
3946 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
3949 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
3951 my_CFRelease(&service_dict
);
3955 static CFDictionaryRef
3956 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
3958 CFDictionaryRef service_dict
;
3960 if (S_service_state_dict
== NULL
) {
3963 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3964 if (service_dict
== NULL
) {
3967 return (CFDictionaryGetValue(service_dict
, entity
));
3970 #ifndef kSCPropNetHostname
3971 #define kSCPropNetHostname CFSTR("Hostname")
3976 copy_dhcp_hostname(CFStringRef serviceID
)
3978 CFDictionaryRef dict
= NULL
;
3979 CFStringRef hostname
= NULL
;
3980 CFDictionaryRef service_dict
= NULL
;
3982 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
3986 service_dict
= ipdict_get_service(dict
);
3987 if (service_dict
== NULL
) {
3990 hostname
= CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
3991 if (hostname
!= NULL
) {
3997 #if !TARGET_IPHONE_SIMULATOR
3999 static struct in6_addr
*
4000 ipv6_service_get_router(CFDictionaryRef service
,
4001 IFIndex
* ifindex_p
, CFStringRef
* ifname_p
)
4003 IPv6RouteListRef routes
;
4004 struct in6_addr
* router
= NULL
;
4006 routes
= ipdict_get_routelist(service
);
4008 && (routes
->flags
& kRouteListFlagsExcludeNWI
) == 0
4009 && (routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
4010 router
= &routes
->list
[0].gateway
;
4011 if (*ifindex_p
== 0) {
4012 *ifindex_p
= routes
->list
[0].ifindex
;
4014 if (*ifname_p
== NULL
) {
4015 *ifname_p
= ipdict_get_ifname(service
);
4022 ipv6_service_update_router(CFStringRef serviceID
, CFDictionaryRef new_service
)
4024 IFIndex ifindex
= 0;
4025 CFStringRef ifname
= NULL
;
4026 char ntopbuf
[INET6_ADDRSTRLEN
];
4027 CFDictionaryRef old_service
;
4028 struct in6_addr
* old_router
;
4029 struct in6_addr
* new_router
;
4032 old_service
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4033 old_router
= ipv6_service_get_router(old_service
, &ifindex
, &ifname
);
4034 new_router
= ipv6_service_get_router(new_service
, &ifindex
, &ifname
);
4035 if (ifname
== NULL
|| ifindex
== 0) {
4038 s
= inet6_dgram_socket();
4042 /* remove the old router if it was defined */
4043 if (old_router
!= NULL
4044 && (new_router
== NULL
4045 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4046 if (siocdrdel_in6(s
, ifindex
, old_router
) < 0) {
4047 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4048 "siocdrdel_in6(%@, %s) failed: %s",
4050 inet_ntop(AF_INET6
, old_router
,
4051 ntopbuf
, sizeof(ntopbuf
)),
4056 "%@ removed default route %s",
4058 inet_ntop(AF_INET6
, old_router
, ntopbuf
, sizeof(ntopbuf
)));
4061 /* add the new router if it is defined */
4062 if (new_router
!= NULL
4063 && (old_router
== NULL
4064 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
4065 if (siocdradd_in6(s
, ifindex
, new_router
, 0) < 0) {
4066 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
4067 "siocdradd_in6(%@, %s) failed: %s",
4069 inet_ntop(AF_INET6
, new_router
,
4070 ntopbuf
, sizeof(ntopbuf
)),
4075 "%@ added default route %s",
4077 inet_ntop(AF_INET6
, new_router
, ntopbuf
, sizeof(ntopbuf
)));
4085 #endif /* !TARGET_IPHONE_SIMULATOR */
4087 #define ALLOW_EMPTY_STRING 0x1
4089 static CF_RETURNS_RETAINED CFTypeRef
4090 sanitize_prop(CFTypeRef val
, uint32_t flags
)
4093 if (isA_CFString(val
)) {
4094 CFMutableStringRef str
;
4096 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
4097 CFStringTrimWhitespace(str
);
4098 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
4112 merge_array_prop(CFMutableDictionaryRef dict
,
4114 CFDictionaryRef state_dict
,
4115 CFDictionaryRef setup_dict
,
4119 CFMutableArrayRef merge_prop
;
4120 CFArrayRef setup_prop
= NULL
;
4121 CFArrayRef state_prop
= NULL
;
4123 if (setup_dict
!= NULL
) {
4124 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
4126 if (state_dict
!= NULL
) {
4127 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
4130 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
4134 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4135 if (setup_prop
!= NULL
) {
4139 n
= CFArrayGetCount(setup_prop
);
4140 for (i
= 0; i
< n
; i
++) {
4143 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
4144 val
= sanitize_prop(val
, flags
);
4146 CFArrayAppendValue(merge_prop
, val
);
4151 if (state_prop
!= NULL
4152 && (setup_prop
== NULL
|| S_append_state
)) {
4155 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
4157 n
= CFArrayGetCount(state_prop
);
4158 for (i
= 0; i
< n
; i
++) {
4161 val
= CFArrayGetValueAtIndex(state_prop
, i
);
4162 val
= sanitize_prop(val
, flags
);
4164 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
4165 CFArrayAppendValue(merge_prop
, val
);
4171 if (CFArrayGetCount(merge_prop
) > 0) {
4172 CFDictionarySetValue(dict
, key
, merge_prop
);
4174 CFRelease(merge_prop
);
4179 pick_prop(CFMutableDictionaryRef dict
,
4181 CFDictionaryRef state_dict
,
4182 CFDictionaryRef setup_dict
,
4185 CFTypeRef val
= NULL
;
4187 if (setup_dict
!= NULL
) {
4188 val
= CFDictionaryGetValue(setup_dict
, key
);
4189 val
= sanitize_prop(val
, flags
);
4191 if (val
== NULL
&& state_dict
!= NULL
) {
4192 val
= CFDictionaryGetValue(state_dict
, key
);
4193 val
= sanitize_prop(val
, flags
);
4196 CFDictionarySetValue(dict
, key
, val
);
4204 ** GetEntityChangesFunc functions
4206 #define IPV4_ROUTES_N_STATIC 5
4207 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4208 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4212 #define IPV4_ROUTES_BUF_DECL(routes) \
4213 IPv4RouteListRef routes; \
4214 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4216 routes = (IPv4RouteListRef)(void *)routes_buf; \
4217 routes->size = IPV4_ROUTES_N_STATIC; \
4218 routes->count = 0; \
4222 IPv4RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4225 CFDataRef routes_data
;
4226 IPV4_ROUTES_BUF_DECL(routes
);
4228 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4230 routes_data
= CFDataCreate(NULL
,
4232 IPv4RouteListComputeSize(r
->count
));
4240 return (routes_data
);
4242 #define IPV6_ROUTES_N_STATIC 3
4243 #define IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4244 (roundup(IPv6RouteListComputeSize(IPV6_ROUTES_N_STATIC), \
4248 #define IPV6_ROUTES_BUF_DECL(routes) \
4249 IPv6RouteListRef routes; \
4250 uint32_t routes_buf[IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4252 routes = (IPv6RouteListRef)(void *)routes_buf; \
4253 routes->size = IPV6_ROUTES_N_STATIC; \
4254 routes->count = 0; \
4258 IPv6RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4261 CFDataRef routes_data
;
4262 IPV6_ROUTES_BUF_DECL(routes
);
4264 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4266 routes_data
= CFDataCreate(NULL
,
4268 IPv6RouteListComputeSize(r
->count
));
4276 return (routes_data
);
4279 static CFDictionaryRef
4280 IPDictCreate(int af
, CFDictionaryRef state_dict
, CFDictionaryRef setup_dict
,
4281 CFNumberRef rank_assertion
)
4283 CFDictionaryRef aggregated_dict
= NULL
;
4284 CFDictionaryRef dict
;
4285 CFMutableDictionaryRef modified_dict
= NULL
;
4286 CFDataRef routes_data
;
4289 if (dict
!= NULL
&& setup_dict
!= NULL
) {
4290 /* look for keys in Setup: that override/merge with State: */
4291 CFArrayRef additional_routes
;
4294 CFStringRef router_prop
;
4295 CFStringRef route_list_prop
;
4300 router_prop
= kSCPropNetIPv4Router
;
4301 route_list_prop
= kSCPropNetIPv4AdditionalRoutes
;
4305 router_prop
= kSCPropNetIPv6Router
;
4306 route_list_prop
= kSCPropNetIPv6AdditionalRoutes
;
4309 router
= CFDictionaryGetValue(setup_dict
, router_prop
);
4311 && !cfstring_to_ipvx(af
, router
, &router_ip
, sizeof(router_ip
))) {
4315 /* AdditionalRoutes */
4317 = CFDictionaryGetValue(setup_dict
, route_list_prop
);
4318 additional_routes
= isA_CFArray(additional_routes
);
4320 if (router
!= NULL
|| additional_routes
!= NULL
) {
4321 modified_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
4322 if (router
!= NULL
) {
4323 CFDictionarySetValue(modified_dict
,
4327 if (additional_routes
!= NULL
) {
4328 CFArrayRef combined_routes
= NULL
;
4329 CFArrayRef state_routes
;
4332 = CFDictionaryGetValue(state_dict
,
4334 if (isA_CFArray(state_routes
) != NULL
) {
4336 = my_CFArrayCreateCombinedArray(additional_routes
,
4338 additional_routes
= combined_routes
;
4340 CFDictionarySetValue(modified_dict
,
4343 if (combined_routes
!= NULL
) {
4344 CFRelease(combined_routes
);
4347 dict
= modified_dict
;
4352 routes_data
= IPv4RouteListDataCreate(dict
, rank_assertion
);
4356 routes_data
= IPv6RouteListDataCreate(dict
, rank_assertion
);
4359 if (routes_data
!= NULL
) {
4360 aggregated_dict
= ipdict_create(dict
, routes_data
);
4361 CFRelease(routes_data
);
4363 if (modified_dict
!= NULL
) {
4364 CFRelease(modified_dict
);
4366 return (aggregated_dict
);
4370 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4371 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4373 CFDictionaryRef dict
= NULL
;
4374 boolean_t changed
= FALSE
;
4375 CFNumberRef rank_assertion
= NULL
;
4376 CFDictionaryRef service_options
;
4378 if (state_dict
== NULL
) {
4381 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4382 if (service_options
!= NULL
) {
4384 = CFDictionaryGetValue(service_options
,
4385 kServiceOptionRankAssertion
);
4387 dict
= IPDictCreate(AF_INET
, state_dict
, setup_dict
, rank_assertion
);
4390 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, dict
);
4392 /* clean up the rank too */
4393 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
4395 my_CFRelease(&dict
);
4400 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4401 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4403 CFDictionaryRef dict
= NULL
;
4404 boolean_t changed
= FALSE
;
4405 CFNumberRef rank_assertion
= NULL
;
4406 CFDictionaryRef service_options
;
4408 if (state_dict
== NULL
) {
4411 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4412 if (service_options
!= NULL
) {
4414 = CFDictionaryGetValue(service_options
,
4415 kServiceOptionRankAssertion
);
4417 dict
= IPDictCreate(AF_INET6
, state_dict
, setup_dict
, rank_assertion
);
4420 #if !TARGET_IPHONE_SIMULATOR
4421 ipv6_service_update_router(serviceID
, dict
);
4422 #endif /* !TARGET_IPHONE_SIMULATOR */
4423 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, dict
);
4425 /* clean up the rank too */
4426 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
4428 my_CFRelease(&dict
);
4434 __private_extern__ CFDictionaryRef
4435 ipv4_dict_create(CFDictionaryRef state_dict
)
4437 return (IPDictCreate(AF_INET
, state_dict
, NULL
, NULL
));
4440 __private_extern__ CFDictionaryRef
4441 ipv6_dict_create(CFDictionaryRef state_dict
)
4443 return (IPDictCreate(AF_INET6
, state_dict
, NULL
, NULL
));
4446 #endif /* TEST_DNS */
4449 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
4450 CFMutableArrayRef out_servers
, CFStringRef interface
)
4455 count
= CFArrayGetCount(in_servers
);
4456 for (i
= 0; i
< count
; i
++) {
4458 struct in6_addr ipv6_addr
;
4459 struct in_addr ip_addr
;
4461 addr
= CFArrayGetValueAtIndex(in_servers
, i
);
4462 assert(addr
!= NULL
);
4464 if (cfstring_to_ip(addr
, &ip_addr
)) {
4466 if ((active_protos
& kProtocolFlagsIPv4
) == 0
4467 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
4469 "no IPv4 connectivity, "
4470 "ignoring DNS server address " IP_FORMAT
,
4477 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
4479 if ((active_protos
& kProtocolFlagsIPv6
) == 0
4480 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
4481 char ntopbuf
[INET6_ADDRSTRLEN
];
4484 "no IPv6 connectivity, "
4485 "ignoring DNS server address %s",
4486 inet_ntop(AF_INET6
, &ipv6_addr
,
4487 ntopbuf
, sizeof(ntopbuf
)));
4491 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
4492 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
4493 && (interface
!= NULL
)
4494 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
4495 // append interface name to IPv6 link local address
4496 addr
= CFStringCreateWithFormat(NULL
, NULL
,
4505 /* bad IP address */
4506 my_log(LOG_NOTICE
, "ignoring bad DNS server address '%@'", addr
);
4510 /* DNS server is valid and one we want */
4511 CFArrayAppendValue(out_servers
, addr
);
4518 merge_dns_servers(CFMutableDictionaryRef new_dict
,
4519 CFArrayRef state_servers
,
4520 CFArrayRef setup_servers
,
4522 Boolean trust_state
,
4523 ProtocolFlags active_protos
,
4524 CFStringRef interface
)
4526 CFMutableArrayRef dns_servers
;
4527 Boolean have_dns_setup
= FALSE
;
4529 if (state_servers
== NULL
&& setup_servers
== NULL
) {
4530 /* no DNS servers */
4533 dns_servers
= CFArrayCreateMutable(NULL
, 0,
4534 &kCFTypeArrayCallBacks
);
4535 if (setup_servers
!= NULL
) {
4536 accumulate_dns_servers(setup_servers
, active_protos
,
4537 dns_servers
, interface
);
4538 if (CFArrayGetCount(dns_servers
) > 0) {
4539 have_dns_setup
= TRUE
;
4542 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
4543 && state_servers
!= NULL
) {
4544 accumulate_dns_servers(state_servers
, active_protos
,
4549 * Here, we determine whether or not we want all queries for this DNS
4550 * configuration to be bound to the associated network interface.
4552 * For dynamically derived network configurations (i.e. from State:)
4553 * this would be the preferred option using the argument "Hey, the
4554 * server told us to use these servers on this network so let's not
4557 * But, when a DNS configuration has been provided by the user/admin
4558 * via the Network pref pane (i.e. from Setup:) we opt to not force
4559 * binding of the outbound queries. The simplest example why we take
4560 * this stance is with a multi-homing configuration. Consider a system
4561 * with one network service associated with "en0" and a second service
4562 * associated with "en1". The "en0" service has been set higher in
4563 * the network service order so it would be primary but the user/admin
4564 * wants the DNS queries to go to a server only accessible via "en1".
4565 * Without this exception we would take the DNS server addresses from
4566 * the Network pref pane (for "en0") and have the queries bound to
4567 * "en0" where they'd never reach their intended destination (via
4568 * "en1"). So, our exception to the rule is that we will not bind
4569 * user/admin configurations to any specific network interface.
4571 * We also add an exception to the "follow the dynamically derived
4572 * network configuration" path for on-the-fly (no Setup: content)
4575 * But, we add an exception to the exception to support our own
4576 * VPN code. Here, we look for a "ServiceID" property in the DNS
4577 * entity. If present, and if it matches, then we extend our
4578 * trust even when there is no Setup: content.
4580 if (CFArrayGetCount(dns_servers
) != 0) {
4581 CFDictionarySetValue(new_dict
,
4582 kSCPropNetDNSServerAddresses
, dns_servers
);
4583 if ((have_setup
&& !have_dns_setup
) || (!have_setup
&& trust_state
)) {
4584 // if this is a "setup"+"state" service with only "state" DNS content (i.e. no
4585 // setup override) or this is a TRUSTED "state"-only service
4586 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
4590 my_CFRelease(&dns_servers
);
4596 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4597 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4599 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4600 boolean_t changed
= FALSE
;
4602 Boolean have_setup
= FALSE
;
4603 CFStringRef interface
= NULL
;
4604 CFDictionaryRef ipv4
;
4605 CFDictionaryRef ipv6
;
4612 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
4613 { kSCPropNetDNSSortList
, 0, FALSE
},
4614 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4615 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
4617 CFMutableDictionaryRef new_dict
= NULL
;
4618 const CFStringRef pick_list
[] = {
4619 kSCPropNetDNSDomainName
,
4620 kSCPropNetDNSOptions
,
4621 kSCPropNetDNSSearchOrder
,
4622 kSCPropNetDNSServerPort
,
4623 kSCPropNetDNSServerTimeout
,
4624 kSCPropNetDNSServiceIdentifier
,
4625 kSCPropNetDNSSupplementalMatchDomainsNoSearch
,
4627 Boolean trust_state
= FALSE
;
4629 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4630 /* there is no DNS content */
4634 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4636 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
4639 active_protos
|= kProtocolFlagsIPv4
;
4640 interface
= ipdict_get_ifname(ipv4
);
4643 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4646 && (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
)
4650 active_protos
|= kProtocolFlagsIPv6
;
4651 if (interface
== NULL
) {
4652 interface
= ipdict_get_ifname(ipv6
);
4657 if (active_protos
== kProtocolFlagsNone
) {
4658 /* there is no IPv4 nor IPv6 */
4659 if (state_dict
== NULL
) {
4660 /* ... and no DNS content that we care about */
4666 if (state_dict
!= NULL
) {
4667 CFStringRef state_serviceID
= NULL
;
4669 if (CFDictionaryGetValueIfPresent(state_dict
,
4670 kSCPropNetDNSConfirmedServiceID
,
4671 (const void **)&state_serviceID
) &&
4672 isA_CFString(state_serviceID
) &&
4673 CFEqual(serviceID
, state_serviceID
)) {
4678 /* merge DNS configuration */
4679 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
4680 &kCFTypeDictionaryKeyCallBacks
,
4681 &kCFTypeDictionaryValueCallBacks
);
4683 if (active_protos
== kProtocolFlagsNone
) {
4684 merge_dns_servers(new_dict
,
4685 my_CFDictionaryGetArray(state_dict
,
4686 kSCPropNetDNSServerAddresses
),
4690 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
4694 merge_dns_servers(new_dict
,
4695 my_CFDictionaryGetArray(state_dict
,
4696 kSCPropNetDNSServerAddresses
),
4697 my_CFDictionaryGetArray(setup_dict
,
4698 kSCPropNetDNSServerAddresses
),
4705 for (i
= 0; i
< countof(merge_list
); i
++) {
4706 merge_array_prop(new_dict
,
4710 merge_list
[i
].flags
,
4711 merge_list
[i
].append
);
4714 for (i
= 0; i
< countof(pick_list
); i
++) {
4722 if (active_protos
== kProtocolFlagsNone
) {
4723 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
4724 if (CFDictionaryContainsKey(new_dict
,
4725 kSCPropNetDNSSupplementalMatchDomains
)) {
4726 /* only keep State: supplemental */
4727 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
4728 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
4729 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
4730 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
4732 if ((interface
== NULL
) && (setup_dict
== NULL
) && (state_dict
!= NULL
)) {
4734 * for supplemental-only configurations, add any scoped (or
4735 * wild-card "*") interface
4737 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4739 } else if (CFDictionaryContainsKey(new_dict
, kSCPropNetDNSServiceIdentifier
) &&
4740 (interface
== NULL
) &&
4741 (state_dict
!= NULL
)) {
4742 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4748 if (CFDictionaryGetCount(new_dict
) == 0) {
4749 my_CFRelease(&new_dict
);
4753 if (interface
!= NULL
) {
4754 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
4757 if (S_append_state
) {
4759 * ensure any specified domain name (e.g. the domain returned by
4760 * a DHCP server) is in the search list.
4762 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
4763 if (isA_CFString(domain
)) {
4766 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
4767 if (isA_CFArray(search
) &&
4768 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
4769 CFMutableArrayRef new_search
;
4771 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
4772 CFArrayAppendValue(new_search
, domain
);
4773 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
4774 my_CFRelease(&new_search
);
4780 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
4781 my_CFRelease(&new_dict
);
4786 merge_dict(const void *key
, const void *value
, void *context
)
4788 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
4790 CFDictionarySetValue(dict
, key
, value
);
4794 #define PROXY_AUTO_DISCOVERY_URL 252
4796 static CF_RETURNS_RETAINED CFStringRef
4797 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
4799 CFStringRef urlString
= NULL
;
4801 if (dhcp_options
!= NULL
) {
4804 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
4807 const UInt8
*urlBytes
;
4810 urlBytes
= CFDataGetBytePtr(data
);
4811 urlLen
= CFDataGetLength(data
);
4812 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
4813 // remove trailing NUL
4821 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
4823 urlString
= CFURLGetString(url
);
4824 if (urlString
!= NULL
) {
4825 CFRetain(urlString
);
4835 static CF_RETURNS_RETAINED CFStringRef
4839 CFStringRef urlString
= NULL
;
4841 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
4843 urlString
= CFURLGetString(url
);
4844 if (urlString
!= NULL
) {
4845 CFRetain(urlString
);
4854 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4855 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4857 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4858 boolean_t changed
= FALSE
;
4859 CFStringRef interface
= NULL
;
4860 CFDictionaryRef ipv4
;
4861 CFDictionaryRef ipv6
;
4862 CFMutableDictionaryRef new_dict
= NULL
;
4868 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4869 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
4872 CFStringRef key1
; /* an "enable" key */
4876 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
4877 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
4878 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
4879 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
4880 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
4881 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
4882 { kSCPropNetProxiesProxyAutoConfigEnable
,
4883 kSCPropNetProxiesProxyAutoConfigURLString
,
4884 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
4885 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
4890 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4891 /* there is no proxy content */
4894 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4895 if (ipdict_get_routelist(ipv4
) != NULL
) {
4896 active_protos
|= kProtocolFlagsIPv4
;
4897 interface
= ipdict_get_ifname(ipv4
);
4899 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4900 if (ipdict_get_routelist(ipv6
) != NULL
) {
4901 active_protos
|= kProtocolFlagsIPv6
;
4902 if (interface
== NULL
) {
4903 interface
= ipdict_get_ifname(ipv6
);
4906 if (active_protos
== kProtocolFlagsNone
) {
4907 /* there is no IPv4 nor IPv6 */
4908 if (state_dict
== NULL
) {
4909 /* ... and no proxy content that we care about */
4915 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
4917 CFMutableDictionaryRef setup_copy
;
4920 * Merge the per-service "Setup:" and "State:" proxy information with
4921 * the "Setup:" information always taking precedence. Additionally,
4922 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
4923 * Port) is defined than all of the values for that group will be
4924 * used. That is, we don't allow mixing some of the values from
4925 * the "Setup:" keys and others from the "State:" keys.
4927 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
4928 for (i
= 0; i
< countof(merge_list
); i
++) {
4929 merge_array_prop(new_dict
,
4933 merge_list
[i
].flags
,
4934 merge_list
[i
].append
);
4937 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
4938 for (i
= 0; i
< countof(pick_list
); i
++) {
4939 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
4941 * if a "Setup:" enabled key has been provided than we want to
4942 * ignore all of the "State:" keys
4944 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
4945 if (pick_list
[i
].key2
!= NULL
) {
4946 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
4948 if (pick_list
[i
].key3
!= NULL
) {
4949 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
4951 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
4952 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
4953 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
4955 * if a "Setup:" enabled key has not been provided and we have
4956 * some" "State:" keys than we remove all of of "Setup:" keys
4958 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
4959 if (pick_list
[i
].key2
!= NULL
) {
4960 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
4962 if (pick_list
[i
].key3
!= NULL
) {
4963 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
4968 /* merge the "Setup:" keys */
4969 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
4970 CFRelease(setup_copy
);
4972 else if (setup_dict
!= NULL
) {
4973 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
4975 else if (state_dict
!= NULL
) {
4976 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
4979 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
4980 CFRelease(new_dict
);
4984 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
4985 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
4989 if (new_dict
!= NULL
) {
4990 CFDictionaryRef dhcp_options
;
4992 CFNumberRef wpad
= NULL
;
4993 int wpadEnabled
= 0;
4994 CFStringRef wpadURL
= NULL
;
4996 if (CFDictionaryGetValueIfPresent(new_dict
,
4997 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
4998 (const void **)&num
) &&
4999 isA_CFNumber(num
)) {
5000 /* if we have a WPAD key */
5002 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
5003 /* if we don't like the enabled key/value */
5011 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
5012 if (!isA_CFNumber(num
) ||
5013 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
5014 /* if we don't like the enabled key/value */
5021 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
5022 if (pacURL
!= NULL
) {
5023 if (!isA_CFString(pacURL
)) {
5024 /* if we don't like the PAC URL */
5030 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
5031 if (!isA_CFString(pacJS
)) {
5032 /* if we don't have (or like) the PAC JavaScript */
5040 * we already have a PAC URL so disable WPAD.
5047 * if WPAD is enabled and we don't already have a PAC URL then
5048 * we check for a DHCP provided URL. If not available, we use
5049 * a PAC URL pointing to a well-known file (wpad.dat) on a
5050 * well-known host (wpad.<domain>).
5052 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
5053 wpadURL
= wpadURL_dhcp(dhcp_options
);
5054 if (wpadURL
== NULL
) {
5055 wpadURL
= wpadURL_dns();
5057 if (wpadURL
== NULL
) {
5058 wpadEnabled
= 0; /* if we don't have a WPAD URL */
5063 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
5064 CFDictionarySetValue(new_dict
,
5065 kSCPropNetProxiesProxyAutoConfigEnable
,
5068 CFDictionarySetValue(new_dict
,
5069 kSCPropNetProxiesProxyAutoConfigURLString
,
5076 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
5077 CFDictionarySetValue(new_dict
,
5078 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
5085 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
5086 my_CFRelease(&new_dict
);
5090 #if !TARGET_OS_IPHONE
5092 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5093 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5095 boolean_t changed
= FALSE
;
5097 CFMutableDictionaryRef new_dict
= NULL
;
5098 const CFStringRef pick_list
[] = {
5099 kSCPropNetSMBNetBIOSName
,
5100 kSCPropNetSMBNetBIOSNodeType
,
5101 #ifdef ADD_NETBIOS_SCOPE
5102 kSCPropNetSMBNetBIOSScope
,
5103 #endif // ADD_NETBIOS_SCOPE
5104 kSCPropNetSMBWorkgroup
,
5107 if (state_dict
== NULL
&& setup_dict
== NULL
) {
5108 /* there is no SMB */
5111 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
5112 && service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
) {
5113 /* there is no IPv4 or IPv6 */
5117 /* merge SMB configuration */
5118 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5119 &kCFTypeDictionaryKeyCallBacks
,
5120 &kCFTypeDictionaryValueCallBacks
);
5121 merge_array_prop(new_dict
,
5122 kSCPropNetSMBWINSAddresses
,
5127 for (i
= 0; i
< countof(pick_list
); i
++) {
5135 if (CFDictionaryGetCount(new_dict
) == 0) {
5136 my_CFRelease(&new_dict
);
5141 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
5142 my_CFRelease(&new_dict
);
5145 #endif /* !TARGET_OS_IPHONE */
5148 services_info_get_interface(CFDictionaryRef services_info
,
5149 CFStringRef serviceID
)
5151 CFStringRef interface
= NULL
;
5152 CFDictionaryRef ipv4_dict
;
5154 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
5156 if (ipv4_dict
!= NULL
) {
5157 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
5160 CFDictionaryRef ipv6_dict
;
5162 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
5164 if (ipv6_dict
!= NULL
) {
5165 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5172 static const struct {
5173 const CFStringRef
* entityName
;
5174 const CFStringRef
* statusKey
;
5175 } transientServiceInfo
[] = {
5176 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
5177 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
5178 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
5182 get_transient_status_changes(CFStringRef serviceID
,
5183 CFDictionaryRef services_info
)
5185 boolean_t changed
= FALSE
;
5188 for (i
= 0; i
< countof(transientServiceInfo
); i
++) {
5189 CFDictionaryRef dict
;
5190 CFNumberRef status
= NULL
;
5191 CFMutableDictionaryRef ts_dict
= NULL
;
5193 dict
= get_service_state_entity(services_info
, serviceID
,
5194 *transientServiceInfo
[i
].entityName
);
5197 status
= CFDictionaryGetValue(dict
,
5198 *transientServiceInfo
[i
].statusKey
);
5201 if (isA_CFNumber(status
) != NULL
) {
5202 ts_dict
= CFDictionaryCreateMutable(NULL
,
5204 &kCFTypeDictionaryKeyCallBacks
,
5205 &kCFTypeDictionaryValueCallBacks
);
5206 CFDictionaryAddValue(ts_dict
,
5207 *transientServiceInfo
[i
].statusKey
,
5211 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
,
5216 if (ts_dict
!= NULL
) {
5224 if_dict_is_expensive(CFDictionaryRef if_dict
)
5226 boolean_t is_expensive
= FALSE
;
5228 if (isA_CFDictionary(if_dict
) != NULL
) {
5229 CFBooleanRef expensive
;
5230 expensive
= CFDictionaryGetValue(if_dict
, kSCPropNetLinkExpensive
);
5231 if (isA_CFBoolean(expensive
) != NULL
5232 && CFBooleanGetValue(expensive
)) {
5233 is_expensive
= TRUE
;
5236 return is_expensive
;
5240 service_is_expensive(CFStringRef serviceID
, CFDictionaryRef services_info
)
5243 boolean_t is_expensive
= FALSE
;
5245 ifname
= services_info_get_interface(services_info
, serviceID
);
5246 if (ifname
!= NULL
) {
5247 CFDictionaryRef if_dict
;
5250 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5251 if_dict
= CFDictionaryGetValue(services_info
, key
);
5253 is_expensive
= if_dict_is_expensive(if_dict
);
5255 return (is_expensive
);
5259 interface_is_expensive(CFStringRef ifname
)
5261 boolean_t is_expensive
= FALSE
;
5263 if (ifname
!= NULL
) {
5264 CFDictionaryRef if_dict
;
5267 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5268 if_dict
= SCDynamicStoreCopyValue(S_session
, key
);
5270 if (if_dict
!= NULL
) {
5271 is_expensive
= if_dict_is_expensive(if_dict
);
5275 return (is_expensive
);
5279 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
5280 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
5282 boolean_t changed
= FALSE
;
5283 boolean_t ip_is_coupled
= FALSE
;
5284 CFMutableDictionaryRef new_dict
= NULL
;
5285 Rank rank_assertion
= kRankAssertionDefault
;
5286 Boolean rank_assertion_is_set
= FALSE
;
5287 CFStringRef setup_rank
= NULL
;
5288 CFStringRef state_rank
= NULL
;
5291 if (state_options
!= NULL
) {
5292 CFBooleanRef coupled
;
5295 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
5296 state_rank
= isA_CFString(state_rank
);
5297 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
5298 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5299 ip_is_coupled
= TRUE
;
5302 if (setup_options
!= NULL
) {
5303 CFBooleanRef coupled
;
5306 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
5307 setup_rank
= isA_CFString(setup_rank
);
5308 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
5309 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5310 ip_is_coupled
= TRUE
;
5314 if (ip_is_coupled
== FALSE
) {
5315 ip_is_coupled
= service_is_expensive(serviceID
, services_info
);
5317 if (setup_rank
!= NULL
|| state_rank
!= NULL
) {
5318 /* rank assertion is set on the service */
5319 Rank setup_assertion
;
5320 Rank state_assertion
;
5321 Boolean state_assertion_is_set
= FALSE
;
5323 setup_assertion
= PrimaryRankGetRankAssertion(setup_rank
, NULL
);
5324 state_assertion
= PrimaryRankGetRankAssertion(state_rank
,
5325 &state_assertion_is_set
);
5326 if (setup_assertion
> state_assertion
) {
5327 rank_assertion
= setup_assertion
;
5328 rank_assertion_is_set
= TRUE
;
5330 else if (state_assertion_is_set
) {
5331 rank_assertion
= state_assertion
;
5332 rank_assertion_is_set
= TRUE
;
5336 if (rank_assertion_is_set
== FALSE
) {
5337 /* check for a rank assertion on the interface */
5338 CFStringRef interface
;
5340 interface
= services_info_get_interface(services_info
, serviceID
);
5341 if (interface
!= NULL
) {
5342 CFNumberRef if_rank
= NULL
;
5344 if (S_if_rank_dict
!= NULL
) {
5345 if_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
5348 = InterfaceRankGetRankAssertion(if_rank
,
5349 &rank_assertion_is_set
);
5351 "serviceID %@ interface %@ rank = %@",
5354 (if_rank
!= NULL
) ? if_rank
: (CFNumberRef
)CFSTR("Not set)"));
5359 if (rank_assertion_is_set
|| ip_is_coupled
) {
5360 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5361 &kCFTypeDictionaryKeyCallBacks
,
5362 &kCFTypeDictionaryValueCallBacks
);
5363 if (rank_assertion_is_set
) {
5364 CFNumberRef new_rank
;
5366 new_rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
,
5367 (const void *)&rank_assertion
);
5368 CFDictionarySetValue(new_dict
, kServiceOptionRankAssertion
,
5370 CFRelease(new_rank
);
5372 if (ip_is_coupled
) {
5373 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
5376 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
5377 my_CFRelease(&new_dict
);
5382 add_service_keys(CFStringRef serviceID
,
5383 CFMutableArrayRef keys
, CFMutableArrayRef patterns
)
5388 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
5392 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5393 key
= setup_service_key(serviceID
, *entityTypeNames
[i
]);
5394 CFArrayAppendValue(keys
, key
);
5396 key
= state_service_key(serviceID
, *entityTypeNames
[i
]);
5397 CFArrayAppendValue(keys
, key
);
5401 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
5402 CFArrayAppendValue(patterns
, key
);
5405 key
= setup_service_key(serviceID
, NULL
);
5406 CFArrayAppendValue(patterns
, key
);
5408 key
= state_service_key(serviceID
, NULL
);
5409 CFArrayAppendValue(patterns
, key
);
5416 add_transient_status_keys(CFStringRef service_id
, CFMutableArrayRef patterns
)
5420 for (i
= 0; i
< countof(transientServiceInfo
); i
++) {
5421 CFStringRef pattern
;
5423 pattern
= state_service_key(service_id
,
5424 *transientServiceInfo
[i
].entityName
);
5425 CFArrayAppendValue(patterns
, pattern
);
5432 static const CFStringRef
*reachabilitySetupKeys
[] = {
5434 &kSCEntNetInterface
,
5441 add_reachability_patterns(CFMutableArrayRef patterns
)
5445 for (i
= 0; i
< countof(reachabilitySetupKeys
); i
++) {
5446 CFStringRef pattern
;
5447 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
5448 CFArrayAppendValue(patterns
, pattern
);
5455 add_vpn_pattern(CFMutableArrayRef patterns
)
5457 CFStringRef pattern
;
5459 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
5460 CFArrayAppendValue(patterns
, pattern
);
5465 add_interface_link_pattern(CFMutableArrayRef patterns
)
5467 CFStringRef pattern
;
5469 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetLink
);
5470 CFArrayAppendValue(patterns
, pattern
);
5474 static CFDictionaryRef
5475 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
5478 CFMutableArrayRef get_keys
;
5479 CFMutableArrayRef get_patterns
;
5480 CFDictionaryRef info
;
5483 count
= CFArrayGetCount(service_list
);
5484 get_keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5485 get_patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5487 CFArrayAppendValue(get_keys
, S_setup_global_ipv4
);
5488 CFArrayAppendValue(get_keys
, S_multicast_resolvers
);
5489 CFArrayAppendValue(get_keys
, S_private_resolvers
);
5491 for (s
= 0; s
< count
; s
++) {
5492 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
5494 add_service_keys(serviceID
, get_keys
, get_patterns
);
5495 add_transient_status_keys(serviceID
, get_keys
);
5498 add_reachability_patterns(get_patterns
);
5500 add_vpn_pattern(get_patterns
);
5502 add_interface_link_pattern(get_patterns
);
5504 info
= SCDynamicStoreCopyMultiple(session
, get_keys
, get_patterns
);
5505 my_CFRelease(&get_keys
);
5506 my_CFRelease(&get_patterns
);
5510 #if !TARGET_IPHONE_SIMULATOR
5513 multicast_route(int sockfd
, int cmd
)
5517 bzero(&route
, sizeof(route
));
5518 route
.dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
5519 route
.mask
.s_addr
= htonl(IN_CLASSD_NET
);
5520 route
.ifindex
= lo0_ifindex();
5521 return (IPv4RouteApply((RouteRef
)&route
, cmd
, sockfd
));
5524 #endif /* !TARGET_IPHONE_SIMULATOR */
5526 #if !TARGET_IPHONE_SIMULATOR
5529 set_ipv6_default_interface(IFIndex ifindex
)
5531 struct in6_ndifreq ndifreq
;
5533 boolean_t success
= FALSE
;
5535 bzero((char *)&ndifreq
, sizeof(ndifreq
));
5536 strlcpy(ndifreq
.ifname
, kLoopbackInterface
, sizeof(ndifreq
.ifname
));
5538 ndifreq
.ifindex
= ifindex
;
5541 ndifreq
.ifindex
= lo0_ifindex();
5543 sock
= inet6_dgram_socket();
5547 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
5549 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5560 #endif /* !TARGET_IPHONE_SIMULATOR */
5562 #if !TARGET_OS_IPHONE
5563 static __inline__
void
5566 (void)unlink(VAR_RUN_RESOLV_CONF
);
5570 set_dns(CFArrayRef val_search_domains
,
5571 CFStringRef val_domain_name
,
5572 CFArrayRef val_servers
,
5573 CFArrayRef val_sortlist
)
5575 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
5577 /* publish new resolv.conf */
5582 SCPrint(TRUE
, f
, CFSTR("#\n"));
5583 SCPrint(TRUE
, f
, CFSTR("# Mac OS X Notice\n"));
5584 SCPrint(TRUE
, f
, CFSTR("#\n"));
5585 SCPrint(TRUE
, f
, CFSTR("# This file is not used by the host name and address resolution\n"));
5586 SCPrint(TRUE
, f
, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
5587 SCPrint(TRUE
, f
, CFSTR("# this Mac OS X system.\n"));
5588 SCPrint(TRUE
, f
, CFSTR("#\n"));
5589 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
5590 SCPrint(TRUE
, f
, CFSTR("#\n"));
5592 if (isA_CFArray(val_search_domains
)) {
5593 SCPrint(TRUE
, f
, CFSTR("search"));
5594 n
= CFArrayGetCount(val_search_domains
);
5595 for (i
= 0; i
< n
; i
++) {
5598 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
5599 if (isA_CFString(domain
)) {
5600 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
5603 SCPrint(TRUE
, f
, CFSTR("\n"));
5605 else if (isA_CFString(val_domain_name
)) {
5606 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
5609 if (isA_CFArray(val_servers
)) {
5610 n
= CFArrayGetCount(val_servers
);
5611 for (i
= 0; i
< n
; i
++) {
5612 CFStringRef nameserver
;
5614 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
5615 if (isA_CFString(nameserver
)) {
5616 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
5621 if (isA_CFArray(val_sortlist
)) {
5622 SCPrint(TRUE
, f
, CFSTR("sortlist"));
5623 n
= CFArrayGetCount(val_sortlist
);
5624 for (i
= 0; i
< n
; i
++) {
5625 CFStringRef address
;
5627 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
5628 if (isA_CFString(address
)) {
5629 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
5632 SCPrint(TRUE
, f
, CFSTR("\n"));
5636 rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
5640 #endif /* !TARGET_OS_IPHONE */
5643 service_get_ip_is_coupled(CFStringRef serviceID
)
5645 CFDictionaryRef dict
;
5646 boolean_t ip_is_coupled
= FALSE
;
5648 dict
= service_dict_get(serviceID
, kSCEntNetService
);
5650 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
5651 ip_is_coupled
= TRUE
;
5654 return (ip_is_coupled
);
5658 my_CFStringCreateWithInAddr(struct in_addr ip
)
5662 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(IP_FORMAT
), IP_LIST(&ip
));
5667 my_CFStringCreateWithIn6Addr(const struct in6_addr
* ip
)
5669 char ntopbuf
[INET6_ADDRSTRLEN
];
5671 (void)inet_ntop(AF_INET6
, ip
, ntopbuf
, sizeof(ntopbuf
));
5672 return (CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), ntopbuf
));
5676 * Function: update_ipv4
5678 * Update the IPv4 configuration based on the latest information.
5679 * Publish the State:/Network/Global/IPv4 information, and update the
5680 * IPv4 routing table.
5683 update_ipv4(CFStringRef primary
,
5684 IPv4RouteListRef new_routelist
,
5685 keyChangeListRef keys
)
5687 #if !TARGET_IPHONE_SIMULATOR
5689 #endif /* !TARGET_IPHONE_SIMULATOR */
5692 if (new_routelist
!= NULL
&& primary
!= NULL
) {
5693 const char * ifn_p
= NULL
;
5694 char ifname
[IFNAMSIZ
];
5696 CFMutableDictionaryRef dict
= NULL
;
5698 dict
= CFDictionaryCreateMutable(NULL
, 0,
5699 &kCFTypeDictionaryKeyCallBacks
,
5700 &kCFTypeDictionaryValueCallBacks
);
5701 /* the first entry is the default route */
5702 r
= new_routelist
->list
;
5703 if (r
->gateway
.s_addr
!= 0) {
5706 str
= my_CFStringCreateWithInAddr(r
->gateway
);
5707 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, str
);
5710 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
5711 if (ifn_p
!= NULL
) {
5712 CFStringRef ifname_cf
;
5714 ifname_cf
= CFStringCreateWithCString(NULL
,
5716 kCFStringEncodingASCII
);
5717 if (ifname_cf
!= NULL
) {
5718 CFDictionarySetValue(dict
,
5719 kSCDynamicStorePropNetPrimaryInterface
,
5721 CFRelease(ifname_cf
);
5724 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
5726 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
5730 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
5734 #if !TARGET_IPHONE_SIMULATOR
5735 sockfd
= open_routing_socket();
5737 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5738 if (S_ipv4_routelist
== NULL
) {
5739 my_log(LOG_DEBUG
, "Old Routes = <none>");
5742 my_log(LOG_DEBUG
, "Old Routes = ");
5743 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
5745 if (new_routelist
== NULL
) {
5746 my_log(LOG_DEBUG
, "New Routes = <none>");
5749 my_log(LOG_DEBUG
, "New Routes = ");
5750 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
5753 /* go through routelist and bind any unbound routes */
5754 IPv4RouteListFinalize(new_routelist
);
5755 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
, sockfd
);
5756 if (new_routelist
!= NULL
) {
5757 (void)multicast_route(sockfd
, RTM_DELETE
);
5760 (void)multicast_route(sockfd
, RTM_ADD
);
5764 if (S_ipv4_routelist
!= NULL
) {
5765 free(S_ipv4_routelist
);
5767 S_ipv4_routelist
= new_routelist
;
5768 #else /* !TARGET_IPHONE_SIMULATOR */
5769 if (new_routelist
!= NULL
) {
5770 free(new_routelist
);
5772 #endif /* !TARGET_IPHONE_SIMULATOR */
5778 * Function: update_ipv6
5780 * Update the IPv6 configuration based on the latest information.
5781 * Publish the State:/Network/Global/IPv6 information, and update the
5782 * IPv6 routing table.
5785 update_ipv6(CFStringRef primary
,
5786 IPv6RouteListRef new_routelist
,
5787 keyChangeListRef keys
)
5789 #if !TARGET_IPHONE_SIMULATOR
5791 #endif /* !TARGET_IPHONE_SIMULATOR */
5794 if (new_routelist
!= NULL
&& primary
!= NULL
) {
5795 const char * ifn_p
= NULL
;
5796 char ifname
[IFNAMSIZ
];
5798 CFMutableDictionaryRef dict
= NULL
;
5800 dict
= CFDictionaryCreateMutable(NULL
, 0,
5801 &kCFTypeDictionaryKeyCallBacks
,
5802 &kCFTypeDictionaryValueCallBacks
);
5803 /* the first entry is the default route */
5804 r
= new_routelist
->list
;
5805 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
5808 router
= my_CFStringCreateWithIn6Addr(&r
->gateway
);
5809 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
, router
);
5812 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
5813 if (ifn_p
!= NULL
) {
5814 CFStringRef ifname_cf
;
5816 ifname_cf
= CFStringCreateWithCString(NULL
,
5818 kCFStringEncodingASCII
);
5819 if (ifname_cf
!= NULL
) {
5820 CFDictionarySetValue(dict
,
5821 kSCDynamicStorePropNetPrimaryInterface
,
5823 CFRelease(ifname_cf
);
5826 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
5828 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
5830 #if !TARGET_IPHONE_SIMULATOR
5831 if (S_scopedroute_v6
) {
5832 set_ipv6_default_interface(r
->ifindex
);
5834 #endif /* !TARGET_IPHONE_SIMULATOR */
5837 #if !TARGET_IPHONE_SIMULATOR
5838 if (S_scopedroute_v6
) {
5839 set_ipv6_default_interface(0);
5841 #endif /* !TARGET_IPHONE_SIMULATOR */
5842 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
5846 #if !TARGET_IPHONE_SIMULATOR
5847 sockfd
= open_routing_socket();
5849 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5850 if (S_ipv6_routelist
== NULL
) {
5851 my_log(LOG_DEBUG
, "Old Routes = <none>");
5854 my_log(LOG_DEBUG
, "Old Routes = ");
5855 IPv6RouteListLog(LOG_DEBUG
, S_ipv6_routelist
);
5857 if (new_routelist
== NULL
) {
5858 my_log(LOG_DEBUG
, "New Routes = <none>");
5861 my_log(LOG_DEBUG
, "New Routes = ");
5862 IPv6RouteListLog(LOG_DEBUG
, new_routelist
);
5865 /* go through routelist and bind any unbound routes */
5866 IPv6RouteListFinalize(new_routelist
);
5867 IPv6RouteListApply(S_ipv6_routelist
, new_routelist
, sockfd
);
5870 if (S_ipv6_routelist
!= NULL
) {
5871 free(S_ipv6_routelist
);
5873 S_ipv6_routelist
= new_routelist
;
5874 #else /* !TARGET_IPHONE_SIMULATOR */
5875 if (new_routelist
!= NULL
) {
5876 free(new_routelist
);
5878 #endif /* !TARGET_IPHONE_SIMULATOR */
5884 update_dns(CFDictionaryRef services_info
,
5885 CFStringRef primary
,
5886 keyChangeListRef keys
)
5888 Boolean changed
= FALSE
;
5889 CFDictionaryRef dict
= NULL
;
5891 if (primary
!= NULL
) {
5892 CFDictionaryRef service_dict
;
5894 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
5895 if (service_dict
!= NULL
) {
5896 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
5900 if (!_SC_CFEqual(S_dns_dict
, dict
)) {
5902 #if !TARGET_OS_IPHONE
5904 #endif /* !TARGET_OS_IPHONE */
5905 keyChangeListRemoveValue(keys
, S_state_global_dns
);
5907 CFMutableDictionaryRef new_dict
;
5909 #if !TARGET_OS_IPHONE
5910 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
5911 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
5912 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
5913 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
5914 #endif /* !TARGET_OS_IPHONE */
5915 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
5916 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
5917 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
5918 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
5919 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
5920 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
5921 CFRelease(new_dict
);
5926 if (dict
!= NULL
) CFRetain(dict
);
5927 if (S_dns_dict
!= NULL
) CFRelease(S_dns_dict
);
5934 update_dnsinfo(CFDictionaryRef services_info
,
5935 CFStringRef primary
,
5936 keyChangeListRef keys
,
5937 CFArrayRef service_order
)
5940 CFDictionaryRef dict
= NULL
;
5941 CFArrayRef multicastResolvers
;
5942 CFArrayRef privateResolvers
;
5944 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
5945 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
5947 if (primary
!= NULL
) {
5948 CFDictionaryRef service_dict
;
5950 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
5951 if (service_dict
!= NULL
) {
5952 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
5956 changed
= dns_configuration_set(dict
,
5957 S_service_state_dict
,
5962 keyChangeListNotifyKey(keys
, S_state_global_dns
);
5968 update_nwi(nwi_state_t state
)
5970 unsigned char signature
[CC_SHA1_DIGEST_LENGTH
];
5971 static unsigned char signature_last
[CC_SHA1_DIGEST_LENGTH
];
5973 _nwi_state_compute_sha1_hash(state
, signature
);
5974 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
5975 my_log(LOG_DEBUG
, "Not updating network information");
5979 // save [new] signature
5980 bcopy(signature
, signature_last
, sizeof(signature
));
5982 // save [new] configuration
5983 my_log(LOG_INFO
, "Updating network information");
5984 S_nwi_state_dump(state
);
5986 if (_nwi_state_store(state
) == FALSE
) {
5987 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
5994 update_proxies(CFDictionaryRef services_info
,
5995 CFStringRef primary
,
5996 keyChangeListRef keys
,
5997 CFArrayRef service_order
)
5999 Boolean changed
= FALSE
;
6000 CFDictionaryRef dict
= NULL
;
6001 CFDictionaryRef new_dict
;
6003 if (primary
!= NULL
) {
6004 CFDictionaryRef service_dict
;
6006 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6007 if (service_dict
!= NULL
) {
6008 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
6012 new_dict
= proxy_configuration_update(dict
,
6013 S_service_state_dict
,
6016 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
6017 if (new_dict
== NULL
) {
6018 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
6020 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
6025 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
6026 S_proxies_dict
= new_dict
;
6031 #if !TARGET_OS_IPHONE
6033 update_smb(CFDictionaryRef services_info
,
6034 CFStringRef primary
,
6035 keyChangeListRef keys
)
6037 Boolean changed
= FALSE
;
6038 CFDictionaryRef dict
= NULL
;
6040 if (primary
!= NULL
) {
6041 CFDictionaryRef service_dict
;
6043 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
6044 if (service_dict
!= NULL
) {
6045 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
6049 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
6051 keyChangeListRemoveValue(keys
, S_state_global_smb
);
6053 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
6058 if (dict
!= NULL
) CFRetain(dict
);
6059 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
6064 #endif /* !TARGET_OS_IPHONE */
6067 get_service_rank(CFArrayRef order
, CFIndex n_order
, CFStringRef serviceID
)
6070 Rank rank
= kRankIndexMask
;
6072 if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
6073 for (i
= 0; i
< n_order
; i
++) {
6074 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
6079 if (CFEqual(serviceID
, s
)) {
6089 ** Service election:
6092 * Function: rank_dict_get_service_rank
6094 * Retrieve the service rank in the given dictionary.
6097 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
6102 rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
6103 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
6105 CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
);
6111 * Function: rank_dict_set_service_rank
6113 * Save the results of ranking the service so we can look it up later without
6114 * repeating all of the ranking code.
6117 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
6118 CFStringRef serviceID
, Rank rank_val
)
6122 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
6124 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
6130 static const CFStringRef
*transientInterfaceEntityNames
[] = {
6136 CollectTransientServices(const void * key
,
6141 CFStringRef service
= key
;
6142 CFMutableArrayRef vif_setup_keys
= context
;
6144 /* This service is either a vpn type service or a comm center service */
6145 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
6149 for (i
= 0; i
< countof(transientInterfaceEntityNames
); i
++) {
6150 if (CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
6151 CFArrayAppendValue(vif_setup_keys
, service
);
6160 static SCNetworkReachabilityFlags
6161 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
6162 CFStringRef service_id
,
6164 CFStringRef vpn_setup_key
)
6167 CFDictionaryRef dict
;
6168 SCNetworkReachabilityFlags flags
= 0;
6171 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6172 kSCDynamicStoreDomainSetup
,
6174 kSCEntNetInterface
);
6175 dict
= CFDictionaryGetValue(services_info
, key
);
6178 if (isA_CFDictionary(dict
)
6179 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
6181 flags
= (kSCNetworkReachabilityFlagsReachable
6182 | kSCNetworkReachabilityFlagsTransientConnection
6183 | kSCNetworkReachabilityFlagsConnectionRequired
);
6185 if (CFEqual(entity
, kSCEntNetPPP
)) {
6187 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
6189 if (!isA_CFDictionary(p_dict
)) {
6193 // get PPP dial-on-traffic status
6194 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
6195 if (isA_CFNumber(num
)) {
6198 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
6200 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6210 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
6212 Boolean ret
= def_value
;
6217 val
= CFDictionaryGetValue(dict
, key
);
6218 if (isA_CFBoolean(val
) != NULL
) {
6219 ret
= CFBooleanGetValue(val
);
6227 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
6228 SCNetworkReachabilityFlags
*reach_flags_v4
,
6229 SCNetworkReachabilityFlags
*reach_flags_v6
)
6233 CFMutableArrayRef vif_setup_keys
;
6235 vif_setup_keys
= CFArrayCreateMutable(NULL
,
6237 &kCFTypeArrayCallBacks
);
6238 CFDictionaryApplyFunction(services_info
, CollectTransientServices
,
6240 count
= CFArrayGetCount(vif_setup_keys
);
6241 for (i
= 0; i
< count
; i
++) {
6242 CFArrayRef components
= NULL
;
6244 CFStringRef service_id
;
6245 CFStringRef vif_setup_key
;
6247 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
6250 * setup key in the following format:
6251 * Setup:/Network/Service/<Service ID>/<Entity>
6253 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
6255 if (CFArrayGetCount(components
) != 5) {
6256 // invalid Setup key encountered
6260 /* service id is the 3rd element */
6261 service_id
= CFArrayGetValueAtIndex(components
, 3);
6263 /* entity id is the 4th element */
6264 entity
= CFArrayGetValueAtIndex(components
, 4);
6267 if (CFEqual(entity
, kSCEntNetPPP
)) {
6268 SCNetworkReachabilityFlags flags
;
6271 flags
= GetReachabilityFlagsFromVPN(services_info
,
6276 /* Check for the v4 reachability flags */
6277 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6278 kSCDynamicStoreDomainSetup
,
6282 if (CFDictionaryContainsKey(services_info
, key
)) {
6283 *reach_flags_v4
|= flags
;
6284 my_log(LOG_DEBUG
, "Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
6289 /* Check for the v6 reachability flags */
6290 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6291 kSCDynamicStoreDomainSetup
,
6295 if (CFDictionaryContainsKey(services_info
, key
)) {
6296 *reach_flags_v6
|= flags
;
6297 my_log(LOG_DEBUG
, "Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
6302 if (components
!= NULL
) {
6303 CFRelease(components
);
6309 if (components
!= NULL
) {
6310 CFRelease(components
);
6314 CFRelease(vif_setup_keys
);
6318 static SCNetworkReachabilityFlags
6319 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
6321 SCNetworkReachabilityFlags flags
= 0;
6323 if (CFEqual(entity
, kSCEntNetPPP
)) {
6326 /* if we're really UP and RUNNING */
6329 /* if we're effectively UP and RUNNING */
6332 /* if we're not connected at all */
6333 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6335 case PPP_STATERESERVED
:
6336 // if we're not connected at all
6337 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6340 /* if we're in the process of [dis]connecting */
6341 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6345 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
6347 case IPSEC_RUNNING
:
6348 /* if we're really UP and RUNNING */
6351 /* if we're not connected at all */
6352 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6355 /* if we're in the process of [dis]connecting */
6356 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6360 else if (CFEqual(entity
, kSCEntNetVPN
)) {
6363 /* if we're really UP and RUNNING */
6368 case VPN_UNLOADING
:
6369 /* if we're not connected at all */
6370 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6373 /* if we're in the process of [dis]connecting */
6374 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6382 VPNAttributesGet(CFStringRef service_id
,
6383 CFDictionaryRef services_info
,
6384 SCNetworkReachabilityFlags
*flags
,
6385 CFStringRef
*server_address
,
6389 CFDictionaryRef entity_dict
;
6391 CFDictionaryRef p_state
= NULL
;
6393 CFStringRef transient_entity
= NULL
;
6395 if (af
== AF_INET
) {
6396 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv4
);
6399 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv6
);
6401 entity_dict
= ipdict_get_service(entity_dict
);
6402 if (entity_dict
== NULL
) {
6406 for (i
= 0; i
< countof(transientServiceInfo
); i
++) {
6407 CFStringRef entity
= *transientServiceInfo
[i
].entityName
;
6409 p_state
= service_dict_get(service_id
, entity
);
6411 /* ensure that this is a VPN Type service */
6412 if (isA_CFDictionary(p_state
)) {
6413 transient_entity
= entity
;
6418 /* Did we find a vpn type service? If not, we are done.*/
6419 if (transient_entity
== NULL
) {
6423 *flags
|= (kSCNetworkReachabilityFlagsReachable
6424 | kSCNetworkReachabilityFlagsTransientConnection
);
6426 /* Get the Server Address */
6427 if (server_address
!= NULL
) {
6428 *server_address
= CFDictionaryGetValue(entity_dict
,
6429 CFSTR("ServerAddress"));
6430 *server_address
= isA_CFString(*server_address
);
6431 if (*server_address
!= NULL
) {
6432 CFRetain(*server_address
);
6437 if (!CFDictionaryGetValueIfPresent(p_state
,
6438 kSCPropNetVPNStatus
, // IPSecStatus, PPPStatus, VPNStatus
6439 (const void **)&num
) ||
6440 !isA_CFNumber(num
) ||
6441 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &status
)) {
6445 *flags
|= GetReachFlagsFromStatus(transient_entity
, status
);
6446 if (CFEqual(transient_entity
, kSCEntNetPPP
)) {
6448 CFDictionaryRef p_setup
;
6451 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6452 kSCDynamicStoreDomainSetup
,
6455 p_setup
= CFDictionaryGetValue(services_info
, key
);
6458 /* get dial-on-traffic status */
6459 if (isA_CFDictionary(p_setup
) &&
6460 CFDictionaryGetValueIfPresent(p_setup
,
6461 kSCPropNetPPPDialOnDemand
,
6462 (const void **)&num
) &&
6463 isA_CFNumber(num
) &&
6464 CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) &&
6465 (ppp_demand
!= 0)) {
6466 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6467 if (status
== PPP_IDLE
) {
6468 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
6476 typedef struct ElectionInfo
{
6482 ElectionResultsRef results
;
6483 CFMutableDictionaryRef rank_dict
;
6484 } ElectionInfo
, * ElectionInfoRef
;
6486 typedef CFDictionaryApplierFunction ElectionFuncRef
;
6489 CandidateRelease(CandidateRef candidate
)
6491 my_CFRelease(&candidate
->serviceID
);
6492 my_CFRelease(&candidate
->if_name
);
6493 my_CFRelease(&candidate
->signature
);
6498 CandidateCopy(CandidateRef dest
, CandidateRef src
)
6501 if (dest
->serviceID
) {
6502 CFRetain(dest
->serviceID
);
6504 if (dest
->if_name
) {
6505 CFRetain(dest
->if_name
);
6507 if(dest
->signature
) {
6508 CFRetain(dest
->signature
);
6513 static ElectionResultsRef
6514 ElectionResultsAlloc(int af
, int size
)
6516 ElectionResultsRef results
;
6518 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
6521 results
->size
= size
;
6526 ElectionResultsRelease(ElectionResultsRef results
)
6531 for (i
= 0, scan
= results
->candidates
;
6534 CandidateRelease(scan
);
6541 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
6546 if (results
== NULL
) {
6547 my_log(level
, "%s: no candidates", prefix
);
6550 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
6551 for (i
= 0, scan
= results
->candidates
;
6554 char ntopbuf
[INET6_ADDRSTRLEN
];
6556 (void)inet_ntop(results
->af
, &scan
->addr
, ntopbuf
, sizeof(ntopbuf
));
6557 my_log(level
, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6558 i
, scan
->if_name
, scan
->serviceID
, ntopbuf
, scan
->rank
,
6559 scan
->ineligible
? " [ineligible]" : "");
6565 * Function: ElectionResultsAddCandidate
6567 * Add the candidate into the election results. Find the insertion point
6568 * by comparing the rank of the candidate with existing entries.
6571 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
6576 if (results
->count
== results
->size
) {
6577 /* this should not happen */
6578 my_log(LOG_NOTICE
, "can't fit another candidate");
6582 /* find the insertion point */
6583 where
= kCFNotFound
;
6584 for (i
= 0; i
< results
->count
; i
++) {
6585 CandidateRef this_candidate
= results
->candidates
+ i
;
6587 if (candidate
->rank
< this_candidate
->rank
) {
6592 /* add it to the end */
6593 if (where
== kCFNotFound
) {
6594 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
6598 /* slide existing entries over */
6599 for (i
= results
->count
; i
> where
; i
--) {
6600 results
->candidates
[i
] = results
->candidates
[i
- 1];
6602 /* insert element */
6603 CandidateCopy(results
->candidates
+ where
, candidate
);
6609 elect_ip(const void * key
, const void * value
, void * context
);
6612 * Function: ElectionResultsCopy
6614 * Visit all of the services and invoke the protocol-specific election
6615 * function. Return the results of the election.
6617 static ElectionResultsRef
6618 ElectionResultsCopy(int af
, CFArrayRef order
)
6623 count
= (int)CFDictionaryGetCount(S_service_state_dict
);
6628 if (af
== AF_INET
) {
6629 info
.entity
= kSCEntNetIPv4
;
6630 info
.rank_dict
= S_ipv4_service_rank_dict
;
6633 info
.entity
= kSCEntNetIPv6
;
6634 info
.rank_dict
= S_ipv6_service_rank_dict
;
6636 info
.results
= ElectionResultsAlloc(af
, count
);
6637 info
.n_services
= count
;
6639 if (order
!= NULL
) {
6640 info
.n_order
= CFArrayGetCount(order
);
6645 CFDictionaryApplyFunction(S_service_state_dict
, elect_ip
, (void *)&info
);
6646 if (info
.results
->count
== 0) {
6647 ElectionResultsRelease(info
.results
);
6648 info
.results
= NULL
;
6650 return (info
.results
);
6654 * Function: ElectionResultsCandidateNeedsDemotion
6656 * Check whether the given candidate requires demotion. A candidate
6657 * might need to be demoted if its IPv4 and IPv6 services must be coupled
6658 * but a higher ranked service has IPv4 or IPv6.
6661 ElectionResultsCandidateNeedsDemotion(ElectionResultsRef other_results
,
6662 CandidateRef candidate
)
6664 CandidateRef other_candidate
;
6665 Boolean ret
= FALSE
;
6667 if (other_results
== NULL
6668 || candidate
->ip_is_coupled
== FALSE
6669 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
6672 other_candidate
= other_results
->candidates
;
6673 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
6674 /* they are over the same interface, no need to demote */
6677 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
6678 /* avoid creating a feedback loop */
6681 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
6682 /* the other candidate isn't eligible to become primary, ignore */
6685 if (candidate
->rank
< other_candidate
->rank
) {
6686 /* we're higher ranked than the other candidate, ignore */
6698 get_signature_sha1(CFStringRef signature
,
6699 unsigned char * sha1
)
6702 CFDataRef signature_data
;
6704 signature_data
= CFStringCreateExternalRepresentation(NULL
,
6706 kCFStringEncodingUTF8
,
6710 CC_SHA1_Update(&ctx
,
6712 (CC_LONG
)CFDataGetLength(signature_data
));
6713 CC_SHA1_Final(sha1
, &ctx
);
6715 CFRelease(signature_data
);
6722 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
6723 CandidateRef candidate
, Boolean not_in_list
,
6724 Boolean not_in_iflist
)
6727 char ifname
[IFNAMSIZ
];
6728 nwi_ifstate_t ifstate
;
6730 if (nwi_state
== NULL
) {
6735 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
6736 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
6738 if (not_in_iflist
) {
6739 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST
;
6741 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
6742 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
6744 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
6745 kCFStringEncodingASCII
);
6746 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
6747 char ntopbuf
[INET6_ADDRSTRLEN
];
6749 (void)inet_ntop(af
, &candidate
->addr
, ntopbuf
, sizeof(ntopbuf
));
6751 "Adding IPv%c [%s] %s "
6752 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
6753 ipvx_char(af
), ifname
, ntopbuf
,
6754 flags
, candidate
->rank
, candidate
->reachability_flags
);
6756 ifstate
= nwi_state_add_ifstate(nwi_state
, ifname
, af
, flags
,
6758 (void *)&candidate
->addr
,
6759 (void *)&candidate
->vpn_server_addr
,
6760 candidate
->reachability_flags
);
6761 if (ifstate
!= NULL
&& candidate
->signature
) {
6762 uint8_t hash
[CC_SHA1_DIGEST_LENGTH
];
6764 get_signature_sha1(candidate
->signature
, hash
);
6765 nwi_ifstate_set_signature(ifstate
, hash
);
6772 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
6774 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
6775 CFStringRef vpn_server_address
= NULL
;
6777 VPNAttributesGet(candidate
->serviceID
,
6780 &vpn_server_address
,
6783 candidate
->reachability_flags
= flags
;
6785 if (vpn_server_address
== NULL
) {
6786 bzero(&candidate
->vpn_server_addr
, sizeof(candidate
->vpn_server_addr
));
6790 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
),
6791 kCFStringEncodingASCII
);
6792 _SC_string_to_sockaddr(buf
,
6794 (void *)&candidate
->vpn_server_addr
,
6795 sizeof(candidate
->vpn_server_addr
));
6797 CFRelease(vpn_server_address
);
6802 * Function: ElectionResultsCopyPrimary
6804 * Use the results of the current protocol and the other protocol to
6805 * determine which service should become primary.
6807 * At the same time, generate the IPv4/IPv6 routing table and
6808 * the nwi_state for the protocol.
6811 ElectionResultsCopyPrimary(ElectionResultsRef results
,
6812 ElectionResultsRef other_results
,
6813 nwi_state_t nwi_state
, int af
,
6814 RouteListRef
* ret_routes
,
6815 CFDictionaryRef services_info
)
6817 CFStringRef primary
= NULL
;
6818 Boolean primary_is_null
= FALSE
;
6819 RouteListRef routes
= NULL
;
6821 if (results
!= NULL
) {
6822 CandidateRef deferred
[results
->count
];
6824 CFStringRef entity_name
;
6827 RouteListInfoRef info
;
6832 entity_name
= kSCEntNetIPv4
;
6833 info
= &IPv4RouteListInfo
;
6834 initial_size
= results
->count
* IPV4_ROUTES_N_STATIC
;
6838 entity_name
= kSCEntNetIPv6
;
6839 info
= &IPv6RouteListInfo
;
6840 initial_size
= results
->count
* IPV6_ROUTES_N_STATIC
;
6844 for (i
= 0, scan
= results
->candidates
;
6847 Boolean is_primary
= FALSE
;
6848 CFDictionaryRef service_dict
;
6849 RouteListRef service_routes
;
6850 Boolean skip
= FALSE
;
6852 if (scan
->ineligible
== FALSE
6854 && RANK_ASSERTION_MASK(scan
->rank
) != kRankAssertionNever
) {
6855 if (ElectionResultsCandidateNeedsDemotion(other_results
,
6857 /* demote the service */
6859 "IPv%c over %@ demoted: not primary for IPv%c",
6860 ipvx_char(af
), scan
->if_name
, ipvx_other_char(af
));
6861 deferred
[deferred_count
++] = scan
;
6865 primary
= CFRetain(scan
->serviceID
);
6869 /* contribute to the routing table */
6870 service_dict
= service_dict_get(scan
->serviceID
, entity_name
);
6871 service_routes
= ipdict_get_routelist(service_dict
);
6872 if (service_routes
!= NULL
) {
6873 Rank rank
= scan
->rank
;
6876 /* routes are RankNever to prevent becoming primary */
6877 rank
= RankMake(rank
, kRankAssertionNever
);
6879 routes
= RouteListAddRouteList(info
, routes
, initial_size
,
6880 service_routes
, rank
);
6881 if ((service_routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
6889 /* if we're skipping the primary, it's NULL */
6891 primary_is_null
= TRUE
;
6894 else if (scan
->ineligible
== FALSE
) {
6895 Boolean not_in_iflist
;
6897 add_reachability_flags_to_candidate(scan
, services_info
, af
);
6899 = (service_routes
->flags
& kRouteListFlagsScopedOnly
) != 0;
6900 add_candidate_to_nwi_state(nwi_state
, af
, scan
,
6905 for (i
= 0; i
< deferred_count
; i
++) {
6906 CandidateRef candidate
= deferred
[i
];
6908 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
6909 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, TRUE
, FALSE
);
6912 if (ret_routes
!= NULL
) {
6913 *ret_routes
= routes
;
6915 else if (routes
!= NULL
) {
6918 if (primary_is_null
) {
6919 my_CFRelease(&primary
);
6927 service_dict_get_signature(CFDictionaryRef service_dict
)
6931 ifname
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
6932 if (isA_CFString(ifname
) == NULL
6933 || confirm_interface_name(service_dict
, ifname
) == FALSE
) {
6936 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
6940 * Function: elect_ip
6942 * Evaluate the service and determine what rank the service should have.
6943 * If it's a suitable candidate, add it to the election results.
6946 elect_ip(const void * key
, const void * value
, void * context
)
6948 CFDictionaryRef all_entities_dict
= (CFDictionaryRef
)value
;
6949 Candidate candidate
;
6951 ElectionInfoRef elect_info
;
6952 CFStringRef if_name
;
6953 CFDictionaryRef ipdict
;
6955 RouteListUnion routelist
;
6956 CFDictionaryRef service_dict
;
6958 elect_info
= (ElectionInfoRef
)context
;
6959 ipdict
= CFDictionaryGetValue(all_entities_dict
, elect_info
->entity
);
6960 if (ipdict
!= NULL
) {
6961 routelist
.ptr
= ipdict_get_routelist(ipdict
);
6962 service_dict
= ipdict_get_service(ipdict
);
6965 routelist
.ptr
= NULL
;
6967 if (routelist
.ptr
== NULL
|| service_dict
== NULL
) {
6968 /* no connectivity */
6971 if_name
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
6972 if (if_name
== NULL
) {
6973 /* need an interface name */
6976 if (CFEqual(if_name
, CFSTR(kLoopbackInterface
))) {
6977 /* don't process loopback */
6980 bzero(&candidate
, sizeof(candidate
));
6981 candidate
.serviceID
= (CFStringRef
)key
;
6982 if ((routelist
.common
->flags
& kRouteListFlagsHasDefault
) == 0) {
6983 /* no default route means it's ineligible to become primary */
6984 candidate
.ineligible
= TRUE
;
6986 candidate
.rank
= get_service_rank(elect_info
->order
, elect_info
->n_order
,
6987 candidate
.serviceID
);
6988 if (elect_info
->af
== AF_INET
) {
6989 default_rank
= routelist
.v4
->list
->rank
;
6990 candidate
.addr
.v4
= routelist
.v4
->list
->ifa
;
6993 default_rank
= routelist
.v6
->list
->rank
;
6994 candidate
.addr
.v6
= routelist
.v6
->list
->ifa
;
6996 primary_rank
= RANK_ASSERTION_MASK(default_rank
);
6997 if (S_ppp_override_primary
) {
7000 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
7001 kCFStringEncodingASCII
)
7002 && (strncmp(PPP_PREFIX
, ifn
, sizeof(PPP_PREFIX
) - 1) == 0)) {
7003 /* PPP override: make ppp* look the best */
7004 primary_rank
= kRankAssertionFirst
;
7007 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
7008 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
7009 candidate
.if_name
= if_name
;
7010 rank_dict_set_service_rank(elect_info
->rank_dict
,
7011 candidate
.serviceID
, candidate
.rank
);
7012 candidate
.signature
= service_dict_get_signature(service_dict
);
7013 ElectionResultsAddCandidate(elect_info
->results
, &candidate
);
7019 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
7021 uint32_t changed
= 0;
7024 /* update service options first (e.g. rank) */
7025 if (get_rank_changes(serviceID
,
7026 get_service_state_entity(services_info
, serviceID
,
7028 get_service_setup_entity(services_info
, serviceID
,
7031 changed
|= (1 << kEntityTypeServiceOptions
);
7033 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7034 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
7035 GetEntityChangesFuncRef func
= entityChangeFunc
[i
];
7036 if ((*func
)(serviceID
,
7037 get_service_state_entity(services_info
, serviceID
,
7038 *entityTypeNames
[i
]),
7039 get_service_setup_entity(services_info
, serviceID
,
7040 *entityTypeNames
[i
]),
7042 changed
|= (1 << i
);
7046 if (get_transient_status_changes(serviceID
, services_info
)) {
7047 changed
|= (1 << kEntityTypeTransientStatus
);
7054 serviceID_get_ifname(CFStringRef serviceID
)
7056 CFDictionaryRef entity_dict
;
7057 CFStringRef ifname
= NULL
;
7059 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
7060 if (entity_dict
== NULL
) {
7061 entity_dict
= service_dict_get(serviceID
, kSCEntNetIPv6
);
7063 if (entity_dict
!= NULL
) {
7064 ifname
= ipdict_get_ifname(entity_dict
);
7069 __private_extern__ boolean_t
7070 check_if_service_expensive(CFStringRef serviceID
)
7073 ifname
= serviceID_get_ifname(serviceID
);
7075 return interface_is_expensive(ifname
);
7079 service_order_get(CFDictionaryRef services_info
)
7081 CFArrayRef order
= NULL
;
7082 CFDictionaryRef ipv4_dict
;
7084 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
7085 S_setup_global_ipv4
);
7086 if (ipv4_dict
!= NULL
) {
7087 CFNumberRef ppp_override
;
7090 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
7091 order
= isA_CFArray(order
);
7093 /* get ppp override primary */
7094 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
7095 kSCPropNetPPPOverridePrimary
);
7096 ppp_override
= isA_CFNumber(ppp_override
);
7097 if (ppp_override
!= NULL
) {
7098 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
7100 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
7103 S_ppp_override_primary
= FALSE
;
7109 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
7110 const char * entity
)
7112 boolean_t changed
= FALSE
;
7113 CFStringRef primary
= *primary_p
;
7115 if (new_primary
!= NULL
) {
7116 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
7117 my_log(LOG_INFO
, "%@ is still primary %s", new_primary
, entity
);
7120 my_CFRelease(primary_p
);
7121 *primary_p
= CFRetain(new_primary
);
7122 my_log(LOG_INFO
, "%@ is the new primary %s", new_primary
, entity
);
7126 else if (primary
!= NULL
) {
7127 my_log(LOG_INFO
, "%@ is no longer primary %s", primary
, entity
);
7128 my_CFRelease(primary_p
);
7135 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
7138 if (service_dict_get(serviceID
, entity
) == NULL
) {
7139 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
7141 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
7145 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
7151 #define N_KEYS_VALUES_STATIC 10
7152 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
7155 count
= CFDictionaryGetCount(S_service_state_dict
);
7156 if (count
<= N_KEYS_VALUES_STATIC
) {
7157 keys
= keys_values_buf
;
7159 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
7161 values
= keys
+ count
;
7162 CFDictionaryGetKeysAndValues(S_service_state_dict
,
7163 (const void * *)keys
,
7164 (const void * *)values
);
7166 for (i
= 0; i
< count
; i
++) {
7167 CFDictionaryRef ipdict
= NULL
;
7168 CFStringRef interface
= NULL
;
7169 CFStringRef serviceID
;
7170 CFDictionaryRef service_dict
;
7172 serviceID
= (CFStringRef
)keys
[i
];
7173 service_dict
= (CFDictionaryRef
)values
[i
];
7175 /* check whether service has IPv4 or IPv6 */
7176 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
7177 if (ipdict
== NULL
) {
7178 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
7179 if (ipdict
== NULL
) {
7183 interface
= ipdict_get_ifname(ipdict
);
7184 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
7186 "Found IP service %@ on interface %@",
7188 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
7191 if (keys
!= keys_values_buf
) {
7197 static __inline__
const char *
7198 get_changed_str(CFStringRef serviceID
, CFStringRef entity
,
7199 CFDictionaryRef old_dict
)
7201 CFDictionaryRef new_dict
= NULL
;
7203 if (serviceID
!= NULL
) {
7204 new_dict
= service_dict_get(serviceID
, entity
);
7207 if (old_dict
== NULL
) {
7208 if (new_dict
!= NULL
) {
7212 if (new_dict
== NULL
) {
7214 } else if (!CFEqual(old_dict
, new_dict
)) {
7221 #if ! TARGET_IPHONE_SIMULATOR
7223 #ifdef SIOCSIFNETSIGNATURE
7224 #define MANAGE_IF_SIGNATURE
7227 inet_dgram_socket(void)
7231 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
7233 my_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
7240 siocsifnetsignature(int s
, const char * ifname
, int af
,
7241 const uint8_t * signature
, int signature_length
)
7243 struct if_nsreq nsreq
;
7245 bzero(&nsreq
, sizeof(nsreq
));
7246 strlcpy(nsreq
.ifnsr_name
, ifname
, sizeof(nsreq
.ifnsr_name
));
7247 nsreq
.ifnsr_family
= af
;
7248 if (signature_length
> 0) {
7249 if (signature_length
> sizeof(nsreq
.ifnsr_data
)) {
7250 signature_length
= sizeof(nsreq
.ifnsr_data
);
7252 nsreq
.ifnsr_len
= signature_length
;
7253 memcpy(nsreq
.ifnsr_data
, signature
, signature_length
);
7255 return (ioctl(s
, SIOCSIFNETSIGNATURE
, &nsreq
));
7259 process_ifstate_difference(nwi_ifstate_t ifstate
, int af
, int sockfd
)
7261 nwi_ifstate_difference_t diff
;
7262 boolean_t set_signature
= FALSE
;
7263 int signature_length
= 0;
7265 diff
= nwi_ifstate_get_difference(ifstate
);
7267 case knwi_ifstate_difference_changed
:
7268 /* set signature for this interface */
7269 set_signature
= TRUE
;
7270 if ((ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_SIGNATURE
) != 0) {
7271 signature_length
= sizeof(ifstate
->signature
);
7274 case knwi_ifstate_difference_removed
:
7275 /* remove signature for this interface */
7276 set_signature
= TRUE
;
7281 if (set_signature
) {
7282 if (siocsifnetsignature(sockfd
, ifstate
->ifname
, af
,
7284 signature_length
) < 0) {
7286 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7287 ifstate
->ifname
, ipvx_char(af
),
7292 my_log(LOG_DEBUG
, "IPv%c Network Signature %s %s",
7294 (signature_length
> 0) ? "Set" : "Cleared",
7296 if (signature_length
> 0
7297 && (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7299 char sig_buf
[signature_length
* 3 + 1];
7302 for (i
= 0; i
< signature_length
; i
++) {
7305 snprintf(byte_buf
, sizeof(byte_buf
),
7306 "%02x ", ifstate
->signature
[i
]);
7307 strlcat(sig_buf
, byte_buf
, sizeof(sig_buf
));
7309 my_log(LOG_DEBUG
, "Signature Bytes: %s", sig_buf
);
7317 process_state_differences(nwi_state_t state
, int af
, int sockfd
)
7323 if (af
== AF_INET
) {
7324 count
= state
->ipv4_count
;
7327 count
= state
->ipv6_count
;
7329 for (i
= 0, scan
= nwi_state_ifstate_list(state
, af
);
7330 i
< count
; i
++, scan
++) {
7331 process_ifstate_difference(scan
, af
, sockfd
);
7336 #endif /* SIOCSIFNETSIGNATURE */
7338 #endif /* !TARGET_IPHONE_SIMULATOR */
7341 process_nwi_changes(CFMutableStringRef log_output
,
7342 nwi_state_t changes_state
,
7343 boolean_t dns_changed
,
7344 boolean_t dnsinfo_changed
,
7345 CFDictionaryRef old_primary_dns
,
7346 boolean_t proxy_changed
,
7347 CFDictionaryRef old_primary_proxy
,
7348 boolean_t smb_changed
,
7349 CFDictionaryRef old_primary_smb
)
7353 if (changes_state
!= NULL
) {
7354 const sa_family_t af_list
[] = {AF_INET
, AF_INET6
};
7356 #ifdef MANAGE_IF_SIGNATURE
7357 int sockfd
= inet_dgram_socket();
7358 #endif /* MANAGE_IF_SIGNATURE */
7360 for (idx
= 0; idx
< countof(af_list
); idx
++) {
7361 int af
= af_list
[idx
];
7362 CFMutableStringRef changes
= NULL
;
7363 CFMutableStringRef primary_str
= NULL
;
7365 #ifdef MANAGE_IF_SIGNATURE
7366 process_state_differences(changes_state
, af
, sockfd
);
7367 #endif /* MANAGE_IF_SIGNATURE */
7368 scan
= nwi_state_get_first_ifstate(changes_state
, af
);
7369 while (scan
!= NULL
) {
7370 const char * changed_str
;
7372 changed_str
= nwi_ifstate_get_diff_str(scan
);
7373 if (changed_str
!= NULL
) {
7375 const char * addr_str
;
7376 char ntopbuf
[INET6_ADDRSTRLEN
];
7378 address
= (void *)nwi_ifstate_get_address(scan
);
7379 addr_str
= inet_ntop(scan
->af
, address
, ntopbuf
,
7381 if (primary_str
== NULL
) {
7382 primary_str
= CFStringCreateMutable(NULL
, 0);
7383 CFStringAppendFormat(primary_str
, NULL
,
7385 nwi_ifstate_get_ifname(scan
),
7386 changed_str
, addr_str
);
7388 if (changes
== NULL
) {
7389 changes
= CFStringCreateMutable(NULL
, 0);
7391 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
7392 nwi_ifstate_get_ifname(scan
));
7393 if (strcmp(changed_str
, "") != 0) {
7394 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
7395 changed_str
, addr_str
);
7399 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
7402 if (primary_str
!= NULL
) {
7403 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
7404 af
== AF_INET
? "v4" : "v6",
7407 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
7408 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
7411 CFStringAppend(log_output
, CFSTR(")"));
7413 my_CFRelease(&primary_str
);
7414 my_CFRelease(&changes
);
7417 #ifdef MANAGE_IF_SIGNATURE
7421 #endif /* MANAGE_IF_SIGNATURE */
7424 if (dns_changed
|| dnsinfo_changed
) {
7427 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
7428 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
7429 str
= "*"; // dnsinfo change w/no change to primary
7431 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
7432 } else if (S_primary_dns
!= NULL
) {
7433 CFStringAppend(log_output
, CFSTR(" DNS"));
7436 if (proxy_changed
) {
7439 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
7440 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
7441 } else if (S_primary_proxies
!= NULL
) {
7442 CFStringAppend(log_output
, CFSTR(" Proxy"));
7445 #if !TARGET_OS_IPHONE
7449 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
7450 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
7451 } else if (S_primary_smb
!= NULL
) {
7452 CFStringAppend(log_output
, CFSTR(" SMB"));
7454 #endif // !TARGET_OS_IPHONE
7460 #pragma mark Network changed notification
7462 static dispatch_queue_t
7463 __network_change_queue()
7465 static dispatch_once_t once
;
7466 static dispatch_queue_t q
;
7468 dispatch_once(&once
, ^{
7469 q
= dispatch_queue_create("network change queue", NULL
);
7475 // Note: must run on __network_change_queue()
7477 post_network_change_when_ready()
7481 if (S_network_change_needed
== 0) {
7485 if (!S_network_change_timeout
&&
7486 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
7487 // if we [still] need to wait for the DNS configuration
7488 // or network information changes to be ack'd
7490 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
7491 S_dnsinfo_synced
? "DNS" : "!DNS",
7492 S_nwi_synced
? "nwi" : "!nwi");
7496 // cancel any running timer
7497 if (S_network_change_timer
!= NULL
) {
7498 dispatch_source_cancel(S_network_change_timer
);
7499 dispatch_release(S_network_change_timer
);
7500 S_network_change_timer
= NULL
;
7501 S_network_change_timeout
= FALSE
;
7504 // set (and log?) the post time
7506 struct timeval elapsed
;
7509 (void) gettimeofday(&end
, NULL
);
7510 timersub(&end
, &S_network_change_start
, &elapsed
);
7512 #define QUERY_TIME__FMT "%ld.%6.6d"
7513 #define QUERY_TIME__DIV 1
7516 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
7517 S_network_change_timeout
? "timeout" : "delayed",
7519 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
7520 S_network_change_needed
);
7523 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
7524 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
7525 if (status
!= NOTIFY_STATUS_OK
) {
7527 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%d", status
);
7531 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
7532 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
7533 if (status
!= NOTIFY_STATUS_OK
) {
7535 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%d", status
);
7539 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
7540 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
7541 if (status
!= NOTIFY_STATUS_OK
) {
7543 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%d", status
);
7547 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
7548 if (status
!= NOTIFY_STATUS_OK
) {
7550 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%d", status
);
7553 S_network_change_needed
= 0;
7557 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
7559 // Note: must run on __network_change_queue()
7561 post_network_change(uint32_t change
)
7563 if (S_network_change_needed
== 0) {
7564 // set the start time
7565 (void) gettimeofday(&S_network_change_start
, NULL
);
7568 // indicate that we need to post a change for ...
7569 S_network_change_needed
|= change
;
7571 // cancel any running timer
7572 if (S_network_change_timer
!= NULL
) {
7573 dispatch_source_cancel(S_network_change_timer
);
7574 dispatch_release(S_network_change_timer
);
7575 S_network_change_timer
= NULL
;
7576 S_network_change_timeout
= FALSE
;
7579 // if needed, start new timer
7580 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
7581 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
7584 __network_change_queue());
7585 dispatch_source_set_event_handler(S_network_change_timer
, ^{
7586 os_activity_t activity_id
;
7588 activity_id
= os_activity_start("posting delayed network change",
7589 OS_ACTIVITY_FLAG_DEFAULT
);
7591 S_network_change_timeout
= TRUE
;
7592 post_network_change_when_ready();
7594 os_activity_end(activity_id
);
7596 dispatch_source_set_timer(S_network_change_timer
,
7597 dispatch_time(DISPATCH_TIME_NOW
,
7598 TRAILING_EDGE_TIMEOUT_NSEC
), // start
7599 DISPATCH_TIME_FOREVER
, // interval
7600 10 * NSEC_PER_MSEC
); // leeway
7601 dispatch_resume(S_network_change_timer
);
7604 post_network_change_when_ready();
7610 #pragma mark Process network (SCDynamicStore) changes
7613 IPMonitorProcessChanges(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
7614 CFArrayRef if_rank_changes
)
7617 uint32_t changes
= 0;
7618 nwi_state_t changes_state
= NULL
;
7619 boolean_t dns_changed
= FALSE
;
7620 boolean_t dnsinfo_changed
= FALSE
;
7621 boolean_t global_ipv4_changed
= FALSE
;
7622 boolean_t global_ipv6_changed
= FALSE
;
7626 CFMutableStringRef network_change_msg
= NULL
;
7628 nwi_state_t old_nwi_state
= NULL
;
7629 CFDictionaryRef old_primary_dns
= NULL
;
7630 CFDictionaryRef old_primary_proxy
= NULL
;
7631 #if !TARGET_OS_IPHONE
7632 CFDictionaryRef old_primary_smb
= NULL
;
7633 #endif // !TARGET_OS_IPHONE
7634 boolean_t proxies_changed
= FALSE
;
7635 boolean_t reachability_changed
= FALSE
;
7636 CFArrayRef service_order
;
7637 CFMutableArrayRef service_changes
= NULL
;
7638 CFDictionaryRef services_info
= NULL
;
7639 #if !TARGET_OS_IPHONE
7640 boolean_t smb_changed
= FALSE
;
7641 #endif // !TARGET_OS_IPHONE
7643 /* populate name/index cache */
7646 if (changed_keys
!= NULL
) {
7647 count
= CFArrayGetCount(changed_keys
);
7648 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7650 "changed keys %@ (%ld)", changed_keys
, count
);
7653 if (if_rank_changes
== NULL
&& count
== 0) {
7657 if (S_primary_dns
!= NULL
) {
7658 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
7659 if (old_primary_dns
!= NULL
) {
7660 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
7664 if (S_primary_proxies
!= NULL
) {
7666 = service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
7667 if (old_primary_proxy
!= NULL
) {
7668 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
7672 #if !TARGET_OS_IPHONE
7673 if (S_primary_smb
!= NULL
) {
7674 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
7675 if (old_primary_smb
!= NULL
) {
7676 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
7679 #endif // !TARGET_OS_IPHONE
7681 keyChangeListInit(&keys
);
7682 service_changes
= CFArrayCreateMutable(NULL
, 0,
7683 &kCFTypeArrayCallBacks
);
7685 for (i
= 0; i
< count
; i
++) {
7686 CFStringRef change
= CFArrayGetValueAtIndex(changed_keys
, i
);
7687 if (CFEqual(change
, S_setup_global_ipv4
)) {
7688 global_ipv4_changed
= TRUE
;
7689 global_ipv6_changed
= TRUE
;
7691 else if (CFEqual(change
, S_multicast_resolvers
)) {
7692 dnsinfo_changed
= TRUE
;
7694 else if (CFEqual(change
, S_private_resolvers
)) {
7695 dnsinfo_changed
= TRUE
;
7697 #if !TARGET_OS_IPHONE
7698 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
7699 dnsinfo_changed
= TRUE
;
7701 #endif /* !TARGET_OS_IPHONE */
7702 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
7703 CFStringRef serviceID
;
7705 serviceID
= parse_component(change
, S_state_service_prefix
);
7707 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
7708 CFRelease(serviceID
);
7711 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
7714 CFStringRef serviceID
= parse_component(change
,
7715 S_setup_service_prefix
);
7717 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
7718 CFRelease(serviceID
);
7721 for (j
= 0; j
< countof(transientInterfaceEntityNames
); j
++) {
7722 if (CFStringHasSuffix(change
,
7723 *transientInterfaceEntityNames
[j
])) {
7724 reachability_changed
= TRUE
;
7729 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
7730 reachability_changed
= TRUE
;
7735 /* determine which serviceIDs are impacted by the interface rank changes */
7736 if (if_rank_changes
!= NULL
) {
7737 n
= CFArrayGetCount(if_rank_changes
);
7738 for (i
= 0; i
< n
; i
++) {
7739 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
7741 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7742 my_log(LOG_DEBUG
, "Interface rank changed %@", ifname
);
7744 append_serviceIDs_for_interface(service_changes
, ifname
);
7748 /* grab a snapshot of everything we need */
7749 services_info
= services_info_copy(session
, service_changes
);
7750 service_order
= service_order_get(services_info
);
7751 if (service_order
!= NULL
) {
7752 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7753 my_log(LOG_DEBUG
, "service_order %@ ", service_order
);
7757 n
= CFArrayGetCount(service_changes
);
7758 for (i
= 0; i
< n
; i
++) {
7760 CFStringRef serviceID
;
7762 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
7763 changes
= service_changed(services_info
, serviceID
);
7764 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
7765 /* if __Service__ (e.g. PrimaryRank) changed */
7766 global_ipv4_changed
= TRUE
;
7767 global_ipv6_changed
= TRUE
;
7770 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
7771 global_ipv4_changed
= TRUE
;
7772 dnsinfo_changed
= TRUE
;
7773 proxies_changed
= TRUE
;
7775 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
7776 global_ipv6_changed
= TRUE
;
7777 dnsinfo_changed
= TRUE
;
7778 proxies_changed
= TRUE
;
7781 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
7782 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
7785 dnsinfo_changed
= TRUE
;
7787 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
7788 proxies_changed
= TRUE
;
7790 #if !TARGET_OS_IPHONE
7791 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
7792 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
7797 if ((changes
& (1 << kEntityTypeTransientStatus
)) != 0
7798 && (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
7799 || service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
)) {
7800 dnsinfo_changed
= TRUE
;
7804 /* ensure S_nwi_state can hold as many services as we have currently */
7805 n_services
= (int)CFDictionaryGetCount(S_service_state_dict
);
7806 old_nwi_state
= nwi_state_make_copy(S_nwi_state
);
7807 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
7809 if (global_ipv4_changed
) {
7810 if (S_ipv4_results
!= NULL
) {
7811 ElectionResultsRelease(S_ipv4_results
);
7814 = ElectionResultsCopy(AF_INET
, service_order
);
7815 ElectionResultsLog(LOG_INFO
, S_ipv4_results
, "IPv4");
7817 if (global_ipv6_changed
) {
7818 if (S_ipv6_results
!= NULL
) {
7819 ElectionResultsRelease(S_ipv6_results
);
7822 = ElectionResultsCopy(AF_INET6
, service_order
);
7823 ElectionResultsLog(LOG_INFO
, S_ipv6_results
, "IPv6");
7825 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
7826 CFStringRef new_primary
;
7827 CFStringRef new_primary_dns
= NULL
;
7828 CFStringRef new_primary_proxies
= NULL
;
7829 #if !TARGET_OS_IPHONE
7830 CFStringRef new_primary_smb
= NULL
;
7831 #endif /* !TARGET_OS_IPHONE */
7832 RouteListUnion new_routelist
;
7834 if (S_nwi_state
!= NULL
) {
7835 nwi_state_clear(S_nwi_state
, AF_INET
);
7836 nwi_state_clear(S_nwi_state
, AF_INET6
);
7840 my_log(LOG_DEBUG
, "electing IPv4 primary");
7841 new_routelist
.ptr
= NULL
;
7842 new_primary
= ElectionResultsCopyPrimary(S_ipv4_results
,
7844 S_nwi_state
, AF_INET
,
7845 &new_routelist
.common
,
7847 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
7848 update_ipv4(S_primary_ipv4
, new_routelist
.v4
, &keys
);
7849 my_CFRelease(&new_primary
);
7852 my_log(LOG_DEBUG
, "electing IPv6 primary");
7853 new_routelist
.ptr
= NULL
;
7854 new_primary
= ElectionResultsCopyPrimary(S_ipv6_results
,
7856 S_nwi_state
, AF_INET6
,
7857 &new_routelist
.common
,
7859 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
7860 update_ipv6(S_primary_ipv6
, new_routelist
.v6
, &keys
);
7861 my_CFRelease(&new_primary
);
7863 nwi_state_finalize(S_nwi_state
);
7865 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
7866 /* decide between IPv4 and IPv6 */
7867 if (rank_service_entity(S_ipv4_service_rank_dict
,
7868 S_primary_ipv4
, kSCEntNetDNS
)
7869 <= rank_service_entity(S_ipv6_service_rank_dict
,
7870 S_primary_ipv6
, kSCEntNetDNS
)) {
7871 new_primary_dns
= S_primary_ipv4
;
7874 new_primary_dns
= S_primary_ipv6
;
7876 if (rank_service_entity(S_ipv4_service_rank_dict
,
7877 S_primary_ipv4
, kSCEntNetProxies
)
7878 <= rank_service_entity(S_ipv6_service_rank_dict
,
7879 S_primary_ipv6
, kSCEntNetProxies
)) {
7880 new_primary_proxies
= S_primary_ipv4
;
7883 new_primary_proxies
= S_primary_ipv6
;
7885 #if !TARGET_OS_IPHONE
7886 if (rank_service_entity(S_ipv4_service_rank_dict
,
7887 S_primary_ipv4
, kSCEntNetSMB
)
7888 <= rank_service_entity(S_ipv6_service_rank_dict
,
7889 S_primary_ipv6
, kSCEntNetSMB
)) {
7890 new_primary_smb
= S_primary_ipv4
;
7893 new_primary_smb
= S_primary_ipv6
;
7895 #endif /* !TARGET_OS_IPHONE */
7898 else if (S_primary_ipv6
!= NULL
) {
7899 new_primary_dns
= S_primary_ipv6
;
7900 new_primary_proxies
= S_primary_ipv6
;
7901 #if !TARGET_OS_IPHONE
7902 new_primary_smb
= S_primary_ipv6
;
7903 #endif /* !TARGET_OS_IPHONE */
7905 else if (S_primary_ipv4
!= NULL
) {
7906 new_primary_dns
= S_primary_ipv4
;
7907 new_primary_proxies
= S_primary_ipv4
;
7908 #if !TARGET_OS_IPHONE
7909 new_primary_smb
= S_primary_ipv4
;
7910 #endif /* !TARGET_OS_IPHONE */
7913 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
7915 dnsinfo_changed
= TRUE
;
7917 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
,
7919 proxies_changed
= TRUE
;
7921 #if !TARGET_OS_IPHONE
7922 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
7925 #endif /* !TARGET_OS_IPHONE */
7928 if (!proxies_changed
&& dnsinfo_changed
7929 && ((G_supplemental_proxies_follow_dns
!= NULL
)
7930 && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
7931 proxies_changed
= TRUE
;
7934 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
7936 if (global_ipv4_changed
|| global_ipv6_changed
7937 || dnsinfo_changed
|| reachability_changed
) {
7938 if (S_nwi_state
!= NULL
) {
7939 S_nwi_state
->generation_count
= mach_absolute_time();
7940 if (global_ipv4_changed
|| global_ipv6_changed
7941 || reachability_changed
) {
7942 SCNetworkReachabilityFlags reach_flags_v4
= 0;
7943 SCNetworkReachabilityFlags reach_flags_v6
= 0;
7945 GetReachabilityFlagsFromTransientServices(services_info
,
7949 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
,
7953 /* Update the per-interface generation count */
7954 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
,
7958 if (update_nwi(S_nwi_state
)) {
7959 changes
|= NETWORK_CHANGE_NET
;
7962 * the DNS configuration includes per-resolver configuration
7963 * reachability flags that are based on the nwi state. Let's
7964 * make sure that we check for changes
7966 dnsinfo_changed
= TRUE
;
7970 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
7971 changes
|= NETWORK_CHANGE_DNS
;
7972 dnsinfo_changed
= TRUE
;
7974 dns_changed
= FALSE
;
7977 if (dnsinfo_changed
) {
7978 if (update_dnsinfo(services_info
, S_primary_dns
,
7979 &keys
, service_order
)) {
7980 changes
|= NETWORK_CHANGE_DNS
;
7982 dnsinfo_changed
= FALSE
;
7985 if (proxies_changed
) {
7986 // if proxy change OR supplemental Proxies follow supplemental DNS
7987 if (update_proxies(services_info
, S_primary_proxies
,
7988 &keys
, service_order
)) {
7989 changes
|= NETWORK_CHANGE_PROXY
;
7991 proxies_changed
= FALSE
;
7994 #if !TARGET_OS_IPHONE
7996 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
7997 changes
|= NETWORK_CHANGE_SMB
;
7999 smb_changed
= FALSE
;
8002 #endif /* !TARGET_OS_IPHONE */
8003 my_CFRelease(&service_changes
);
8004 my_CFRelease(&services_info
);
8007 network_change_msg
= CFStringCreateMutable(NULL
, 0);
8008 process_nwi_changes(network_change_msg
,
8015 #if !TARGET_OS_IPHONE
8018 #else // !TARGET_OS_IPHONE
8019 FALSE
, // smb_changed
8020 NULL
// old_primary_smb
8021 #endif // !TARGET_OS_IPHONE
8025 keyChangeListApplyToStore(&keys
, session
);
8026 my_CFRelease(&old_primary_dns
);
8027 my_CFRelease(&old_primary_proxy
);
8028 #if !TARGET_OS_IPHONE
8029 my_CFRelease(&old_primary_smb
);
8030 #endif // !TARGET_OS_IPHONE
8033 dispatch_async(__network_change_queue(), ^{
8034 post_network_change(changes
);
8038 if ((network_change_msg
!= NULL
)
8039 && (CFStringGetLength(network_change_msg
) != 0)) {
8040 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
8041 } else if (keyChangeListActive(&keys
)) {
8042 my_log(LOG_NOTICE
, "network changed");
8044 my_log(LOG_INFO
, "network event w/no changes");
8047 my_CFRelease(&network_change_msg
);
8049 if (changes_state
!= NULL
) {
8050 nwi_state_free(changes_state
);
8052 if (old_nwi_state
!= NULL
) {
8053 nwi_state_free(old_nwi_state
);
8055 keyChangeListFree(&keys
);
8057 /* release the name/index cache */
8058 my_if_freenameindex();
8064 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
8067 IPMonitorProcessChanges(session
, changed_keys
, NULL
);
8074 #if !TARGET_OS_IPHONE
8075 const _scprefs_observer_type type
= scprefs_observer_type_mcx
;
8077 const _scprefs_observer_type type
= scprefs_observer_type_global
;
8079 static dispatch_queue_t proxy_cb_queue
;
8081 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
8082 _scprefs_observer_watch(type
,
8083 "com.apple.SystemConfiguration.plist",
8086 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
8087 notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
8088 my_log(LOG_INFO
, "Notifying:\n%@",
8089 S_state_global_proxies
);
8094 #include "IPMonitorControlPrefs.h"
8096 __private_extern__ SCLoggerRef
8099 return (S_IPMonitor_logger
);
8103 prefs_changed(__unused SCPreferencesRef prefs
)
8105 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
8106 S_IPMonitor_debug
= kDebugFlagDefault
;
8107 S_IPMonitor_verbose
= TRUE
;
8108 SCLoggerSetFlags(S_IPMonitor_logger
, kSCLoggerFlagsFile
| kSCLoggerFlagsDefault
);
8109 my_log(LOG_DEBUG
, "Setting logging verbose mode on");
8111 my_log(LOG_DEBUG
, "Setting logging verbose mode off");
8112 S_IPMonitor_debug
= 0;
8113 S_IPMonitor_verbose
= FALSE
;
8114 SCLoggerSetFlags(S_IPMonitor_logger
, kSCLoggerFlagsDefault
);
8119 #define LOGGER_ID CFSTR("com.apple.networking.IPMonitor")
8123 S_IPMonitor_logger
= SCLoggerCreate(LOGGER_ID
);
8129 #if !TARGET_IPHONE_SIMULATOR
8140 struct rt_msghdr
* rtm
;
8141 struct sockaddr_in
*sin
;
8147 mib
[4] = NET_RT_FLAGS
;
8148 mib
[5] = RTF_STATIC
| RTF_DYNAMIC
;
8149 for (i
= 0; i
< 3; i
++) {
8150 if (sysctl(mib
, N_MIB
, NULL
, &needed
, NULL
, 0) < 0) {
8153 if ((buf
= malloc(needed
)) == NULL
) {
8156 if (sysctl(mib
, N_MIB
, buf
, &needed
, NULL
, 0) >= 0) {
8166 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
8169 /* ALIGN: assume kernel provides necessary alignment */
8170 rtm
= (struct rt_msghdr
*)(void *)next
;
8171 sin
= (struct sockaddr_in
*)(rtm
+ 1);
8173 addr
= ntohl(sin
->sin_addr
.s_addr
);
8174 if (IN_LOOPBACK(addr
)) {
8176 "flush_routes: ignoring loopback route");
8179 if (IN_LOCAL_GROUP(addr
)) {
8181 "flush_routes: ignoring multicast route");
8184 rtm
->rtm_type
= RTM_DELETE
;
8185 rtm
->rtm_seq
= ++rtm_seq
;
8186 if (write(s
, rtm
, rtm
->rtm_msglen
) < 0) {
8188 "flush_routes: removing route for "
8189 IP_FORMAT
" failed: %s",
8190 IP_LIST(&sin
->sin_addr
),
8195 "flush_routes: removed route for " IP_FORMAT
,
8196 IP_LIST(&sin
->sin_addr
));
8204 flush_inet_routes(void)
8208 s
= open_routing_socket();
8215 #else /* !TARGET_IPHONE_SIMULATOR */
8218 flush_inet_routes(void)
8222 #endif /* !TARGET_IPHONE_SIMULATOR */
8229 CFMutableArrayRef keys
= NULL
;
8230 CFStringRef pattern
;
8231 CFMutableArrayRef patterns
= NULL
;
8232 CFRunLoopSourceRef rls
= NULL
;
8234 if (S_is_network_boot() != 0) {
8239 flush_inet_routes();
8242 if (S_is_scoped_routing_enabled() != 0) {
8243 S_scopedroute
= TRUE
;
8246 if (S_is_scoped_v6_routing_enabled() != 0) {
8247 S_scopedroute_v6
= TRUE
;
8250 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
8251 IPMonitorNotify
, NULL
);
8252 if (S_session
== NULL
) {
8254 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8255 SCErrorString(SCError()));
8259 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8260 kSCDynamicStoreDomainState
,
8263 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8264 kSCDynamicStoreDomainState
,
8267 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8268 kSCDynamicStoreDomainState
,
8270 S_state_global_proxies
8271 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8272 kSCDynamicStoreDomainState
,
8274 #if !TARGET_OS_IPHONE
8276 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8277 kSCDynamicStoreDomainState
,
8279 #endif /* !TARGET_OS_IPHONE */
8281 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8282 kSCDynamicStoreDomainSetup
,
8284 S_state_service_prefix
8285 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8286 kSCDynamicStoreDomainState
,
8289 S_setup_service_prefix
8290 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8291 kSCDynamicStoreDomainSetup
,
8294 S_service_state_dict
8295 = CFDictionaryCreateMutable(NULL
, 0,
8296 &kCFTypeDictionaryKeyCallBacks
,
8297 &kCFTypeDictionaryValueCallBacks
);
8299 S_ipv4_service_rank_dict
8300 = CFDictionaryCreateMutable(NULL
, 0,
8301 &kCFTypeDictionaryKeyCallBacks
,
8302 &kCFTypeDictionaryValueCallBacks
);
8304 S_ipv6_service_rank_dict
8305 = CFDictionaryCreateMutable(NULL
, 0,
8306 &kCFTypeDictionaryKeyCallBacks
,
8307 &kCFTypeDictionaryValueCallBacks
);
8309 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8310 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8312 /* register for State: and Setup: per-service notifications */
8313 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
8315 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
8316 CFArrayAppendValue(patterns
, pattern
);
8319 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
8320 CFArrayAppendValue(patterns
, pattern
);
8323 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
8324 CFArrayAppendValue(patterns
, pattern
);
8327 /* register for State: per-service PPP/VPN/IPSec status notifications */
8328 add_transient_status_keys(kSCCompAnyRegex
, patterns
);
8330 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
8331 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
8333 /* add notifier for multicast DNS configuration (Bonjour/.local) */
8334 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8335 kSCDynamicStoreDomainState
,
8337 CFSTR(kDNSServiceCompMulticastDNS
));
8338 CFArrayAppendValue(keys
, S_multicast_resolvers
);
8340 /* add notifier for private DNS configuration (Back to My Mac) */
8341 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8342 kSCDynamicStoreDomainState
,
8344 CFSTR(kDNSServiceCompPrivateDNS
));
8345 CFArrayAppendValue(keys
, S_private_resolvers
);
8347 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
8349 "SCDynamicStoreSetNotificationKeys() failed: %s",
8350 SCErrorString(SCError()));
8354 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
8357 "SCDynamicStoreCreateRunLoopSource() failed: %s",
8358 SCErrorString(SCError()));
8362 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
8365 /* initialize dns configuration */
8366 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
8367 #if !TARGET_OS_IPHONE
8369 #endif /* !TARGET_OS_IPHONE */
8370 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
8372 #if !TARGET_OS_IPHONE
8373 /* initialize SMB configuration */
8374 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
8375 #endif /* !TARGET_OS_IPHONE */
8380 my_CFRelease(&keys
);
8381 my_CFRelease(&patterns
);
8389 /* initialize multicast route */
8390 update_ipv4(NULL
, NULL
, NULL
);
8395 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
8399 boolean_t ret
= def
;
8401 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
8403 ret
= CFBooleanGetValue(b
);
8408 #if !TARGET_IPHONE_SIMULATOR
8409 #include "IPMonitorControlServer.h"
8412 InterfaceRankChanged(void * info
)
8414 os_activity_t activity_id
;
8415 CFDictionaryRef assertions
= NULL
;
8418 activity_id
= os_activity_start("processing IPMonitor [rank] change",
8419 OS_ACTIVITY_FLAG_DEFAULT
);
8421 changes
= IPMonitorControlServerCopyInterfaceRankInformation(&assertions
);
8422 if (S_if_rank_dict
!= NULL
) {
8423 CFRelease(S_if_rank_dict
);
8425 S_if_rank_dict
= assertions
;
8426 if (changes
!= NULL
) {
8427 IPMonitorProcessChanges(S_session
, NULL
, changes
);
8431 os_activity_end(activity_id
);
8437 StartIPMonitorControlServer(void)
8439 CFRunLoopSourceContext context
;
8440 CFRunLoopSourceRef rls
;
8442 bzero(&context
, sizeof(context
));
8443 context
.perform
= InterfaceRankChanged
;
8444 rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
8445 if (IPMonitorControlServerStart(CFRunLoopGetCurrent(), rls
,
8446 &S_bundle_logging_verbose
) == FALSE
) {
8447 my_log(LOG_ERR
, "IPMonitorControlServerStart failed");
8450 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
8451 kCFRunLoopDefaultMode
);
8457 #endif /* !TARGET_IPHONE_SIMULATOR */
8461 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
8463 CFDictionaryRef info_dict
;
8465 info_dict
= CFBundleGetInfoDictionary(bundle
);
8467 if (info_dict
!= NULL
) {
8469 = S_get_plist_boolean(info_dict
,
8470 CFSTR("AppendStateArrayToSetupArray"),
8473 if (bundleVerbose
) {
8474 S_IPMonitor_debug
= kDebugFlagDefault
;
8475 S_bundle_logging_verbose
= TRUE
;
8476 S_IPMonitor_verbose
= TRUE
;
8481 /* register to receive changes to verbose and read the initial setting */
8482 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
8483 prefs_changed(NULL
);
8485 load_DNSConfiguration(bundle
, // bundle
8486 S_IPMonitor_logger
, // SCLogger
8487 ^(Boolean inSync
) { // syncHandler
8488 dispatch_async(__network_change_queue(), ^{
8489 S_dnsinfo_synced
= inSync
;
8492 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
8493 // all of the DNS service ack's should result
8494 // in a [new] network change being posted
8495 post_network_change(NETWORK_CHANGE_DNS
);
8497 post_network_change_when_ready();
8502 load_NetworkInformation(bundle
, // bundle
8503 S_IPMonitor_logger
, // SCLogger
8504 ^(Boolean inSync
) { // syncHandler
8505 dispatch_async(__network_change_queue(), ^{
8506 S_nwi_synced
= inSync
;
8507 post_network_change_when_ready();
8510 #if !TARGET_IPHONE_SIMULATOR
8511 StartIPMonitorControlServer();
8512 #endif /* !TARGET_OS_IPHONE */
8514 dns_configuration_init(bundle
);
8516 proxy_configuration_init(bundle
);
8520 #if !TARGET_OS_IPHONE
8521 if (S_session
!= NULL
) {
8522 dns_configuration_monitor(S_session
, IPMonitorNotify
);
8524 #endif /* !TARGET_OS_IPHONE */
8526 #if !TARGET_IPHONE_SIMULATOR
8527 load_hostname(TRUE
);
8528 #endif /* !TARGET_IPHONE_SIMULATOR */
8530 #if !TARGET_OS_IPHONE
8531 load_smb_configuration(TRUE
);
8532 #endif /* !TARGET_OS_IPHONE */
8539 #pragma mark Standalone test code
8542 #ifdef TEST_IPMONITOR
8544 #include "dns-configuration.c"
8546 #if !TARGET_IPHONE_SIMULATOR
8547 #include "set-hostname.c"
8548 #endif /* !TARGET_IPHONE_SIMULATOR */
8551 main(int argc
, char **argv
)
8555 S_IPMonitor_debug
= kDebugFlag1
;
8557 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
8560 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
8562 S_IPMonitor_debug
= kDebugFlag1
;
8568 #endif /* TEST_IPMONITOR */
8570 #ifdef TEST_ROUTELIST
8571 #include "dns-configuration.c"
8572 #include "set-hostname.c"
8577 const char * gateway
;
8578 const char * ifname
;
8583 #ifdef TEST_IPV4_ROUTELIST
8589 const char * router
;
8590 const char * ifname
;
8592 const CFStringRef
* primary_rank
;
8593 struct route
* additional_routes
;
8594 int additional_routes_count
;
8595 struct route
* excluded_routes
;
8596 int excluded_routes_count
;
8597 } IPv4ServiceContents
;
8599 typedef const IPv4ServiceContents
* IPv4ServiceContentsRef
;
8601 struct route loop_routelist
[] = {
8602 { "1.1.1.1", 32, "1.1.1.2", NULL
},
8603 { "1.1.1.2", 32, "1.1.1.3", NULL
},
8604 { "1.1.1.3", 32, "1.1.1.4", NULL
},
8605 { "1.1.1.4", 32, "1.1.1.5", NULL
},
8606 { "1.1.1.5", 32, "1.1.1.6", NULL
},
8607 { "1.1.1.6", 32, "1.1.1.7", NULL
},
8608 { "1.1.1.7", 32, "1.1.1.8", NULL
},
8609 { "1.1.1.8", 32, "1.1.1.9", NULL
},
8610 { "1.1.1.9", 32, "1.1.1.10", NULL
},
8611 { "1.1.1.10", 32, "1.1.1.11", NULL
},
8612 { "1.1.1.11", 32, "1.1.1.1", NULL
},
8615 struct route vpn_routelist
[] = {
8616 { "10.1.3.0", 24, "17.153.46.24", NULL
},
8617 { "10.1.4.0", 24, "17.153.46.24", NULL
},
8618 { "10.1.5.0", 24, "17.153.46.24", NULL
},
8619 { "10.1.6.0", 24, "17.153.46.24", NULL
},
8620 { "10.1.7.0", 24, "17.153.46.24", NULL
},
8621 { "10.16.0.0", 12, "17.153.46.24", NULL
},
8622 { "10.45.0.0", 16, "17.153.46.24", NULL
},
8623 { "10.53.0.0", 16, "17.153.46.24", NULL
},
8624 { "10.70.0.0", 15, "17.153.46.24", NULL
},
8625 { "10.74.0.0", 15, "17.153.46.24", NULL
},
8626 { "10.90.0.0", 15, "17.153.46.24", NULL
},
8627 { "10.91.0.0", 16, "17.153.46.24", NULL
},
8628 { "10.100.0.0", 16, "17.153.46.24", NULL
},
8629 { "10.113.0.0", 16, "17.153.46.24", NULL
},
8630 { "10.128.0.0", 9, "17.153.46.24", NULL
},
8631 { "17.0.0.0", 9, "17.153.46.24", NULL
},
8632 { "17.34.0.0", 16, "17.153.46.24", NULL
},
8633 { "17.112.156.53", 32, "17.153.46.24", NULL
},
8634 { "17.128.0.0", 10, "17.153.46.24", NULL
},
8635 { "17.149.0.121", 32, "17.153.46.24", NULL
},
8636 { "17.149.7.200", 32, "17.153.46.24", NULL
},
8637 { "17.153.46.24", 32, "17.153.46.24", NULL
},
8638 { "17.192.0.0", 12, "17.153.46.24", NULL
},
8639 { "17.208.0.0", 15, "17.153.46.24", NULL
},
8640 { "17.211.0.0", 16, "17.153.46.24", NULL
},
8641 { "17.212.0.0", 14, "17.153.46.24", NULL
},
8642 { "17.216.0.0", 13, "17.153.46.24", NULL
},
8643 { "17.224.0.0", 12, "17.153.46.24", NULL
},
8644 { "17.240.0.0", 16, "17.153.46.24", NULL
},
8645 { "17.241.0.0", 16, "17.153.46.24", NULL
},
8646 { "17.248.0.0", 14, "17.153.46.24", NULL
},
8647 { "17.251.104.200", 32, "17.153.46.24", NULL
},
8648 { "17.252.0.0", 16, "17.153.46.24", NULL
},
8649 { "17.253.0.0", 16, "17.153.46.24", NULL
},
8650 { "17.254.0.0", 16, "17.153.46.24", NULL
},
8651 { "17.255.0.0", 16, "17.153.46.24", NULL
},
8652 { "151.193.141.0", 27, "17.153.46.24", NULL
},
8653 { "172.16.2.0", 24, "17.153.46.24", NULL
},
8654 { "192.35.50.0", 24, "17.153.46.24", NULL
},
8655 { "204.179.20.0", 24, "17.153.46.24", NULL
},
8656 { "206.112.116.0", 24, "17.153.46.24", NULL
},
8659 struct route vpn_routelist_ext
[] = {
8660 { "17.151.63.82", 32, "10.0.0.1", "en0" },
8661 { "17.151.63.81", 32, "17.151.63.81", "en0" },
8662 { "17.151.63.80", 32, NULL
, NULL
},
8663 { "17.1.0.0", 16, NULL
, NULL
},
8664 { "17.2.0.0", 24, NULL
, NULL
},
8665 { "10.0.0.0", 24, NULL
, NULL
},
8669 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
8671 const IPv4ServiceContents en0_10
= {
8672 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, NULL
, NULL
, 0, NULL
, 0
8675 const IPv4ServiceContents en0_15
= {
8676 "10.0.0.19", 24, NULL
, "10.0.0.1", "en0", 15, NULL
, NULL
, 0, NULL
, 0
8679 const IPv4ServiceContents en0_30
= {
8680 "10.0.0.11", 24, NULL
, "10.0.0.1", "en0", 30, NULL
, NULL
, 0, NULL
, 0
8683 const IPv4ServiceContents en0_40
= {
8684 "10.0.0.12", 24, NULL
, "10.0.0.1", "en0", 40, NULL
, NULL
, 0, NULL
, 0
8687 const IPv4ServiceContents en0_50
= {
8688 "10.0.0.13", 24, NULL
, "10.0.0.1", "en0", 50, NULL
, NULL
, 0, NULL
, 0
8691 const IPv4ServiceContents en0_110
= {
8692 "192.168.2.10", 24, NULL
, "192.168.2.1", "en0", 110, NULL
, NULL
, 0, NULL
, 0
8695 const IPv4ServiceContents en0_1
= {
8696 "17.202.40.191", 22, NULL
, "17.202.20.1", "en0", 1, NULL
, NULL
, 0, NULL
, 0
8699 const IPv4ServiceContents en1_20
= {
8700 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, NULL
, NULL
, 0, NULL
, 0
8703 const IPv4ServiceContents en1_2
= {
8704 "17.202.42.24", 22, NULL
, "17.202.20.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
8707 const IPv4ServiceContents en1_125
= {
8708 "192.168.2.20", 24, NULL
, "192.168.2.1", "en1", 125, NULL
, NULL
, 0, NULL
, 0
8711 const IPv4ServiceContents fw0_25
= {
8712 "192.168.2.30", 24, NULL
, "192.168.2.1", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
8715 const IPv4ServiceContents fw0_21
= {
8716 "192.168.3.30", 24, NULL
, "192.168.3.1", "fw0", 21, NULL
, NULL
, 0, NULL
, 0
8719 const IPv4ServiceContents ppp0_0_1
= {
8720 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
, NULL
, 0, NULL
, 0
8723 const IPv4ServiceContents utun0
= {
8724 "17.153.46.24", -1, "17.153.46.24", "17.153.46.24", "utun0", 20, NULL
, vpn_routelist
, countof(vpn_routelist
), vpn_routelist_ext
, countof(vpn_routelist_ext
)
8727 const IPv4ServiceContents en0_test6
= {
8728 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 2, NULL
, NULL
, 0, NULL
, 0
8731 const IPv4ServiceContents en1_test6
= {
8732 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 3, NULL
, NULL
, 0, NULL
, 0
8735 const IPv4ServiceContents en2_test6
= {
8736 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
8739 const IPv4ServiceContents en0_test7
= {
8740 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 3, NULL
, NULL
, 0, NULL
, 0
8743 const IPv4ServiceContents en1_test7
= {
8744 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
8747 const IPv4ServiceContents en2_test7
= {
8748 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
8751 const IPv4ServiceContents fw0_test6_and_7
= {
8752 "169.254.11.33", 16, NULL
, NULL
, "fw0", 0x0ffffff, NULL
, NULL
, 0, NULL
, 0
8755 const IPv4ServiceContents en0_10_last
= {
8756 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
, NULL
, 0, NULL
, 0
8759 const IPv4ServiceContents en0_10_never
= {
8760 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
8763 const IPv4ServiceContents en1_20_first
= {
8764 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
, NULL
, 0, NULL
, 0
8767 const IPv4ServiceContents en1_20_never
= {
8768 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
8771 const IPv4ServiceContents en1_20_other_never
= {
8772 "192.168.2.50", 24, NULL
, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
8775 const IPv4ServiceContents en0_linklocal
= {
8776 "169.254.22.44", 16, NULL
, NULL
, "en0", 0xfffff, NULL
, NULL
, 0, NULL
, 0
8779 const IPv4ServiceContents en0_route_loop
= {
8780 "192.168.130.16", 24, NULL
, "192.168.130.1", "en0", 2, NULL
, loop_routelist
, countof(loop_routelist
), NULL
, 0
8785 IPv4ServiceContentsRef test
[];
8786 } IPv4RouteTest
, * IPv4RouteTestRef
;
8788 static IPv4RouteTest test1
= {
8802 static IPv4RouteTest test2
= {
8815 static IPv4RouteTest test3
= {
8832 static IPv4RouteTest test4
= {
8844 static IPv4RouteTest test5
= {
8857 static IPv4RouteTest test6
= {
8868 static IPv4RouteTest test7
= {
8879 static IPv4RouteTest test8
= {
8888 static IPv4RouteTest test9
= {
8898 static IPv4RouteTest test10
= {
8908 static IPv4RouteTest test11
= {
8918 static IPv4RouteTest test12
= {
8927 static IPv4RouteTest test13
= {
8936 static IPv4RouteTest test14
= {
8944 static IPv4RouteTest test15
= {
8952 static IPv4RouteTest test16
= {
8961 static IPv4RouteTest test17
= {
8965 &en1_20_other_never
,
8970 static IPv4RouteTest test18
= {
8978 static IPv4RouteTestRef ipv4_tests
[] = {
9001 ipv4_prefix_length_is_valid(int prefix_length
)
9003 if (prefix_length
< 0 || prefix_length
> IPV4_ROUTE_ALL_BITS_SET
) {
9010 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9013 CFStringRef prop_val
;
9018 prop_val
= CFStringCreateWithCString(NULL
,
9020 kCFStringEncodingASCII
);
9021 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9022 CFRelease(prop_val
);
9027 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9031 CFStringRef prop_val
;
9036 prop_val
= CFStringCreateWithCString(NULL
,
9038 kCFStringEncodingASCII
);
9039 array
= CFArrayCreate(NULL
,
9040 (const void **)&prop_val
, 1,
9041 &kCFTypeArrayCallBacks
);
9042 CFRelease(prop_val
);
9043 CFDictionarySetValue(dict
, prop_name
, array
);
9049 dict_add_ip(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9054 str
= my_CFStringCreateWithInAddr(ip
);
9055 CFDictionarySetValue(dict
, prop_name
, str
);
9061 dict_add_ip_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9067 str
= my_CFStringCreateWithInAddr(ip
);
9068 array
= CFArrayCreate(NULL
,
9069 (const void **)&str
, 1,
9070 &kCFTypeArrayCallBacks
);
9072 CFDictionarySetValue(dict
, prop_name
, array
);
9078 dict_insert_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9079 struct route
* routes
, int routes_count
)
9082 CFMutableArrayRef route_list
;
9083 struct route
* scan
;
9085 if (routes
== NULL
|| routes_count
== 0) {
9088 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9089 &kCFTypeArrayCallBacks
);
9090 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9091 struct in_addr mask
;
9092 CFMutableDictionaryRef route_dict
;
9095 = CFDictionaryCreateMutable(NULL
, 0,
9096 &kCFTypeDictionaryKeyCallBacks
,
9097 &kCFTypeDictionaryValueCallBacks
);
9098 dict_add_string(route_dict
, kSCPropNetIPv4RouteDestinationAddress
,
9100 if (ipv4_prefix_length_is_valid(scan
->prefix_length
)) {
9101 mask
.s_addr
= htonl(prefix_to_mask32(scan
->prefix_length
));
9102 dict_add_ip(route_dict
, kSCPropNetIPv4RouteSubnetMask
, mask
);
9104 dict_add_string(route_dict
, kSCPropNetIPv4RouteGatewayAddress
,
9106 dict_add_string(route_dict
, kSCPropNetIPv4RouteInterfaceName
,
9108 CFArrayAppendValue(route_list
, route_dict
);
9109 CFRelease(route_dict
);
9111 CFDictionarySetValue(dict
, prop_name
, route_list
);
9112 CFRelease(route_list
);
9116 static CFDictionaryRef
9117 make_IPv4_dict(IPv4ServiceContentsRef t
)
9119 CFMutableDictionaryRef dict
;
9121 dict
= CFDictionaryCreateMutable(NULL
, 0,
9122 &kCFTypeDictionaryKeyCallBacks
,
9123 &kCFTypeDictionaryValueCallBacks
);
9124 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
9125 if (ipv4_prefix_length_is_valid(t
->prefix_length
)) {
9126 struct in_addr mask
;
9128 mask
.s_addr
= htonl(prefix_to_mask32(t
->prefix_length
));
9129 dict_add_ip_as_array(dict
, kSCPropNetIPv4SubnetMasks
, mask
);
9131 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
9132 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
9133 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9134 dict_add_string(dict
, kSCPropConfirmedInterfaceName
, t
->ifname
);
9135 dict_insert_routes(dict
, kSCPropNetIPv4AdditionalRoutes
,
9136 t
->additional_routes
, t
->additional_routes_count
);
9137 dict_insert_routes(dict
, kSCPropNetIPv4ExcludedRoutes
,
9138 t
->excluded_routes
, t
->excluded_routes_count
);
9143 kDirectionForwards
= 0,
9144 kDirectionBackwards
= 1
9148 kLogRouteDisabled
= 0,
9149 kLogRouteEnabled
= 1
9152 static IPv4RouteListRef
9153 make_IPv4RouteList_for_test(IPv4RouteListRef list
,
9154 IPv4ServiceContentsRef test
,
9157 CFDictionaryRef dict
;
9160 Rank rank_assertion
= kRankAssertionDefault
;
9161 CFNumberRef rank_assertion_cf
= NULL
;
9162 Boolean rank_assertion_is_set
= FALSE
;
9163 IPv4RouteListRef ret
= NULL
;
9164 IPV4_ROUTES_BUF_DECL(routes
);
9166 dict
= make_IPv4_dict(test
);
9168 fprintf(stderr
, "make_IPv4_dict failed\n");
9171 if (test
->primary_rank
!= NULL
) {
9173 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9174 &rank_assertion_is_set
);
9175 if (rank_assertion_is_set
) {
9177 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9180 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
9182 my_CFRelease(&rank_assertion_cf
);
9184 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
9188 if (rank_assertion
== kRankAssertionScoped
) {
9189 rank_assertion
= kRankAssertionNever
;
9191 rank
= RankMake(test
->rank
, rank_assertion
);
9192 if (log_it
== kLogRouteEnabled
9193 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9196 descr
= IPv4RouteListCopyDescription(r
);
9197 SCPrint(TRUE
, stdout
, CFSTR("Adding %@"), descr
);
9200 ret
= IPv4RouteListAddRouteList(list
, 1, r
, rank
);
9208 static IPv4RouteListRef
9209 make_IPv4RouteList(IPv4ServiceContentsRef
* test
, Direction direction
,
9212 IPv4RouteListRef ret
= NULL
;
9213 IPv4ServiceContentsRef
* scan
;
9215 switch (direction
) {
9216 case kDirectionBackwards
:
9217 for (scan
= test
; *scan
!= NULL
; scan
++) {
9218 /* find the end of the list */
9220 for (scan
--; scan
>= test
; scan
--) {
9221 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9225 case kDirectionForwards
:
9226 for (scan
= test
; *scan
!= NULL
; scan
++) {
9227 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
9231 IPv4RouteListFinalize(ret
);
9235 #define EMPHASIS_CHARS "================="
9238 * Function: routelist_build_test
9240 * Runs through the given set of routes first in the forward direction,
9241 * then again backwards. We should end up with exactly the same set of
9242 * routes at the end.
9245 routelist_build_test(IPv4RouteTestRef test
)
9248 boolean_t ret
= FALSE
;
9249 IPv4RouteListRef routes1
;
9250 IPv4RouteListRef routes2
;
9252 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
9253 EMPHASIS_CHARS
"\n",
9256 routes1
= make_IPv4RouteList(test
->test
, kDirectionForwards
,
9258 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9259 if (routes1
!= NULL
) {
9260 descr
= IPv4RouteListCopyDescription(routes1
);
9261 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9265 routes2
= make_IPv4RouteList(test
->test
, kDirectionBackwards
,
9267 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9268 if (routes2
!= NULL
) {
9269 descr
= IPv4RouteListCopyDescription(routes2
);
9270 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9274 if ((routes1
!= NULL
&& routes2
== NULL
)
9275 || (routes1
== NULL
&& routes2
!= NULL
)) {
9276 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
9277 (routes1
!= NULL
) ? "not " : "",
9278 (routes2
!= NULL
) ? "not " : "");
9280 else if (routes1
!= NULL
&& routes2
!= NULL
) {
9281 /* check if they are different */
9282 if (routes1
->count
!= routes2
->count
) {
9283 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
9284 routes1
->count
, routes2
->count
);
9286 else if (bcmp(routes1
, routes2
,
9287 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
9288 fprintf(stderr
, "routes1 and routes2 are different\n");
9291 printf("routes1 and routes2 are the same\n");
9295 if (routes1
!= NULL
) {
9298 if (routes2
!= NULL
) {
9301 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
9302 EMPHASIS_CHARS
"\n",
9303 test
->name
, ret
? "PASSED" : "FAILED");
9308 apply_test(IPv4RouteTestRef old_test
, IPv4RouteTestRef new_test
)
9310 IPv4RouteListRef new_routes
;
9311 IPv4RouteListRef old_routes
;
9313 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
9314 EMPHASIS_CHARS
"\n",
9315 old_test
->name
, new_test
->name
);
9317 old_routes
= make_IPv4RouteList(old_test
->test
, kDirectionForwards
,
9319 new_routes
= make_IPv4RouteList(new_test
->test
, kDirectionForwards
,
9321 if (old_routes
== NULL
) {
9322 printf("No Old Routes\n");
9325 printf("Old routes ('%s') = ", old_test
->name
);
9326 IPv4RouteListPrint(old_routes
);
9329 /* apply the old routes */
9330 IPv4RouteListApply(NULL
, old_routes
, -1);
9332 if (new_routes
== NULL
) {
9333 printf("No New Routes\n");
9336 printf("New Routes ('%s') = ", new_test
->name
);
9337 IPv4RouteListPrint(new_routes
);
9340 /* apply the new routes */
9341 IPv4RouteListApply(old_routes
, new_routes
, -1);
9343 if (old_routes
!= NULL
) {
9346 if (new_routes
!= NULL
) {
9349 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
9350 EMPHASIS_CHARS
"\n",
9351 old_test
->name
, new_test
->name
);
9356 main(int argc
, char **argv
)
9358 IPv4RouteTestRef
* test
;
9361 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
9362 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
9364 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9366 S_scopedroute
= (argc
< 3);
9367 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9368 if (routelist_build_test(*test
) == FALSE
) {
9369 fprintf(stderr
, "%s failed\n", (*test
)->name
);
9373 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9374 IPv4RouteTestRef
* test2
;
9376 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
9377 apply_test(*test
, *test2
);
9378 apply_test(*test2
, *test
);
9385 printf("\nChecking for leaks\n");
9386 sprintf(cmd
, "leaks %d 2>&1", getpid());
9394 #endif /* TEST_IPV4_ROUTELIST */
9396 #ifdef TEST_IPV6_ROUTELIST
9404 typedef const IPv6Address
* IPv6AddressRef
;
9407 IPv6AddressRef addr
;
9409 const char * router
;
9410 const char * ifname
;
9412 const CFStringRef
* primary_rank
;
9413 struct route
* additional_routes
;
9414 int additional_routes_count
;
9415 struct route
* excluded_routes
;
9416 int excluded_routes_count
;
9417 } IPv6ServiceContents
;
9419 typedef const IPv6ServiceContents
* IPv6ServiceContentsRef
;
9421 struct route loop_routelist
[] = {
9422 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
9423 "2620:149:4:f01:225:ff:fecc:89a2", NULL
},
9424 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
9425 "2620:149:4:f01:225:ff:fecc:89a3", NULL
},
9426 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
9427 "2620:149:4:f01:225:ff:fecc:89a4", NULL
},
9428 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
9429 "2620:149:4:f01:225:ff:fecc:89a5", NULL
},
9430 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
9431 "2620:149:4:f01:225:ff:fecc:89a6", NULL
},
9432 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
9433 "2620:149:4:f01:225:ff:fecc:89a7", NULL
},
9434 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
9435 "2620:149:4:f01:225:ff:fecc:89a8", NULL
},
9436 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
9437 "2620:149:4:f01:225:ff:fecc:89a9", NULL
},
9438 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
9439 "2620:149:4:f01:225:ff:fecc:89aa", NULL
},
9440 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
9441 "2620:149:4:f01:225:ff:fecc:89ab", NULL
},
9442 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
9443 "2620:149:4:f01:225:ff:fecc:89a1", NULL
},
9446 struct route vpn_routelist
[] = {
9447 { "2010:470:1f05:3cb::", 64,
9448 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9449 { "2010:222:3fa5:acb::", 48,
9450 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9451 { "2010:222:3fa5:1234::", 40,
9452 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9453 { "2010:222:3fa5:5678::", 40,
9457 struct route vpn_routelist_ext
[] = {
9458 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL
, NULL
},
9461 struct route en1_routelist_ext
[] = {
9462 { "2020:299:abcd:ef12::", 64, NULL
, NULL
},
9466 static const IPv6Address en0_addr1
[] = {
9467 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL
},
9468 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL
}
9471 static const IPv6Address en0_addr2
[] = {
9472 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL
},
9473 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL
}
9476 static const IPv6Address en0_addr3
[] = {
9477 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL
},
9478 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL
}
9481 static const IPv6Address en0_addr4
[] = {
9482 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL
},
9483 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL
}
9486 static const IPv6Address en0_addr5
[] = {
9487 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL
},
9488 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL
}
9491 static const IPv6Address en0_addr6
[] = {
9492 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL
},
9495 static const IPv6Address en0_lladdr
[] = {
9496 { "fe80::cabc:c8ff:fe96:96af", 64, NULL
}
9499 static const IPv6Address en1_addr
[] = {
9500 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL
},
9501 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL
}
9504 static const IPv6Address utun0_addr
[] = {
9505 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL
},
9508 static const IPv6Address fw0_addr1
[] = {
9509 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL
},
9510 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL
}
9514 * address+address-count
9515 * router ifname pri rank additional-routes+count excluded-routes+count
9518 static const IPv6ServiceContents en0_10
= {
9519 en0_addr1
, countof(en0_addr1
),
9520 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9523 static const IPv6ServiceContents en0_15
= {
9524 en0_addr2
, countof(en0_addr2
),
9525 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9528 static const IPv6ServiceContents en0_30
= {
9529 en0_addr3
, countof(en0_addr3
),
9530 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9533 static const IPv6ServiceContents en0_40
= {
9534 en0_addr4
, countof(en0_addr4
),
9535 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9538 static const IPv6ServiceContents en0_50
= {
9539 en0_addr5
, countof(en0_addr5
),
9540 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9543 static const IPv6ServiceContents en0_10_a
= {
9544 en0_addr6
, countof(en0_addr6
),
9545 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9548 static const IPv6ServiceContents fw0_25
= {
9549 fw0_addr1
, countof(fw0_addr1
),
9550 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9553 static const IPv6ServiceContents en1_20
= {
9554 en1_addr
, countof(en1_addr
),
9555 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9558 static const IPv6ServiceContents en1_10_ext
= {
9559 en1_addr
, countof(en1_addr
),
9560 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL
, NULL
, 0,
9561 en1_routelist_ext
, countof(en1_routelist_ext
)
9564 static const IPv6ServiceContents en0_0_lladdr
= {
9565 en0_lladdr
, countof(en0_lladdr
),
9566 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL
, NULL
, 0, NULL
, 0
9569 static const IPv6ServiceContents en0_loop
= {
9570 en0_addr1
, countof(en0_addr1
),
9571 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
,
9572 loop_routelist
, countof(loop_routelist
), NULL
, 0
9575 static const IPv6ServiceContents utun0
= {
9576 utun0_addr
, countof(utun0_addr
),
9577 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL
,
9578 vpn_routelist
, countof(vpn_routelist
),
9579 vpn_routelist_ext
, countof(vpn_routelist_ext
),
9584 IPv6ServiceContentsRef test
[];
9585 } IPv6RouteTest
, * IPv6RouteTestRef
;
9587 static IPv6RouteTest test1
= {
9601 static IPv6RouteTest test2
= {
9614 static IPv6RouteTest test3
= {
9623 static IPv6RouteTest test4
= {
9632 static IPv6RouteTest test5
= {
9644 static IPv6RouteTestRef ipv6_tests
[] = {
9655 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9658 CFStringRef prop_val
;
9663 prop_val
= CFStringCreateWithCString(NULL
,
9665 kCFStringEncodingASCII
);
9666 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9667 CFRelease(prop_val
);
9672 dict_add_int(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9677 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
9678 CFDictionarySetValue(dict
, prop_name
, num
);
9684 dict_insert_v6_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9685 struct route
* routes
, int routes_count
)
9688 CFMutableArrayRef route_list
;
9689 struct route
* scan
;
9691 if (routes
== NULL
|| routes_count
== 0) {
9694 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9695 &kCFTypeArrayCallBacks
);
9696 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9697 CFMutableDictionaryRef route_dict
;
9699 route_dict
= CFDictionaryCreateMutable(NULL
, 0,
9700 &kCFTypeDictionaryKeyCallBacks
,
9701 &kCFTypeDictionaryValueCallBacks
);
9702 dict_add_string(route_dict
, kSCPropNetIPv6RouteDestinationAddress
,
9704 dict_add_int(route_dict
, kSCPropNetIPv6PrefixLength
,
9705 scan
->prefix_length
);
9706 dict_add_string(route_dict
, kSCPropNetIPv6RouteGatewayAddress
,
9708 dict_add_string(route_dict
, kSCPropNetIPv6RouteInterfaceName
,
9710 CFArrayAppendValue(route_list
, route_dict
);
9711 CFRelease(route_dict
);
9713 CFDictionarySetValue(dict
, prop_name
, route_list
);
9714 CFRelease(route_list
);
9719 array_add_string(CFMutableArrayRef array
, const char * c_str
)
9723 str
= CFStringCreateWithCString(NULL
,
9725 kCFStringEncodingUTF8
);
9726 CFArrayAppendValue(array
, str
);
9732 array_add_int(CFMutableArrayRef array
, int int_val
)
9736 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
9737 CFArrayAppendValue(array
, num
);
9743 dict_add_ipv6_addressing(CFMutableDictionaryRef dict
,
9744 IPv6AddressRef list
, int list_count
)
9746 CFMutableArrayRef addr
= NULL
;
9747 CFMutableArrayRef dest
= NULL
;
9749 CFMutableArrayRef prefix
= NULL
;
9750 IPv6AddressRef scan
;
9752 if (list
== NULL
|| list_count
== 0) {
9755 for (i
= 0, scan
= list
; i
< list_count
; i
++, scan
++) {
9756 if (scan
->addr
!= NULL
) {
9758 addr
= CFArrayCreateMutable(NULL
, list_count
,
9759 &kCFTypeArrayCallBacks
);
9761 array_add_string(addr
, scan
->addr
);
9763 if (scan
->prefix_length
>= 0) {
9764 if (prefix
== NULL
) {
9765 prefix
= CFArrayCreateMutable(NULL
, list_count
,
9766 &kCFTypeArrayCallBacks
);
9768 array_add_int(prefix
, scan
->prefix_length
);
9770 if (scan
->dest
!= NULL
) {
9772 dest
= CFArrayCreateMutable(NULL
, list_count
,
9773 &kCFTypeArrayCallBacks
);
9775 array_add_string(dest
, scan
->dest
);
9779 CFDictionarySetValue(dict
, kSCPropNetIPv6Addresses
, addr
);
9783 CFDictionarySetValue(dict
, kSCPropNetIPv6DestAddresses
, dest
);
9786 if (prefix
!= NULL
) {
9787 CFDictionarySetValue(dict
, kSCPropNetIPv6PrefixLength
, prefix
);
9793 static CFDictionaryRef
9794 make_IPv6_dict(IPv6ServiceContentsRef t
)
9796 CFMutableDictionaryRef dict
;
9798 dict
= CFDictionaryCreateMutable(NULL
, 0,
9799 &kCFTypeDictionaryKeyCallBacks
,
9800 &kCFTypeDictionaryValueCallBacks
);
9801 dict_add_ipv6_addressing(dict
, t
->addr
, t
->addr_count
);
9802 dict_add_string(dict
, kSCPropNetIPv6Router
, t
->router
);
9803 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9804 dict_insert_v6_routes(dict
, kSCPropNetIPv6AdditionalRoutes
,
9805 t
->additional_routes
, t
->additional_routes_count
);
9806 dict_insert_v6_routes(dict
, kSCPropNetIPv6ExcludedRoutes
,
9807 t
->excluded_routes
, t
->excluded_routes_count
);
9812 kDirectionForwards
= 0,
9813 kDirectionBackwards
= 1
9817 kLogRouteDisabled
= 0,
9818 kLogRouteEnabled
= 1
9821 static IPv6RouteListRef
9822 make_IPv6RouteList_for_test(IPv6RouteListRef list
,
9823 IPv6ServiceContentsRef test
,
9826 CFDictionaryRef dict
;
9829 Rank rank_assertion
= kRankAssertionDefault
;
9830 CFNumberRef rank_assertion_cf
= NULL
;
9831 Boolean rank_assertion_is_set
= FALSE
;
9832 IPv6RouteListRef ret
= NULL
;
9833 IPV6_ROUTES_BUF_DECL(routes
);
9835 dict
= make_IPv6_dict(test
);
9837 fprintf(stderr
, "make_IPv6_dict failed\n");
9840 if (test
->primary_rank
!= NULL
) {
9842 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9843 &rank_assertion_is_set
);
9844 if (rank_assertion_is_set
) {
9846 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9849 r
= IPv6RouteListCreateWithDictionary(routes
, dict
,
9851 my_CFRelease(&rank_assertion_cf
);
9853 fprintf(stderr
, "IPv6RouteListCreateWithDictionary failed\n");
9857 if (rank_assertion
== kRankAssertionScoped
) {
9858 rank_assertion
= kRankAssertionNever
;
9860 rank
= RankMake(test
->rank
, rank_assertion
);
9861 if (log_it
== kLogRouteEnabled
9862 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9865 descr
= IPv6RouteListCopyDescription(r
);
9866 SCPrint(TRUE
, stdout
, CFSTR("Adding %@"), descr
);
9869 ret
= IPv6RouteListAddRouteList(list
, 1, r
, rank
);
9877 static IPv6RouteListRef
9878 make_IPv6RouteList(IPv6ServiceContentsRef
* test
, Direction direction
,
9881 IPv6RouteListRef ret
= NULL
;
9882 IPv6ServiceContentsRef
* scan
;
9884 switch (direction
) {
9885 case kDirectionBackwards
:
9886 for (scan
= test
; *scan
!= NULL
; scan
++) {
9887 /* find the end of the list */
9889 for (scan
--; scan
>= test
; scan
--) {
9890 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
9894 case kDirectionForwards
:
9895 for (scan
= test
; *scan
!= NULL
; scan
++) {
9896 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
9900 IPv6RouteListFinalize(ret
);
9904 #define EMPHASIS_CHARS "================="
9907 * Function: routelist_build_test
9909 * Runs through the given set of routes first in the forward direction,
9910 * then again backwards. We should end up with exactly the same set of
9911 * routes at the end.
9914 routelist_build_test(IPv6RouteTestRef test
)
9917 boolean_t ret
= FALSE
;
9918 IPv6RouteListRef routes1
;
9919 IPv6RouteListRef routes2
;
9921 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
9922 EMPHASIS_CHARS
"\n",
9925 routes1
= make_IPv6RouteList(test
->test
, kDirectionForwards
,
9927 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9928 if (routes1
!= NULL
) {
9929 descr
= IPv6RouteListCopyDescription(routes1
);
9930 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9934 routes2
= make_IPv6RouteList(test
->test
, kDirectionBackwards
,
9936 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9937 if (routes2
!= NULL
) {
9938 descr
= IPv6RouteListCopyDescription(routes2
);
9939 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9943 if ((routes1
!= NULL
&& routes2
== NULL
)
9944 || (routes1
== NULL
&& routes2
!= NULL
)) {
9945 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
9946 (routes1
!= NULL
) ? "not " : "",
9947 (routes2
!= NULL
) ? "not " : "");
9949 else if (routes1
!= NULL
&& routes2
!= NULL
) {
9950 /* check if they are different */
9951 if (routes1
->count
!= routes2
->count
) {
9952 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
9953 routes1
->count
, routes2
->count
);
9955 else if (bcmp(routes1
, routes2
,
9956 IPv6RouteListComputeSize(routes1
->count
)) != 0) {
9957 fprintf(stderr
, "routes1 and routes2 are different\n");
9960 printf("routes1 and routes2 are the same\n");
9964 if (routes1
!= NULL
) {
9967 if (routes2
!= NULL
) {
9970 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
9971 EMPHASIS_CHARS
"\n",
9972 test
->name
, ret
? "PASSED" : "FAILED");
9977 apply_test(IPv6RouteTestRef old_test
, IPv6RouteTestRef new_test
)
9979 IPv6RouteListRef new_routes
;
9980 IPv6RouteListRef old_routes
;
9982 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
9983 EMPHASIS_CHARS
"\n",
9984 old_test
->name
, new_test
->name
);
9986 old_routes
= make_IPv6RouteList(old_test
->test
, kDirectionForwards
,
9988 new_routes
= make_IPv6RouteList(new_test
->test
, kDirectionForwards
,
9990 if (old_routes
== NULL
) {
9991 printf("No Old Routes\n");
9994 printf("Old routes ('%s') = ", old_test
->name
);
9995 IPv6RouteListPrint(old_routes
);
9998 /* apply the old routes */
9999 IPv6RouteListApply(NULL
, old_routes
, -1);
10000 if (new_routes
== NULL
) {
10001 printf("No New Routes\n");
10004 printf("New Routes ('%s') = ", new_test
->name
);
10005 IPv6RouteListPrint(new_routes
);
10008 /* apply the new routes */
10009 IPv6RouteListApply(old_routes
, new_routes
, -1);
10010 if (old_routes
!= NULL
) {
10013 if (new_routes
!= NULL
) {
10016 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
10017 EMPHASIS_CHARS
"\n",
10018 old_test
->name
, new_test
->name
);
10023 main(int argc
, char **argv
)
10025 IPv6RouteTestRef
* test
;
10028 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
10029 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
10031 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
10033 S_scopedroute_v6
= (argc
< 3);
10034 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10035 if (routelist_build_test(*test
) == FALSE
) {
10036 fprintf(stderr
, "%s failed\n", (*test
)->name
);
10040 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
10041 IPv6RouteTestRef
* test2
;
10043 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
10044 apply_test(*test
, *test2
);
10045 apply_test(*test2
, *test
);
10052 printf("\nChecking for leaks\n");
10053 sprintf(cmd
, "leaks %d 2>&1", getpid());
10061 #endif /* TEST_IPV6_ROUTELIST */