2 * Copyright (c) 2000-2014 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
389 #define ROUTELIST_COMMON \
392 RouteListFlags flags;
396 } RouteListCommon
, * RouteListRef
;
400 IPv4Route list
[1]; /* variable length */
401 } IPv4RouteList
, * IPv4RouteListRef
;
405 IPv6Route list
[1]; /* variable length */
406 } IPv6RouteList
, * IPv6RouteListRef
;
421 * Election Information
422 * - information about the current best services
430 struct sockaddr_in v4
;
431 struct sockaddr_in6 v6
;
434 typedef struct Candidate
{
435 CFStringRef serviceID
;
438 boolean_t ip_is_coupled
;
439 SCNetworkReachabilityFlags reachability_flags
;
441 in_sockaddr vpn_server_addr
;
442 CFStringRef signature
;
443 } Candidate
, * CandidateRef
;
445 typedef struct ElectionResults
{
449 Candidate candidates
[1];
450 } ElectionResults
, * ElectionResultsRef
;
452 static __inline__
size_t
453 ElectionResultsComputeSize(unsigned int n
)
455 return (offsetof(ElectionResults
, candidates
[n
]));
461 * A 32-bit value to encode the relative rank of a service.
463 * The top 8 bits are used to hold the rank assertion (first, default, last,
466 * The bottom 24 bits are used to store the service index (i.e. the
467 * position within the service order array).
469 #define RANK_ASSERTION_MAKE(r) ((Rank)(r) << 24)
470 #define kRankAssertionFirst RANK_ASSERTION_MAKE(0)
471 #define kRankAssertionDefault RANK_ASSERTION_MAKE(1)
472 #define kRankAssertionLast RANK_ASSERTION_MAKE(2)
473 #define kRankAssertionNever RANK_ASSERTION_MAKE(3)
474 #define kRankAssertionScoped RANK_ASSERTION_MAKE(4)
475 #define kRankAssertionMask RANK_ASSERTION_MAKE(0xff)
476 #define RANK_ASSERTION_MASK(r) ((Rank)(r) & kRankAssertionMask)
477 #define RANK_ASSERTION_GET(r) ((Rank)(r) >> 24)
478 #define RANK_INDEX_MAKE(r) ((Rank)(r))
479 #define kRankIndexMask RANK_INDEX_MAKE(0xffffff)
480 #define RANK_INDEX_MASK(r) ((Rank)(r) & kRankIndexMask)
482 static __inline__ Rank
483 RankMake(uint32_t service_index
, Rank primary_rank
)
485 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
489 InterfaceRankGetRankAssertion(CFNumberRef rank_cf
, Boolean
* ret_is_set
)
491 SCNetworkServicePrimaryRank if_rank
;
492 Boolean is_set
= FALSE
;
493 Rank rank
= kRankAssertionDefault
;
496 && CFNumberGetValue(rank_cf
, kCFNumberSInt32Type
, &if_rank
)
497 && if_rank
!= kSCNetworkServicePrimaryRankDefault
) {
498 if (if_rank
== kSCNetworkServicePrimaryRankFirst
) {
499 rank
= kRankAssertionFirst
;
502 rank
= RANK_ASSERTION_MAKE(if_rank
);
506 if (ret_is_set
!= NULL
) {
507 *ret_is_set
= is_set
;
513 PrimaryRankGetRankAssertion(CFStringRef rank_str
, Boolean
* is_set
)
517 const CFStringRef
* name
;
520 { &kSCValNetServicePrimaryRankFirst
, kRankAssertionFirst
},
521 { &kSCValNetServicePrimaryRankLast
, kRankAssertionLast
},
522 { &kSCValNetServicePrimaryRankNever
, kRankAssertionNever
},
523 { &kSCValNetServicePrimaryRankScoped
, kRankAssertionScoped
}
526 if (rank_str
!= NULL
) {
527 for (i
= 0; i
< countof(values
); i
++) {
528 if (CFEqual(rank_str
, *(values
[i
].name
))) {
529 if (is_set
!= NULL
) {
532 return (values
[i
].rank_assertion
);
536 if (is_set
!= NULL
) {
539 return (kRankAssertionDefault
);
542 /* SCDynamicStore session */
543 static SCDynamicStoreRef S_session
= NULL
;
545 /* debug output flags */
546 static uint32_t S_IPMonitor_debug
= 0;
547 static Boolean S_IPMonitor_verbose
= FALSE
;
549 /* are we netbooted? If so, don't touch the default route */
550 static boolean_t S_netboot
= FALSE
;
552 /* is scoped routing enabled? */
553 static boolean_t S_scopedroute
= FALSE
;
554 static boolean_t S_scopedroute_v6
= FALSE
;
556 /* dictionary to hold per-service state: key is the serviceID */
557 static CFMutableDictionaryRef S_service_state_dict
= NULL
;
558 static CFMutableDictionaryRef S_ipv4_service_rank_dict
= NULL
;
559 static CFMutableDictionaryRef S_ipv6_service_rank_dict
= NULL
;
561 /* dictionary to hold per-interface rank information */
562 static CFDictionaryRef S_if_rank_dict
;
564 /* if set, a PPP interface overrides the primary */
565 static boolean_t S_ppp_override_primary
= FALSE
;
567 /* the current primary serviceID's */
568 static CFStringRef S_primary_ipv4
= NULL
;
569 static CFStringRef S_primary_ipv6
= NULL
;
570 static CFStringRef S_primary_dns
= NULL
;
571 static CFStringRef S_primary_proxies
= NULL
;
573 /* the current election results */
574 static ElectionResultsRef S_ipv4_results
;
575 static ElectionResultsRef S_ipv6_results
;
577 static CFStringRef S_state_global_ipv4
= NULL
;
578 static CFStringRef S_state_global_ipv6
= NULL
;
579 static CFStringRef S_state_global_dns
= NULL
;
580 static CFStringRef S_state_global_proxies
= NULL
;
581 static CFStringRef S_state_service_prefix
= NULL
;
582 static CFStringRef S_setup_global_ipv4
= NULL
;
583 static CFStringRef S_setup_service_prefix
= NULL
;
585 static CFStringRef S_multicast_resolvers
= NULL
;
586 static CFStringRef S_private_resolvers
= NULL
;
588 #if !TARGET_IPHONE_SIMULATOR
589 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
590 static IPv6RouteListRef S_ipv6_routelist
= NULL
;
592 #endif /* !TARGET_IPHONE_SIMULATOR */
594 static boolean_t S_append_state
= FALSE
;
596 static CFDictionaryRef S_dns_dict
= NULL
;
598 static Boolean S_dnsinfo_synced
= TRUE
;
600 static nwi_state_t S_nwi_state
= NULL
;
601 static Boolean S_nwi_synced
= TRUE
;
603 static CFDictionaryRef S_proxies_dict
= NULL
;
605 // Note: access should be gated with __network_change_queue()
606 static uint32_t S_network_change_needed
= 0;
607 #define NETWORK_CHANGE_NET 1<<0
608 #define NETWORK_CHANGE_DNS 1<<1
609 #define NETWORK_CHANGE_PROXY 1<<2
610 #if !TARGET_OS_IPHONE
611 #define NETWORK_CHANGE_SMB 1<<3
612 #endif /* !TARGET_OS_IPHONE */
613 static struct timeval S_network_change_start
;
614 static Boolean S_network_change_timeout
= FALSE
;
615 static dispatch_source_t S_network_change_timer
= NULL
;
617 #if !TARGET_OS_IPHONE
618 static CFStringRef S_primary_smb
= NULL
;
619 static CFStringRef S_state_global_smb
= NULL
;
620 static CFDictionaryRef S_smb_dict
= NULL
;
621 #endif /* !TARGET_OS_IPHONE */
623 #if !TARGET_OS_IPHONE
624 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
625 #endif /* !TARGET_OS_IPHONE */
628 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
629 #endif /* KERN_NETBOOT */
632 ** entityType*, GetEntityChanges*
633 ** - definitions for the entity types we handle
640 #if !TARGET_OS_IPHONE
642 #endif /* !TARGET_OS_IPHONE */
644 kEntityTypeTransientStatus
,
645 kEntityTypeServiceOptions
= 31
648 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
649 &kSCEntNetIPv4
, /* 0 */
650 &kSCEntNetIPv6
, /* 1 */
651 &kSCEntNetDNS
, /* 2 */
652 &kSCEntNetProxies
, /* 3 */
653 #if !TARGET_OS_IPHONE
654 &kSCEntNetSMB
, /* 4 */
655 #endif /* !TARGET_OS_IPHONE */
659 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
);
661 static __inline__
char
664 return ((af
== AF_INET
) ? '4' : '6');
667 static __inline__
char
668 ipvx_other_char(int af
)
670 return ((af
== AF_INET
) ? '6' : '4');
674 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
676 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
677 * Routes CFData containing IPv4RouteList/IPv6RouteList
678 * Service dictionary containing kSCEntNetIPv[46] service entity
680 #define kIPDictRoutes CFSTR("Routes") /* data */
681 #define kIPDictService CFSTR("Service") /* dict */
683 static CFDictionaryRef
684 ipdict_create(CFDictionaryRef dict
, CFDataRef routes_data
)
689 keys
[0] = kIPDictService
;
691 keys
[1] = kIPDictRoutes
;
692 values
[1] = routes_data
;
693 return (CFDictionaryCreate(NULL
,
694 (const void * *)keys
,
697 &kCFTypeDictionaryKeyCallBacks
,
698 &kCFTypeDictionaryValueCallBacks
));
702 ipdict_get_routelist(CFDictionaryRef dict
)
704 void * routes_list
= NULL
;
709 routes
= CFDictionaryGetValue(dict
, kIPDictRoutes
);
710 if (routes
!= NULL
) {
711 routes_list
= (void *)CFDataGetBytePtr(routes
);
714 return (routes_list
);
717 static CFDictionaryRef
718 ipdict_get_service(CFDictionaryRef dict
)
720 CFDictionaryRef ip_dict
= NULL
;
723 ip_dict
= CFDictionaryGetValue(dict
, kIPDictService
);
729 ipdict_get_ifname(CFDictionaryRef dict
)
731 CFStringRef ifname
= NULL
;
732 CFDictionaryRef ip_dict
;
734 ip_dict
= ipdict_get_service(dict
);
735 if (ip_dict
!= NULL
) {
736 ifname
= CFDictionaryGetValue(ip_dict
, kSCPropInterfaceName
);
741 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
742 CFDictionaryRef state_dict
,
743 CFDictionaryRef setup_dict
,
744 CFDictionaryRef info
);
745 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
747 static GetEntityChangesFunc get_ipv4_changes
;
748 static GetEntityChangesFunc get_ipv6_changes
;
749 static GetEntityChangesFunc get_dns_changes
;
750 static GetEntityChangesFunc get_proxies_changes
;
751 #if !TARGET_OS_IPHONE
752 static GetEntityChangesFunc get_smb_changes
;
753 #endif /* !TARGET_OS_IPHONE */
756 my_CFRelease(void * t
);
759 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
762 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
764 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
765 get_ipv4_changes
, /* 0 */
766 get_ipv6_changes
, /* 1 */
767 get_dns_changes
, /* 2 */
768 get_proxies_changes
,/* 3 */
769 #if !TARGET_OS_IPHONE
770 get_smb_changes
, /* 4 */
771 #endif /* !TARGET_OS_IPHONE */
776 ** - mechanism to do an atomic update of the SCDynamicStore
777 ** when the content needs to be changed across multiple functions
780 CFMutableArrayRef notify
;
781 CFMutableArrayRef remove
;
782 CFMutableDictionaryRef set
;
783 } keyChangeList
, * keyChangeListRef
;
786 keyChangeListInit(keyChangeListRef keys
)
788 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
789 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
790 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
791 &kCFTypeDictionaryKeyCallBacks
,
792 &kCFTypeDictionaryValueCallBacks
);
797 keyChangeListFree(keyChangeListRef keys
)
799 my_CFRelease(&keys
->notify
);
800 my_CFRelease(&keys
->remove
);
801 my_CFRelease(&keys
->set
);
806 keyChangeListActive(keyChangeListRef keys
)
808 return ((CFDictionaryGetCount(keys
->set
) > 0) ||
809 (CFArrayGetCount(keys
->remove
) > 0) ||
810 (CFArrayGetCount(keys
->notify
) > 0));
814 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
816 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
821 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
823 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
824 CFDictionaryRemoveValue(keys
->set
, key
);
829 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
831 my_CFArrayRemoveValue(keys
->remove
, key
);
832 CFDictionarySetValue(keys
->set
, key
, value
);
837 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
839 CFArrayRef notify
= keys
->notify
;
840 CFArrayRef remove
= keys
->remove
;
841 CFDictionaryRef set
= keys
->set
;
843 if (CFArrayGetCount(notify
) == 0) {
846 if (CFArrayGetCount(remove
) == 0) {
849 if (CFDictionaryGetCount(set
) == 0) {
852 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
855 if (S_IPMonitor_debug
& kDebugFlag1
) {
857 my_log(LOG_DEBUG
, "IPMonitor: Setting:\n%@", set
);
859 if (remove
!= NULL
) {
860 my_log(LOG_DEBUG
, "IPMonitor: Removing:\n%@", remove
);
862 if (notify
!= NULL
) {
863 my_log(LOG_DEBUG
, "IPMonitor: Notifying:\n%@", notify
);
866 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
872 S_nwi_ifstate_dump(nwi_ifstate_t ifstate
, int i
)
874 const char * addr_str
;
876 char ntopbuf
[INET6_ADDRSTRLEN
];
877 char vpn_ntopbuf
[INET6_ADDRSTRLEN
];
878 const struct sockaddr
* vpn_addr
;
880 address
= nwi_ifstate_get_address(ifstate
);
881 addr_str
= inet_ntop(ifstate
->af
, address
, ntopbuf
, sizeof(ntopbuf
));
882 vpn_addr
= nwi_ifstate_get_vpn_server(ifstate
);
883 if (vpn_addr
!= NULL
) {
884 _SC_sockaddr_to_string(nwi_ifstate_get_vpn_server(ifstate
),
886 sizeof(vpn_ntopbuf
));
889 " [%d]: %s%s%s%s rank 0x%x iaddr %s%s%s reach_flags 0x%x",
891 ifstate
->diff_str
!= NULL
? ifstate
->diff_str
: "",
892 (ifstate
->flags
& NWI_IFSTATE_FLAGS_HAS_DNS
) != 0
894 (ifstate
->flags
& NWI_IFSTATE_FLAGS_NOT_IN_LIST
) != 0
898 (vpn_addr
!= NULL
) ? " vpn_server_addr: " : "",
899 (vpn_addr
!= NULL
) ? vpn_ntopbuf
: "",
900 ifstate
->reach_flags
);
905 S_nwi_state_dump(nwi_state_t state
)
911 my_log(LOG_DEBUG
, "nwi_state = <none>");
916 "gen=%llu size=%u #v4=%u #v6=%u "
917 "reach_flags=(v4=0x%x, v6=0x%x) }",
918 state
->generation_count
,
922 nwi_state_get_reachability_flags(state
, AF_INET
),
923 nwi_state_get_reachability_flags(state
, AF_INET6
));
924 if (state
->ipv4_count
) {
925 my_log(LOG_DEBUG
, "IPv4:");
926 for (i
= 0, scan
= state
->nwi_ifstates
;
927 i
< state
->ipv4_count
; i
++, scan
++) {
928 S_nwi_ifstate_dump(scan
, i
);
931 if (state
->ipv6_count
) {
932 my_log(LOG_DEBUG
, "IPv6:");
933 for (i
= 0, scan
= state
->nwi_ifstates
+ state
->ipv6_start
;
934 i
< state
->ipv6_count
; i
++, scan
++) {
935 S_nwi_ifstate_dump(scan
, i
);
949 mib
[1] = KERN_NETBOOT
;
950 len
= sizeof(netboot
);
951 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
955 static int rtm_seq
= 0;
957 #if !TARGET_IPHONE_SIMULATOR
959 open_routing_socket(void)
963 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
965 "IPMonitor: open_routing_socket: socket failed, %s",
971 static __inline__
int
974 return (socket(AF_INET6
, SOCK_DGRAM
, 0));
978 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
980 struct in6_defrouter dr
;
981 struct sockaddr_in6
* sin6
;
983 bzero(&dr
, sizeof(dr
));
985 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
986 sin6
->sin6_family
= AF_INET6
;
987 sin6
->sin6_addr
= *addr
;
989 dr
.if_index
= if_index
;
990 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
994 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
996 struct in6_defrouter dr
;
997 struct sockaddr_in6
* sin6
;
999 bzero(&dr
, sizeof(dr
));
1001 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
1002 sin6
->sin6_family
= AF_INET6
;
1003 sin6
->sin6_addr
= *addr
;
1004 dr
.if_index
= if_index
;
1005 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
1008 #endif /* !TARGET_IPHONE_SIMULATOR */
1011 S_is_scoped_routing_enabled()
1013 int scopedroute
= 0;
1014 size_t len
= sizeof(scopedroute
);
1016 if ((sysctlbyname("net.inet.ip.scopedroute",
1019 && (errno
!= ENOENT
)) {
1020 my_log(LOG_ERR
, "sysctlbyname() failed: %s", strerror(errno
));
1022 return (scopedroute
);
1026 S_is_scoped_v6_routing_enabled()
1028 int scopedroute_v6
= 0;
1029 size_t len
= sizeof(scopedroute_v6
);
1031 if ((sysctlbyname("net.inet6.ip6.scopedroute",
1032 &scopedroute_v6
, &len
,
1034 && (errno
!= ENOENT
)) {
1035 my_log(LOG_ERR
, "sysctlbyname() failed: %s", strerror(errno
));
1037 return (scopedroute_v6
);
1041 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
1043 CFIndex n
= CFArrayGetCount(arr
);
1045 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
1048 CFArrayAppendValue(arr
, new);
1053 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
1057 i
= CFArrayGetFirstIndexOfValue(arr
,
1058 CFRangeMake(0, CFArrayGetCount(arr
)),
1060 if (i
!= kCFNotFound
) {
1061 CFArrayRemoveValueAtIndex(arr
, i
);
1067 my_CFArrayCreateCombinedArray(CFArrayRef array1
, CFArrayRef array2
)
1069 CFMutableArrayRef combined
;
1071 combined
= CFArrayCreateMutableCopy(NULL
, 0, array1
);
1072 CFArrayAppendArray(combined
,
1074 CFRangeMake(0, CFArrayGetCount(array2
)));
1079 my_CFRelease(void * t
)
1081 void * * obj
= (void * *)t
;
1090 static CFDictionaryRef
1091 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
1093 if (isA_CFDictionary(dict
) == NULL
) {
1096 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
1100 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
1102 if (isA_CFDictionary(dict
) == NULL
) {
1105 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
1109 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, int addr_size
)
1113 if (isA_CFString(str
) == NULL
) {
1119 if (addr_size
< sizeof(struct in_addr
)) {
1124 if (addr_size
< sizeof(struct in6_addr
)) {
1131 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
1132 if (inet_pton(family
, buf
, addr
) == 1) {
1136 bzero(addr
, addr_size
);
1142 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
1144 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
1149 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
1151 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
1155 cfnumber_to_int(CFNumberRef num
, int * int_val
)
1157 if (isA_CFNumber(num
) == NULL
) {
1160 return (CFNumberGetValue(num
, kCFNumberIntType
, int_val
));
1163 static CF_RETURNS_RETAINED CFStringRef
1164 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
1166 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1167 kSCDynamicStoreDomainSetup
,
1172 static CF_RETURNS_RETAINED CFStringRef
1173 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
1175 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1176 kSCDynamicStoreDomainState
,
1182 interface_entity_key_copy(CFStringRef ifname
, CFStringRef entity
)
1184 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
1185 kSCDynamicStoreDomainState
,
1190 static CFDictionaryRef
1191 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1194 CFStringRef setup_key
;
1195 CFDictionaryRef setup_dict
;
1197 setup_key
= setup_service_key(serviceID
, entity
);
1198 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
1199 my_CFRelease(&setup_key
);
1200 return (setup_dict
);
1203 static CFDictionaryRef
1204 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
1207 CFStringRef state_key
;
1208 CFDictionaryRef state_dict
;
1210 state_key
= state_service_key(serviceID
, entity
);
1211 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
1212 my_CFRelease(&state_key
);
1213 return (state_dict
);
1217 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1221 ip_list
= CFDictionaryGetValue(dict
, prop
);
1222 if (isA_CFArray(ip_list
) != NULL
1223 && CFArrayGetCount(ip_list
) > 0
1224 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1231 dict_get_first_ipv6(CFDictionaryRef dict
, CFStringRef prop
,
1232 struct in6_addr
* ip_p
)
1236 ip_list
= CFDictionaryGetValue(dict
, prop
);
1237 if (isA_CFArray(ip_list
) != NULL
1238 && CFArrayGetCount(ip_list
) > 0
1239 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
1246 dict_get_first_int(CFDictionaryRef dict
, CFStringRef prop
,
1251 list
= CFDictionaryGetValue(dict
, prop
);
1252 if (isA_CFArray(list
) != NULL
1253 && CFArrayGetCount(list
) > 0
1254 && cfnumber_to_int(CFArrayGetValueAtIndex(list
, 0), val
)) {
1261 dict_get_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
1265 val
= CFDictionaryGetValue(dict
, prop
);
1266 return (cfstring_to_ip(val
, ip_p
));
1270 dict_get_ipv6(CFDictionaryRef dict
, CFStringRef prop
, struct in6_addr
* ip_p
)
1274 val
= CFDictionaryGetValue(dict
, prop
);
1275 return (cfstring_to_ip6(val
, ip_p
));
1279 dict_get_int(CFDictionaryRef dict
, CFStringRef prop
, int * intval
)
1283 val
= CFDictionaryGetValue(dict
, prop
);
1284 return (cfnumber_to_int(val
, intval
));
1288 get_override_primary(CFDictionaryRef dict
)
1292 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
1293 if (isA_CFNumber(override
) != NULL
) {
1296 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
1301 else if (isA_CFBoolean(override
) != NULL
) {
1302 if (CFBooleanGetValue(override
)) {
1314 (*RouteListComputeSize
)(CFIndex n
);
1317 (*RouteIsEqual
)(RouteRef a
, RouteRef b
);
1320 (*RouteApply
)(RouteRef route
, int cmd
, int sockfd
);
1322 typedef const void *
1323 (*RouteGateway
)(RouteRef route
);
1326 (*RouteSetGateway
)(RouteRef route
, const void * address
);
1328 typedef const void *
1329 (*RouteDestination
)(RouteRef route
);
1332 (*RouteSameSubnet
)(RouteRef route
, const void * address
);
1335 (*RouteCopyDescription
)(RouteRef route
);
1338 (*RouteLog
)(int priority
, RouteRef route
, const char * msg
);
1341 RouteListComputeSize list_compute_size
;
1343 RouteIsEqual route_equal
;
1344 RouteApply route_apply
;
1345 RouteGateway route_gateway
;
1346 RouteSetGateway route_set_gateway
;
1347 RouteDestination route_destination
;
1348 RouteSameSubnet route_same_subnet
;
1350 RouteCopyDescription route_copy_description
;
1357 typedef const RouteListInfo
* RouteListInfoRef
;
1360 RouteListInfoRef info
;
1361 RouteListRef old_routes
;
1362 RouteListRef new_routes
;
1365 } RouteListApplyContext
, * RouteListApplyContextRef
;
1369 RouteAddressCompare(RouteListInfoRef info
,
1373 return (memcmp(addr1
, addr2
, info
->address_size
));
1377 RouteCompare(RouteListInfoRef info
,
1378 RouteRef a
, Rank a_rank
,
1379 RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
1382 RouteDestination route_destination
;
1383 RouteCopyDescription route_copy_description
;
1386 route_destination
= info
->route_destination
;
1387 route_copy_description
= info
->route_copy_description
;
1388 cmp
= RouteAddressCompare(info
,
1389 (*route_destination
)(a
),
1390 (*route_destination
)(b
));
1392 cmp
= a
->prefix_length
- b
->prefix_length
;
1394 int index_cmp
= a
->ifindex
- b
->ifindex
;
1396 if (index_cmp
== 0) {
1399 else if ((a
->ifindex
== 0 || b
->ifindex
== 0)
1400 && (a
->flags
& kRouteFlagsIsScoped
) == 0
1401 && (b
->flags
& kRouteFlagsIsScoped
) == 0) {
1403 * Either of the routes specifies no interface and neither
1404 * route is scoped. Claim they are equal to eliminate the
1411 cmp
= RankCompare(a_rank
, b_rank
);
1418 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
1426 else if (cmp
== 0) {
1432 a_str
= (*route_copy_description
)(a
);
1433 b_str
= (*route_copy_description
)(b
);
1434 my_log(LOG_DEBUG
, "%@ rank 0x%x %c %@ rank 0x%x",
1435 a_str
, a_rank
, ch
, b_str
, b_rank
);
1443 RouteListGetRouteAtIndexSimple(RouteListInfoRef info
, RouteListRef routes
,
1446 return ((void *)routes
+ (*info
->list_compute_size
)(where
));
1450 RouteListGetRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1453 if (routes
->count
== 0
1454 || where
>= routes
->count
) {
1457 return (RouteListGetRouteAtIndexSimple(info
, routes
, where
));
1461 RouteListGetFirstRoute(RouteListInfoRef info
, RouteListRef routes
)
1463 return (RouteListGetRouteAtIndexSimple(info
, routes
, 0));
1466 #if !TARGET_IPHONE_SIMULATOR
1468 RouteListRouteIndex(RouteListInfoRef info
, RouteListRef routes
,
1471 return (((void *)route
1472 - (void *)RouteListGetFirstRoute(info
, routes
))
1473 / info
->element_size
);
1475 #endif /* !TARGET_IPHONE_SIMULATOR */
1478 RouteGetNextRoute(RouteListInfoRef info
, RouteRef route
)
1480 return ((RouteRef
)(((void *)route
) + info
->element_size
));
1484 RouteListAddRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1485 RouteRef this_route
, CFIndex where
)
1487 RouteRef insert_route
;
1489 if (where
== kCFNotFound
) {
1490 /* add it to the end */
1492 = RouteListGetRouteAtIndexSimple(info
, routes
, routes
->count
);
1495 /* make space at [where] */
1496 insert_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1498 (void *)insert_route
+ info
->element_size
,
1499 info
->element_size
* (routes
->count
- where
));
1501 /* copy the route */
1502 bcopy(this_route
, insert_route
, info
->element_size
);
1504 return (insert_route
);
1508 RouteListRemoveRouteAtIndex(RouteListInfoRef info
, RouteListRef routes
,
1511 if (routes
->count
== 0
1512 || where
>= routes
->count
) {
1516 if (where
== routes
->count
) {
1517 /* last slot, decrementing gets rid of it */
1520 RouteRef remove_route
;
1522 remove_route
= RouteListGetRouteAtIndexSimple(info
, routes
, where
);
1523 bcopy((void *)remove_route
+ info
->element_size
,
1525 info
->element_size
* (routes
->count
- where
));
1531 * Function: RouteListAddRoute
1534 * Add the given route to the list of routes, eliminating lower-ranked
1535 * duplicates on the same interface, and marking any lower ranked duplicates
1536 * on other interfaces with kRouteFlagsIsScoped.
1538 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1541 * Route list updated with the given route, possibly a different pointer,
1542 * due to using realloc'd memory.
1552 RouteListAddRoute(RouteListInfoRef info
,
1553 RouteListRef routes
, int init_size
,
1554 RouteRef this_route
, Rank this_rank
)
1557 RouteRef first_scan
= NULL
;
1560 Scope scope_which
= kScopeNone
;
1561 CFIndex where
= kCFNotFound
;
1563 if (routes
== NULL
) {
1564 size_t alloc_size
= (*info
->list_compute_size
)(init_size
);
1566 routes
= (RouteListRef
)malloc(alloc_size
);
1567 bzero(routes
, sizeof(*routes
));
1568 routes
->size
= init_size
;
1570 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1572 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1574 boolean_t same_dest
;
1576 cmp
= RouteCompare(info
, this_route
, this_rank
, scan
, scan
->rank
,
1578 if (same_dest
== TRUE
&& first_scan
== NULL
) {
1582 if (where
== kCFNotFound
) {
1583 if (same_dest
== TRUE
1584 && (first_scan
->flags
& kRouteFlagsIsScoped
) == 0) {
1585 if ((scan
->flags
& kRouteFlagsIsScoped
) != 0) {
1586 ROUTELIST_DEBUG(kDebugFlag8
,
1587 "Hit 1: set scope on self\n");
1588 scope_which
= kScopeThis
;
1591 ROUTELIST_DEBUG(kDebugFlag8
,
1592 "Hit 2: set scope on next\n");
1593 scope_which
= kScopeNext
;
1596 /* remember our insertion point, but keep going to find a dup */
1600 else if (cmp
== 0) {
1603 if (where
!= kCFNotFound
1604 && scan
->ifindex
== this_route
->ifindex
1605 && scan
->exclude_ifindex
== 0
1606 && this_route
->exclude_ifindex
== 0) {
1607 /* this route is a duplicate */
1608 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 3: removing [%ld]\n", i
);
1609 RouteListRemoveRouteAtIndex(info
, routes
, i
);
1613 * this_route is "better" than scan if this_route is not excluded
1614 * and scan is excluded or this_route sorts ahead of scan
1616 if (this_route
->exclude_ifindex
== 0
1617 && (scan
->exclude_ifindex
!= 0 || this_rank
< scan
->rank
)) {
1618 IFIndex ifindex
= 0;
1619 boolean_t is_scoped
= FALSE
;
1621 if (scan
->flags
& kRouteFlagsIsScoped
) {
1624 if (this_rank
< scan
->rank
) {
1625 ROUTELIST_DEBUG(kDebugFlag8
,
1626 "Hit 4a: replacing [%ld]"
1627 " rank 0x%x < 0x%x\n",
1628 i
, this_rank
, scan
->rank
);
1631 ROUTELIST_DEBUG(kDebugFlag8
,
1632 "Hit 4b: replacing [%ld] excluded route\n",
1635 if (scan
->ifindex
!= 0) {
1636 ifindex
= scan
->ifindex
;
1638 else if (this_route
->ifindex
!= 0) {
1639 ifindex
= this_route
->ifindex
;
1641 bcopy(this_route
, scan
, info
->element_size
);
1642 scan
->rank
= this_rank
;
1643 scan
->ifindex
= ifindex
;
1644 scan
->exclude_ifindex
= 0;
1646 /* preserve whether route was scoped */
1647 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 5: preserved scope\n");
1648 scan
->flags
|= kRouteFlagsIsScoped
;
1655 if (same_dest
== TRUE
) {
1656 if (scope_which
== kScopeNone
) {
1657 ROUTELIST_DEBUG(kDebugFlag8
, "Hit 6: set scope on self\n");
1658 scope_which
= kScopeThis
;
1661 #ifdef TEST_ROUTELIST
1662 else if (where
!= kCFNotFound
) {
1663 /* not possible because we maintain a sorted list */
1665 "Hit 7: moved past routes - can't happen\n");
1669 #endif /* TEST_ROUTELIST */
1673 if (routes
->size
== routes
->count
) {
1675 RouteListRef new_routes
;
1678 /* double the size */
1679 old_size
= routes
->size
;
1680 how_many
= old_size
* 2;
1681 new_routes
= (RouteListRef
)
1682 reallocf(routes
, (*info
->list_compute_size
)(how_many
));
1683 if (new_routes
== NULL
) {
1688 ROUTELIST_DEBUG(kDebugFlag8
, "increasing size from %d to %d\n",
1689 old_size
, how_many
);
1690 new_routes
->size
= how_many
;
1691 routes
= new_routes
;
1694 /* add/insert the new route */
1695 this_route
= RouteListAddRouteAtIndex(info
, routes
, this_route
, where
);
1696 this_route
->rank
= this_rank
;
1698 if (RANK_ASSERTION_MASK(this_rank
) == kRankAssertionNever
) {
1699 flags
|= kRouteFlagsIsScoped
;
1701 switch (scope_which
) {
1703 flags
|= kRouteFlagsIsScoped
;
1706 this_route
= RouteListGetRouteAtIndex(info
, routes
, where
+ 1);
1707 flags
|= kRouteFlagsIsScoped
;
1713 if (this_route
!= NULL
&& flags
!= 0) {
1714 this_route
->flags
|= flags
;
1722 * Function: RouteListAddRouteList
1724 * Invoke RouteListAddRoute for each route in the given list
1725 * 'service_routes' combining them into a combined list 'routes'.
1728 * See RouteListAddRoute for more information.
1731 RouteListAddRouteList(RouteListInfoRef info
,
1732 RouteListRef routes
, int init_size
,
1733 RouteListRef service_routes
, Rank rank
)
1738 for (i
= 0, scan
= RouteListGetFirstRoute(info
, service_routes
);
1739 i
< service_routes
->count
;
1740 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1744 && (service_routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
1745 /* only apply rank to first element of the list (default route) */
1749 this_rank
= RANK_INDEX_MASK(rank
) | RANK_ASSERTION_MASK(scan
->rank
);
1751 routes
= RouteListAddRoute(info
, routes
, init_size
, scan
, this_rank
);
1757 RouteAddInterfaceToDescription(RouteRef r
, CFMutableStringRef str
)
1759 char if_name
[IFNAMSIZ
];
1761 if (my_if_indextoname2(r
->ifindex
, if_name
) != NULL
) {
1762 CFStringAppendFormat(str
, NULL
,
1766 if (my_if_indextoname2(r
->exclude_ifindex
, if_name
) != NULL
) {
1767 CFStringAppendFormat(str
, NULL
,
1775 RouteAddFlagsToDescription(RouteRef r
, CFMutableStringRef str
)
1777 if ((r
->flags
& kRouteFlagsIsNULL
) != 0) {
1778 CFStringAppend(str
, CFSTR(" [null]"));
1781 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
1783 switch (rank_assertion
) {
1784 case kRankAssertionFirst
:
1785 CFStringAppend(str
, CFSTR(" [first]"));
1787 case kRankAssertionLast
:
1788 CFStringAppend(str
, CFSTR(" [last]"));
1790 case kRankAssertionNever
:
1791 CFStringAppend(str
, CFSTR(" [never]"));
1796 if ((r
->flags
& kRouteFlagsKernelManaged
) != 0) {
1797 CFStringAppend(str
, CFSTR(" [kern]"));
1799 if ((r
->flags
& kRouteFlagsIsScoped
) != 0) {
1800 CFStringAppend(str
, CFSTR(" [SCOPED]"));
1806 #if !TARGET_IPHONE_SIMULATOR
1808 RouteListFindRoute(RouteListInfoRef info
, RouteListRef routes
, RouteRef route
)
1811 RouteRef match
= NULL
;
1814 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
1816 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
1817 if ((*info
->route_equal
)(scan
, route
)) {
1827 kRouteLookupFlagsNone
= 0x0,
1828 kRouteLookupFlagsExcludeInterface
= 0x1
1832 RouteListLookup(RouteListInfoRef info
,
1833 RouteListRef routes
,
1834 const void * address
,
1837 RouteLookupFlags lookup_flags
)
1839 RouteRef best_match
= NULL
;
1843 for (i
= 0, candidate
= RouteListGetFirstRoute(info
, routes
);
1845 i
++, candidate
= RouteGetNextRoute(info
, candidate
)) {
1846 if (candidate
->ifindex
== 0 || candidate
->exclude_ifindex
!= 0) {
1847 /* ignore exclude routes */
1850 if ((lookup_flags
& kRouteLookupFlagsExcludeInterface
) != 0) {
1851 /* exclude interfaces with the same interface index */
1852 if (ifindex
== candidate
->ifindex
) {
1856 else if (ifindex
!= candidate
->ifindex
) {
1859 if ((candidate
->flags
& kRouteFlagsHasGateway
) != 0
1860 && RouteAddressCompare(info
,
1861 (*info
->route_gateway
)(candidate
),
1863 /* skip route whose gateway is the address we're looking for */
1866 if ((candidate
->flags
& kRouteFlagsIsHost
) != 0) {
1867 /* if host route and we're looking for an exact match */
1868 if (n_bits
== info
->all_bits_set
1869 && RouteAddressCompare(info
,
1870 (*info
->route_destination
)(candidate
),
1872 /* found exact match */
1873 best_match
= candidate
;
1879 /* verify that address is on the same subnet */
1880 if ((*info
->route_same_subnet
)(candidate
, address
) == FALSE
) {
1881 /* different subnet */
1885 if (candidate
->prefix_length
== n_bits
) {
1887 best_match
= candidate
;
1890 if (candidate
->prefix_length
> n_bits
) {
1891 /* matched too many bits */
1894 if (best_match
== NULL
1895 || candidate
->prefix_length
> best_match
->prefix_length
) {
1896 best_match
= candidate
;
1899 return (best_match
);
1904 * Function: RouteProcess
1906 * Function to process adding or removing the specified route.
1907 * In the case of adding, that may involve first processing the gateway
1908 * route (recursively).
1911 RouteProcess(RouteRef route
,
1913 RouteListApplyContextRef context
)
1915 RouteLog route_log
= context
->info
->route_log
;
1916 RouteApply route_apply
= context
->info
->route_apply
;
1917 RouteGateway route_gateway
= context
->info
->route_gateway
;
1921 case kRouteCommandAdd
:
1922 if ((route
->control_flags
& kControlFlagsProcessed
) != 0) {
1923 return ((route
->control_flags
& kControlFlagsAdded
) != 0);
1925 route
->control_flags
|= kControlFlagsProcessed
;
1926 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
1928 RouteRef gateway_route
;
1931 = RouteListLookup(context
->info
,
1932 context
->new_routes
,
1933 (*route_gateway
)(route
),
1934 context
->info
->all_bits_set
,
1936 kRouteLookupFlagsNone
);
1937 if (gateway_route
== NULL
) {
1938 (*route_log
)(LOG_NOTICE
, route
,
1939 "IPMonitor RouteProcess: no gateway route");
1942 #define MAX_RECURSE_DEPTH 10
1943 /* avoid infinite recursion */
1944 if (context
->depth
== MAX_RECURSE_DEPTH
) {
1945 (*route_log
)(LOG_NOTICE
, route
,
1946 "IPMonitor RouteProcess: "
1947 "routing loop detected, not adding");
1950 /* recurse to add gateway route */
1952 added
= RouteProcess(gateway_route
,
1956 if (added
== FALSE
) {
1957 (*route_log
)(LOG_NOTICE
, route
,
1958 "IPMonitor RouteProcess: failed to add");
1963 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1964 if (retval
== EEXIST
) {
1965 /* delete and add again */
1966 (void)(*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
1967 retval
= (*route_apply
)(route
, RTM_ADD
, context
->sockfd
);
1972 "IPMonitor RouteProcess failed to add route, %s:",
1974 (*route_log
)(LOG_NOTICE
, route
, NULL
);
1977 case EROUTENOTAPPLIED
:
1978 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1982 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
1983 snprintf(buf
, sizeof(buf
), "%sAdd new[%ld]",
1985 RouteListRouteIndex(context
->info
,
1986 context
->new_routes
,
1988 (*route_log
)(LOG_DEBUG
, route
, buf
);
1990 route
->control_flags
|= kControlFlagsAdded
;
1994 case kRouteCommandRemove
:
1995 retval
= (*route_apply
)(route
, RTM_DELETE
, context
->sockfd
);
1999 case EROUTENOTAPPLIED
:
2000 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2004 str
= (retval
== EROUTENOTAPPLIED
) ? "!" : "";
2005 snprintf(buf
, sizeof(buf
), "%sRemove old[%ld]%s",
2007 RouteListRouteIndex(context
->info
,
2008 context
->old_routes
,
2010 (retval
== ESRCH
) ? "(ESRCH)" : "");
2011 (*route_log
)(LOG_DEBUG
, route
, buf
);
2016 "IPMonitor RouteProcess failed to remove"
2017 " route, %s", strerror(retval
));
2018 (*route_log
)(LOG_NOTICE
, route
, NULL
);
2029 RouteListApply(RouteListInfoRef info
,
2030 RouteListRef old_routes
, RouteListRef new_routes
,
2033 RouteListApplyContext context
;
2037 if (old_routes
== new_routes
&& old_routes
== NULL
) {
2038 /* both old and new are NULL, so there's nothing to do */
2041 bzero(&context
, sizeof(context
));
2042 context
.old_routes
= old_routes
;
2043 context
.new_routes
= new_routes
;
2044 context
.sockfd
= sockfd
;
2045 context
.info
= info
;
2046 if (old_routes
!= NULL
) {
2047 for (i
= 0, scan
= RouteListGetFirstRoute(info
, old_routes
);
2048 i
< old_routes
->count
;
2049 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2050 RouteRef new_route
= NULL
;
2052 if (new_routes
!= NULL
) {
2053 new_route
= RouteListFindRoute(info
, new_routes
, scan
);
2055 if (new_route
== NULL
) {
2056 if ((scan
->control_flags
& kControlFlagsAdded
) != 0) {
2057 RouteProcess(scan
, kRouteCommandRemove
, &context
);
2062 if (new_routes
!= NULL
) {
2063 if (old_routes
!= NULL
) {
2064 /* preserve the control flags from any old routes */
2065 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2066 i
< new_routes
->count
;
2067 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2068 RouteRef old_route
= NULL
;
2070 old_route
= RouteListFindRoute(info
, old_routes
, scan
);
2071 if (old_route
!= NULL
) {
2072 /* preserve the control state in the new route */
2073 scan
->control_flags
= old_route
->control_flags
;
2077 /* add any routes that need to be added */
2078 for (i
= 0, scan
= RouteListGetFirstRoute(info
, new_routes
);
2079 i
< new_routes
->count
;
2080 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2081 if ((scan
->control_flags
& kControlFlagsProcessed
) != 0) {
2084 RouteProcess(scan
, kRouteCommandAdd
, &context
);
2090 * Function: RouteListFinalize
2092 * Look for excluded routes. If the excluded route does not have an assigned
2093 * interface, search for a route that *does not* go over the excluded
2096 * If the excluded route does have an assigned interface, search for a route
2097 * that *does* go over the assigned interface.
2099 * Set the gateway on the excluded route to match the gateway of the found
2103 RouteListFinalize(RouteListInfoRef info
, RouteListRef routes
)
2108 if (routes
== NULL
) {
2111 for (i
= 0, scan
= RouteListGetFirstRoute(info
, routes
);
2113 i
++, scan
= RouteGetNextRoute(info
, scan
)) {
2116 RouteLookupFlags flags
;
2118 if (scan
->exclude_ifindex
== 0) {
2121 if (scan
->ifindex
== 0) {
2122 ifindex
= scan
->exclude_ifindex
;
2123 flags
= kRouteLookupFlagsExcludeInterface
;
2126 ifindex
= scan
->ifindex
;
2127 flags
= kRouteLookupFlagsNone
;
2129 route
= RouteListLookup(info
, routes
,
2130 (*info
->route_destination
)(scan
),
2131 scan
->prefix_length
, ifindex
, flags
);
2132 if (route
== NULL
) {
2133 (*info
->route_log
)(LOG_NOTICE
, (RouteRef
)scan
,
2134 "IPMonitor: can't resolve excluded route");
2137 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
2138 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)scan
, "Excluded route");
2139 (*info
->route_log
)(LOG_DEBUG
, (RouteRef
)route
, "Resolved to");
2141 scan
->ifindex
= route
->ifindex
;
2142 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2143 (*info
->route_set_gateway
)(scan
, (*info
->route_gateway
)(route
));
2144 scan
->flags
|= kRouteFlagsHasGateway
;
2145 if (scan
->prefix_length
== info
->all_bits_set
) {
2146 scan
->flags
|= kRouteFlagsIsHost
;
2150 /* routes directly to interface */
2151 scan
->flags
&= ~(kRouteFlagsHasGateway
| kRouteFlagsIsHost
);
2157 #endif /* !TARGET_IPHONE_SIMULATOR */
2163 #define IPV4_ROUTE_ALL_BITS_SET 32
2165 static __inline__
struct in_addr
2166 subnet_addr(struct in_addr addr
, struct in_addr mask
)
2170 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
2175 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
2177 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
2178 CFStringAppendFormat(str
, NULL
,
2179 CFSTR("Host " IP_FORMAT
),
2183 CFStringAppendFormat(str
, NULL
,
2184 CFSTR("Net " IP_FORMAT
),
2186 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
2189 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
2190 CFStringAppendFormat(str
, NULL
,
2191 CFSTR(" Gate " IP_FORMAT
),
2192 IP_LIST(&r
->gateway
));
2194 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
2195 if (r
->ifa
.s_addr
!= 0) {
2196 CFStringAppendFormat(str
, NULL
,
2197 CFSTR(" Ifa " IP_FORMAT
),
2200 RouteAddFlagsToDescription((RouteRef
)r
, str
);
2205 IPv4RouteCopyDescription(RouteRef r
)
2207 CFMutableStringRef str
;
2209 str
= CFStringCreateMutable(NULL
, 0);
2210 IPv4RouteCopyDescriptionWithString((IPv4RouteRef
)r
, str
);
2214 #ifdef TEST_IPV4_ROUTELIST
2215 static CFMutableStringRef
2216 IPv4RouteListCopyDescription(IPv4RouteListRef routes
);
2219 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2221 CFStringRef str
= IPv4RouteCopyDescription(route
);
2224 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2227 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
2233 static __inline__
void
2234 IPv4RouteListPrint(IPv4RouteListRef routes
)
2236 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2238 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
2243 #else /* TEST_IPV4_ROUTELIST */
2245 static __inline__
void
2246 IPv4RouteLog(int level
, RouteRef route
, const char * msg
)
2248 CFStringRef str
= IPv4RouteCopyDescription(route
);
2251 my_log(level
, "%@", str
);
2254 my_log(level
, "%s: %@", msg
, str
);
2260 #endif /* TEST_IPV4_ROUTELIST */
2263 IPv4RouteIsEqual(RouteRef r_scan
, RouteRef r_route
)
2265 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2266 IPv4RouteRef scan
= (IPv4RouteRef
)r_scan
;
2268 return ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
2269 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
2270 && (scan
->ifindex
== route
->ifindex
)
2271 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
2272 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
2273 && (scan
->flags
== route
->flags
));
2276 static CFMutableStringRef
2277 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
2281 CFMutableStringRef str
;
2283 str
= CFStringCreateMutable(NULL
, 0);
2284 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
2286 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
2287 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
2288 IPv4RouteCopyDescriptionWithString(r
, str
);
2290 CFStringAppend(str
, CFSTR("\n}"));
2295 IPv4RouteListComputeSize(CFIndex n
)
2297 return (offsetof(IPv4RouteList
, list
[n
]));
2301 count_prefix_bits_set(uint32_t n
)
2304 const static int8_t bits
[16] = {
2323 for (count
= 0; n
!= 0; n
>>= 4) {
2324 int nbits
= bits
[n
& 0x0f];
2335 prefix_to_mask32(unsigned int prefix_length
)
2337 if (prefix_length
> 32 || prefix_length
== 0) {
2340 return (0xffffffff << (32 - prefix_length
));
2344 mask_get_prefix_length(struct in_addr mask
)
2348 count
= count_prefix_bits_set(mask
.s_addr
);
2352 val
= prefix_to_mask32(count
);
2353 if (ntohl(mask
.s_addr
) != val
) {
2354 /* expected mask based on prefix length doesn't match */
2362 IPv4RouteSetPrefixLength(IPv4RouteRef route
)
2366 length
= mask_get_prefix_length(route
->mask
);
2370 route
->prefix_length
= length
;
2375 IPv4RouteGateway(RouteRef r_route
)
2377 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2378 return (&route
->gateway
);
2382 IPv4RouteSetGateway(RouteRef r_route
, const void * address
)
2384 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2386 route
->gateway
= *((struct in_addr
*)address
);
2391 IPv4RouteDestination(RouteRef r_route
)
2393 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2394 return (&route
->dest
);
2398 IPv4RouteSameSubnet(RouteRef r_route
, const void * addr
)
2400 const struct in_addr
* address
;
2401 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2403 address
= (const struct in_addr
*)addr
;
2404 return ((address
->s_addr
& route
->mask
.s_addr
) == route
->dest
.s_addr
);
2408 * Define: ROUTE_MSG_ADDRS_SPACE
2410 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2411 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2412 * someone changes the code and doesn't think to modify this.
2414 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2415 + 2 * sizeof(struct sockaddr_dl) \
2418 struct rt_msghdr hdr
;
2419 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2423 * Function: IPv4RouteApply
2425 * Add or remove the specified route to/from the kernel routing table.
2428 IPv4RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
2432 IPv4RouteRef route
= (IPv4RouteRef
)r_route
;
2435 struct sockaddr_in
* in_p
;
2436 struct sockaddr_dl
* dl_p
;
2440 if (S_netboot
&& route
->dest
.s_addr
== 0) {
2441 /* don't touch the default route */
2442 return (EROUTENOTAPPLIED
);
2444 if ((route
->flags
& kRouteFlagsIsScoped
) != 0
2445 && !S_scopedroute
) {
2446 return (EROUTENOTAPPLIED
);
2448 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
2449 return (EROUTENOTAPPLIED
);
2451 if (route
->ifindex
== 0) {
2453 "IPMonitor IPv4RouteApply: " IP_FORMAT
2454 " no interface specified, ignoring",
2455 IP_LIST(&route
->dest
));
2459 #ifdef TEST_IPV4_ROUTELIST
2461 #else /* TEST_IPV4_ROUTELIST */
2463 #endif /* TEST_IPV4_ROUTELIST */
2465 memset(&rtmsg
, 0, sizeof(rtmsg
));
2466 rtmsg
.hdr
.rtm_type
= cmd
;
2467 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2468 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2469 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
2470 if (route
->ifa
.s_addr
!= 0) {
2471 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
2473 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2474 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
2475 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
2478 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
2479 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
2480 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
2483 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
2484 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
2486 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
2487 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
2488 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2491 rtaddr
.ptr
= rtmsg
.addrs
;
2494 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2495 rtaddr
.in_p
->sin_family
= AF_INET
;
2496 rtaddr
.in_p
->sin_addr
= route
->dest
;
2497 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2500 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2501 /* gateway is an IP address */
2502 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2503 rtaddr
.in_p
->sin_family
= AF_INET
;
2504 rtaddr
.in_p
->sin_addr
= route
->gateway
;
2505 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2508 /* gateway is the interface itself */
2509 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2510 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2511 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2512 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2516 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
2517 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2518 rtaddr
.in_p
->sin_family
= AF_INET
;
2519 rtaddr
.in_p
->sin_addr
= route
->mask
;
2520 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2524 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
2525 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2526 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2527 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
2528 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2530 /* interface address */
2531 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
2532 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2533 rtaddr
.in_p
->sin_family
= AF_INET
;
2534 rtaddr
.in_p
->sin_addr
= route
->ifa
;
2535 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2538 /* apply the route */
2539 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
2540 rtmsg
.hdr
.rtm_msglen
= len
;
2541 if (write(sockfd
, &rtmsg
, len
) == -1) {
2547 static const RouteListInfo IPv4RouteListInfo
= {
2548 IPv4RouteListComputeSize
,
2553 IPv4RouteSetGateway
,
2554 IPv4RouteDestination
,
2555 IPv4RouteSameSubnet
,
2557 IPv4RouteCopyDescription
,
2560 sizeof(struct in_addr
),
2561 IPV4_ROUTE_ALL_BITS_SET
2564 #if !TARGET_IPHONE_SIMULATOR
2565 static __inline__
void
2566 IPv4RouteListLog(int level
, IPv4RouteListRef routes
)
2568 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
2570 my_log(level
, "%@", str
);
2576 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
2579 RouteListApply(&IPv4RouteListInfo
,
2580 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
2586 IPv4RouteListFinalize(IPv4RouteListRef routes
)
2588 RouteListFinalize(&IPv4RouteListInfo
, (RouteListRef
)routes
);
2591 #endif /* !TARGET_IPHONE_SIMULATOR */
2593 #ifdef TEST_IPV4_ROUTELIST
2594 static IPv4RouteListRef
2595 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
2596 IPv4RouteListRef service_routes
, Rank rank
)
2598 return ((IPv4RouteListRef
)
2599 RouteListAddRouteList(&IPv4RouteListInfo
,
2600 (RouteListRef
)routes
, init_size
,
2601 (RouteListRef
)service_routes
, rank
));
2603 #endif /* TEST_IPV4_ROUTELIST */
2606 plist_get_string(CFDictionaryRef dict
, CFStringRef prop_name
,
2607 char * buf
, int buf_size
)
2611 val
= CFDictionaryGetValue(dict
, prop_name
);
2612 if (isA_CFString(val
) == NULL
) {
2615 if (CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingUTF8
)
2623 struct in_addr addr
;
2626 IFIndex exclude_ifindex
;
2627 IPv4RouteRef
* route_p
;
2630 } AddIPv4RouteContext
, * AddIPv4RouteContextRef
;
2633 AddIPv4Route(const void * value
, void * context
)
2635 AddIPv4RouteContextRef ctx
= (AddIPv4RouteContextRef
)context
;
2636 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
2637 IPv4RouteRef r
= *ctx
->route_p
;
2639 dict
= isA_CFDictionary(dict
);
2641 || !dict_get_ip(dict
, kSCPropNetIPv4RouteDestinationAddress
, &r
->dest
)
2642 || !dict_get_ip(dict
, kSCPropNetIPv4RouteSubnetMask
, &r
->mask
)) {
2643 /* one less route than we expected */
2645 my_log(LOG_NOTICE
, "IPMonitor: %s route is not a dictionary",
2649 my_log(LOG_NOTICE
, "IPMonitor: %s route is invalid, %@",
2654 if (IPv4RouteSetPrefixLength(r
) == FALSE
) {
2655 my_log(LOG_NOTICE
, "IPMonitor: %s route has invalid subnet mask, %@",
2659 r
->rank
= ctx
->rank
;
2660 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
2661 if (ctx
->ifindex
!= 0) {
2662 r
->ifindex
= ctx
->ifindex
;
2664 if (ctx
->exclude_ifindex
== 0
2665 && dict_get_ip(dict
,
2666 kSCPropNetIPv4RouteGatewayAddress
,
2668 r
->flags
|= kRouteFlagsHasGateway
;
2669 if (r
->prefix_length
== IPV4_ROUTE_ALL_BITS_SET
) {
2670 r
->flags
|= kRouteFlagsIsHost
;
2675 char ifname
[IFNAMSIZ
];
2677 if (plist_get_string(dict
, kSCPropNetIPv4RouteInterfaceName
,
2678 ifname
, sizeof(ifname
)) != NULL
) {
2681 ifindex
= my_if_nametoindex(ifname
);
2684 "IPMonitor %s: interface %s does not exist, %@",
2685 ctx
->descr
, ifname
, dict
);
2688 else if (ifindex
== ctx
->ifindex
) {
2690 "IPMonitor %s: interface %s unexpected, %@",
2691 ctx
->descr
, ifname
, dict
);
2694 r
->ifindex
= ifindex
;
2707 confirm_interface_name(CFDictionaryRef dict
, CFStringRef ifname
)
2709 CFStringRef confirmed_ifname
;
2710 boolean_t confirmed
;
2713 = CFDictionaryGetValue(dict
, kSCPropConfirmedInterfaceName
);
2714 if (isA_CFString(confirmed_ifname
) != NULL
) {
2715 confirmed
= CFEqual(confirmed_ifname
, ifname
);
2724 * Function: IPv4RouteListCreateWithDictionary
2727 * Given the service ipv4 entity dictionary, generate the list of routes.
2728 * Currently, this includes just the default route and subnet route,
2729 * if the service has a subnet mask.
2732 * If the passed in route_list is NULL or too small, this routine
2733 * allocates malloc'd memory to hold the routes.
2735 static IPv4RouteListRef
2736 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
2737 CFDictionaryRef dict
,
2738 CFNumberRef rank_assertion
)
2740 boolean_t add_default
= FALSE
;
2741 boolean_t add_router_subnet
= FALSE
;
2742 boolean_t add_subnet
= FALSE
;
2743 struct in_addr addr
= { 0 };
2744 CFArrayRef additional_routes
= NULL
;
2745 CFIndex additional_routes_count
;
2746 boolean_t allow_additional_routes
= FALSE
;
2747 boolean_t exclude_from_nwi
= FALSE
;
2748 CFArrayRef excluded_routes
= NULL
;
2749 CFIndex excluded_routes_count
;
2750 RouteFlags flags
= 0;
2752 char ifname
[IFNAMSIZ
];
2753 CFStringRef ifname_cf
;
2754 struct in_addr mask
= { 0 };
2756 int prefix_length
= 0;
2757 Rank primary_rank
= kRankAssertionDefault
;
2759 Rank rank
= kRankAssertionDefault
;
2760 struct in_addr router
= { 0 };
2761 struct in_addr subnet
= { 0 };
2766 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
2767 ifname
, sizeof(ifname
));
2768 if (ifname_cf
== NULL
) {
2771 ifindex
= my_if_nametoindex(ifname
);
2773 /* interface doesn't exist */
2776 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
2777 if (dict_get_ip(dict
, kSCPropNetIPv4Router
, &router
) == FALSE
) {
2778 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
2780 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
2781 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
2783 subnet
= subnet_addr(addr
, mask
);
2784 /* ignore link-local subnets, let IPConfiguration handle them for now */
2785 if (ntohl(subnet
.s_addr
) != IN_LINKLOCALNETNUM
) {
2786 prefix_length
= mask_get_prefix_length(mask
);
2787 if (prefix_length
< 0) {
2789 "IPMonitor: ignoring bad subnet mask "
2791 IP_LIST(&mask
), ifname
);
2798 else if (router
.s_addr
== 0) {
2799 exclude_from_nwi
= TRUE
;
2802 if (addr
.s_addr
== 0) {
2803 /* invalid/non-existent address */
2806 if (rank_assertion
!= NULL
) {
2807 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
2810 if (router
.s_addr
== 0) {
2811 /* if no router is configured, demote the rank if necessary */
2812 switch (primary_rank
) {
2813 case kRankAssertionLast
:
2814 case kRankAssertionNever
:
2815 case kRankAssertionScoped
:
2816 /* rank is already demoted */
2819 /* demote to RankLast */
2820 primary_rank
= kRankAssertionLast
;
2826 * If the router address is our address and the subnet mask is
2827 * not 255.255.255.255, assume all routes are local to the interface.
2829 if (addr
.s_addr
== router
.s_addr
2830 && mask
.s_addr
!= INADDR_BROADCAST
) {
2831 ; /* all routes local */
2834 flags
|= kRouteFlagsHasGateway
;
2836 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
2837 primary_rank
= kRankAssertionFirst
;
2841 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
2842 exclude_from_nwi
= TRUE
;
2843 flags
|= kRouteFlagsIsNULL
;
2846 switch (primary_rank
) {
2847 case kRankAssertionScoped
:
2848 /* Scoped means all routes for the service get scoped */
2849 primary_rank
= rank
= kRankAssertionNever
;
2850 flags
|= kRouteFlagsIsScoped
;
2852 case kRankAssertionNever
:
2853 /* Never means just the default route gets scoped */
2854 rank
= kRankAssertionLast
;
2855 flags
|= kRouteFlagsIsScoped
;
2858 rank
= primary_rank
;
2862 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2863 add_router_subnet
= TRUE
;
2867 if (ifindex
!= lo0_ifindex()) {
2871 if (allow_additional_routes
) {
2873 = CFDictionaryGetValue(dict
, kSCPropNetIPv4AdditionalRoutes
);
2874 additional_routes
= isA_CFArray(additional_routes
);
2875 if (additional_routes
!= NULL
) {
2876 additional_routes_count
= CFArrayGetCount(additional_routes
);
2877 n
+= additional_routes_count
;
2880 = CFDictionaryGetValue(dict
, kSCPropNetIPv4ExcludedRoutes
);
2881 excluded_routes
= isA_CFArray(excluded_routes
);
2882 if (excluded_routes
!= NULL
) {
2883 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
2884 n
+= excluded_routes_count
;
2887 if (routes
== NULL
|| routes
->size
< n
) {
2888 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
2891 bzero(routes
, IPv4RouteListComputeSize(n
));
2893 if (exclude_from_nwi
) {
2894 routes
->flags
|= kRouteListFlagsExcludeNWI
;
2897 /* start at the beginning */
2901 /* add the default route */
2902 routes
->flags
|= kRouteListFlagsHasDefault
;
2903 r
->ifindex
= ifindex
;
2906 if ((flags
& kRouteFlagsHasGateway
) != 0) {
2907 r
->gateway
= router
;
2912 r
->rank
= primary_rank
;
2916 /* add the subnet route */
2918 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2919 r
->flags
|= kRouteFlagsIsNULL
;
2921 r
->ifindex
= ifindex
;
2925 r
->prefix_length
= prefix_length
;
2931 /* add the router subnet route */
2932 if (add_router_subnet
) {
2933 if ((flags
& kRouteFlagsIsNULL
) != 0) {
2934 r
->flags
|= kRouteFlagsIsNULL
;
2936 r
->ifindex
= ifindex
;
2939 r
->mask
.s_addr
= INADDR_BROADCAST
;
2940 r
->prefix_length
= IPV4_ROUTE_ALL_BITS_SET
;
2946 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
2947 AddIPv4RouteContext context
;
2949 bzero(&context
, sizeof(context
));
2950 context
.count_p
= &routes
->count
;
2951 context
.route_p
= &r
;
2952 context
.rank
= rank
;
2954 /* additional routes */
2955 if (additional_routes
!= NULL
) {
2956 context
.ifindex
= ifindex
;
2957 context
.addr
= addr
;
2958 context
.descr
= "AdditionalRoutes";
2959 CFArrayApplyFunction(additional_routes
,
2960 CFRangeMake(0, additional_routes_count
),
2961 AddIPv4Route
, &context
);
2963 /* excluded routes */
2964 if (excluded_routes
!= NULL
) {
2965 context
.descr
= "ExcludedRoutes";
2966 /* exclude this interface */
2967 context
.ifindex
= 0;
2968 context
.exclude_ifindex
= ifindex
;
2969 CFArrayApplyFunction(excluded_routes
,
2970 CFRangeMake(0, excluded_routes_count
),
2971 AddIPv4Route
, &context
);
2980 #define IPV6_ROUTE_ALL_BITS_SET 128
2983 ipv6_prefix_length_is_valid(int prefix_length
)
2985 if (prefix_length
< 0 || prefix_length
> IPV6_ROUTE_ALL_BITS_SET
) {
2992 * from netinet6/in6.c
2995 in6_len2mask(struct in6_addr
* mask
, int len
)
2999 bzero(mask
, sizeof(*mask
));
3000 for (i
= 0; i
< len
/ 8; i
++)
3001 mask
->s6_addr
[i
] = 0xff;
3003 mask
->s6_addr
[i
] = (0xff00 >> (len
% 8)) & 0xff;
3007 in6_maskaddr(struct in6_addr
* addr
, const struct in6_addr
* mask
)
3011 for (i
= 0; i
< sizeof(addr
->s6_addr
); i
++) {
3012 addr
->s6_addr
[i
] &= mask
->s6_addr
[i
];
3018 in6_netaddr(struct in6_addr
* addr
, int len
)
3020 struct in6_addr mask
;
3022 in6_len2mask(&mask
, len
);
3023 in6_maskaddr(addr
, &mask
);
3028 in6_addr_scope_linklocal(struct in6_addr
* addr
, IFIndex ifindex
)
3030 if (IN6_IS_ADDR_LINKLOCAL(addr
)) {
3031 addr
->__u6_addr
.__u6_addr16
[1] = htons(ifindex
);
3037 string_append_in6_addr(CFMutableStringRef str
, const struct in6_addr
* addr
)
3039 char ntopbuf
[INET6_ADDRSTRLEN
];
3041 CFStringAppendCString(str
,
3042 inet_ntop(AF_INET6
, addr
, ntopbuf
, sizeof(ntopbuf
)),
3043 kCFStringEncodingASCII
);
3048 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r
, CFMutableStringRef str
)
3050 if ((r
->flags
& kRouteFlagsIsHost
) != 0) {
3051 CFStringAppend(str
, CFSTR("Host "));
3052 string_append_in6_addr(str
, &r
->dest
);
3055 CFStringAppend(str
, CFSTR("Net "));
3056 string_append_in6_addr(str
, &r
->dest
);
3057 CFStringAppendFormat(str
, NULL
, CFSTR("/%d"),
3060 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
3061 CFStringAppend(str
, CFSTR(" Gate "));
3062 string_append_in6_addr(str
, &r
->gateway
);
3064 RouteAddInterfaceToDescription((RouteRef
)r
, str
);
3065 if (!IN6_ARE_ADDR_EQUAL(&r
->ifa
, &in6addr_any
)) {
3066 CFStringAppend(str
, CFSTR(" Ifa "));
3067 string_append_in6_addr(str
, &r
->ifa
);
3069 RouteAddFlagsToDescription((RouteRef
)r
, str
);
3074 IPv6RouteCopyDescription(RouteRef r
)
3076 CFMutableStringRef str
;
3078 str
= CFStringCreateMutable(NULL
, 0);
3079 IPv6RouteCopyDescriptionWithString((IPv6RouteRef
)r
, str
);
3083 static CFMutableStringRef
3084 IPv6RouteListCopyDescription(IPv6RouteListRef routes
)
3088 CFMutableStringRef str
;
3090 str
= CFStringCreateMutable(NULL
, 0);
3091 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv6RouteList[%d]> = {"),
3093 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
3094 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
3095 IPv6RouteCopyDescriptionWithString(r
, str
);
3097 CFStringAppend(str
, CFSTR("\n}"));
3101 #ifdef TEST_IPV6_ROUTELIST
3104 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3106 CFStringRef str
= IPv6RouteCopyDescription(route
);
3109 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3112 SCPrint(TRUE
, stdout
, CFSTR("%s: %@\n"), msg
, str
);
3118 static __inline__
void
3119 IPv6RouteListPrint(IPv6RouteListRef routes
)
3121 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3123 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
3128 #else /* TEST_IPV6_ROUTELIST */
3130 static __inline__
void
3131 IPv6RouteLog(int level
, RouteRef route
, const char * msg
)
3133 CFStringRef str
= IPv6RouteCopyDescription(route
);
3136 my_log(level
, "%@", str
);
3139 my_log(level
, "%s: %@", msg
, str
);
3145 #endif /* TEST_IPV6_ROUTELIST */
3148 IPv6RouteListComputeSize(CFIndex n
)
3150 return (offsetof(IPv6RouteList
, list
[n
]));
3155 struct in6_addr
* addr
;
3158 IFIndex exclude_ifindex
;
3159 IPv6RouteRef
* route_p
;
3162 } AddIPv6RouteContext
, * AddIPv6RouteContextRef
;
3165 AddIPv6Route(const void * value
, void * context
)
3167 AddIPv6RouteContextRef ctx
= (AddIPv6RouteContextRef
)context
;
3168 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
3169 IPv6RouteRef r
= *ctx
->route_p
;
3171 dict
= isA_CFDictionary(dict
);
3173 || !dict_get_ipv6(dict
, kSCPropNetIPv6RouteDestinationAddress
, &r
->dest
)
3174 || !dict_get_int(dict
, kSCPropNetIPv6RoutePrefixLength
,
3176 || !ipv6_prefix_length_is_valid(r
->prefix_length
)) {
3177 /* one less route than we expected */
3179 my_log(LOG_NOTICE
, "IPMonitor: %s route is not a dictionary",
3183 my_log(LOG_NOTICE
, "IPMonitor: %s route is invalid, %@",
3188 r
->rank
= ctx
->rank
;
3189 r
->exclude_ifindex
= ctx
->exclude_ifindex
;
3190 if (ctx
->ifindex
!= 0) {
3191 r
->ifindex
= ctx
->ifindex
;
3192 r
->ifa
= *ctx
->addr
;
3193 if (ctx
->exclude_ifindex
== 0
3194 && dict_get_ipv6(dict
,
3195 kSCPropNetIPv6RouteGatewayAddress
,
3197 r
->flags
|= kRouteFlagsHasGateway
;
3198 if (r
->prefix_length
== IPV6_ROUTE_ALL_BITS_SET
) {
3199 r
->flags
|= kRouteFlagsIsHost
;
3204 char ifname
[IFNAMSIZ
];
3206 if (plist_get_string(dict
, kSCPropNetIPv6RouteInterfaceName
,
3207 ifname
, sizeof(ifname
)) != NULL
) {
3210 ifindex
= my_if_nametoindex(ifname
);
3213 "IPMonitor %s: interface %s does not exist, %@",
3214 ctx
->descr
, ifname
, dict
);
3217 else if (ifindex
== ctx
->ifindex
) {
3219 "IPMonitor %s: interface %s unexpected, %@",
3220 ctx
->descr
, ifname
, dict
);
3223 r
->ifindex
= ifindex
;
3236 * Function: IPv6RouteListCreateWithDictionary
3239 * Given the service IPv6 entity dictionary, generate the list of routes.
3242 * If the passed in route_list is NULL or too small, this routine
3243 * allocates malloc'd memory to hold the routes.
3245 static IPv6RouteListRef
3246 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes
,
3247 CFDictionaryRef dict
,
3248 CFNumberRef rank_assertion
)
3250 boolean_t add_default
= FALSE
;
3251 boolean_t add_prefix
= FALSE
;
3252 struct in6_addr addr
;
3253 CFArrayRef additional_routes
= NULL
;
3254 CFIndex additional_routes_count
;
3255 boolean_t allow_additional_routes
= FALSE
;
3256 boolean_t exclude_from_nwi
= FALSE
;
3257 CFArrayRef excluded_routes
= NULL
;
3258 CFIndex excluded_routes_count
;
3259 RouteFlags flags
= 0;
3261 char ifname
[IFNAMSIZ
];
3262 CFStringRef ifname_cf
;
3264 int prefix_length
= 0;
3265 Rank primary_rank
= kRankAssertionDefault
;
3267 Rank rank
= kRankAssertionDefault
;
3268 struct in6_addr router
= in6addr_any
;
3273 ifname_cf
= plist_get_string(dict
, kSCPropInterfaceName
,
3274 ifname
, sizeof(ifname
));
3275 if (ifname_cf
== NULL
) {
3278 ifindex
= my_if_nametoindex(ifname
);
3280 /* interface doesn't exist */
3283 allow_additional_routes
= confirm_interface_name(dict
, ifname_cf
);
3284 if (dict_get_ipv6(dict
, kSCPropNetIPv6Router
, &router
) == FALSE
) {
3285 (void)dict_get_first_ipv6(dict
, kSCPropNetIPv6DestAddresses
, &router
);
3287 if (dict_get_first_ipv6(dict
, kSCPropNetIPv6Addresses
, &addr
)) {
3288 if (IN6_IS_ADDR_UNSPECIFIED(&addr
)) {
3291 if (dict_get_first_int(dict
, kSCPropNetIPv6PrefixLength
,
3293 && !IN6_IS_ADDR_LINKLOCAL(&addr
)
3294 && ipv6_prefix_length_is_valid(prefix_length
)) {
3306 if (rank_assertion
!= NULL
) {
3307 (void)CFNumberGetValue(rank_assertion
, kCFNumberSInt32Type
,
3310 if (!IN6_IS_ADDR_UNSPECIFIED(&router
)) {
3311 if (ifindex
!= lo0_ifindex()) {
3316 * If the router address is our address and the prefix length is
3317 * not 128, assume all routes are local to the interface.
3319 if (IN6_ARE_ADDR_EQUAL(&router
, &addr
)
3320 && prefix_length
!= IPV6_ROUTE_ALL_BITS_SET
) {
3321 ; /* all routes local */
3324 flags
|= kRouteFlagsHasGateway
;
3326 if (rank_assertion
== NULL
&& get_override_primary(dict
)) {
3327 primary_rank
= kRankAssertionFirst
;
3330 if (S_dict_get_boolean(dict
, kIsNULL
, FALSE
)) {
3331 exclude_from_nwi
= TRUE
;
3332 flags
|= kRouteFlagsIsNULL
;
3335 switch (primary_rank
) {
3336 case kRankAssertionScoped
:
3337 /* Scoped means all routes for the service get scoped */
3338 primary_rank
= rank
= kRankAssertionNever
;
3339 flags
|= kRouteFlagsIsScoped
;
3341 case kRankAssertionNever
:
3342 /* Never means just the default route gets scoped */
3343 rank
= kRankAssertionLast
;
3344 flags
|= kRouteFlagsIsScoped
;
3347 rank
= primary_rank
;
3351 if (allow_additional_routes
) {
3353 = CFDictionaryGetValue(dict
, kSCPropNetIPv6AdditionalRoutes
);
3354 additional_routes
= isA_CFArray(additional_routes
);
3355 if (additional_routes
!= NULL
) {
3356 additional_routes_count
= CFArrayGetCount(additional_routes
);
3357 n
+= additional_routes_count
;
3359 excluded_routes
= CFDictionaryGetValue(dict
,
3360 kSCPropNetIPv6ExcludedRoutes
);
3361 excluded_routes
= isA_CFArray(excluded_routes
);
3362 if (excluded_routes
!= NULL
) {
3363 excluded_routes_count
= CFArrayGetCount(excluded_routes
);
3364 n
+= excluded_routes_count
;
3371 /* need IPv6LL subnet route */
3374 if (routes
== NULL
|| routes
->size
< n
) {
3375 routes
= (IPv6RouteListRef
)malloc(IPv6RouteListComputeSize(n
));
3378 bzero(routes
, IPv6RouteListComputeSize(n
));
3380 if (exclude_from_nwi
) {
3381 routes
->flags
|= kRouteListFlagsExcludeNWI
;
3384 /* start at the beginning */
3387 /* add the default route */
3388 routes
->flags
|= kRouteListFlagsHasDefault
;
3389 r
->ifindex
= ifindex
;
3392 if ((flags
& kRouteFlagsHasGateway
) != 0) {
3393 r
->gateway
= router
;
3398 r
->rank
= primary_rank
;
3399 if (S_scopedroute_v6
) {
3400 r
->flags
|= kRouteFlagsKernelManaged
;
3406 /* add IPv6LL route */
3407 r
->ifindex
= ifindex
;
3408 r
->dest
.s6_addr
[0] = 0xfe;
3409 r
->dest
.s6_addr
[1] = 0x80;
3410 r
->prefix_length
= 64;
3412 r
->flags
|= kRouteFlagsKernelManaged
;
3416 /* add the prefix route(s) */
3418 r
->flags
|= kRouteFlagsKernelManaged
;
3419 if ((flags
& kRouteFlagsIsNULL
) != 0) {
3420 r
->flags
|= kRouteFlagsIsNULL
;
3422 r
->ifindex
= ifindex
;
3425 in6_netaddr(&r
->dest
, prefix_length
);
3426 r
->prefix_length
= prefix_length
;
3432 if (additional_routes
!= NULL
|| excluded_routes
!= NULL
) {
3433 AddIPv6RouteContext context
;
3435 bzero(&context
, sizeof(context
));
3436 context
.count_p
= &routes
->count
;
3437 context
.route_p
= &r
;
3438 context
.rank
= rank
;
3440 /* additional routes */
3441 if (additional_routes
!= NULL
) {
3442 context
.ifindex
= ifindex
;
3443 context
.addr
= &addr
;
3444 context
.descr
= "AdditionalRoutes";
3445 CFArrayApplyFunction(additional_routes
,
3446 CFRangeMake(0, additional_routes_count
),
3447 AddIPv6Route
, &context
);
3449 /* excluded routes */
3450 if (excluded_routes
!= NULL
) {
3451 context
.descr
= "ExcludedRoutes";
3452 /* exclude this interface */
3453 context
.ifindex
= 0;
3454 context
.exclude_ifindex
= ifindex
;
3455 context
.addr
= NULL
;
3456 CFArrayApplyFunction(excluded_routes
,
3457 CFRangeMake(0, excluded_routes_count
),
3458 AddIPv6Route
, &context
);
3465 IPv6RouteGateway(RouteRef r_route
)
3467 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3468 return (&route
->gateway
);
3472 IPv6RouteSetGateway(RouteRef r_route
, const void * address
)
3474 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3476 route
->gateway
= *((struct in6_addr
*)address
);
3481 IPv6RouteDestination(RouteRef r_route
)
3483 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3484 return (&route
->dest
);
3487 static __inline__
int
3488 in6_addr_cmp(const struct in6_addr
* a
, const struct in6_addr
* b
)
3490 return (memcmp(a
->s6_addr
, b
->s6_addr
, sizeof(struct in6_addr
)));
3494 IPv6RouteIsEqual(RouteRef r_route1
, RouteRef r_route2
)
3496 IPv6RouteRef route1
= (IPv6RouteRef
)r_route1
;
3497 IPv6RouteRef route2
= (IPv6RouteRef
)r_route2
;
3499 return (route1
->prefix_length
== route2
->prefix_length
3500 && route1
->ifindex
== route2
->ifindex
3501 && route1
->flags
== route2
->flags
3502 && in6_addr_cmp(&route1
->dest
, &route2
->dest
) == 0
3503 && in6_addr_cmp(&route1
->ifa
, &route2
->ifa
) == 0
3504 && in6_addr_cmp(&route1
->gateway
, &route2
->gateway
) == 0);
3508 IPv6RouteSameSubnet(RouteRef r_route
, const void * addr
)
3510 const struct in6_addr
* address
= (const struct in6_addr
*)addr
;
3511 struct in6_addr netaddr
;
3512 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3515 in6_netaddr(&netaddr
, route
->prefix_length
);
3516 return (in6_addr_cmp(&netaddr
, &route
->dest
) == 0);
3520 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3523 struct rt_msghdr hdr
;
3524 char addrs
[V6_ROUTE_MSG_ADDRS_SPACE
];
3528 * Function: IPv6RouteApply
3530 * Add or remove the specified route to/from the kernel routing table.
3533 IPv6RouteApply(RouteRef r_route
, int cmd
, int sockfd
)
3537 IPv6RouteRef route
= (IPv6RouteRef
)r_route
;
3540 struct sockaddr_in6
* in_p
;
3541 struct sockaddr_dl
* dl_p
;
3545 if ((route
->flags
& kRouteFlagsIsScoped
) != 0
3546 && !S_scopedroute_v6
) {
3547 return (EROUTENOTAPPLIED
);
3549 if ((route
->flags
& kRouteFlagsKernelManaged
) != 0) {
3550 /* the kernel manages this route, don't touch it */
3551 return (EROUTENOTAPPLIED
);
3553 if ((route
->flags
& kRouteFlagsIsNULL
) != 0) {
3554 return (EROUTENOTAPPLIED
);
3556 if (route
->ifindex
== 0) {
3557 IPv6RouteLog(LOG_NOTICE
, (RouteRef
)route
,
3558 "IPMonitor IPv6RouteApply: no interface specified");
3562 #ifdef TEST_IPV6_ROUTELIST
3564 #else /* TEST_IPV6_ROUTELIST */
3566 #endif /* TEST_IPV6_ROUTELIST */
3568 memset(&rtmsg
, 0, sizeof(rtmsg
));
3569 rtmsg
.hdr
.rtm_type
= cmd
;
3570 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3571 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3572 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_IFP
;
3573 if (!IN6_IS_ADDR_UNSPECIFIED(&route
->ifa
)) {
3574 rtmsg
.hdr
.rtm_addrs
|= RTA_IFA
;
3576 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3577 if ((route
->flags
& kRouteFlagsIsHost
) != 0) {
3578 rtmsg
.hdr
.rtm_flags
|= RTF_HOST
;
3581 rtmsg
.hdr
.rtm_addrs
|= RTA_NETMASK
;
3582 if ((route
->flags
& kRouteFlagsHasGateway
) == 0) {
3583 rtmsg
.hdr
.rtm_flags
|= RTF_CLONING
;
3586 if ((route
->flags
& kRouteFlagsHasGateway
) != 0) {
3587 rtmsg
.hdr
.rtm_flags
|= RTF_GATEWAY
;
3589 if ((route
->flags
& kRouteFlagsIsScoped
) != 0) {
3590 rtmsg
.hdr
.rtm_index
= route
->ifindex
;
3591 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
3594 rtaddr
.ptr
= rtmsg
.addrs
;
3597 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3598 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3599 rtaddr
.in_p
->sin6_addr
= route
->dest
;
3600 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3601 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3604 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
3605 /* gateway is an IP address */
3606 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3607 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3608 rtaddr
.in_p
->sin6_addr
= route
->gateway
;
3609 in6_addr_scope_linklocal(&rtaddr
.in_p
->sin6_addr
, route
->ifindex
);
3610 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3613 /* gateway is the interface itself */
3614 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3615 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3616 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3617 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3621 if ((rtmsg
.hdr
.rtm_addrs
& RTA_NETMASK
) != 0) {
3622 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3623 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3624 in6_len2mask(&rtaddr
.in_p
->sin6_addr
, route
->prefix_length
);
3625 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3629 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFP
) != 0) {
3630 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3631 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3632 rtaddr
.dl_p
->sdl_index
= route
->ifindex
;
3633 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3635 /* interface address */
3636 if ((rtmsg
.hdr
.rtm_addrs
& RTA_IFA
) != 0) {
3637 rtaddr
.in_p
->sin6_len
= sizeof(*rtaddr
.in_p
);
3638 rtaddr
.in_p
->sin6_family
= AF_INET6
;
3639 rtaddr
.in_p
->sin6_addr
= route
->ifa
;
3640 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3643 /* apply the route */
3644 len
= (int)(sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
));
3645 rtmsg
.hdr
.rtm_msglen
= len
;
3646 if (write(sockfd
, &rtmsg
, len
) == -1) {
3652 static const RouteListInfo IPv6RouteListInfo
= {
3653 IPv6RouteListComputeSize
,
3658 IPv6RouteSetGateway
,
3659 IPv6RouteDestination
,
3660 IPv6RouteSameSubnet
,
3662 IPv6RouteCopyDescription
,
3665 sizeof(struct in6_addr
),
3666 IPV6_ROUTE_ALL_BITS_SET
3669 #ifdef TEST_IPV6_ROUTELIST
3670 static IPv6RouteListRef
3671 IPv6RouteListAddRouteList(IPv6RouteListRef routes
, int init_size
,
3672 IPv6RouteListRef service_routes
, Rank rank
)
3674 return ((IPv6RouteListRef
)
3675 RouteListAddRouteList(&IPv6RouteListInfo
,
3676 (RouteListRef
)routes
, init_size
,
3677 (RouteListRef
)service_routes
, rank
));
3679 #endif /* TEST_IPV6_ROUTELIST */
3681 #if !TARGET_IPHONE_SIMULATOR
3682 static __inline__
void
3683 IPv6RouteListLog(int level
, IPv6RouteListRef routes
)
3685 CFStringRef str
= IPv6RouteListCopyDescription(routes
);
3687 my_log(level
, "%@", str
);
3693 IPv6RouteListFinalize(IPv6RouteListRef routes
)
3695 RouteListFinalize(&IPv6RouteListInfo
, (RouteListRef
)routes
);
3700 IPv6RouteListApply(IPv6RouteListRef old_routes
, IPv6RouteListRef new_routes
,
3703 RouteListApply(&IPv6RouteListInfo
,
3704 (RouteListRef
)old_routes
, (RouteListRef
)new_routes
,
3708 #endif /* !TARGET_IPHONE_SIMULATOR */
3711 * Function: parse_component
3713 * Given a string 'key' and a string prefix 'prefix',
3714 * return the next component in the slash '/' separated
3718 * 1. key = "a/b/c" prefix = "a/"
3720 * 2. key = "a/b/c" prefix = "a/b/"
3723 static CF_RETURNS_RETAINED CFStringRef
3724 parse_component(CFStringRef key
, CFStringRef prefix
)
3726 CFMutableStringRef comp
;
3729 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
3732 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
3736 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
3737 range
= CFStringFind(comp
, CFSTR("/"), 0);
3738 if (range
.location
== kCFNotFound
) {
3741 range
.length
= CFStringGetLength(comp
) - range
.location
;
3742 CFStringDelete(comp
, range
);
3746 __private_extern__ boolean_t
3747 service_contains_protocol(CFDictionaryRef service
, int af
)
3749 boolean_t contains_protocol
= FALSE
;
3751 RouteListRef routes
;
3752 CFDictionaryRef dict
;
3754 entity
= (af
== AF_INET
) ? kSCEntNetIPv4
: kSCEntNetIPv6
;
3755 dict
= CFDictionaryGetValue(service
, entity
);
3759 routes
= ipdict_get_routelist(dict
);
3760 if (routes
== NULL
) {
3763 if ((routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
3766 contains_protocol
= TRUE
;
3769 return (contains_protocol
);
3773 static CFMutableDictionaryRef
3774 service_dict_copy(CFStringRef serviceID
)
3776 CFDictionaryRef d
= NULL
;
3777 CFMutableDictionaryRef service_dict
;
3779 /* create a modifyable dictionary, a copy or a new one */
3780 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3783 = CFDictionaryCreateMutable(NULL
, 0,
3784 &kCFTypeDictionaryKeyCallBacks
,
3785 &kCFTypeDictionaryValueCallBacks
);
3788 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
3790 return (service_dict
);
3794 log_service_entity(int level
, CFStringRef serviceID
, CFStringRef entity
,
3795 CFStringRef operation
, CFTypeRef val
)
3797 CFMutableStringRef this_val
= NULL
;
3803 if ((is_ipv4
= CFEqual(entity
, kSCEntNetIPv4
))
3804 || (is_ipv6
= CFEqual(entity
, kSCEntNetIPv6
))) {
3805 RouteListUnion routes
;
3807 routes
.ptr
= ipdict_get_routelist(val
);
3808 if (routes
.ptr
!= NULL
) {
3809 CFDictionaryRef service_dict
= NULL
;
3812 this_val
= IPv4RouteListCopyDescription(routes
.v4
);
3815 this_val
= IPv6RouteListCopyDescription(routes
.v6
);
3817 service_dict
= ipdict_get_service(val
);
3818 if (service_dict
!= NULL
) {
3819 CFStringAppendFormat(this_val
, NULL
,
3820 CFSTR("\n<Service> = %@"),
3828 val
= CFSTR("<none>");
3830 my_log(level
, "IPMonitor: serviceID %@ %@ %@ value = %@",
3831 serviceID
, operation
, entity
, val
);
3832 my_CFRelease(&this_val
);
3837 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
3840 boolean_t changed
= FALSE
;
3842 CFMutableDictionaryRef service_dict
;
3844 service_dict
= service_dict_copy(serviceID
);
3845 old_val
= CFDictionaryGetValue(service_dict
, entity
);
3846 if (new_val
== NULL
) {
3847 if (old_val
!= NULL
) {
3848 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3849 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3850 CFSTR("Removed:"), old_val
);
3852 CFDictionaryRemoveValue(service_dict
, entity
);
3857 if (old_val
== NULL
|| CFEqual(new_val
, old_val
) == FALSE
) {
3858 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3859 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3860 CFSTR("Changed: old"), old_val
);
3861 log_service_entity(LOG_DEBUG
, serviceID
, entity
,
3862 CFSTR("Changed: new"), new_val
);
3864 CFDictionarySetValue(service_dict
, entity
, new_val
);
3868 if (CFDictionaryGetCount(service_dict
) == 0) {
3869 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
3872 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
3874 my_CFRelease(&service_dict
);
3878 static CFDictionaryRef
3879 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
3881 CFDictionaryRef service_dict
;
3883 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
3884 if (service_dict
== NULL
) {
3887 return (CFDictionaryGetValue(service_dict
, entity
));
3890 #ifndef kSCPropNetHostname
3891 #define kSCPropNetHostname CFSTR("Hostname")
3896 copy_dhcp_hostname(CFStringRef serviceID
)
3898 CFDictionaryRef dict
= NULL
;
3899 CFStringRef hostname
= NULL
;
3900 CFDictionaryRef service_dict
= NULL
;
3902 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
3906 service_dict
= ipdict_get_service(dict
);
3907 if (service_dict
== NULL
) {
3910 hostname
= CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
3911 if (hostname
!= NULL
) {
3917 #if !TARGET_IPHONE_SIMULATOR
3919 static struct in6_addr
*
3920 ipv6_service_get_router(CFDictionaryRef service
,
3921 IFIndex
* ifindex_p
, CFStringRef
* ifname_p
)
3923 IPv6RouteListRef routes
;
3924 struct in6_addr
* router
= NULL
;
3926 routes
= ipdict_get_routelist(service
);
3928 && (routes
->flags
& kRouteListFlagsExcludeNWI
) == 0
3929 && (routes
->flags
& kRouteListFlagsHasDefault
) != 0) {
3930 router
= &routes
->list
[0].gateway
;
3931 if (*ifindex_p
== 0) {
3932 *ifindex_p
= routes
->list
[0].ifindex
;
3934 if (*ifname_p
== NULL
) {
3935 *ifname_p
= ipdict_get_ifname(service
);
3942 ipv6_service_update_router(CFStringRef serviceID
, CFDictionaryRef new_service
)
3944 IFIndex ifindex
= 0;
3945 CFStringRef ifname
= NULL
;
3946 char ntopbuf
[INET6_ADDRSTRLEN
];
3947 CFDictionaryRef old_service
;
3948 struct in6_addr
* old_router
;
3949 struct in6_addr
* new_router
;
3952 old_service
= service_dict_get(serviceID
, kSCEntNetIPv6
);
3953 old_router
= ipv6_service_get_router(old_service
, &ifindex
, &ifname
);
3954 new_router
= ipv6_service_get_router(new_service
, &ifindex
, &ifname
);
3955 if (ifname
== NULL
|| ifindex
== 0) {
3958 s
= inet6_dgram_socket();
3961 "IPMonitor: ipv6_service_update_router: socket failed, %s",
3965 /* remove the old router if it was defined */
3966 if (old_router
!= NULL
3967 && (new_router
== NULL
3968 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
3969 if (siocdrdel_in6(s
, ifindex
, old_router
) < 0) {
3971 || (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3972 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
3973 "IPMonitor: siocdrdel_in6(%@, %s) failed, %s",
3975 inet_ntop(AF_INET6
, old_router
,
3976 ntopbuf
, sizeof(ntopbuf
)),
3980 else if (S_IPMonitor_debug
& kDebugFlag1
) {
3982 "IPMonitor: %@ removed default route %s",
3984 inet_ntop(AF_INET6
, old_router
, ntopbuf
, sizeof(ntopbuf
)));
3987 /* add the new router if it is defined */
3988 if (new_router
!= NULL
3989 && (old_router
== NULL
3990 || !IN6_ARE_ADDR_EQUAL(old_router
, new_router
))) {
3991 if (siocdradd_in6(s
, ifindex
, new_router
, 0) < 0) {
3993 || (S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3994 my_log((errno
== EINVAL
) ? LOG_DEBUG
: LOG_ERR
,
3995 "IPMonitor: siocdradd_in6(%@, %s) failed, %s",
3997 inet_ntop(AF_INET6
, new_router
,
3998 ntopbuf
, sizeof(ntopbuf
)),
4002 else if (S_IPMonitor_debug
& kDebugFlag1
) {
4004 "IPMonitor: %@ added default route %s",
4006 inet_ntop(AF_INET6
, new_router
, ntopbuf
, sizeof(ntopbuf
)));
4014 #endif /* !TARGET_IPHONE_SIMULATOR */
4016 #define ALLOW_EMPTY_STRING 0x1
4018 static CF_RETURNS_RETAINED CFTypeRef
4019 sanitize_prop(CFTypeRef val
, uint32_t flags
)
4022 if (isA_CFString(val
)) {
4023 CFMutableStringRef str
;
4025 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
4026 CFStringTrimWhitespace(str
);
4027 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
4041 merge_array_prop(CFMutableDictionaryRef dict
,
4043 CFDictionaryRef state_dict
,
4044 CFDictionaryRef setup_dict
,
4048 CFMutableArrayRef merge_prop
;
4049 CFArrayRef setup_prop
= NULL
;
4050 CFArrayRef state_prop
= NULL
;
4052 if (setup_dict
!= NULL
) {
4053 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
4055 if (state_dict
!= NULL
) {
4056 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
4059 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
4063 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4064 if (setup_prop
!= NULL
) {
4068 n
= CFArrayGetCount(setup_prop
);
4069 for (i
= 0; i
< n
; i
++) {
4072 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
4073 val
= sanitize_prop(val
, flags
);
4075 CFArrayAppendValue(merge_prop
, val
);
4080 if (state_prop
!= NULL
4081 && (setup_prop
== NULL
|| S_append_state
)) {
4084 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
4086 n
= CFArrayGetCount(state_prop
);
4087 for (i
= 0; i
< n
; i
++) {
4090 val
= CFArrayGetValueAtIndex(state_prop
, i
);
4091 val
= sanitize_prop(val
, flags
);
4093 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
4094 CFArrayAppendValue(merge_prop
, val
);
4100 if (CFArrayGetCount(merge_prop
) > 0) {
4101 CFDictionarySetValue(dict
, key
, merge_prop
);
4103 CFRelease(merge_prop
);
4108 pick_prop(CFMutableDictionaryRef dict
,
4110 CFDictionaryRef state_dict
,
4111 CFDictionaryRef setup_dict
,
4114 CFTypeRef val
= NULL
;
4116 if (setup_dict
!= NULL
) {
4117 val
= CFDictionaryGetValue(setup_dict
, key
);
4118 val
= sanitize_prop(val
, flags
);
4120 if (val
== NULL
&& state_dict
!= NULL
) {
4121 val
= CFDictionaryGetValue(state_dict
, key
);
4122 val
= sanitize_prop(val
, flags
);
4125 CFDictionarySetValue(dict
, key
, val
);
4133 ** GetEntityChangesFunc functions
4135 #define IPV4_ROUTES_N_STATIC 5
4136 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4137 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4141 #define IPV4_ROUTES_BUF_DECL(routes) \
4142 IPv4RouteListRef routes; \
4143 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4145 routes = (IPv4RouteListRef)(void *)routes_buf; \
4146 routes->size = IPV4_ROUTES_N_STATIC; \
4147 routes->count = 0; \
4151 IPv4RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4154 CFDataRef routes_data
;
4155 IPV4_ROUTES_BUF_DECL(routes
);
4157 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4159 routes_data
= CFDataCreate(NULL
,
4161 IPv4RouteListComputeSize(r
->count
));
4169 return (routes_data
);
4171 #define IPV6_ROUTES_N_STATIC 3
4172 #define IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4173 (roundup(IPv6RouteListComputeSize(IPV6_ROUTES_N_STATIC), \
4177 #define IPV6_ROUTES_BUF_DECL(routes) \
4178 IPv6RouteListRef routes; \
4179 uint32_t routes_buf[IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4181 routes = (IPv6RouteListRef)(void *)routes_buf; \
4182 routes->size = IPV6_ROUTES_N_STATIC; \
4183 routes->count = 0; \
4187 IPv6RouteListDataCreate(CFDictionaryRef dict
, CFNumberRef rank_assertion
)
4190 CFDataRef routes_data
;
4191 IPV6_ROUTES_BUF_DECL(routes
);
4193 r
= IPv6RouteListCreateWithDictionary(routes
, dict
, rank_assertion
);
4195 routes_data
= CFDataCreate(NULL
,
4197 IPv6RouteListComputeSize(r
->count
));
4205 return (routes_data
);
4208 static CFDictionaryRef
4209 IPDictCreate(int af
, CFDictionaryRef state_dict
, CFDictionaryRef setup_dict
,
4210 CFNumberRef rank_assertion
)
4212 CFDictionaryRef aggregated_dict
= NULL
;
4213 CFDictionaryRef dict
;
4214 CFMutableDictionaryRef modified_dict
= NULL
;
4215 CFDataRef routes_data
;
4218 if (dict
!= NULL
&& setup_dict
!= NULL
) {
4219 /* look for keys in Setup: that override/merge with State: */
4220 CFArrayRef additional_routes
;
4223 CFStringRef router_prop
;
4224 CFStringRef route_list_prop
;
4229 router_prop
= kSCPropNetIPv4Router
;
4230 route_list_prop
= kSCPropNetIPv4AdditionalRoutes
;
4234 router_prop
= kSCPropNetIPv6Router
;
4235 route_list_prop
= kSCPropNetIPv6AdditionalRoutes
;
4238 router
= CFDictionaryGetValue(setup_dict
, router_prop
);
4240 && !cfstring_to_ipvx(af
, router
, &router_ip
, sizeof(router_ip
))) {
4244 /* AdditionalRoutes */
4246 = CFDictionaryGetValue(setup_dict
, route_list_prop
);
4247 additional_routes
= isA_CFArray(additional_routes
);
4249 if (router
!= NULL
|| additional_routes
!= NULL
) {
4250 modified_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
4251 if (router
!= NULL
) {
4252 CFDictionarySetValue(modified_dict
,
4256 if (additional_routes
!= NULL
) {
4257 CFArrayRef combined_routes
= NULL
;
4258 CFArrayRef state_routes
;
4261 = CFDictionaryGetValue(state_dict
,
4263 if (isA_CFArray(state_routes
) != NULL
) {
4265 = my_CFArrayCreateCombinedArray(additional_routes
,
4267 additional_routes
= combined_routes
;
4269 CFDictionarySetValue(modified_dict
,
4272 if (combined_routes
!= NULL
) {
4273 CFRelease(combined_routes
);
4276 dict
= modified_dict
;
4281 routes_data
= IPv4RouteListDataCreate(dict
, rank_assertion
);
4285 routes_data
= IPv6RouteListDataCreate(dict
, rank_assertion
);
4288 if (routes_data
!= NULL
) {
4289 aggregated_dict
= ipdict_create(dict
, routes_data
);
4290 CFRelease(routes_data
);
4292 if (modified_dict
!= NULL
) {
4293 CFRelease(modified_dict
);
4295 return (aggregated_dict
);
4299 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4300 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4302 CFDictionaryRef dict
= NULL
;
4303 boolean_t changed
= FALSE
;
4304 CFNumberRef rank_assertion
= NULL
;
4305 CFDictionaryRef service_options
;
4307 if (state_dict
== NULL
) {
4310 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4311 if (service_options
!= NULL
) {
4313 = CFDictionaryGetValue(service_options
,
4314 kServiceOptionRankAssertion
);
4316 dict
= IPDictCreate(AF_INET
, state_dict
, setup_dict
, rank_assertion
);
4319 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, dict
);
4321 /* clean up the rank too */
4322 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
4324 my_CFRelease(&dict
);
4329 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4330 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4332 CFDictionaryRef dict
= NULL
;
4333 boolean_t changed
= FALSE
;
4334 CFNumberRef rank_assertion
= NULL
;
4335 CFDictionaryRef service_options
;
4337 if (state_dict
== NULL
) {
4340 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
4341 if (service_options
!= NULL
) {
4343 = CFDictionaryGetValue(service_options
,
4344 kServiceOptionRankAssertion
);
4346 dict
= IPDictCreate(AF_INET6
, state_dict
, setup_dict
, rank_assertion
);
4349 #if !TARGET_IPHONE_SIMULATOR
4350 ipv6_service_update_router(serviceID
, dict
);
4351 #endif /* !TARGET_IPHONE_SIMULATOR */
4352 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, dict
);
4354 /* clean up the rank too */
4355 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
4357 my_CFRelease(&dict
);
4363 __private_extern__ CFDictionaryRef
4364 ipv4_dict_create(CFDictionaryRef state_dict
)
4366 return (IPDictCreate(AF_INET
, state_dict
, NULL
, NULL
));
4369 __private_extern__ CFDictionaryRef
4370 ipv6_dict_create(CFDictionaryRef state_dict
)
4372 return (IPDictCreate(AF_INET6
, state_dict
, NULL
, NULL
));
4375 #endif /* TEST_DNS */
4378 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
4379 CFMutableArrayRef out_servers
, CFStringRef interface
)
4384 count
= CFArrayGetCount(in_servers
);
4385 for (i
= 0; i
< count
; i
++) {
4387 struct in6_addr ipv6_addr
;
4388 struct in_addr ip_addr
;
4390 addr
= CFArrayGetValueAtIndex(in_servers
, i
);
4391 assert(addr
!= NULL
);
4393 if (cfstring_to_ip(addr
, &ip_addr
)) {
4395 if ((active_protos
& kProtocolFlagsIPv4
) == 0
4396 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
4397 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4399 "IPMonitor: no IPv4 connectivity, "
4400 "ignoring DNS server address " IP_FORMAT
,
4408 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
4410 if ((active_protos
& kProtocolFlagsIPv6
) == 0
4411 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
4412 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4413 char ntopbuf
[INET6_ADDRSTRLEN
];
4416 "IPMonitor: no IPv6 connectivity, "
4417 "ignoring DNS server address %s",
4418 inet_ntop(AF_INET6
, &ipv6_addr
,
4419 ntopbuf
, sizeof(ntopbuf
)));
4424 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
4425 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
4426 && (interface
!= NULL
)
4427 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
4428 // append interface name to IPv6 link local address
4429 addr
= CFStringCreateWithFormat(NULL
, NULL
,
4438 /* bad IP address */
4440 "IPMonitor: ignoring bad DNS server address '%@'",
4445 /* DNS server is valid and one we want */
4446 CFArrayAppendValue(out_servers
, addr
);
4453 merge_dns_servers(CFMutableDictionaryRef new_dict
,
4454 CFArrayRef state_servers
,
4455 CFArrayRef setup_servers
,
4457 ProtocolFlags active_protos
,
4458 CFStringRef interface
)
4460 CFMutableArrayRef dns_servers
;
4461 Boolean have_dns_setup
= FALSE
;
4463 if (state_servers
== NULL
&& setup_servers
== NULL
) {
4464 /* no DNS servers */
4467 dns_servers
= CFArrayCreateMutable(NULL
, 0,
4468 &kCFTypeArrayCallBacks
);
4469 if (setup_servers
!= NULL
) {
4470 accumulate_dns_servers(setup_servers
, active_protos
,
4471 dns_servers
, interface
);
4472 if (CFArrayGetCount(dns_servers
) > 0) {
4473 have_dns_setup
= TRUE
;
4476 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
4477 && state_servers
!= NULL
) {
4478 accumulate_dns_servers(state_servers
, active_protos
,
4483 * Here, we determine whether or not we want all queries for this DNS
4484 * configuration to be bound to the associated network interface.
4486 * For dynamically derived network configurations (i.e. from State:)
4487 * this would be the preferred option using the argument "Hey, the
4488 * server told us to use these servers on this network so let's not
4491 * But, when a DNS configuration has been provided by the user/admin
4492 * via the Network pref pane (i.e. from Setup:) we opt to not force
4493 * binding of the outbound queries. The simplest example why we take
4494 * this stance is with a multi-homing configuration. Consider a system
4495 * with one network service associated with "en0" and a second service
4496 * associated with "en1". The "en0" service has been set higher in
4497 * the network service order so it would be primary but the user/admin
4498 * wants the DNS queries to go to a server only accessible via "en1".
4499 * Without this exception we would take the DNS server addresses from
4500 * the Network pref pane (for "en0") and have the queries bound to
4501 * "en0" where they'd never reach their intended destination (via
4502 * "en1"). So, our exception to the rule is that we will not bind
4503 * user/admin configurations to any specific network interface.
4505 * We also add an exception to the "follow the dynamically derived
4506 * network configuration" path for on-the-fly (no Setup: content)
4509 if (CFArrayGetCount(dns_servers
) != 0) {
4510 CFDictionarySetValue(new_dict
,
4511 kSCPropNetDNSServerAddresses
, dns_servers
);
4512 if (have_setup
&& !have_dns_setup
) {
4513 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
4517 my_CFRelease(&dns_servers
);
4523 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4524 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4526 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4527 boolean_t changed
= FALSE
;
4529 Boolean have_setup
= FALSE
;
4530 CFStringRef interface
= NULL
;
4531 CFDictionaryRef ipv4
;
4532 CFDictionaryRef ipv6
;
4539 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
4540 { kSCPropNetDNSSortList
, 0, FALSE
},
4541 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4542 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
4544 CFMutableDictionaryRef new_dict
= NULL
;
4545 const CFStringRef pick_list
[] = {
4546 kSCPropNetDNSDomainName
,
4547 kSCPropNetDNSOptions
,
4548 kSCPropNetDNSSearchOrder
,
4549 kSCPropNetDNSServerPort
,
4550 kSCPropNetDNSServerTimeout
,
4551 kSCPropNetDNSServiceIdentifier
,
4552 kSCPropNetDNSSupplementalMatchDomainsNoSearch
,
4555 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4556 /* there is no DNS content */
4560 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4562 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
4565 active_protos
|= kProtocolFlagsIPv4
;
4566 interface
= ipdict_get_ifname(ipv4
);
4569 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4572 && (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
)
4576 active_protos
|= kProtocolFlagsIPv6
;
4577 if (interface
== NULL
) {
4578 interface
= ipdict_get_ifname(ipv6
);
4583 if (active_protos
== kProtocolFlagsNone
) {
4584 /* there is no IPv4 nor IPv6 */
4585 if (state_dict
== NULL
) {
4586 /* ... and no DNS content that we care about */
4592 /* merge DNS configuration */
4593 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
4594 &kCFTypeDictionaryKeyCallBacks
,
4595 &kCFTypeDictionaryValueCallBacks
);
4597 if (active_protos
== kProtocolFlagsNone
) {
4598 merge_dns_servers(new_dict
,
4599 my_CFDictionaryGetArray(state_dict
,
4600 kSCPropNetDNSServerAddresses
),
4603 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
4607 merge_dns_servers(new_dict
,
4608 my_CFDictionaryGetArray(state_dict
,
4609 kSCPropNetDNSServerAddresses
),
4610 my_CFDictionaryGetArray(setup_dict
,
4611 kSCPropNetDNSServerAddresses
),
4617 for (i
= 0; i
< countof(merge_list
); i
++) {
4618 merge_array_prop(new_dict
,
4622 merge_list
[i
].flags
,
4623 merge_list
[i
].append
);
4626 for (i
= 0; i
< countof(pick_list
); i
++) {
4634 if (active_protos
== kProtocolFlagsNone
) {
4635 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
4636 if (CFDictionaryContainsKey(new_dict
,
4637 kSCPropNetDNSSupplementalMatchDomains
)) {
4638 /* only keep State: supplemental */
4639 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
4640 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
4641 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
4642 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
4644 if ((interface
== NULL
) && (setup_dict
== NULL
) && (state_dict
!= NULL
)) {
4646 * for supplemental-only configurations, add any scoped (or
4647 * wild-card "*") interface
4649 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4651 } else if (CFDictionaryContainsKey(new_dict
, kSCPropNetDNSServiceIdentifier
) &&
4652 (interface
== NULL
) &&
4653 (state_dict
!= NULL
)) {
4654 interface
= CFDictionaryGetValue(state_dict
, kSCPropInterfaceName
);
4660 if (CFDictionaryGetCount(new_dict
) == 0) {
4661 my_CFRelease(&new_dict
);
4665 if (interface
!= NULL
) {
4666 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
4669 if (S_append_state
) {
4671 * ensure any specified domain name (e.g. the domain returned by
4672 * a DHCP server) is in the search list.
4674 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
4675 if (isA_CFString(domain
)) {
4678 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
4679 if (isA_CFArray(search
) &&
4680 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
4681 CFMutableArrayRef new_search
;
4683 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
4684 CFArrayAppendValue(new_search
, domain
);
4685 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
4686 my_CFRelease(&new_search
);
4692 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
4693 my_CFRelease(&new_dict
);
4698 merge_dict(const void *key
, const void *value
, void *context
)
4700 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
4702 CFDictionarySetValue(dict
, key
, value
);
4706 #define PROXY_AUTO_DISCOVERY_URL 252
4708 static CF_RETURNS_RETAINED CFStringRef
4709 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
4711 CFStringRef urlString
= NULL
;
4713 if (dhcp_options
!= NULL
) {
4716 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
4719 const UInt8
*urlBytes
;
4722 urlBytes
= CFDataGetBytePtr(data
);
4723 urlLen
= CFDataGetLength(data
);
4724 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
4725 // remove trailing NUL
4733 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
4735 urlString
= CFURLGetString(url
);
4736 if (urlString
!= NULL
) {
4737 CFRetain(urlString
);
4747 static CF_RETURNS_RETAINED CFStringRef
4751 CFStringRef urlString
= NULL
;
4753 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
4755 urlString
= CFURLGetString(url
);
4756 if (urlString
!= NULL
) {
4757 CFRetain(urlString
);
4766 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
4767 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
4769 ProtocolFlags active_protos
= kProtocolFlagsNone
;
4770 boolean_t changed
= FALSE
;
4771 CFStringRef interface
= NULL
;
4772 CFDictionaryRef ipv4
;
4773 CFDictionaryRef ipv6
;
4774 CFMutableDictionaryRef new_dict
= NULL
;
4780 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
4781 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
4784 CFStringRef key1
; /* an "enable" key */
4788 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
4789 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
4790 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
4791 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
4792 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
4793 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
4794 { kSCPropNetProxiesProxyAutoConfigEnable
,
4795 kSCPropNetProxiesProxyAutoConfigURLString
,
4796 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
4797 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
4802 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
4803 /* there is no proxy content */
4806 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
4807 if (ipdict_get_routelist(ipv4
) != NULL
) {
4808 active_protos
|= kProtocolFlagsIPv4
;
4809 interface
= ipdict_get_ifname(ipv4
);
4811 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
4812 if (ipdict_get_routelist(ipv6
) != NULL
) {
4813 active_protos
|= kProtocolFlagsIPv6
;
4814 if (interface
== NULL
) {
4815 interface
= ipdict_get_ifname(ipv6
);
4818 if (active_protos
== kProtocolFlagsNone
) {
4819 /* there is no IPv4 nor IPv6 */
4820 if (state_dict
== NULL
) {
4821 /* ... and no proxy content that we care about */
4827 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
4829 CFMutableDictionaryRef setup_copy
;
4832 * Merge the per-service "Setup:" and "State:" proxy information with
4833 * the "Setup:" information always taking precedence. Additionally,
4834 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
4835 * Port) is defined than all of the values for that group will be
4836 * used. That is, we don't allow mixing some of the values from
4837 * the "Setup:" keys and others from the "State:" keys.
4839 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
4840 for (i
= 0; i
< countof(merge_list
); i
++) {
4841 merge_array_prop(new_dict
,
4845 merge_list
[i
].flags
,
4846 merge_list
[i
].append
);
4849 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
4850 for (i
= 0; i
< countof(pick_list
); i
++) {
4851 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
4853 * if a "Setup:" enabled key has been provided than we want to
4854 * ignore all of the "State:" keys
4856 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
4857 if (pick_list
[i
].key2
!= NULL
) {
4858 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
4860 if (pick_list
[i
].key3
!= NULL
) {
4861 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
4863 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
4864 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
4865 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
4867 * if a "Setup:" enabled key has not been provided and we have
4868 * some" "State:" keys than we remove all of of "Setup:" keys
4870 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
4871 if (pick_list
[i
].key2
!= NULL
) {
4872 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
4874 if (pick_list
[i
].key3
!= NULL
) {
4875 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
4880 /* merge the "Setup:" keys */
4881 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
4882 CFRelease(setup_copy
);
4884 else if (setup_dict
!= NULL
) {
4885 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
4887 else if (state_dict
!= NULL
) {
4888 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
4891 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
4892 CFRelease(new_dict
);
4896 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
4897 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
4901 if (new_dict
!= NULL
) {
4902 CFDictionaryRef dhcp_options
;
4904 CFNumberRef wpad
= NULL
;
4905 int wpadEnabled
= 0;
4906 CFStringRef wpadURL
= NULL
;
4908 if (CFDictionaryGetValueIfPresent(new_dict
,
4909 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
4910 (const void **)&num
) &&
4911 isA_CFNumber(num
)) {
4912 /* if we have a WPAD key */
4914 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
4915 /* if we don't like the enabled key/value */
4923 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
4924 if (!isA_CFNumber(num
) ||
4925 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
4926 /* if we don't like the enabled key/value */
4933 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
4934 if (pacURL
!= NULL
) {
4935 if (!isA_CFString(pacURL
)) {
4936 /* if we don't like the PAC URL */
4942 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
4943 if (!isA_CFString(pacJS
)) {
4944 /* if we don't have (or like) the PAC JavaScript */
4952 * we already have a PAC URL so disable WPAD.
4959 * if WPAD is enabled and we don't already have a PAC URL then
4960 * we check for a DHCP provided URL. If not available, we use
4961 * a PAC URL pointing to a well-known file (wpad.dat) on a
4962 * well-known host (wpad.<domain>).
4964 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
4965 wpadURL
= wpadURL_dhcp(dhcp_options
);
4966 if (wpadURL
== NULL
) {
4967 wpadURL
= wpadURL_dns();
4969 if (wpadURL
== NULL
) {
4970 wpadEnabled
= 0; /* if we don't have a WPAD URL */
4975 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
4976 CFDictionarySetValue(new_dict
,
4977 kSCPropNetProxiesProxyAutoConfigEnable
,
4980 CFDictionarySetValue(new_dict
,
4981 kSCPropNetProxiesProxyAutoConfigURLString
,
4988 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
4989 CFDictionarySetValue(new_dict
,
4990 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
4997 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
4998 my_CFRelease(&new_dict
);
5002 #if !TARGET_OS_IPHONE
5004 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
5005 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
5007 boolean_t changed
= FALSE
;
5009 CFMutableDictionaryRef new_dict
= NULL
;
5010 const CFStringRef pick_list
[] = {
5011 kSCPropNetSMBNetBIOSName
,
5012 kSCPropNetSMBNetBIOSNodeType
,
5013 #ifdef ADD_NETBIOS_SCOPE
5014 kSCPropNetSMBNetBIOSScope
,
5015 #endif // ADD_NETBIOS_SCOPE
5016 kSCPropNetSMBWorkgroup
,
5019 if (state_dict
== NULL
&& setup_dict
== NULL
) {
5020 /* there is no SMB */
5023 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
5024 && service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
) {
5025 /* there is no IPv4 or IPv6 */
5029 /* merge SMB configuration */
5030 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5031 &kCFTypeDictionaryKeyCallBacks
,
5032 &kCFTypeDictionaryValueCallBacks
);
5033 merge_array_prop(new_dict
,
5034 kSCPropNetSMBWINSAddresses
,
5039 for (i
= 0; i
< countof(pick_list
); i
++) {
5047 if (CFDictionaryGetCount(new_dict
) == 0) {
5048 my_CFRelease(&new_dict
);
5053 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
5054 my_CFRelease(&new_dict
);
5057 #endif /* !TARGET_OS_IPHONE */
5060 services_info_get_interface(CFDictionaryRef services_info
,
5061 CFStringRef serviceID
)
5063 CFStringRef interface
= NULL
;
5064 CFDictionaryRef ipv4_dict
;
5066 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
5068 if (ipv4_dict
!= NULL
) {
5069 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
5072 CFDictionaryRef ipv6_dict
;
5074 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
5076 if (ipv6_dict
!= NULL
) {
5077 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
5084 static const struct {
5085 const CFStringRef
* entityName
;
5086 const CFStringRef
* statusKey
;
5087 } transientServiceInfo
[] = {
5088 { &kSCEntNetIPSec
, &kSCPropNetIPSecStatus
},
5089 { &kSCEntNetPPP
, &kSCPropNetPPPStatus
},
5090 { &kSCEntNetVPN
, &kSCPropNetVPNStatus
},
5094 get_transient_status_changes(CFStringRef serviceID
,
5095 CFDictionaryRef services_info
)
5097 boolean_t changed
= FALSE
;
5100 for (i
= 0; i
< countof(transientServiceInfo
); i
++) {
5101 CFDictionaryRef dict
;
5102 CFNumberRef status
= NULL
;
5103 CFMutableDictionaryRef ts_dict
= NULL
;
5105 dict
= get_service_state_entity(services_info
, serviceID
,
5106 *transientServiceInfo
[i
].entityName
);
5109 status
= CFDictionaryGetValue(dict
,
5110 *transientServiceInfo
[i
].statusKey
);
5113 if (isA_CFNumber(status
) != NULL
) {
5114 ts_dict
= CFDictionaryCreateMutable(NULL
,
5116 &kCFTypeDictionaryKeyCallBacks
,
5117 &kCFTypeDictionaryValueCallBacks
);
5118 CFDictionaryAddValue(ts_dict
,
5119 *transientServiceInfo
[i
].statusKey
,
5123 if (service_dict_set(serviceID
, *transientServiceInfo
[i
].entityName
,
5128 if (ts_dict
!= NULL
) {
5136 service_is_expensive(CFStringRef serviceID
, CFDictionaryRef services_info
)
5139 boolean_t is_expensive
= FALSE
;
5141 ifname
= services_info_get_interface(services_info
, serviceID
);
5142 if (ifname
!= NULL
) {
5143 CFDictionaryRef if_dict
;
5146 key
= interface_entity_key_copy(ifname
, kSCEntNetLink
);
5147 if_dict
= CFDictionaryGetValue(services_info
, key
);
5149 if (isA_CFDictionary(if_dict
) != NULL
) {
5150 CFBooleanRef expensive
;
5152 expensive
= CFDictionaryGetValue(if_dict
, kSCPropNetLinkExpensive
);
5153 if (isA_CFBoolean(expensive
) != NULL
5154 && CFBooleanGetValue(expensive
)) {
5155 is_expensive
= TRUE
;
5159 return (is_expensive
);
5163 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
5164 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
5166 boolean_t changed
= FALSE
;
5167 boolean_t ip_is_coupled
= FALSE
;
5168 CFMutableDictionaryRef new_dict
= NULL
;
5169 Rank rank_assertion
= kRankAssertionDefault
;
5170 Boolean rank_assertion_is_set
= FALSE
;
5171 CFStringRef setup_rank
= NULL
;
5172 CFStringRef state_rank
= NULL
;
5175 if (state_options
!= NULL
) {
5176 CFBooleanRef coupled
;
5179 = CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
5180 state_rank
= isA_CFString(state_rank
);
5181 coupled
= CFDictionaryGetValue(state_options
, kIPIsCoupled
);
5182 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5183 ip_is_coupled
= TRUE
;
5186 if (setup_options
!= NULL
) {
5187 CFBooleanRef coupled
;
5190 = CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
5191 setup_rank
= isA_CFString(setup_rank
);
5192 coupled
= CFDictionaryGetValue(setup_options
, kIPIsCoupled
);
5193 if (isA_CFBoolean(coupled
) != NULL
&& CFBooleanGetValue(coupled
)) {
5194 ip_is_coupled
= TRUE
;
5198 if (ip_is_coupled
== FALSE
) {
5199 ip_is_coupled
= service_is_expensive(serviceID
, services_info
);
5201 if (setup_rank
!= NULL
|| state_rank
!= NULL
) {
5202 /* rank assertion is set on the service */
5203 Rank setup_assertion
;
5204 Rank state_assertion
;
5205 Boolean state_assertion_is_set
= FALSE
;
5207 setup_assertion
= PrimaryRankGetRankAssertion(setup_rank
, NULL
);
5208 state_assertion
= PrimaryRankGetRankAssertion(state_rank
,
5209 &state_assertion_is_set
);
5210 if (setup_assertion
> state_assertion
) {
5211 rank_assertion
= setup_assertion
;
5212 rank_assertion_is_set
= TRUE
;
5214 else if (state_assertion_is_set
) {
5215 rank_assertion
= state_assertion
;
5216 rank_assertion_is_set
= TRUE
;
5220 if (rank_assertion_is_set
== FALSE
) {
5221 /* check for a rank assertion on the interface */
5222 CFStringRef interface
;
5224 interface
= services_info_get_interface(services_info
, serviceID
);
5225 if (interface
!= NULL
) {
5226 CFNumberRef if_rank
= NULL
;
5228 if (S_if_rank_dict
!= NULL
) {
5229 if_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
5232 = InterfaceRankGetRankAssertion(if_rank
,
5233 &rank_assertion_is_set
);
5234 if (S_IPMonitor_debug
& kDebugFlag1
) {
5236 "serviceID %@ interface %@ rank = %@",
5237 serviceID
, interface
, if_rank
);
5243 if (rank_assertion_is_set
|| ip_is_coupled
) {
5244 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
5245 &kCFTypeDictionaryKeyCallBacks
,
5246 &kCFTypeDictionaryValueCallBacks
);
5247 if (rank_assertion_is_set
) {
5248 CFNumberRef new_rank
;
5250 new_rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
,
5251 (const void *)&rank_assertion
);
5252 CFDictionarySetValue(new_dict
, kServiceOptionRankAssertion
,
5254 CFRelease(new_rank
);
5256 if (ip_is_coupled
) {
5257 CFDictionarySetValue(new_dict
, kIPIsCoupled
, kCFBooleanTrue
);
5260 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
5261 my_CFRelease(&new_dict
);
5266 add_service_keys(CFStringRef serviceID
,
5267 CFMutableArrayRef keys
, CFMutableArrayRef patterns
)
5272 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
5276 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
5277 key
= setup_service_key(serviceID
, *entityTypeNames
[i
]);
5278 CFArrayAppendValue(keys
, key
);
5280 key
= state_service_key(serviceID
, *entityTypeNames
[i
]);
5281 CFArrayAppendValue(keys
, key
);
5285 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
5286 CFArrayAppendValue(patterns
, key
);
5289 key
= setup_service_key(serviceID
, NULL
);
5290 CFArrayAppendValue(patterns
, key
);
5292 key
= state_service_key(serviceID
, NULL
);
5293 CFArrayAppendValue(patterns
, key
);
5300 add_transient_status_keys(CFStringRef service_id
, CFMutableArrayRef patterns
)
5304 for (i
= 0; i
< countof(transientServiceInfo
); i
++) {
5305 CFStringRef pattern
;
5307 pattern
= state_service_key(service_id
,
5308 *transientServiceInfo
[i
].entityName
);
5309 CFArrayAppendValue(patterns
, pattern
);
5316 static const CFStringRef
*reachabilitySetupKeys
[] = {
5318 &kSCEntNetInterface
,
5325 add_reachability_patterns(CFMutableArrayRef patterns
)
5329 for (i
= 0; i
< countof(reachabilitySetupKeys
); i
++) {
5330 CFStringRef pattern
;
5331 pattern
= setup_service_key(kSCCompAnyRegex
, *reachabilitySetupKeys
[i
]);
5332 CFArrayAppendValue(patterns
, pattern
);
5339 add_vpn_pattern(CFMutableArrayRef patterns
)
5341 CFStringRef pattern
;
5343 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
5344 CFArrayAppendValue(patterns
, pattern
);
5349 add_interface_link_pattern(CFMutableArrayRef patterns
)
5351 CFStringRef pattern
;
5353 pattern
= interface_entity_key_copy(kSCCompAnyRegex
, kSCEntNetLink
);
5354 CFArrayAppendValue(patterns
, pattern
);
5358 static CFDictionaryRef
5359 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
5362 CFMutableArrayRef get_keys
;
5363 CFMutableArrayRef get_patterns
;
5364 CFDictionaryRef info
;
5367 count
= CFArrayGetCount(service_list
);
5368 get_keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5369 get_patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5371 CFArrayAppendValue(get_keys
, S_setup_global_ipv4
);
5372 CFArrayAppendValue(get_keys
, S_multicast_resolvers
);
5373 CFArrayAppendValue(get_keys
, S_private_resolvers
);
5375 for (s
= 0; s
< count
; s
++) {
5376 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
5378 add_service_keys(serviceID
, get_keys
, get_patterns
);
5379 add_transient_status_keys(serviceID
, get_keys
);
5382 add_reachability_patterns(get_patterns
);
5384 add_vpn_pattern(get_patterns
);
5386 add_interface_link_pattern(get_patterns
);
5388 info
= SCDynamicStoreCopyMultiple(session
, get_keys
, get_patterns
);
5389 my_CFRelease(&get_keys
);
5390 my_CFRelease(&get_patterns
);
5394 #if !TARGET_IPHONE_SIMULATOR
5397 multicast_route(int sockfd
, int cmd
)
5401 bzero(&route
, sizeof(route
));
5402 route
.dest
.s_addr
= htonl(INADDR_UNSPEC_GROUP
);
5403 route
.mask
.s_addr
= htonl(IN_CLASSD_NET
);
5404 route
.ifindex
= lo0_ifindex();
5405 return (IPv4RouteApply((RouteRef
)&route
, cmd
, sockfd
));
5408 #endif /* !TARGET_IPHONE_SIMULATOR */
5410 #if !TARGET_IPHONE_SIMULATOR
5413 set_ipv6_default_interface(IFIndex ifindex
)
5415 struct in6_ndifreq ndifreq
;
5417 boolean_t success
= FALSE
;
5419 bzero((char *)&ndifreq
, sizeof(ndifreq
));
5420 strlcpy(ndifreq
.ifname
, kLoopbackInterface
, sizeof(ndifreq
.ifname
));
5422 ndifreq
.ifindex
= ifindex
;
5425 ndifreq
.ifindex
= lo0_ifindex();
5427 sock
= inet6_dgram_socket();
5430 "IPMonitor: set_ipv6_default_interface: socket failed, %s",
5434 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
5436 "IPMonitor: ioctl(SIOCSDEFIFACE_IN6) failed, %s",
5447 #endif /* !TARGET_IPHONE_SIMULATOR */
5449 #if !TARGET_OS_IPHONE
5450 static __inline__
void
5453 (void)unlink(VAR_RUN_RESOLV_CONF
);
5457 set_dns(CFArrayRef val_search_domains
,
5458 CFStringRef val_domain_name
,
5459 CFArrayRef val_servers
,
5460 CFArrayRef val_sortlist
)
5462 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
5464 /* publish new resolv.conf */
5469 SCPrint(TRUE
, f
, CFSTR("#\n"));
5470 SCPrint(TRUE
, f
, CFSTR("# Mac OS X Notice\n"));
5471 SCPrint(TRUE
, f
, CFSTR("#\n"));
5472 SCPrint(TRUE
, f
, CFSTR("# This file is not used by the host name and address resolution\n"));
5473 SCPrint(TRUE
, f
, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
5474 SCPrint(TRUE
, f
, CFSTR("# this Mac OS X system.\n"));
5475 SCPrint(TRUE
, f
, CFSTR("#\n"));
5476 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
5477 SCPrint(TRUE
, f
, CFSTR("#\n"));
5479 if (isA_CFArray(val_search_domains
)) {
5480 SCPrint(TRUE
, f
, CFSTR("search"));
5481 n
= CFArrayGetCount(val_search_domains
);
5482 for (i
= 0; i
< n
; i
++) {
5485 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
5486 if (isA_CFString(domain
)) {
5487 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
5490 SCPrint(TRUE
, f
, CFSTR("\n"));
5492 else if (isA_CFString(val_domain_name
)) {
5493 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
5496 if (isA_CFArray(val_servers
)) {
5497 n
= CFArrayGetCount(val_servers
);
5498 for (i
= 0; i
< n
; i
++) {
5499 CFStringRef nameserver
;
5501 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
5502 if (isA_CFString(nameserver
)) {
5503 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
5508 if (isA_CFArray(val_sortlist
)) {
5509 SCPrint(TRUE
, f
, CFSTR("sortlist"));
5510 n
= CFArrayGetCount(val_sortlist
);
5511 for (i
= 0; i
< n
; i
++) {
5512 CFStringRef address
;
5514 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
5515 if (isA_CFString(address
)) {
5516 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
5519 SCPrint(TRUE
, f
, CFSTR("\n"));
5523 rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
5527 #endif /* !TARGET_OS_IPHONE */
5530 service_get_ip_is_coupled(CFStringRef serviceID
)
5532 CFDictionaryRef dict
;
5533 boolean_t ip_is_coupled
= FALSE
;
5535 dict
= service_dict_get(serviceID
, kSCEntNetService
);
5537 if (CFDictionaryContainsKey(dict
, kIPIsCoupled
)) {
5538 ip_is_coupled
= TRUE
;
5541 return (ip_is_coupled
);
5545 my_CFStringCreateWithInAddr(struct in_addr ip
)
5549 str
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR(IP_FORMAT
), IP_LIST(&ip
));
5554 my_CFStringCreateWithIn6Addr(const struct in6_addr
* ip
)
5556 char ntopbuf
[INET6_ADDRSTRLEN
];
5558 (void)inet_ntop(AF_INET6
, ip
, ntopbuf
, sizeof(ntopbuf
));
5559 return (CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%s"), ntopbuf
));
5563 * Function: update_ipv4
5565 * Update the IPv4 configuration based on the latest information.
5566 * Publish the State:/Network/Global/IPv4 information, and update the
5567 * IPv4 routing table.
5570 update_ipv4(CFStringRef primary
,
5571 IPv4RouteListRef new_routelist
,
5572 keyChangeListRef keys
)
5574 #if !TARGET_IPHONE_SIMULATOR
5576 #endif /* !TARGET_IPHONE_SIMULATOR */
5579 if (new_routelist
!= NULL
&& primary
!= NULL
) {
5580 const char * ifn_p
= NULL
;
5581 char ifname
[IFNAMSIZ
];
5583 CFMutableDictionaryRef dict
= NULL
;
5585 dict
= CFDictionaryCreateMutable(NULL
, 0,
5586 &kCFTypeDictionaryKeyCallBacks
,
5587 &kCFTypeDictionaryValueCallBacks
);
5588 /* the first entry is the default route */
5589 r
= new_routelist
->list
;
5590 if (r
->gateway
.s_addr
!= 0) {
5593 str
= my_CFStringCreateWithInAddr(r
->gateway
);
5594 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, str
);
5597 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
5598 if (ifn_p
!= NULL
) {
5599 CFStringRef ifname_cf
;
5601 ifname_cf
= CFStringCreateWithCString(NULL
,
5603 kCFStringEncodingASCII
);
5604 if (ifname_cf
!= NULL
) {
5605 CFDictionarySetValue(dict
,
5606 kSCDynamicStorePropNetPrimaryInterface
,
5608 CFRelease(ifname_cf
);
5611 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
5613 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
5617 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
5621 #if !TARGET_IPHONE_SIMULATOR
5622 sockfd
= open_routing_socket();
5624 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5625 if (S_ipv4_routelist
== NULL
) {
5626 my_log(LOG_DEBUG
, "Old Routes = <none>");
5629 my_log(LOG_DEBUG
, "Old Routes = ");
5630 IPv4RouteListLog(LOG_DEBUG
, S_ipv4_routelist
);
5632 if (new_routelist
== NULL
) {
5633 my_log(LOG_DEBUG
, "New Routes = <none>");
5636 my_log(LOG_DEBUG
, "New Routes = ");
5637 IPv4RouteListLog(LOG_DEBUG
, new_routelist
);
5640 /* go through routelist and bind any unbound routes */
5641 IPv4RouteListFinalize(new_routelist
);
5642 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
, sockfd
);
5643 if (new_routelist
!= NULL
) {
5644 (void)multicast_route(sockfd
, RTM_DELETE
);
5647 (void)multicast_route(sockfd
, RTM_ADD
);
5651 if (S_ipv4_routelist
!= NULL
) {
5652 free(S_ipv4_routelist
);
5654 S_ipv4_routelist
= new_routelist
;
5655 #else /* !TARGET_IPHONE_SIMULATOR */
5656 if (new_routelist
!= NULL
) {
5657 free(new_routelist
);
5659 #endif /* !TARGET_IPHONE_SIMULATOR */
5665 * Function: update_ipv6
5667 * Update the IPv6 configuration based on the latest information.
5668 * Publish the State:/Network/Global/IPv6 information, and update the
5669 * IPv6 routing table.
5672 update_ipv6(CFStringRef primary
,
5673 IPv6RouteListRef new_routelist
,
5674 keyChangeListRef keys
)
5676 #if !TARGET_IPHONE_SIMULATOR
5678 #endif /* !TARGET_IPHONE_SIMULATOR */
5681 if (new_routelist
!= NULL
&& primary
!= NULL
) {
5682 const char * ifn_p
= NULL
;
5683 char ifname
[IFNAMSIZ
];
5685 CFMutableDictionaryRef dict
= NULL
;
5687 dict
= CFDictionaryCreateMutable(NULL
, 0,
5688 &kCFTypeDictionaryKeyCallBacks
,
5689 &kCFTypeDictionaryValueCallBacks
);
5690 /* the first entry is the default route */
5691 r
= new_routelist
->list
;
5692 if ((r
->flags
& kRouteFlagsHasGateway
) != 0) {
5695 router
= my_CFStringCreateWithIn6Addr(&r
->gateway
);
5696 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
, router
);
5699 ifn_p
= my_if_indextoname(r
->ifindex
, ifname
);
5700 if (ifn_p
!= NULL
) {
5701 CFStringRef ifname_cf
;
5703 ifname_cf
= CFStringCreateWithCString(NULL
,
5705 kCFStringEncodingASCII
);
5706 if (ifname_cf
!= NULL
) {
5707 CFDictionarySetValue(dict
,
5708 kSCDynamicStorePropNetPrimaryInterface
,
5710 CFRelease(ifname_cf
);
5713 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
5715 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
5717 #if !TARGET_IPHONE_SIMULATOR
5718 if (S_scopedroute_v6
) {
5719 set_ipv6_default_interface(r
->ifindex
);
5721 #endif /* !TARGET_IPHONE_SIMULATOR */
5724 #if !TARGET_IPHONE_SIMULATOR
5725 if (S_scopedroute_v6
) {
5726 set_ipv6_default_interface(0);
5728 #endif /* !TARGET_IPHONE_SIMULATOR */
5729 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
5733 #if !TARGET_IPHONE_SIMULATOR
5734 sockfd
= open_routing_socket();
5736 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5737 if (S_ipv6_routelist
== NULL
) {
5738 my_log(LOG_DEBUG
, "Old Routes = <none>");
5741 my_log(LOG_DEBUG
, "Old Routes = ");
5742 IPv6RouteListLog(LOG_DEBUG
, S_ipv6_routelist
);
5744 if (new_routelist
== NULL
) {
5745 my_log(LOG_DEBUG
, "New Routes = <none>");
5748 my_log(LOG_DEBUG
, "New Routes = ");
5749 IPv6RouteListLog(LOG_DEBUG
, new_routelist
);
5752 /* go through routelist and bind any unbound routes */
5753 IPv6RouteListFinalize(new_routelist
);
5754 IPv6RouteListApply(S_ipv6_routelist
, new_routelist
, sockfd
);
5757 if (S_ipv6_routelist
!= NULL
) {
5758 free(S_ipv6_routelist
);
5760 S_ipv6_routelist
= new_routelist
;
5761 #else /* !TARGET_IPHONE_SIMULATOR */
5762 if (new_routelist
!= NULL
) {
5763 free(new_routelist
);
5765 #endif /* !TARGET_IPHONE_SIMULATOR */
5771 update_dns(CFDictionaryRef services_info
,
5772 CFStringRef primary
,
5773 keyChangeListRef keys
)
5775 Boolean changed
= FALSE
;
5776 CFDictionaryRef dict
= NULL
;
5778 if (primary
!= NULL
) {
5779 CFDictionaryRef service_dict
;
5781 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
5782 if (service_dict
!= NULL
) {
5783 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
5787 if (!_SC_CFEqual(S_dns_dict
, dict
)) {
5789 #if !TARGET_OS_IPHONE
5791 #endif /* !TARGET_OS_IPHONE */
5792 keyChangeListRemoveValue(keys
, S_state_global_dns
);
5794 CFMutableDictionaryRef new_dict
;
5796 #if !TARGET_OS_IPHONE
5797 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
5798 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
5799 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
5800 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
5801 #endif /* !TARGET_OS_IPHONE */
5802 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
5803 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
5804 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
5805 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
5806 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
5807 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
5808 CFRelease(new_dict
);
5813 if (dict
!= NULL
) CFRetain(dict
);
5814 if (S_dns_dict
!= NULL
) CFRelease(S_dns_dict
);
5821 update_dnsinfo(CFDictionaryRef services_info
,
5822 CFStringRef primary
,
5823 keyChangeListRef keys
,
5824 CFArrayRef service_order
)
5827 CFDictionaryRef dict
= NULL
;
5828 CFArrayRef multicastResolvers
;
5829 CFArrayRef privateResolvers
;
5831 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
5832 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
5834 if (primary
!= NULL
) {
5835 CFDictionaryRef service_dict
;
5837 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
5838 if (service_dict
!= NULL
) {
5839 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
5843 changed
= dns_configuration_set(dict
,
5844 S_service_state_dict
,
5849 keyChangeListNotifyKey(keys
, S_state_global_dns
);
5855 update_nwi(nwi_state_t state
)
5857 unsigned char signature
[CC_SHA1_DIGEST_LENGTH
];
5858 static unsigned char signature_last
[CC_SHA1_DIGEST_LENGTH
];
5860 _nwi_state_signature(state
, signature
, sizeof(signature
));
5861 if (bcmp(signature
, signature_last
, sizeof(signature
)) == 0) {
5865 // save [new] signature
5866 bcopy(signature
, signature_last
, sizeof(signature
));
5868 // save [new] configuration
5869 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
5870 my_log(LOG_DEBUG
, "Updating network information");
5871 S_nwi_state_dump(state
);
5873 if (_nwi_state_store(state
) == FALSE
) {
5874 my_log(LOG_ERR
, "Notifying nwi_state_store failed");
5881 update_proxies(CFDictionaryRef services_info
,
5882 CFStringRef primary
,
5883 keyChangeListRef keys
,
5884 CFArrayRef service_order
)
5886 Boolean changed
= FALSE
;
5887 CFDictionaryRef dict
= NULL
;
5888 CFDictionaryRef new_dict
;
5890 if (primary
!= NULL
) {
5891 CFDictionaryRef service_dict
;
5893 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
5894 if (service_dict
!= NULL
) {
5895 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
5899 new_dict
= proxy_configuration_update(dict
,
5900 S_service_state_dict
,
5903 if (!_SC_CFEqual(S_proxies_dict
, new_dict
)) {
5904 if (new_dict
== NULL
) {
5905 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
5907 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
5912 if (S_proxies_dict
!= NULL
) CFRelease(S_proxies_dict
);
5913 S_proxies_dict
= new_dict
;
5918 #if !TARGET_OS_IPHONE
5920 update_smb(CFDictionaryRef services_info
,
5921 CFStringRef primary
,
5922 keyChangeListRef keys
)
5924 Boolean changed
= FALSE
;
5925 CFDictionaryRef dict
= NULL
;
5927 if (primary
!= NULL
) {
5928 CFDictionaryRef service_dict
;
5930 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
5931 if (service_dict
!= NULL
) {
5932 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
5936 if (!_SC_CFEqual(S_smb_dict
, dict
)) {
5938 keyChangeListRemoveValue(keys
, S_state_global_smb
);
5940 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
5945 if (dict
!= NULL
) CFRetain(dict
);
5946 if (S_smb_dict
!= NULL
) CFRelease(S_smb_dict
);
5951 #endif /* !TARGET_OS_IPHONE */
5954 get_service_rank(CFArrayRef order
, int n_order
, CFStringRef serviceID
)
5957 Rank rank
= kRankIndexMask
;
5959 if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
5960 for (i
= 0; i
< n_order
; i
++) {
5961 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
5966 if (CFEqual(serviceID
, s
)) {
5976 ** Service election:
5979 * Function: rank_dict_get_service_rank
5981 * Retrieve the service rank in the given dictionary.
5984 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
5989 rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
5990 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
5992 CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
);
5998 * Function: rank_dict_set_service_rank
6000 * Save the results of ranking the service so we can look it up later without
6001 * repeating all of the ranking code.
6004 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
6005 CFStringRef serviceID
, Rank rank_val
)
6009 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
6011 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
6017 static const CFStringRef
*transientInterfaceEntityNames
[] = {
6023 CollectTransientServices(const void * key
,
6028 CFStringRef service
= key
;
6029 CFMutableArrayRef vif_setup_keys
= context
;
6031 /* This service is either a vpn type service or a comm center service */
6032 if (!CFStringHasPrefix(service
, kSCDynamicStoreDomainSetup
)) {
6036 for (i
= 0; i
< countof(transientInterfaceEntityNames
); i
++) {
6037 if (CFStringHasSuffix(service
, *transientInterfaceEntityNames
[i
])) {
6038 CFArrayAppendValue(vif_setup_keys
, service
);
6047 static SCNetworkReachabilityFlags
6048 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info
,
6049 CFStringRef service_id
,
6051 CFStringRef vpn_setup_key
)
6054 CFDictionaryRef dict
;
6055 SCNetworkReachabilityFlags flags
= 0;
6058 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6059 kSCDynamicStoreDomainSetup
,
6061 kSCEntNetInterface
);
6062 dict
= CFDictionaryGetValue(services_info
, key
);
6065 if (isA_CFDictionary(dict
)
6066 && CFDictionaryContainsKey(dict
, kSCPropNetInterfaceDeviceName
)) {
6068 flags
= (kSCNetworkReachabilityFlagsReachable
6069 | kSCNetworkReachabilityFlagsTransientConnection
6070 | kSCNetworkReachabilityFlagsConnectionRequired
);
6072 if (CFEqual(entity
, kSCEntNetPPP
)) {
6074 CFDictionaryRef p_dict
= CFDictionaryGetValue(services_info
, vpn_setup_key
);
6076 if (!isA_CFDictionary(p_dict
)) {
6080 // get PPP dial-on-traffic status
6081 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
6082 if (isA_CFNumber(num
)) {
6085 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
6087 flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6097 S_dict_get_boolean(CFDictionaryRef dict
, CFStringRef key
, Boolean def_value
)
6099 Boolean ret
= def_value
;
6104 val
= CFDictionaryGetValue(dict
, key
);
6105 if (isA_CFBoolean(val
) != NULL
) {
6106 ret
= CFBooleanGetValue(val
);
6114 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info
,
6115 SCNetworkReachabilityFlags
*reach_flags_v4
,
6116 SCNetworkReachabilityFlags
*reach_flags_v6
)
6120 CFMutableArrayRef vif_setup_keys
;
6122 vif_setup_keys
= CFArrayCreateMutable(NULL
,
6124 &kCFTypeArrayCallBacks
);
6125 CFDictionaryApplyFunction(services_info
, CollectTransientServices
,
6127 count
= CFArrayGetCount(vif_setup_keys
);
6128 for (i
= 0; i
< count
; i
++) {
6129 CFArrayRef components
= NULL
;
6131 CFStringRef service_id
;
6132 CFStringRef vif_setup_key
;
6134 vif_setup_key
= CFArrayGetValueAtIndex(vif_setup_keys
, i
);
6137 * setup key in the following format:
6138 * Setup:/Network/Service/<Service ID>/<Entity>
6140 components
= CFStringCreateArrayBySeparatingStrings(NULL
, vif_setup_key
, CFSTR("/"));
6142 if (CFArrayGetCount(components
) != 5) {
6143 // invalid Setup key encountered
6147 /* service id is the 3rd element */
6148 service_id
= CFArrayGetValueAtIndex(components
, 3);
6150 /* entity id is the 4th element */
6151 entity
= CFArrayGetValueAtIndex(components
, 4);
6154 if (CFEqual(entity
, kSCEntNetPPP
)) {
6155 SCNetworkReachabilityFlags flags
;
6158 flags
= GetReachabilityFlagsFromVPN(services_info
,
6163 /* Check for the v4 reachability flags */
6164 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6165 kSCDynamicStoreDomainSetup
,
6169 if (CFDictionaryContainsKey(services_info
, key
)) {
6170 *reach_flags_v4
|= flags
;
6171 my_log(LOG_DEBUG
, "Service %@ setting ipv4 reach flags: %d", service_id
, *reach_flags_v4
);
6176 /* Check for the v6 reachability flags */
6177 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6178 kSCDynamicStoreDomainSetup
,
6182 if (CFDictionaryContainsKey(services_info
, key
)) {
6183 *reach_flags_v6
|= flags
;
6184 my_log(LOG_DEBUG
, "Service %@ setting ipv6 reach flags: %d", service_id
, *reach_flags_v6
);
6189 if (components
!= NULL
) {
6190 CFRelease(components
);
6196 if (components
!= NULL
) {
6197 CFRelease(components
);
6201 CFRelease(vif_setup_keys
);
6205 static SCNetworkReachabilityFlags
6206 GetReachFlagsFromStatus(CFStringRef entity
, int status
)
6208 SCNetworkReachabilityFlags flags
= 0;
6210 if (CFEqual(entity
, kSCEntNetPPP
)) {
6213 /* if we're really UP and RUNNING */
6216 /* if we're effectively UP and RUNNING */
6219 /* if we're not connected at all */
6220 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6222 case PPP_STATERESERVED
:
6223 // if we're not connected at all
6224 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6227 /* if we're in the process of [dis]connecting */
6228 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6232 else if (CFEqual(entity
, kSCEntNetIPSec
)) {
6234 case IPSEC_RUNNING
:
6235 /* if we're really UP and RUNNING */
6238 /* if we're not connected at all */
6239 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6242 /* if we're in the process of [dis]connecting */
6243 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6247 else if (CFEqual(entity
, kSCEntNetVPN
)) {
6250 /* if we're really UP and RUNNING */
6255 case VPN_UNLOADING
:
6256 /* if we're not connected at all */
6257 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6260 /* if we're in the process of [dis]connecting */
6261 flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
6269 VPNAttributesGet(CFStringRef service_id
,
6270 CFDictionaryRef services_info
,
6271 SCNetworkReachabilityFlags
*flags
,
6272 CFStringRef
*server_address
,
6276 CFDictionaryRef entity_dict
;
6278 CFDictionaryRef p_state
= NULL
;
6280 CFStringRef transient_entity
= NULL
;
6282 if (af
== AF_INET
) {
6283 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv4
);
6286 entity_dict
= service_dict_get(service_id
, kSCEntNetIPv6
);
6288 entity_dict
= ipdict_get_service(entity_dict
);
6289 if (entity_dict
== NULL
) {
6293 for (i
= 0; i
< countof(transientServiceInfo
); i
++) {
6294 CFStringRef entity
= *transientServiceInfo
[i
].entityName
;
6296 p_state
= service_dict_get(service_id
, entity
);
6298 /* ensure that this is a VPN Type service */
6299 if (isA_CFDictionary(p_state
)) {
6300 transient_entity
= entity
;
6305 /* Did we find a vpn type service? If not, we are done.*/
6306 if (transient_entity
== NULL
) {
6310 *flags
|= (kSCNetworkReachabilityFlagsReachable
6311 | kSCNetworkReachabilityFlagsTransientConnection
);
6313 /* Get the Server Address */
6314 if (server_address
!= NULL
) {
6315 *server_address
= CFDictionaryGetValue(entity_dict
,
6316 CFSTR("ServerAddress"));
6317 *server_address
= isA_CFString(*server_address
);
6318 if (*server_address
!= NULL
) {
6319 CFRetain(*server_address
);
6324 if (!CFDictionaryGetValueIfPresent(p_state
,
6325 kSCPropNetVPNStatus
, // IPSecStatus, PPPStatus, VPNStatus
6326 (const void **)&num
) ||
6327 !isA_CFNumber(num
) ||
6328 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &status
)) {
6332 *flags
|= GetReachFlagsFromStatus(transient_entity
, status
);
6333 if (CFEqual(transient_entity
, kSCEntNetPPP
)) {
6335 CFDictionaryRef p_setup
;
6338 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
6339 kSCDynamicStoreDomainSetup
,
6342 p_setup
= CFDictionaryGetValue(services_info
, key
);
6345 /* get dial-on-traffic status */
6346 if (isA_CFDictionary(p_setup
) &&
6347 CFDictionaryGetValueIfPresent(p_setup
,
6348 kSCPropNetPPPDialOnDemand
,
6349 (const void **)&num
) &&
6350 isA_CFNumber(num
) &&
6351 CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) &&
6352 (ppp_demand
!= 0)) {
6353 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
6354 if (status
== PPP_IDLE
) {
6355 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
6363 typedef struct ElectionInfo
{
6369 ElectionResultsRef results
;
6370 CFMutableDictionaryRef rank_dict
;
6371 } ElectionInfo
, * ElectionInfoRef
;
6373 typedef CFDictionaryApplierFunction ElectionFuncRef
;
6376 CandidateRelease(CandidateRef candidate
)
6378 my_CFRelease(&candidate
->serviceID
);
6379 my_CFRelease(&candidate
->if_name
);
6380 my_CFRelease(&candidate
->signature
);
6385 CandidateCopy(CandidateRef dest
, CandidateRef src
)
6388 if (dest
->serviceID
) {
6389 CFRetain(dest
->serviceID
);
6391 if (dest
->if_name
) {
6392 CFRetain(dest
->if_name
);
6394 if(dest
->signature
) {
6395 CFRetain(dest
->signature
);
6400 static ElectionResultsRef
6401 ElectionResultsAlloc(int af
, int size
)
6403 ElectionResultsRef results
;
6405 results
= (ElectionResultsRef
)malloc(ElectionResultsComputeSize(size
));
6408 results
->size
= size
;
6413 ElectionResultsRelease(ElectionResultsRef results
)
6418 for (i
= 0, scan
= results
->candidates
;
6421 CandidateRelease(scan
);
6428 ElectionResultsLog(int level
, ElectionResultsRef results
, const char * prefix
)
6433 if (results
== NULL
) {
6434 my_log(level
, "%s: no candidates", prefix
);
6437 my_log(level
, "%s: %d candidates", prefix
, results
->count
);
6438 for (i
= 0, scan
= results
->candidates
;
6441 char ntopbuf
[INET6_ADDRSTRLEN
];
6443 (void)inet_ntop(results
->af
, &scan
->addr
, ntopbuf
, sizeof(ntopbuf
));
6444 my_log(level
, "%d. %@ serviceID=%@ addr=%s rank=0x%x",
6445 i
, scan
->if_name
, scan
->serviceID
, ntopbuf
, scan
->rank
);
6451 * Function: ElectionResultsAddCandidate
6453 * Add the candidate into the election results. Find the insertion point
6454 * by comparing the rank of the candidate with existing entries.
6457 ElectionResultsAddCandidate(ElectionResultsRef results
, CandidateRef candidate
)
6462 if (results
->count
== results
->size
) {
6463 /* this should not happen */
6464 my_log(LOG_NOTICE
, "can't fit another candidate");
6468 /* find the insertion point */
6469 where
= kCFNotFound
;
6470 for (i
= 0; i
< results
->count
; i
++) {
6471 CandidateRef this_candidate
= results
->candidates
+ i
;
6473 if (candidate
->rank
< this_candidate
->rank
) {
6478 /* add it to the end */
6479 if (where
== kCFNotFound
) {
6480 CandidateCopy(results
->candidates
+ results
->count
, candidate
);
6484 /* slide existing entries over */
6485 for (i
= results
->count
; i
> where
; i
--) {
6486 results
->candidates
[i
] = results
->candidates
[i
- 1];
6488 /* insert element */
6489 CandidateCopy(results
->candidates
+ where
, candidate
);
6495 elect_ip(const void * key
, const void * value
, void * context
);
6498 * Function: ElectionResultsCopy
6500 * Visit all of the services and invoke the protocol-specific election
6501 * function. Return the results of the election.
6503 static ElectionResultsRef
6504 ElectionResultsCopy(int af
, CFArrayRef order
, int n_order
)
6509 count
= (int)CFDictionaryGetCount(S_service_state_dict
);
6514 if (af
== AF_INET
) {
6515 info
.entity
= kSCEntNetIPv4
;
6516 info
.rank_dict
= S_ipv4_service_rank_dict
;
6519 info
.entity
= kSCEntNetIPv6
;
6520 info
.rank_dict
= S_ipv6_service_rank_dict
;
6522 info
.results
= ElectionResultsAlloc(af
, count
);
6523 info
.n_services
= count
;
6525 info
.n_order
= n_order
;
6526 CFDictionaryApplyFunction(S_service_state_dict
, elect_ip
, (void *)&info
);
6527 if (info
.results
->count
== 0) {
6528 ElectionResultsRelease(info
.results
);
6529 info
.results
= NULL
;
6531 return (info
.results
);
6535 * Function: ElectionResultsCandidateNeedsDemotion
6537 * Check whether the given candidate requires demotion. A candidate
6538 * might need to be demoted if its IPv4 and IPv6 services must be coupled
6539 * but a higher ranked service has IPv4 or IPv6.
6542 ElectionResultsCandidateNeedsDemotion(ElectionResultsRef other_results
,
6543 CandidateRef candidate
)
6545 CandidateRef other_candidate
;
6546 Boolean ret
= FALSE
;
6548 if (other_results
== NULL
6549 || candidate
->ip_is_coupled
== FALSE
6550 || RANK_ASSERTION_MASK(candidate
->rank
) == kRankAssertionNever
) {
6553 other_candidate
= other_results
->candidates
;
6554 if (CFEqual(other_candidate
->if_name
, candidate
->if_name
)) {
6555 /* they are over the same interface, no need to demote */
6558 if (CFStringHasPrefix(other_candidate
->if_name
, CFSTR("stf"))) {
6559 /* avoid creating a feedback loop */
6562 if (RANK_ASSERTION_MASK(other_candidate
->rank
) == kRankAssertionNever
) {
6563 /* the other candidate isn't eligible to become primary, ignore */
6566 if (candidate
->rank
< other_candidate
->rank
) {
6567 /* we're higher ranked than the other candidate, ignore */
6579 get_signature_sha1(CFStringRef signature
,
6580 unsigned char * sha1
)
6583 CFDataRef signature_data
;
6585 signature_data
= CFStringCreateExternalRepresentation(NULL
,
6587 kCFStringEncodingUTF8
,
6591 CC_SHA1_Update(&ctx
,
6593 (CC_LONG
)CFDataGetLength(signature_data
));
6594 CC_SHA1_Final(sha1
, &ctx
);
6596 CFRelease(signature_data
);
6603 add_candidate_to_nwi_state(nwi_state_t nwi_state
, int af
,
6604 CandidateRef candidate
, Rank rank
)
6607 char ifname
[IFNAMSIZ
];
6608 nwi_ifstate_t ifstate
;
6610 if (nwi_state
== NULL
) {
6614 if (RANK_ASSERTION_MASK(rank
) == kRankAssertionNever
) {
6615 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
6617 if (service_dict_get(candidate
->serviceID
, kSCEntNetDNS
) != NULL
) {
6618 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
6620 CFStringGetCString(candidate
->if_name
, ifname
, sizeof(ifname
),
6621 kCFStringEncodingASCII
);
6622 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
6623 char ntopbuf
[INET6_ADDRSTRLEN
];
6625 (void)inet_ntop(af
, &candidate
->addr
, ntopbuf
, sizeof(ntopbuf
));
6627 "Inserting IPv%c [%s] %s "
6628 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
6629 ipvx_char(af
), ifname
, ntopbuf
,
6630 flags
, rank
, candidate
->reachability_flags
);
6632 ifstate
= nwi_insert_ifstate(nwi_state
, ifname
, af
, flags
, rank
,
6633 (void *)&candidate
->addr
,
6634 (void *)&candidate
->vpn_server_addr
,
6635 candidate
->reachability_flags
);
6636 if (ifstate
!= NULL
&& candidate
->signature
) {
6637 uint8_t hash
[CC_SHA1_DIGEST_LENGTH
];
6639 get_signature_sha1(candidate
->signature
, hash
);
6640 nwi_ifstate_set_signature(ifstate
, hash
);
6647 add_reachability_flags_to_candidate(CandidateRef candidate
, CFDictionaryRef services_info
, int af
)
6649 SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
6650 CFStringRef vpn_server_address
= NULL
;
6652 VPNAttributesGet(candidate
->serviceID
,
6655 &vpn_server_address
,
6658 candidate
->reachability_flags
= flags
;
6660 if (vpn_server_address
== NULL
) {
6661 bzero(&candidate
->vpn_server_addr
, sizeof(candidate
->vpn_server_addr
));
6665 CFStringGetCString(vpn_server_address
, buf
, sizeof(buf
),
6666 kCFStringEncodingASCII
);
6667 _SC_string_to_sockaddr(buf
,
6669 (void *)&candidate
->vpn_server_addr
,
6670 sizeof(candidate
->vpn_server_addr
));
6672 CFRelease(vpn_server_address
);
6677 * Function: ElectionResultsCopyPrimary
6679 * Use the results of the current protocol and the other protocol to
6680 * determine which service should become primary.
6682 * At the same time, generate the IPv4/IPv6 routing table and
6683 * the nwi_state for the protocol.
6686 ElectionResultsCopyPrimary(ElectionResultsRef results
,
6687 ElectionResultsRef other_results
,
6688 nwi_state_t nwi_state
, int af
,
6689 RouteListRef
* ret_routes
,
6690 CFDictionaryRef services_info
)
6692 CFStringRef primary
= NULL
;
6693 Boolean primary_is_null
= FALSE
;
6694 RouteListRef routes
= NULL
;
6696 if (nwi_state
!= NULL
) {
6697 nwi_state_clear(nwi_state
, af
);
6699 if (results
!= NULL
) {
6700 CandidateRef deferred
[results
->count
];
6702 CFStringRef entity_name
;
6705 RouteListInfoRef info
;
6710 entity_name
= kSCEntNetIPv4
;
6711 info
= &IPv4RouteListInfo
;
6712 initial_size
= results
->count
* IPV4_ROUTES_N_STATIC
;
6716 entity_name
= kSCEntNetIPv6
;
6717 info
= &IPv6RouteListInfo
;
6718 initial_size
= results
->count
* IPV6_ROUTES_N_STATIC
;
6722 for (i
= 0, scan
= results
->candidates
;
6725 Boolean is_primary
= FALSE
;
6726 Rank rank
= scan
->rank
;
6727 CFDictionaryRef service_dict
;
6728 RouteListRef service_routes
;
6729 Boolean skip
= FALSE
;
6732 && RANK_ASSERTION_MASK(rank
) != kRankAssertionNever
) {
6733 if (ElectionResultsCandidateNeedsDemotion(other_results
,
6735 /* demote to RankNever */
6737 "IPv%c over %@ demoted: not primary for IPv%c",
6738 ipvx_char(af
), scan
->if_name
, ipvx_other_char(af
));
6739 rank
= RankMake(rank
, kRankAssertionNever
);
6740 deferred
[deferred_count
++] = scan
;
6744 primary
= CFRetain(scan
->serviceID
);
6748 /* contribute to the routing table */
6749 service_dict
= service_dict_get(scan
->serviceID
, entity_name
);
6750 service_routes
= ipdict_get_routelist(service_dict
);
6751 if (service_routes
!= NULL
) {
6752 routes
= RouteListAddRouteList(info
, routes
, initial_size
,
6753 service_routes
, rank
);
6754 if ((service_routes
->flags
& kRouteListFlagsExcludeNWI
) != 0) {
6762 /* if we're skipping the primary, it's NULL */
6764 primary_is_null
= TRUE
;
6768 if (primary_is_null
) {
6769 /* everything after the primary must be Never */
6770 rank
= RankMake(rank
, kRankAssertionNever
);
6772 add_reachability_flags_to_candidate(scan
, services_info
, af
);
6773 add_candidate_to_nwi_state(nwi_state
, af
, scan
, rank
);
6776 for (i
= 0; i
< deferred_count
; i
++) {
6777 CandidateRef candidate
= deferred
[i
];
6780 /* demote to RankNever */
6781 rank
= RankMake(candidate
->rank
, kRankAssertionNever
);
6782 add_reachability_flags_to_candidate(candidate
, services_info
, af
);
6783 add_candidate_to_nwi_state(nwi_state
, af
, candidate
, rank
);
6786 if (nwi_state
!= NULL
) {
6787 nwi_state_set_last(nwi_state
, af
);
6789 if (ret_routes
!= NULL
) {
6790 *ret_routes
= routes
;
6792 else if (routes
!= NULL
) {
6795 if (primary_is_null
) {
6796 my_CFRelease(&primary
);
6804 service_dict_get_signature(CFDictionaryRef service_dict
)
6808 ifname
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
6809 if (isA_CFString(ifname
) == NULL
6810 || confirm_interface_name(service_dict
, ifname
) == FALSE
) {
6813 return (CFDictionaryGetValue(service_dict
, kStoreKeyNetworkSignature
));
6817 * Function: elect_ip
6819 * Evaluate the service and determine what rank the service should have.
6820 * If it's a suitable candidate, add it to the election results.
6823 elect_ip(const void * key
, const void * value
, void * context
)
6825 CFDictionaryRef all_entities_dict
= (CFDictionaryRef
)value
;
6826 Candidate candidate
;
6828 ElectionInfoRef elect_info
;
6829 CFStringRef if_name
;
6830 CFDictionaryRef ipdict
;
6832 RouteListUnion routelist
;
6833 CFDictionaryRef service_dict
;
6835 elect_info
= (ElectionInfoRef
)context
;
6836 ipdict
= CFDictionaryGetValue(all_entities_dict
, elect_info
->entity
);
6837 if (ipdict
!= NULL
) {
6838 routelist
.ptr
= ipdict_get_routelist(ipdict
);
6839 service_dict
= ipdict_get_service(ipdict
);
6842 routelist
.ptr
= NULL
;
6844 if (routelist
.ptr
== NULL
|| service_dict
== NULL
) {
6845 /* no connectivity */
6848 if ((routelist
.common
->flags
& kRouteListFlagsHasDefault
) == 0) {
6849 /* no default route, not a candidate for being primary */
6852 if_name
= CFDictionaryGetValue(service_dict
, kSCPropInterfaceName
);
6853 if (if_name
== NULL
) {
6854 /* need an interface name */
6857 if (CFEqual(if_name
, CFSTR(kLoopbackInterface
))) {
6858 /* don't process loopback */
6861 bzero(&candidate
, sizeof(candidate
));
6862 candidate
.serviceID
= (CFStringRef
)key
;
6863 candidate
.rank
= get_service_rank(elect_info
->order
, elect_info
->n_order
,
6864 candidate
.serviceID
);
6865 if (elect_info
->af
== AF_INET
) {
6866 default_rank
= routelist
.v4
->list
->rank
;
6867 candidate
.addr
.v4
= routelist
.v4
->list
->ifa
;
6870 default_rank
= routelist
.v6
->list
->rank
;
6871 candidate
.addr
.v6
= routelist
.v6
->list
->ifa
;
6873 primary_rank
= RANK_ASSERTION_MASK(default_rank
);
6874 if (S_ppp_override_primary
) {
6877 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
6878 kCFStringEncodingASCII
)
6879 && (strncmp(PPP_PREFIX
, ifn
, sizeof(PPP_PREFIX
) - 1) == 0)) {
6880 /* PPP override: make ppp* look the best */
6881 primary_rank
= kRankAssertionFirst
;
6884 candidate
.rank
= RankMake(candidate
.rank
, primary_rank
);
6885 candidate
.ip_is_coupled
= service_get_ip_is_coupled(candidate
.serviceID
);
6886 candidate
.if_name
= if_name
;
6887 rank_dict_set_service_rank(elect_info
->rank_dict
,
6888 candidate
.serviceID
, candidate
.rank
);
6889 candidate
.signature
= service_dict_get_signature(service_dict
);
6890 ElectionResultsAddCandidate(elect_info
->results
, &candidate
);
6896 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
6898 uint32_t changed
= 0;
6901 /* update service options first (e.g. rank) */
6902 if (get_rank_changes(serviceID
,
6903 get_service_state_entity(services_info
, serviceID
,
6905 get_service_setup_entity(services_info
, serviceID
,
6908 changed
|= (1 << kEntityTypeServiceOptions
);
6910 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
6911 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
6912 GetEntityChangesFuncRef func
= entityChangeFunc
[i
];
6913 if ((*func
)(serviceID
,
6914 get_service_state_entity(services_info
, serviceID
,
6915 *entityTypeNames
[i
]),
6916 get_service_setup_entity(services_info
, serviceID
,
6917 *entityTypeNames
[i
]),
6919 changed
|= (1 << i
);
6923 if (get_transient_status_changes(serviceID
, services_info
)) {
6924 changed
|= (1 << kEntityTypeTransientStatus
);
6931 service_order_get(CFDictionaryRef services_info
)
6933 CFArrayRef order
= NULL
;
6934 CFDictionaryRef ipv4_dict
;
6936 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
6937 S_setup_global_ipv4
);
6938 if (ipv4_dict
!= NULL
) {
6939 CFNumberRef ppp_override
;
6942 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
6943 order
= isA_CFArray(order
);
6945 /* get ppp override primary */
6946 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
6947 kSCPropNetPPPOverridePrimary
);
6948 ppp_override
= isA_CFNumber(ppp_override
);
6949 if (ppp_override
!= NULL
) {
6950 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
6952 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
6955 S_ppp_override_primary
= FALSE
;
6961 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
6962 const char * entity
)
6964 boolean_t changed
= FALSE
;
6965 CFStringRef primary
= *primary_p
;
6967 if (new_primary
!= NULL
) {
6968 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
6969 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6971 "IPMonitor: %@ is still primary %s",
6972 new_primary
, entity
);
6976 my_CFRelease(primary_p
);
6977 *primary_p
= CFRetain(new_primary
);
6978 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6980 "IPMonitor: %@ is the new primary %s",
6981 new_primary
, entity
);
6986 else if (primary
!= NULL
) {
6987 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
6989 "IPMonitor: %@ is no longer primary %s",
6992 my_CFRelease(primary_p
);
6999 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
7002 if (service_dict_get(serviceID
, entity
) == NULL
) {
7003 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
7005 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
7009 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
7015 #define N_KEYS_VALUES_STATIC 10
7016 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
7019 count
= CFDictionaryGetCount(S_service_state_dict
);
7020 if (count
<= N_KEYS_VALUES_STATIC
) {
7021 keys
= keys_values_buf
;
7023 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
7025 values
= keys
+ count
;
7026 CFDictionaryGetKeysAndValues(S_service_state_dict
,
7027 (const void * *)keys
,
7028 (const void * *)values
);
7030 for (i
= 0; i
< count
; i
++) {
7031 CFDictionaryRef ipdict
= NULL
;
7032 CFStringRef interface
= NULL
;
7033 CFStringRef serviceID
;
7034 CFDictionaryRef service_dict
;
7036 serviceID
= (CFStringRef
)keys
[i
];
7037 service_dict
= (CFDictionaryRef
)values
[i
];
7039 /* check whether service has IPv4 or IPv6 */
7040 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
7041 if (ipdict
== NULL
) {
7042 ipdict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
7043 if (ipdict
== NULL
) {
7047 interface
= ipdict_get_ifname(ipdict
);
7048 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
7049 if (S_IPMonitor_debug
& kDebugFlag1
) {
7051 "Found IP service %@ on interface %@.",
7054 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
7057 if (keys
!= keys_values_buf
) {
7063 static __inline__
const char *
7064 get_changed_str(CFStringRef serviceID
, CFStringRef entity
,
7065 CFDictionaryRef old_dict
)
7067 CFDictionaryRef new_dict
= NULL
;
7069 if (serviceID
!= NULL
) {
7070 new_dict
= service_dict_get(serviceID
, entity
);
7073 if (old_dict
== NULL
) {
7074 if (new_dict
!= NULL
) {
7078 if (new_dict
== NULL
) {
7080 } else if (!CFEqual(old_dict
, new_dict
)) {
7087 static CF_RETURNS_RETAINED CFStringRef
7088 generate_log_changes(nwi_state_t changes_state
,
7089 boolean_t dns_changed
,
7090 boolean_t dnsinfo_changed
,
7091 CFDictionaryRef old_primary_dns
,
7092 boolean_t proxy_changed
,
7093 CFDictionaryRef old_primary_proxy
,
7094 boolean_t smb_changed
,
7095 CFDictionaryRef old_primary_smb
7099 CFMutableStringRef log_output
;
7102 log_output
= CFStringCreateMutable(NULL
, 0);
7104 if (changes_state
!= NULL
) {
7105 for (idx
= 0; idx
< countof(nwi_af_list
); idx
++) {
7106 CFMutableStringRef changes
= NULL
;
7107 CFMutableStringRef primary_str
= NULL
;
7109 scan
= nwi_state_get_first_ifstate(changes_state
, nwi_af_list
[idx
]);
7111 while (scan
!= NULL
) {
7112 const char * changed_str
;
7114 changed_str
= nwi_ifstate_get_diff_str(scan
);
7115 if (changed_str
!= NULL
) {
7117 const char * addr_str
;
7118 char ntopbuf
[INET6_ADDRSTRLEN
];
7120 address
= (void *)nwi_ifstate_get_address(scan
);
7121 addr_str
= inet_ntop(scan
->af
, address
, ntopbuf
,
7123 if (primary_str
== NULL
) {
7124 primary_str
= CFStringCreateMutable(NULL
, 0);
7125 CFStringAppendFormat(primary_str
, NULL
,
7127 nwi_ifstate_get_ifname(scan
),
7128 changed_str
, addr_str
);
7130 if (changes
== NULL
) {
7131 changes
= CFStringCreateMutable(NULL
, 0);
7133 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
7134 nwi_ifstate_get_ifname(scan
));
7135 if (strcmp(changed_str
, "") != 0) {
7136 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
7137 changed_str
, addr_str
);
7141 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
7144 if (primary_str
!= NULL
) {
7145 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
7146 nwi_af_list
[idx
] == AF_INET
? "v4" : "v6",
7149 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
7150 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
7153 CFStringAppend(log_output
, CFSTR(")"));
7155 my_CFRelease(&primary_str
);
7156 my_CFRelease(&changes
);
7161 if (dns_changed
|| dnsinfo_changed
) {
7164 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
7165 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
7166 str
= "*"; // dnsinfo change w/no change to primary
7168 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
7169 } else if (S_primary_dns
!= NULL
) {
7170 CFStringAppend(log_output
, CFSTR(" DNS"));
7173 if (proxy_changed
) {
7176 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
7177 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
7178 } else if (S_primary_proxies
!= NULL
) {
7179 CFStringAppend(log_output
, CFSTR(" Proxy"));
7182 #if !TARGET_OS_IPHONE
7186 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
7187 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
7188 } else if (S_primary_smb
!= NULL
) {
7189 CFStringAppend(log_output
, CFSTR(" SMB"));
7191 #endif // !TARGET_OS_IPHONE
7197 #pragma mark Network changed notification
7199 static dispatch_queue_t
7200 __network_change_queue()
7202 static dispatch_once_t once
;
7203 static dispatch_queue_t q
;
7205 dispatch_once(&once
, ^{
7206 q
= dispatch_queue_create("network change queue", NULL
);
7212 // Note: must run on __network_change_queue()
7214 post_network_change_when_ready()
7218 if (S_network_change_needed
== 0) {
7222 if (!S_network_change_timeout
&&
7223 (!S_dnsinfo_synced
|| !S_nwi_synced
)) {
7224 // if we [still] need to wait for the DNS configuration
7225 // or network information changes to be ack'd
7227 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7229 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s, %s)",
7230 S_dnsinfo_synced
? "DNS" : "!DNS",
7231 S_nwi_synced
? "nwi" : "!nwi");
7236 // cancel any running timer
7237 if (S_network_change_timer
!= NULL
) {
7238 dispatch_source_cancel(S_network_change_timer
);
7239 dispatch_release(S_network_change_timer
);
7240 S_network_change_timer
= NULL
;
7241 S_network_change_timeout
= FALSE
;
7244 // set (and log?) the post time
7245 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7246 struct timeval elapsed
;
7249 (void) gettimeofday(&end
, NULL
);
7250 timersub(&end
, &S_network_change_start
, &elapsed
);
7252 #define QUERY_TIME__FMT "%ld.%6.6d"
7253 #define QUERY_TIME__DIV 1
7256 "Post \"" _SC_NOTIFY_NETWORK_CHANGE
"\" (%s: " QUERY_TIME__FMT
": 0x%x)",
7257 S_network_change_timeout
? "timeout" : "delayed",
7259 elapsed
.tv_usec
/ QUERY_TIME__DIV
,
7260 S_network_change_needed
);
7263 if ((S_network_change_needed
& NETWORK_CHANGE_NET
) != 0) {
7264 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI
);
7265 if (status
!= NOTIFY_STATUS_OK
) {
7267 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI
") failed: error=%d", status
);
7271 if ((S_network_change_needed
& NETWORK_CHANGE_DNS
) != 0) {
7272 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS
);
7273 if (status
!= NOTIFY_STATUS_OK
) {
7275 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS
") failed: error=%d", status
);
7279 if ((S_network_change_needed
& NETWORK_CHANGE_PROXY
) != 0) {
7280 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
7281 if (status
!= NOTIFY_STATUS_OK
) {
7283 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY
") failed: error=%d", status
);
7287 status
= notify_post(_SC_NOTIFY_NETWORK_CHANGE
);
7288 if (status
!= NOTIFY_STATUS_OK
) {
7290 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE
") failed: error=%d", status
);
7293 S_network_change_needed
= 0;
7297 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
7299 // Note: must run on __network_change_queue()
7301 post_network_change(uint32_t change
)
7303 if (S_network_change_needed
== 0) {
7304 // set the start time
7305 (void) gettimeofday(&S_network_change_start
, NULL
);
7308 // indicate that we need to post a change for ...
7309 S_network_change_needed
|= change
;
7311 // cancel any running timer
7312 if (S_network_change_timer
!= NULL
) {
7313 dispatch_source_cancel(S_network_change_timer
);
7314 dispatch_release(S_network_change_timer
);
7315 S_network_change_timer
= NULL
;
7316 S_network_change_timeout
= FALSE
;
7319 // if needed, start new timer
7320 if (!S_dnsinfo_synced
|| !S_nwi_synced
) {
7321 S_network_change_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
7324 __network_change_queue());
7325 dispatch_source_set_event_handler(S_network_change_timer
, ^{
7326 S_network_change_timeout
= TRUE
;
7327 post_network_change_when_ready();
7329 dispatch_source_set_timer(S_network_change_timer
,
7330 dispatch_time(DISPATCH_TIME_NOW
,
7331 TRAILING_EDGE_TIMEOUT_NSEC
), // start
7332 DISPATCH_TIME_FOREVER
, // interval
7333 10 * NSEC_PER_MSEC
); // leeway
7334 dispatch_resume(S_network_change_timer
);
7337 post_network_change_when_ready();
7343 #pragma mark Process network (SCDynamicStore) changes
7346 IPMonitorProcessChanges(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
7347 CFArrayRef if_rank_changes
)
7350 uint32_t changes
= 0;
7351 nwi_state_t changes_state
= NULL
;
7352 boolean_t dns_changed
= FALSE
;
7353 boolean_t dnsinfo_changed
= FALSE
;
7354 boolean_t global_ipv4_changed
= FALSE
;
7355 boolean_t global_ipv6_changed
= FALSE
;
7359 CFStringRef network_change_msg
= NULL
;
7361 int n_service_order
= 0;
7362 nwi_state_t old_nwi_state
= NULL
;
7363 CFDictionaryRef old_primary_dns
= NULL
;
7364 CFDictionaryRef old_primary_proxy
= NULL
;
7365 #if !TARGET_OS_IPHONE
7366 CFDictionaryRef old_primary_smb
= NULL
;
7367 #endif // !TARGET_OS_IPHONE
7368 boolean_t proxies_changed
= FALSE
;
7369 boolean_t reachability_changed
= FALSE
;
7370 CFArrayRef service_order
;
7371 CFMutableArrayRef service_changes
= NULL
;
7372 CFDictionaryRef services_info
= NULL
;
7373 #if !TARGET_OS_IPHONE
7374 boolean_t smb_changed
= FALSE
;
7375 #endif // !TARGET_OS_IPHONE
7377 /* populate name/index cache */
7380 if (changed_keys
!= NULL
) {
7381 count
= CFArrayGetCount(changed_keys
);
7382 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7384 "IPMonitor: changed keys %@ (%ld)", changed_keys
, count
);
7387 if (if_rank_changes
== NULL
&& count
== 0) {
7391 if (S_primary_dns
!= NULL
) {
7392 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
7393 if (old_primary_dns
!= NULL
) {
7394 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
7398 if (S_primary_proxies
!= NULL
) {
7400 = service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
7401 if (old_primary_proxy
!= NULL
) {
7402 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
7406 #if !TARGET_OS_IPHONE
7407 if (S_primary_smb
!= NULL
) {
7408 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
7409 if (old_primary_smb
!= NULL
) {
7410 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
7413 #endif // !TARGET_OS_IPHONE
7415 keyChangeListInit(&keys
);
7416 service_changes
= CFArrayCreateMutable(NULL
, 0,
7417 &kCFTypeArrayCallBacks
);
7419 for (i
= 0; i
< count
; i
++) {
7420 CFStringRef change
= CFArrayGetValueAtIndex(changed_keys
, i
);
7421 if (CFEqual(change
, S_setup_global_ipv4
)) {
7422 global_ipv4_changed
= TRUE
;
7423 global_ipv6_changed
= TRUE
;
7425 else if (CFEqual(change
, S_multicast_resolvers
)) {
7426 dnsinfo_changed
= TRUE
;
7428 else if (CFEqual(change
, S_private_resolvers
)) {
7429 dnsinfo_changed
= TRUE
;
7431 #if !TARGET_OS_IPHONE
7432 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
7433 dnsinfo_changed
= TRUE
;
7435 #endif /* !TARGET_OS_IPHONE */
7436 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
7437 CFStringRef serviceID
;
7439 serviceID
= parse_component(change
, S_state_service_prefix
);
7441 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
7442 CFRelease(serviceID
);
7445 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
7448 CFStringRef serviceID
= parse_component(change
,
7449 S_setup_service_prefix
);
7451 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
7452 CFRelease(serviceID
);
7455 for (j
= 0; j
< countof(transientInterfaceEntityNames
); j
++) {
7456 if (CFStringHasSuffix(change
,
7457 *transientInterfaceEntityNames
[j
])) {
7458 reachability_changed
= TRUE
;
7463 if (CFStringHasSuffix(change
, kSCEntNetInterface
)) {
7464 reachability_changed
= TRUE
;
7469 /* determine which serviceIDs are impacted by the interface rank changes */
7470 if (if_rank_changes
!= NULL
) {
7471 n
= CFArrayGetCount(if_rank_changes
);
7472 for (i
= 0; i
< n
; i
++) {
7473 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
7475 if (S_IPMonitor_debug
& kDebugFlag1
) {
7476 my_log(LOG_DEBUG
, "Interface rank changed %@",
7479 append_serviceIDs_for_interface(service_changes
, ifname
);
7483 /* grab a snapshot of everything we need */
7484 services_info
= services_info_copy(session
, service_changes
);
7485 service_order
= service_order_get(services_info
);
7486 if (service_order
!= NULL
) {
7487 n_service_order
= (int)CFArrayGetCount(service_order
);
7488 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7490 "IPMonitor: service_order %@ ", service_order
);
7494 n
= CFArrayGetCount(service_changes
);
7495 for (i
= 0; i
< n
; i
++) {
7497 CFStringRef serviceID
;
7499 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
7500 changes
= service_changed(services_info
, serviceID
);
7501 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
7502 /* if __Service__ (e.g. PrimaryRank) changed */
7503 global_ipv4_changed
= TRUE
;
7504 global_ipv6_changed
= TRUE
;
7507 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
7508 global_ipv4_changed
= TRUE
;
7509 dnsinfo_changed
= TRUE
;
7510 proxies_changed
= TRUE
;
7512 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
7513 global_ipv6_changed
= TRUE
;
7514 dnsinfo_changed
= TRUE
;
7515 proxies_changed
= TRUE
;
7518 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
7519 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
7522 dnsinfo_changed
= TRUE
;
7524 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
7525 proxies_changed
= TRUE
;
7527 #if !TARGET_OS_IPHONE
7528 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
7529 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
7534 if ((changes
& (1 << kEntityTypeTransientStatus
)) != 0
7535 && (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
7536 || service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
)) {
7537 dnsinfo_changed
= TRUE
;
7541 /* ensure S_nwi_state can hold as many services as we have currently */
7542 n_services
= (int)CFDictionaryGetCount(S_service_state_dict
);
7543 old_nwi_state
= nwi_state_copy_priv(S_nwi_state
);
7544 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
7546 if (global_ipv4_changed
) {
7547 if (S_ipv4_results
!= NULL
) {
7548 ElectionResultsRelease(S_ipv4_results
);
7551 = ElectionResultsCopy(AF_INET
, service_order
, n_service_order
);
7552 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7553 ElectionResultsLog(LOG_DEBUG
, S_ipv4_results
, "IPv4");
7556 if (global_ipv6_changed
) {
7557 if (S_ipv6_results
!= NULL
) {
7558 ElectionResultsRelease(S_ipv6_results
);
7561 = ElectionResultsCopy(AF_INET6
, service_order
, n_service_order
);
7562 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7563 ElectionResultsLog(LOG_DEBUG
, S_ipv6_results
, "IPv6");
7566 if (global_ipv4_changed
|| global_ipv6_changed
|| dnsinfo_changed
) {
7567 CFStringRef new_primary
;
7568 RouteListUnion new_routelist
;
7571 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7573 "IPMonitor: electing IPv4 primary");
7575 new_routelist
.ptr
= NULL
;
7576 new_primary
= ElectionResultsCopyPrimary(S_ipv4_results
,
7578 S_nwi_state
, AF_INET
,
7579 &new_routelist
.common
,
7581 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
7582 update_ipv4(S_primary_ipv4
, new_routelist
.v4
, &keys
);
7583 my_CFRelease(&new_primary
);
7586 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
7588 "IPMonitor: electing IPv6 primary");
7590 new_routelist
.ptr
= NULL
;
7591 new_primary
= ElectionResultsCopyPrimary(S_ipv6_results
,
7593 S_nwi_state
, AF_INET6
,
7594 &new_routelist
.common
,
7596 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
7597 update_ipv6(S_primary_ipv6
, new_routelist
.v6
, &keys
);
7598 my_CFRelease(&new_primary
);
7601 if (global_ipv4_changed
|| global_ipv6_changed
) {
7602 CFStringRef new_primary_dns
= NULL
;
7603 CFStringRef new_primary_proxies
= NULL
;
7604 #if !TARGET_OS_IPHONE
7605 CFStringRef new_primary_smb
= NULL
;
7606 #endif /* !TARGET_OS_IPHONE */
7608 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
7609 /* decide between IPv4 and IPv6 */
7610 if (rank_service_entity(S_ipv4_service_rank_dict
,
7611 S_primary_ipv4
, kSCEntNetDNS
)
7612 <= rank_service_entity(S_ipv6_service_rank_dict
,
7613 S_primary_ipv6
, kSCEntNetDNS
)) {
7614 new_primary_dns
= S_primary_ipv4
;
7617 new_primary_dns
= S_primary_ipv6
;
7619 if (rank_service_entity(S_ipv4_service_rank_dict
,
7620 S_primary_ipv4
, kSCEntNetProxies
)
7621 <= rank_service_entity(S_ipv6_service_rank_dict
,
7622 S_primary_ipv6
, kSCEntNetProxies
)) {
7623 new_primary_proxies
= S_primary_ipv4
;
7626 new_primary_proxies
= S_primary_ipv6
;
7628 #if !TARGET_OS_IPHONE
7629 if (rank_service_entity(S_ipv4_service_rank_dict
,
7630 S_primary_ipv4
, kSCEntNetSMB
)
7631 <= rank_service_entity(S_ipv6_service_rank_dict
,
7632 S_primary_ipv6
, kSCEntNetSMB
)) {
7633 new_primary_smb
= S_primary_ipv4
;
7636 new_primary_smb
= S_primary_ipv6
;
7638 #endif /* !TARGET_OS_IPHONE */
7641 else if (S_primary_ipv6
!= NULL
) {
7642 new_primary_dns
= S_primary_ipv6
;
7643 new_primary_proxies
= S_primary_ipv6
;
7644 #if !TARGET_OS_IPHONE
7645 new_primary_smb
= S_primary_ipv6
;
7646 #endif /* !TARGET_OS_IPHONE */
7648 else if (S_primary_ipv4
!= NULL
) {
7649 new_primary_dns
= S_primary_ipv4
;
7650 new_primary_proxies
= S_primary_ipv4
;
7651 #if !TARGET_OS_IPHONE
7652 new_primary_smb
= S_primary_ipv4
;
7653 #endif /* !TARGET_OS_IPHONE */
7656 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
7658 dnsinfo_changed
= TRUE
;
7660 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
,
7662 proxies_changed
= TRUE
;
7664 #if !TARGET_OS_IPHONE
7665 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
7668 #endif /* !TARGET_OS_IPHONE */
7671 if (!proxies_changed
&& dnsinfo_changed
7672 && ((G_supplemental_proxies_follow_dns
!= NULL
)
7673 && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))) {
7674 proxies_changed
= TRUE
;
7677 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
7679 if (global_ipv4_changed
|| global_ipv6_changed
7680 || dnsinfo_changed
|| reachability_changed
) {
7681 if (S_nwi_state
!= NULL
) {
7682 S_nwi_state
->generation_count
= mach_absolute_time();
7683 if (global_ipv4_changed
|| global_ipv6_changed
7684 || reachability_changed
) {
7685 SCNetworkReachabilityFlags reach_flags_v4
= 0;
7686 SCNetworkReachabilityFlags reach_flags_v6
= 0;
7688 GetReachabilityFlagsFromTransientServices(services_info
,
7692 _nwi_state_set_reachability_flags(S_nwi_state
, reach_flags_v4
,
7696 /* Update the per-interface generation count */
7697 _nwi_state_update_interface_generations(old_nwi_state
, S_nwi_state
,
7701 if (update_nwi(S_nwi_state
)) {
7702 changes
|= NETWORK_CHANGE_NET
;
7705 * the DNS configuration includes per-resolver configuration
7706 * reachability flags that are based on the nwi state. Let's
7707 * make sure that we check for changes
7709 dnsinfo_changed
= TRUE
;
7713 if (update_dns(services_info
, S_primary_dns
, &keys
)) {
7714 changes
|= NETWORK_CHANGE_DNS
;
7715 dnsinfo_changed
= TRUE
;
7717 dns_changed
= FALSE
;
7720 if (dnsinfo_changed
) {
7721 if (update_dnsinfo(services_info
, S_primary_dns
,
7722 &keys
, service_order
)) {
7723 changes
|= NETWORK_CHANGE_DNS
;
7725 dnsinfo_changed
= FALSE
;
7728 if (proxies_changed
) {
7729 // if proxy change OR supplemental Proxies follow supplemental DNS
7730 if (update_proxies(services_info
, S_primary_proxies
,
7731 &keys
, service_order
)) {
7732 changes
|= NETWORK_CHANGE_PROXY
;
7734 proxies_changed
= FALSE
;
7737 #if !TARGET_OS_IPHONE
7739 if (update_smb(services_info
, S_primary_smb
, &keys
)) {
7740 changes
|= NETWORK_CHANGE_SMB
;
7742 smb_changed
= FALSE
;
7745 #endif /* !TARGET_OS_IPHONE */
7746 my_CFRelease(&service_changes
);
7747 my_CFRelease(&services_info
);
7750 network_change_msg
=
7751 generate_log_changes(changes_state
,
7757 #if !TARGET_OS_IPHONE
7760 #else // !TARGET_OS_IPHONE
7761 FALSE
, // smb_changed
7762 NULL
// old_primary_smb
7763 #endif // !TARGET_OS_IPHONE
7767 keyChangeListApplyToStore(&keys
, session
);
7768 my_CFRelease(&old_primary_dns
);
7769 my_CFRelease(&old_primary_proxy
);
7770 #if !TARGET_OS_IPHONE
7771 my_CFRelease(&old_primary_smb
);
7772 #endif // !TARGET_OS_IPHONE
7775 dispatch_async(__network_change_queue(), ^{
7776 post_network_change(changes
);
7780 if ((network_change_msg
!= NULL
)
7781 && (CFStringGetLength(network_change_msg
) != 0)) {
7782 my_log(LOG_NOTICE
, "network changed:%@", network_change_msg
);
7783 } else if (keyChangeListActive(&keys
)) {
7784 my_log(LOG_NOTICE
, "network changed.");
7786 my_log(LOG_DEBUG
, "network event w/no changes");
7789 my_CFRelease(&network_change_msg
);
7791 if (changes_state
!= NULL
) {
7792 nwi_state_release(changes_state
);
7794 if (old_nwi_state
!= NULL
) {
7795 nwi_state_release(old_nwi_state
);
7797 keyChangeListFree(&keys
);
7799 /* release the name/index cache */
7800 my_if_freenameindex();
7806 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
7809 IPMonitorProcessChanges(session
, changed_keys
, NULL
);
7816 #if !TARGET_OS_IPHONE
7817 const _scprefs_observer_type type
= scprefs_observer_type_mcx
;
7819 const _scprefs_observer_type type
= scprefs_observer_type_global
;
7821 static dispatch_queue_t proxy_cb_queue
;
7823 proxy_cb_queue
= dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL
);
7824 _scprefs_observer_watch(type
,
7825 "com.apple.SystemConfiguration.plist",
7828 SCDynamicStoreNotifyValue(NULL
, S_state_global_proxies
);
7829 notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY
);
7830 my_log(LOG_DEBUG
, "IPMonitor: Notifying:\n%@",
7831 S_state_global_proxies
);
7836 #include "IPMonitorControlPrefs.h"
7838 __private_extern__ SCLoggerRef
7841 return (S_IPMonitor_logger
);
7845 prefs_changed(__unused SCPreferencesRef prefs
)
7847 if (S_bundle_logging_verbose
|| IPMonitorControlPrefsIsVerbose()) {
7848 S_IPMonitor_debug
= kDebugFlagDefault
;
7849 S_IPMonitor_verbose
= TRUE
;
7850 SCLoggerSetFlags(S_IPMonitor_logger
, kSCLoggerFlagsFile
| kSCLoggerFlagsDefault
);
7851 my_log(LOG_DEBUG
, "IPMonitor: Setting logging verbose mode on.");
7853 my_log(LOG_DEBUG
, "IPMonitor: Setting logging verbose mode off.");
7854 S_IPMonitor_debug
= 0;
7855 S_IPMonitor_verbose
= FALSE
;
7856 SCLoggerSetFlags(S_IPMonitor_logger
, kSCLoggerFlagsDefault
);
7861 #define LOGGER_ID CFSTR("com.apple.networking.IPMonitor")
7865 if (S_IPMonitor_logger
!= NULL
) {
7868 S_IPMonitor_logger
= SCLoggerCreate(LOGGER_ID
);
7874 #if !TARGET_IPHONE_SIMULATOR
7885 struct rt_msghdr
* rtm
;
7886 struct sockaddr_in
*sin
;
7892 mib
[4] = NET_RT_FLAGS
;
7893 mib
[5] = RTF_STATIC
| RTF_DYNAMIC
;
7894 for (i
= 0; i
< 3; i
++) {
7895 if (sysctl(mib
, N_MIB
, NULL
, &needed
, NULL
, 0) < 0) {
7898 if ((buf
= malloc(needed
)) == NULL
) {
7901 if (sysctl(mib
, N_MIB
, buf
, &needed
, NULL
, 0) >= 0) {
7911 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
7914 /* ALIGN: assume kernel provides necessary alignment */
7915 rtm
= (struct rt_msghdr
*)(void *)next
;
7916 sin
= (struct sockaddr_in
*)(rtm
+ 1);
7918 addr
= ntohl(sin
->sin_addr
.s_addr
);
7919 if (IN_LOOPBACK(addr
)) {
7921 "IPMonitor: flush_routes: ignoring loopback route");
7924 if (IN_LOCAL_GROUP(addr
)) {
7926 "IPMonitor: flush_routes: ignoring multicast route");
7929 rtm
->rtm_type
= RTM_DELETE
;
7930 rtm
->rtm_seq
= ++rtm_seq
;
7931 if (write(s
, rtm
, rtm
->rtm_msglen
) < 0) {
7933 "IPMonitor: flush_routes: removing route for "
7934 IP_FORMAT
" failed, %s",
7935 IP_LIST(&sin
->sin_addr
),
7940 "IPMonitor: flush_routes: removed route for " IP_FORMAT
,
7941 IP_LIST(&sin
->sin_addr
));
7949 flush_inet_routes(void)
7953 s
= open_routing_socket();
7960 #else /* !TARGET_IPHONE_SIMULATOR */
7963 flush_inet_routes(void)
7967 #endif /* !TARGET_IPHONE_SIMULATOR */
7974 CFMutableArrayRef keys
= NULL
;
7975 CFStringRef pattern
;
7976 CFMutableArrayRef patterns
= NULL
;
7977 CFRunLoopSourceRef rls
= NULL
;
7979 if (S_is_network_boot() != 0) {
7984 flush_inet_routes();
7987 if (S_is_scoped_routing_enabled() != 0) {
7988 S_scopedroute
= TRUE
;
7991 if (S_is_scoped_v6_routing_enabled() != 0) {
7992 S_scopedroute_v6
= TRUE
;
7995 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
7996 IPMonitorNotify
, NULL
);
7997 if (S_session
== NULL
) {
7999 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8000 SCErrorString(SCError()));
8004 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8005 kSCDynamicStoreDomainState
,
8008 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8009 kSCDynamicStoreDomainState
,
8012 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8013 kSCDynamicStoreDomainState
,
8015 S_state_global_proxies
8016 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8017 kSCDynamicStoreDomainState
,
8019 #if !TARGET_OS_IPHONE
8021 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8022 kSCDynamicStoreDomainState
,
8024 #endif /* !TARGET_OS_IPHONE */
8026 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
8027 kSCDynamicStoreDomainSetup
,
8029 S_state_service_prefix
8030 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8031 kSCDynamicStoreDomainState
,
8034 S_setup_service_prefix
8035 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
8036 kSCDynamicStoreDomainSetup
,
8039 S_service_state_dict
8040 = CFDictionaryCreateMutable(NULL
, 0,
8041 &kCFTypeDictionaryKeyCallBacks
,
8042 &kCFTypeDictionaryValueCallBacks
);
8044 S_ipv4_service_rank_dict
8045 = CFDictionaryCreateMutable(NULL
, 0,
8046 &kCFTypeDictionaryKeyCallBacks
,
8047 &kCFTypeDictionaryValueCallBacks
);
8049 S_ipv6_service_rank_dict
8050 = CFDictionaryCreateMutable(NULL
, 0,
8051 &kCFTypeDictionaryKeyCallBacks
,
8052 &kCFTypeDictionaryValueCallBacks
);
8054 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8055 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
8057 /* register for State: and Setup: per-service notifications */
8058 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
8060 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetPPP
);
8061 CFArrayAppendValue(patterns
, pattern
);
8064 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetVPN
);
8065 CFArrayAppendValue(patterns
, pattern
);
8068 pattern
= setup_service_key(kSCCompAnyRegex
, kSCEntNetInterface
);
8069 CFArrayAppendValue(patterns
, pattern
);
8072 /* register for State: per-service PPP/VPN/IPSec status notifications */
8073 add_transient_status_keys(kSCCompAnyRegex
, patterns
);
8075 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
8076 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
8078 /* add notifier for multicast DNS configuration (Bonjour/.local) */
8079 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8080 kSCDynamicStoreDomainState
,
8082 CFSTR(kDNSServiceCompMulticastDNS
));
8083 CFArrayAppendValue(keys
, S_multicast_resolvers
);
8085 /* add notifier for private DNS configuration (Back to My Mac) */
8086 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
8087 kSCDynamicStoreDomainState
,
8089 CFSTR(kDNSServiceCompPrivateDNS
));
8090 CFArrayAppendValue(keys
, S_private_resolvers
);
8092 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
8094 "IPMonitor ip_plugin_init "
8095 "SCDynamicStoreSetNotificationKeys failed: %s",
8096 SCErrorString(SCError()));
8100 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
8103 "IPMonitor ip_plugin_init "
8104 "SCDynamicStoreCreateRunLoopSource failed: %s",
8105 SCErrorString(SCError()));
8109 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
8112 /* initialize dns configuration */
8113 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
8114 #if !TARGET_OS_IPHONE
8116 #endif /* !TARGET_OS_IPHONE */
8117 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
8119 #if !TARGET_OS_IPHONE
8120 /* initialize SMB configuration */
8121 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
8122 #endif /* !TARGET_OS_IPHONE */
8127 my_CFRelease(&keys
);
8128 my_CFRelease(&patterns
);
8136 /* initialize multicast route */
8137 update_ipv4(NULL
, NULL
, NULL
);
8142 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
8146 boolean_t ret
= def
;
8148 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
8150 ret
= CFBooleanGetValue(b
);
8155 #if !TARGET_IPHONE_SIMULATOR
8156 #include "IPMonitorControlServer.h"
8159 InterfaceRankChanged(void * info
)
8161 CFDictionaryRef assertions
= NULL
;
8164 changes
= IPMonitorControlServerCopyInterfaceRankInformation(&assertions
);
8165 if (S_if_rank_dict
!= NULL
) {
8166 CFRelease(S_if_rank_dict
);
8168 S_if_rank_dict
= assertions
;
8169 if (changes
!= NULL
) {
8170 IPMonitorProcessChanges(S_session
, NULL
, changes
);
8177 StartIPMonitorControlServer(void)
8179 CFRunLoopSourceContext context
;
8180 CFRunLoopSourceRef rls
;
8182 bzero(&context
, sizeof(context
));
8183 context
.perform
= InterfaceRankChanged
;
8184 rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
8185 if (IPMonitorControlServerStart(CFRunLoopGetCurrent(), rls
,
8186 &S_bundle_logging_verbose
) == FALSE
) {
8187 my_log(LOG_ERR
, "IPMonitorControlServerStart failed");
8190 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
,
8191 kCFRunLoopDefaultMode
);
8197 #endif /* !TARGET_IPHONE_SIMULATOR */
8201 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
8203 CFDictionaryRef info_dict
;
8205 info_dict
= CFBundleGetInfoDictionary(bundle
);
8207 if (info_dict
!= NULL
) {
8209 = S_get_plist_boolean(info_dict
,
8210 CFSTR("AppendStateArrayToSetupArray"),
8213 if (bundleVerbose
) {
8214 S_IPMonitor_debug
= kDebugFlagDefault
;
8215 S_bundle_logging_verbose
= bundleVerbose
;
8216 S_IPMonitor_verbose
= TRUE
;
8221 /* register to receive changes to verbose and read the initial setting */
8222 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed
);
8223 prefs_changed(NULL
);
8226 load_DNSConfiguration(bundle
, // bundle
8227 S_IPMonitor_logger
, // SCLogger
8228 &S_bundle_logging_verbose
, // bundleVerbose
8229 ^(Boolean inSync
) { // syncHandler
8230 dispatch_async(__network_change_queue(), ^{
8231 S_dnsinfo_synced
= inSync
;
8234 ((S_network_change_needed
& NETWORK_CHANGE_DNS
) == 0)) {
8235 // all of the mDNSResponder ack's should result
8236 // in a [new] network change being posted
8237 post_network_change(NETWORK_CHANGE_DNS
);
8239 post_network_change_when_ready();
8244 load_NetworkInformation(bundle
, // bundle
8245 S_IPMonitor_logger
, // SCLogger
8246 &S_bundle_logging_verbose
, // bundleVerbose
8247 ^(Boolean inSync
) { // syncHandler
8248 dispatch_async(__network_change_queue(), ^{
8249 S_nwi_synced
= inSync
;
8250 post_network_change_when_ready();
8253 #if !TARGET_IPHONE_SIMULATOR
8254 StartIPMonitorControlServer();
8255 #endif /* !TARGET_OS_IPHONE */
8257 dns_configuration_init(bundle
);
8259 proxy_configuration_init(bundle
);
8263 #if !TARGET_OS_IPHONE
8264 if (S_session
!= NULL
) {
8265 dns_configuration_monitor(S_session
, IPMonitorNotify
);
8267 #endif /* !TARGET_OS_IPHONE */
8269 #if !TARGET_IPHONE_SIMULATOR
8270 load_hostname((S_IPMonitor_debug
& kDebugFlag1
) != 0);
8271 #endif /* !TARGET_IPHONE_SIMULATOR */
8273 #if !TARGET_OS_IPHONE
8274 load_smb_configuration((S_IPMonitor_debug
& kDebugFlag1
) != 0);
8275 #endif /* !TARGET_OS_IPHONE */
8282 #pragma mark Standalone test code
8285 #ifdef TEST_IPMONITOR
8287 #include "dns-configuration.c"
8289 #if !TARGET_IPHONE_SIMULATOR
8290 #include "set-hostname.c"
8291 #endif /* !TARGET_IPHONE_SIMULATOR */
8294 main(int argc
, char **argv
)
8298 S_IPMonitor_debug
= kDebugFlag1
;
8300 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
8303 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
8305 S_IPMonitor_debug
= kDebugFlag1
;
8311 #endif /* TEST_IPMONITOR */
8313 #ifdef TEST_ROUTELIST
8314 #include "dns-configuration.c"
8315 #include "set-hostname.c"
8320 const char * gateway
;
8321 const char * ifname
;
8326 #ifdef TEST_IPV4_ROUTELIST
8332 const char * router
;
8333 const char * ifname
;
8335 const CFStringRef
* primary_rank
;
8336 struct route
* additional_routes
;
8337 int additional_routes_count
;
8338 struct route
* excluded_routes
;
8339 int excluded_routes_count
;
8340 } IPv4ServiceContents
;
8342 typedef const IPv4ServiceContents
* IPv4ServiceContentsRef
;
8344 struct route loop_routelist
[] = {
8345 { "1.1.1.1", 32, "1.1.1.2", NULL
},
8346 { "1.1.1.2", 32, "1.1.1.3", NULL
},
8347 { "1.1.1.3", 32, "1.1.1.4", NULL
},
8348 { "1.1.1.4", 32, "1.1.1.5", NULL
},
8349 { "1.1.1.5", 32, "1.1.1.6", NULL
},
8350 { "1.1.1.6", 32, "1.1.1.7", NULL
},
8351 { "1.1.1.7", 32, "1.1.1.8", NULL
},
8352 { "1.1.1.8", 32, "1.1.1.9", NULL
},
8353 { "1.1.1.9", 32, "1.1.1.10", NULL
},
8354 { "1.1.1.10", 32, "1.1.1.11", NULL
},
8355 { "1.1.1.11", 32, "1.1.1.1", NULL
},
8358 struct route vpn_routelist
[] = {
8359 { "10.1.3.0", 24, "17.153.46.24", NULL
},
8360 { "10.1.4.0", 24, "17.153.46.24", NULL
},
8361 { "10.1.5.0", 24, "17.153.46.24", NULL
},
8362 { "10.1.6.0", 24, "17.153.46.24", NULL
},
8363 { "10.1.7.0", 24, "17.153.46.24", NULL
},
8364 { "10.16.0.0", 12, "17.153.46.24", NULL
},
8365 { "10.45.0.0", 16, "17.153.46.24", NULL
},
8366 { "10.53.0.0", 16, "17.153.46.24", NULL
},
8367 { "10.70.0.0", 15, "17.153.46.24", NULL
},
8368 { "10.74.0.0", 15, "17.153.46.24", NULL
},
8369 { "10.90.0.0", 15, "17.153.46.24", NULL
},
8370 { "10.91.0.0", 16, "17.153.46.24", NULL
},
8371 { "10.100.0.0", 16, "17.153.46.24", NULL
},
8372 { "10.113.0.0", 16, "17.153.46.24", NULL
},
8373 { "10.128.0.0", 9, "17.153.46.24", NULL
},
8374 { "17.0.0.0", 9, "17.153.46.24", NULL
},
8375 { "17.34.0.0", 16, "17.153.46.24", NULL
},
8376 { "17.112.156.53", 32, "17.153.46.24", NULL
},
8377 { "17.128.0.0", 10, "17.153.46.24", NULL
},
8378 { "17.149.0.121", 32, "17.153.46.24", NULL
},
8379 { "17.149.7.200", 32, "17.153.46.24", NULL
},
8380 { "17.153.46.24", 32, "17.153.46.24", NULL
},
8381 { "17.192.0.0", 12, "17.153.46.24", NULL
},
8382 { "17.208.0.0", 15, "17.153.46.24", NULL
},
8383 { "17.211.0.0", 16, "17.153.46.24", NULL
},
8384 { "17.212.0.0", 14, "17.153.46.24", NULL
},
8385 { "17.216.0.0", 13, "17.153.46.24", NULL
},
8386 { "17.224.0.0", 12, "17.153.46.24", NULL
},
8387 { "17.240.0.0", 16, "17.153.46.24", NULL
},
8388 { "17.241.0.0", 16, "17.153.46.24", NULL
},
8389 { "17.248.0.0", 14, "17.153.46.24", NULL
},
8390 { "17.251.104.200", 32, "17.153.46.24", NULL
},
8391 { "17.252.0.0", 16, "17.153.46.24", NULL
},
8392 { "17.253.0.0", 16, "17.153.46.24", NULL
},
8393 { "17.254.0.0", 16, "17.153.46.24", NULL
},
8394 { "17.255.0.0", 16, "17.153.46.24", NULL
},
8395 { "151.193.141.0", 27, "17.153.46.24", NULL
},
8396 { "172.16.2.0", 24, "17.153.46.24", NULL
},
8397 { "192.35.50.0", 24, "17.153.46.24", NULL
},
8398 { "204.179.20.0", 24, "17.153.46.24", NULL
},
8399 { "206.112.116.0", 24, "17.153.46.24", NULL
},
8402 struct route vpn_routelist_ext
[] = {
8403 { "17.151.63.82", 32, "10.0.0.1", "en0" },
8404 { "17.151.63.81", 32, "17.151.63.81", "en0" },
8405 { "17.151.63.80", 32, NULL
, NULL
},
8406 { "17.1.0.0", 16, NULL
, NULL
},
8407 { "17.2.0.0", 24, NULL
, NULL
},
8408 { "10.0.0.0", 24, NULL
, NULL
},
8412 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
8414 const IPv4ServiceContents en0_10
= {
8415 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, NULL
, NULL
, 0, NULL
, 0
8418 const IPv4ServiceContents en0_15
= {
8419 "10.0.0.19", 24, NULL
, "10.0.0.1", "en0", 15, NULL
, NULL
, 0, NULL
, 0
8422 const IPv4ServiceContents en0_30
= {
8423 "10.0.0.11", 24, NULL
, "10.0.0.1", "en0", 30, NULL
, NULL
, 0, NULL
, 0
8426 const IPv4ServiceContents en0_40
= {
8427 "10.0.0.12", 24, NULL
, "10.0.0.1", "en0", 40, NULL
, NULL
, 0, NULL
, 0
8430 const IPv4ServiceContents en0_50
= {
8431 "10.0.0.13", 24, NULL
, "10.0.0.1", "en0", 50, NULL
, NULL
, 0, NULL
, 0
8434 const IPv4ServiceContents en0_110
= {
8435 "192.168.2.10", 24, NULL
, "192.168.2.1", "en0", 110, NULL
, NULL
, 0, NULL
, 0
8438 const IPv4ServiceContents en0_1
= {
8439 "17.202.40.191", 22, NULL
, "17.202.20.1", "en0", 1, NULL
, NULL
, 0, NULL
, 0
8442 const IPv4ServiceContents en1_20
= {
8443 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, NULL
, NULL
, 0, NULL
, 0
8446 const IPv4ServiceContents en1_2
= {
8447 "17.202.42.24", 22, NULL
, "17.202.20.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
8450 const IPv4ServiceContents en1_125
= {
8451 "192.168.2.20", 24, NULL
, "192.168.2.1", "en1", 125, NULL
, NULL
, 0, NULL
, 0
8454 const IPv4ServiceContents fw0_25
= {
8455 "192.168.2.30", 24, NULL
, "192.168.2.1", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
8458 const IPv4ServiceContents fw0_21
= {
8459 "192.168.3.30", 24, NULL
, "192.168.3.1", "fw0", 21, NULL
, NULL
, 0, NULL
, 0
8462 const IPv4ServiceContents ppp0_0_1
= {
8463 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
, NULL
, 0, NULL
, 0
8466 const IPv4ServiceContents utun0
= {
8467 "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
)
8470 const IPv4ServiceContents en0_test6
= {
8471 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 2, NULL
, NULL
, 0, NULL
, 0
8474 const IPv4ServiceContents en1_test6
= {
8475 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 3, NULL
, NULL
, 0, NULL
, 0
8478 const IPv4ServiceContents en2_test6
= {
8479 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
8482 const IPv4ServiceContents en0_test7
= {
8483 "17.202.42.113", 22, NULL
, "17.202.40.1", "en0", 3, NULL
, NULL
, 0, NULL
, 0
8486 const IPv4ServiceContents en1_test7
= {
8487 "17.202.42.111", 22, NULL
, "17.202.40.1", "en1", 2, NULL
, NULL
, 0, NULL
, 0
8490 const IPv4ServiceContents en2_test7
= {
8491 "17.255.98.164", 20, NULL
, "17.255.96.1", "en2", 1, NULL
, NULL
, 0, NULL
, 0
8494 const IPv4ServiceContents fw0_test6_and_7
= {
8495 "169.254.11.33", 16, NULL
, NULL
, "fw0", 0x0ffffff, NULL
, NULL
, 0, NULL
, 0
8498 const IPv4ServiceContents en0_10_last
= {
8499 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
, NULL
, 0, NULL
, 0
8502 const IPv4ServiceContents en0_10_never
= {
8503 "10.0.0.10", 24, NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
8506 const IPv4ServiceContents en1_20_first
= {
8507 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
, NULL
, 0, NULL
, 0
8510 const IPv4ServiceContents en1_20_never
= {
8511 "10.0.0.20", 24, NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
8514 const IPv4ServiceContents en1_20_other_never
= {
8515 "192.168.2.50", 24, NULL
, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever
, NULL
, 0, NULL
, 0
8518 const IPv4ServiceContents en0_linklocal
= {
8519 "169.254.22.44", 16, NULL
, NULL
, "en0", 0xfffff, NULL
, NULL
, 0, NULL
, 0
8522 const IPv4ServiceContents en0_route_loop
= {
8523 "192.168.130.16", 24, NULL
, "192.168.130.1", "en0", 2, NULL
, loop_routelist
, countof(loop_routelist
), NULL
, 0
8528 IPv4ServiceContentsRef test
[];
8529 } IPv4RouteTest
, * IPv4RouteTestRef
;
8531 static IPv4RouteTest test1
= {
8545 static IPv4RouteTest test2
= {
8558 static IPv4RouteTest test3
= {
8575 static IPv4RouteTest test4
= {
8587 static IPv4RouteTest test5
= {
8600 static IPv4RouteTest test6
= {
8611 static IPv4RouteTest test7
= {
8622 static IPv4RouteTest test8
= {
8631 static IPv4RouteTest test9
= {
8641 static IPv4RouteTest test10
= {
8651 static IPv4RouteTest test11
= {
8661 static IPv4RouteTest test12
= {
8670 static IPv4RouteTest test13
= {
8679 static IPv4RouteTest test14
= {
8687 static IPv4RouteTest test15
= {
8695 static IPv4RouteTest test16
= {
8704 static IPv4RouteTest test17
= {
8708 &en1_20_other_never
,
8713 static IPv4RouteTest test18
= {
8721 static IPv4RouteTestRef ipv4_tests
[] = {
8744 ipv4_prefix_length_is_valid(int prefix_length
)
8746 if (prefix_length
< 0 || prefix_length
> IPV4_ROUTE_ALL_BITS_SET
) {
8753 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
8756 CFStringRef prop_val
;
8761 prop_val
= CFStringCreateWithCString(NULL
,
8763 kCFStringEncodingASCII
);
8764 CFDictionarySetValue(dict
, prop_name
, prop_val
);
8765 CFRelease(prop_val
);
8770 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
8774 CFStringRef prop_val
;
8779 prop_val
= CFStringCreateWithCString(NULL
,
8781 kCFStringEncodingASCII
);
8782 array
= CFArrayCreate(NULL
,
8783 (const void **)&prop_val
, 1,
8784 &kCFTypeArrayCallBacks
);
8785 CFRelease(prop_val
);
8786 CFDictionarySetValue(dict
, prop_name
, array
);
8792 dict_add_ip(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
8797 str
= my_CFStringCreateWithInAddr(ip
);
8798 CFDictionarySetValue(dict
, prop_name
, str
);
8804 dict_add_ip_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
8810 str
= my_CFStringCreateWithInAddr(ip
);
8811 array
= CFArrayCreate(NULL
,
8812 (const void **)&str
, 1,
8813 &kCFTypeArrayCallBacks
);
8815 CFDictionarySetValue(dict
, prop_name
, array
);
8821 dict_insert_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
8822 struct route
* routes
, int routes_count
)
8825 CFMutableArrayRef route_list
;
8826 struct route
* scan
;
8828 if (routes
== NULL
|| routes_count
== 0) {
8831 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
8832 &kCFTypeArrayCallBacks
);
8833 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
8834 struct in_addr mask
;
8835 CFMutableDictionaryRef route_dict
;
8838 = CFDictionaryCreateMutable(NULL
, 0,
8839 &kCFTypeDictionaryKeyCallBacks
,
8840 &kCFTypeDictionaryValueCallBacks
);
8841 dict_add_string(route_dict
, kSCPropNetIPv4RouteDestinationAddress
,
8843 if (ipv4_prefix_length_is_valid(scan
->prefix_length
)) {
8844 mask
.s_addr
= htonl(prefix_to_mask32(scan
->prefix_length
));
8845 dict_add_ip(route_dict
, kSCPropNetIPv4RouteSubnetMask
, mask
);
8847 dict_add_string(route_dict
, kSCPropNetIPv4RouteGatewayAddress
,
8849 dict_add_string(route_dict
, kSCPropNetIPv4RouteInterfaceName
,
8851 CFArrayAppendValue(route_list
, route_dict
);
8852 CFRelease(route_dict
);
8854 CFDictionarySetValue(dict
, prop_name
, route_list
);
8855 CFRelease(route_list
);
8859 static CFDictionaryRef
8860 make_IPv4_dict(IPv4ServiceContentsRef t
)
8862 CFMutableDictionaryRef dict
;
8864 dict
= CFDictionaryCreateMutable(NULL
, 0,
8865 &kCFTypeDictionaryKeyCallBacks
,
8866 &kCFTypeDictionaryValueCallBacks
);
8867 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
8868 if (ipv4_prefix_length_is_valid(t
->prefix_length
)) {
8869 struct in_addr mask
;
8871 mask
.s_addr
= htonl(prefix_to_mask32(t
->prefix_length
));
8872 dict_add_ip_as_array(dict
, kSCPropNetIPv4SubnetMasks
, mask
);
8874 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
8875 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
8876 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
8877 dict_add_string(dict
, kSCPropConfirmedInterfaceName
, t
->ifname
);
8878 dict_insert_routes(dict
, kSCPropNetIPv4AdditionalRoutes
,
8879 t
->additional_routes
, t
->additional_routes_count
);
8880 dict_insert_routes(dict
, kSCPropNetIPv4ExcludedRoutes
,
8881 t
->excluded_routes
, t
->excluded_routes_count
);
8886 kDirectionForwards
= 0,
8887 kDirectionBackwards
= 1
8891 kLogRouteDisabled
= 0,
8892 kLogRouteEnabled
= 1
8895 static IPv4RouteListRef
8896 make_IPv4RouteList_for_test(IPv4RouteListRef list
,
8897 IPv4ServiceContentsRef test
,
8900 CFDictionaryRef dict
;
8903 Rank rank_assertion
= kRankAssertionDefault
;
8904 CFNumberRef rank_assertion_cf
= NULL
;
8905 Boolean rank_assertion_is_set
= FALSE
;
8906 IPv4RouteListRef ret
= NULL
;
8907 IPV4_ROUTES_BUF_DECL(routes
);
8909 dict
= make_IPv4_dict(test
);
8911 fprintf(stderr
, "make_IPv4_dict failed\n");
8914 if (test
->primary_rank
!= NULL
) {
8916 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
8917 &rank_assertion_is_set
);
8918 if (rank_assertion_is_set
) {
8920 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
8923 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
8925 my_CFRelease(&rank_assertion_cf
);
8927 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
8931 if (rank_assertion
== kRankAssertionScoped
) {
8932 rank_assertion
= kRankAssertionNever
;
8934 rank
= RankMake(test
->rank
, rank_assertion
);
8935 if (log_it
== kLogRouteEnabled
8936 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
8939 descr
= IPv4RouteListCopyDescription(r
);
8940 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Adding %@"), descr
);
8943 ret
= IPv4RouteListAddRouteList(list
, 1, r
, rank
);
8951 static IPv4RouteListRef
8952 make_IPv4RouteList(IPv4ServiceContentsRef
* test
, Direction direction
,
8955 IPv4RouteListRef ret
= NULL
;
8956 IPv4ServiceContentsRef
* scan
;
8958 switch (direction
) {
8959 case kDirectionBackwards
:
8960 for (scan
= test
; *scan
!= NULL
; scan
++) {
8961 /* find the end of the list */
8963 for (scan
--; scan
>= test
; scan
--) {
8964 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
8968 case kDirectionForwards
:
8969 for (scan
= test
; *scan
!= NULL
; scan
++) {
8970 ret
= make_IPv4RouteList_for_test(ret
, *scan
, log_it
);
8974 IPv4RouteListFinalize(ret
);
8978 #define EMPHASIS_CHARS "================="
8981 * Function: routelist_build_test
8983 * Runs through the given set of routes first in the forward direction,
8984 * then again backwards. We should end up with exactly the same set of
8985 * routes at the end.
8988 routelist_build_test(IPv4RouteTestRef test
)
8991 boolean_t ret
= FALSE
;
8992 IPv4RouteListRef routes1
;
8993 IPv4RouteListRef routes2
;
8995 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
8996 EMPHASIS_CHARS
"\n",
8999 routes1
= make_IPv4RouteList(test
->test
, kDirectionForwards
,
9001 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9002 if (routes1
!= NULL
) {
9003 descr
= IPv4RouteListCopyDescription(routes1
);
9004 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9008 routes2
= make_IPv4RouteList(test
->test
, kDirectionBackwards
,
9010 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9011 if (routes2
!= NULL
) {
9012 descr
= IPv4RouteListCopyDescription(routes2
);
9013 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9017 if ((routes1
!= NULL
&& routes2
== NULL
)
9018 || (routes1
== NULL
&& routes2
!= NULL
)) {
9019 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
9020 (routes1
!= NULL
) ? "not " : "",
9021 (routes2
!= NULL
) ? "not " : "");
9023 else if (routes1
!= NULL
&& routes2
!= NULL
) {
9024 /* check if they are different */
9025 if (routes1
->count
!= routes2
->count
) {
9026 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
9027 routes1
->count
, routes2
->count
);
9029 else if (bcmp(routes1
, routes2
,
9030 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
9031 fprintf(stderr
, "routes1 and routes2 are different\n");
9034 printf("routes1 and routes2 are the same\n");
9038 if (routes1
!= NULL
) {
9041 if (routes2
!= NULL
) {
9044 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
9045 EMPHASIS_CHARS
"\n",
9046 test
->name
, ret
? "PASSED" : "FAILED");
9051 apply_test(IPv4RouteTestRef old_test
, IPv4RouteTestRef new_test
)
9053 IPv4RouteListRef new_routes
;
9054 IPv4RouteListRef old_routes
;
9056 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
9057 EMPHASIS_CHARS
"\n",
9058 old_test
->name
, new_test
->name
);
9060 old_routes
= make_IPv4RouteList(old_test
->test
, kDirectionForwards
,
9062 new_routes
= make_IPv4RouteList(new_test
->test
, kDirectionForwards
,
9064 if (old_routes
== NULL
) {
9065 printf("No Old Routes\n");
9068 printf("Old routes ('%s') = ", old_test
->name
);
9069 IPv4RouteListPrint(old_routes
);
9072 /* apply the old routes */
9073 IPv4RouteListApply(NULL
, old_routes
, -1);
9075 if (new_routes
== NULL
) {
9076 printf("No New Routes\n");
9079 printf("New Routes ('%s') = ", new_test
->name
);
9080 IPv4RouteListPrint(new_routes
);
9083 /* apply the new routes */
9084 IPv4RouteListApply(old_routes
, new_routes
, -1);
9086 if (old_routes
!= NULL
) {
9089 if (new_routes
!= NULL
) {
9092 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
9093 EMPHASIS_CHARS
"\n",
9094 old_test
->name
, new_test
->name
);
9099 main(int argc
, char **argv
)
9101 IPv4RouteTestRef
* test
;
9104 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
9105 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
9107 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9109 S_scopedroute
= (argc
< 3);
9110 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9111 if (routelist_build_test(*test
) == FALSE
) {
9112 fprintf(stderr
, "%s failed\n", (*test
)->name
);
9116 for (test
= ipv4_tests
; *test
!= NULL
; test
++) {
9117 IPv4RouteTestRef
* test2
;
9119 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
9120 apply_test(*test
, *test2
);
9121 apply_test(*test2
, *test
);
9128 printf("\nChecking for leaks\n");
9129 sprintf(cmd
, "leaks %d 2>&1", getpid());
9137 #endif /* TEST_IPV4_ROUTELIST */
9139 #ifdef TEST_IPV6_ROUTELIST
9147 typedef const IPv6Address
* IPv6AddressRef
;
9150 IPv6AddressRef addr
;
9152 const char * router
;
9153 const char * ifname
;
9155 const CFStringRef
* primary_rank
;
9156 struct route
* additional_routes
;
9157 int additional_routes_count
;
9158 struct route
* excluded_routes
;
9159 int excluded_routes_count
;
9160 } IPv6ServiceContents
;
9162 typedef const IPv6ServiceContents
* IPv6ServiceContentsRef
;
9164 struct route loop_routelist
[] = {
9165 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
9166 "2620:149:4:f01:225:ff:fecc:89a2", NULL
},
9167 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
9168 "2620:149:4:f01:225:ff:fecc:89a3", NULL
},
9169 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
9170 "2620:149:4:f01:225:ff:fecc:89a4", NULL
},
9171 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
9172 "2620:149:4:f01:225:ff:fecc:89a5", NULL
},
9173 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
9174 "2620:149:4:f01:225:ff:fecc:89a6", NULL
},
9175 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
9176 "2620:149:4:f01:225:ff:fecc:89a7", NULL
},
9177 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
9178 "2620:149:4:f01:225:ff:fecc:89a8", NULL
},
9179 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
9180 "2620:149:4:f01:225:ff:fecc:89a9", NULL
},
9181 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
9182 "2620:149:4:f01:225:ff:fecc:89aa", NULL
},
9183 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
9184 "2620:149:4:f01:225:ff:fecc:89ab", NULL
},
9185 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
9186 "2620:149:4:f01:225:ff:fecc:89a1", NULL
},
9189 struct route vpn_routelist
[] = {
9190 { "2010:470:1f05:3cb::", 64,
9191 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9192 { "2010:222:3fa5:acb::", 48,
9193 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9194 { "2010:222:3fa5:1234::", 40,
9195 "fe80::2d0:bcff:fe3d:8c00", NULL
},
9196 { "2010:222:3fa5:5678::", 40,
9200 struct route vpn_routelist_ext
[] = {
9201 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL
, NULL
},
9204 struct route en1_routelist_ext
[] = {
9205 { "2020:299:abcd:ef12::", 64, NULL
, NULL
},
9209 static const IPv6Address en0_addr1
[] = {
9210 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL
},
9211 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL
}
9214 static const IPv6Address en0_addr2
[] = {
9215 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL
},
9216 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL
}
9219 static const IPv6Address en0_addr3
[] = {
9220 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL
},
9221 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL
}
9224 static const IPv6Address en0_addr4
[] = {
9225 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL
},
9226 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL
}
9229 static const IPv6Address en0_addr5
[] = {
9230 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL
},
9231 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL
}
9234 static const IPv6Address en0_addr6
[] = {
9235 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL
},
9238 static const IPv6Address en0_lladdr
[] = {
9239 { "fe80::cabc:c8ff:fe96:96af", 64, NULL
}
9242 static const IPv6Address en1_addr
[] = {
9243 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL
},
9244 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL
}
9247 static const IPv6Address utun0_addr
[] = {
9248 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL
},
9251 static const IPv6Address fw0_addr1
[] = {
9252 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL
},
9253 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL
}
9257 * address+address-count
9258 * router ifname pri rank additional-routes+count excluded-routes+count
9261 static const IPv6ServiceContents en0_10
= {
9262 en0_addr1
, countof(en0_addr1
),
9263 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9266 static const IPv6ServiceContents en0_15
= {
9267 en0_addr2
, countof(en0_addr2
),
9268 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL
, NULL
, 0, NULL
, 0
9271 static const IPv6ServiceContents en0_30
= {
9272 en0_addr3
, countof(en0_addr3
),
9273 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL
, NULL
, 0, NULL
, 0
9276 static const IPv6ServiceContents en0_40
= {
9277 en0_addr4
, countof(en0_addr4
),
9278 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL
, NULL
, 0, NULL
, 0
9281 static const IPv6ServiceContents en0_50
= {
9282 en0_addr5
, countof(en0_addr5
),
9283 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL
, NULL
, 0, NULL
, 0
9286 static const IPv6ServiceContents en0_10_a
= {
9287 en0_addr6
, countof(en0_addr6
),
9288 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
, NULL
, 0, NULL
, 0
9291 static const IPv6ServiceContents fw0_25
= {
9292 fw0_addr1
, countof(fw0_addr1
),
9293 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL
, NULL
, 0, NULL
, 0
9296 static const IPv6ServiceContents en1_20
= {
9297 en1_addr
, countof(en1_addr
),
9298 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL
, NULL
, 0, NULL
, 0
9301 static const IPv6ServiceContents en1_10_ext
= {
9302 en1_addr
, countof(en1_addr
),
9303 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL
, NULL
, 0,
9304 en1_routelist_ext
, countof(en1_routelist_ext
)
9307 static const IPv6ServiceContents en0_0_lladdr
= {
9308 en0_lladdr
, countof(en0_lladdr
),
9309 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL
, NULL
, 0, NULL
, 0
9312 static const IPv6ServiceContents en0_loop
= {
9313 en0_addr1
, countof(en0_addr1
),
9314 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL
,
9315 loop_routelist
, countof(loop_routelist
), NULL
, 0
9318 static const IPv6ServiceContents utun0
= {
9319 utun0_addr
, countof(utun0_addr
),
9320 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL
,
9321 vpn_routelist
, countof(vpn_routelist
),
9322 vpn_routelist_ext
, countof(vpn_routelist_ext
),
9327 IPv6ServiceContentsRef test
[];
9328 } IPv6RouteTest
, * IPv6RouteTestRef
;
9330 static IPv6RouteTest test1
= {
9344 static IPv6RouteTest test2
= {
9357 static IPv6RouteTest test3
= {
9366 static IPv6RouteTest test4
= {
9375 static IPv6RouteTest test5
= {
9387 static IPv6RouteTestRef ipv6_tests
[] = {
9398 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9401 CFStringRef prop_val
;
9406 prop_val
= CFStringCreateWithCString(NULL
,
9408 kCFStringEncodingASCII
);
9409 CFDictionarySetValue(dict
, prop_name
, prop_val
);
9410 CFRelease(prop_val
);
9415 dict_add_int(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9420 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
9421 CFDictionarySetValue(dict
, prop_name
, num
);
9427 dict_insert_v6_routes(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
9428 struct route
* routes
, int routes_count
)
9431 CFMutableArrayRef route_list
;
9432 struct route
* scan
;
9434 if (routes
== NULL
|| routes_count
== 0) {
9437 route_list
= CFArrayCreateMutable(NULL
, routes_count
,
9438 &kCFTypeArrayCallBacks
);
9439 for (i
= 0, scan
= routes
; i
< routes_count
; i
++, scan
++) {
9440 CFMutableDictionaryRef route_dict
;
9442 route_dict
= CFDictionaryCreateMutable(NULL
, 0,
9443 &kCFTypeDictionaryKeyCallBacks
,
9444 &kCFTypeDictionaryValueCallBacks
);
9445 dict_add_string(route_dict
, kSCPropNetIPv6RouteDestinationAddress
,
9447 dict_add_int(route_dict
, kSCPropNetIPv6PrefixLength
,
9448 scan
->prefix_length
);
9449 dict_add_string(route_dict
, kSCPropNetIPv6RouteGatewayAddress
,
9451 dict_add_string(route_dict
, kSCPropNetIPv6RouteInterfaceName
,
9453 CFArrayAppendValue(route_list
, route_dict
);
9454 CFRelease(route_dict
);
9456 CFDictionarySetValue(dict
, prop_name
, route_list
);
9457 CFRelease(route_list
);
9462 array_add_string(CFMutableArrayRef array
, const char * c_str
)
9466 str
= CFStringCreateWithCString(NULL
,
9468 kCFStringEncodingUTF8
);
9469 CFArrayAppendValue(array
, str
);
9475 array_add_int(CFMutableArrayRef array
, int int_val
)
9479 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &int_val
);
9480 CFArrayAppendValue(array
, num
);
9486 dict_add_ipv6_addressing(CFMutableDictionaryRef dict
,
9487 IPv6AddressRef list
, int list_count
)
9489 CFMutableArrayRef addr
= NULL
;
9490 CFMutableArrayRef dest
= NULL
;
9492 CFMutableArrayRef prefix
= NULL
;
9493 IPv6AddressRef scan
;
9495 if (list
== NULL
|| list_count
== 0) {
9498 for (i
= 0, scan
= list
; i
< list_count
; i
++, scan
++) {
9499 if (scan
->addr
!= NULL
) {
9501 addr
= CFArrayCreateMutable(NULL
, list_count
,
9502 &kCFTypeArrayCallBacks
);
9504 array_add_string(addr
, scan
->addr
);
9506 if (scan
->prefix_length
>= 0) {
9507 if (prefix
== NULL
) {
9508 prefix
= CFArrayCreateMutable(NULL
, list_count
,
9509 &kCFTypeArrayCallBacks
);
9511 array_add_int(prefix
, scan
->prefix_length
);
9513 if (scan
->dest
!= NULL
) {
9515 dest
= CFArrayCreateMutable(NULL
, list_count
,
9516 &kCFTypeArrayCallBacks
);
9518 array_add_string(dest
, scan
->dest
);
9522 CFDictionarySetValue(dict
, kSCPropNetIPv6Addresses
, addr
);
9526 CFDictionarySetValue(dict
, kSCPropNetIPv6DestAddresses
, dest
);
9529 if (prefix
!= NULL
) {
9530 CFDictionarySetValue(dict
, kSCPropNetIPv6PrefixLength
, prefix
);
9536 static CFDictionaryRef
9537 make_IPv6_dict(IPv6ServiceContentsRef t
)
9539 CFMutableDictionaryRef dict
;
9541 dict
= CFDictionaryCreateMutable(NULL
, 0,
9542 &kCFTypeDictionaryKeyCallBacks
,
9543 &kCFTypeDictionaryValueCallBacks
);
9544 dict_add_ipv6_addressing(dict
, t
->addr
, t
->addr_count
);
9545 dict_add_string(dict
, kSCPropNetIPv6Router
, t
->router
);
9546 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
9547 dict_insert_v6_routes(dict
, kSCPropNetIPv6AdditionalRoutes
,
9548 t
->additional_routes
, t
->additional_routes_count
);
9549 dict_insert_v6_routes(dict
, kSCPropNetIPv6ExcludedRoutes
,
9550 t
->excluded_routes
, t
->excluded_routes_count
);
9555 kDirectionForwards
= 0,
9556 kDirectionBackwards
= 1
9560 kLogRouteDisabled
= 0,
9561 kLogRouteEnabled
= 1
9564 static IPv6RouteListRef
9565 make_IPv6RouteList_for_test(IPv6RouteListRef list
,
9566 IPv6ServiceContentsRef test
,
9569 CFDictionaryRef dict
;
9572 Rank rank_assertion
= kRankAssertionDefault
;
9573 CFNumberRef rank_assertion_cf
= NULL
;
9574 Boolean rank_assertion_is_set
= FALSE
;
9575 IPv6RouteListRef ret
= NULL
;
9576 IPV6_ROUTES_BUF_DECL(routes
);
9578 dict
= make_IPv6_dict(test
);
9580 fprintf(stderr
, "make_IPv6_dict failed\n");
9583 if (test
->primary_rank
!= NULL
) {
9585 = PrimaryRankGetRankAssertion(*test
->primary_rank
,
9586 &rank_assertion_is_set
);
9587 if (rank_assertion_is_set
) {
9589 = CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rank_assertion
);
9592 r
= IPv6RouteListCreateWithDictionary(routes
, dict
,
9594 my_CFRelease(&rank_assertion_cf
);
9596 fprintf(stderr
, "IPv6RouteListCreateWithDictionary failed\n");
9600 if (rank_assertion
== kRankAssertionScoped
) {
9601 rank_assertion
= kRankAssertionNever
;
9603 rank
= RankMake(test
->rank
, rank_assertion
);
9604 if (log_it
== kLogRouteEnabled
9605 && (S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9608 descr
= IPv6RouteListCopyDescription(r
);
9609 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Adding %@"), descr
);
9612 ret
= IPv6RouteListAddRouteList(list
, 1, r
, rank
);
9620 static IPv6RouteListRef
9621 make_IPv6RouteList(IPv6ServiceContentsRef
* test
, Direction direction
,
9624 IPv6RouteListRef ret
= NULL
;
9625 IPv6ServiceContentsRef
* scan
;
9627 switch (direction
) {
9628 case kDirectionBackwards
:
9629 for (scan
= test
; *scan
!= NULL
; scan
++) {
9630 /* find the end of the list */
9632 for (scan
--; scan
>= test
; scan
--) {
9633 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
9637 case kDirectionForwards
:
9638 for (scan
= test
; *scan
!= NULL
; scan
++) {
9639 ret
= make_IPv6RouteList_for_test(ret
, *scan
, log_it
);
9643 IPv6RouteListFinalize(ret
);
9647 #define EMPHASIS_CHARS "================="
9650 * Function: routelist_build_test
9652 * Runs through the given set of routes first in the forward direction,
9653 * then again backwards. We should end up with exactly the same set of
9654 * routes at the end.
9657 routelist_build_test(IPv6RouteTestRef test
)
9660 boolean_t ret
= FALSE
;
9661 IPv6RouteListRef routes1
;
9662 IPv6RouteListRef routes2
;
9664 printf("\n" EMPHASIS_CHARS
"> RouteList Build '%s' <"
9665 EMPHASIS_CHARS
"\n",
9668 routes1
= make_IPv6RouteList(test
->test
, kDirectionForwards
,
9670 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9671 if (routes1
!= NULL
) {
9672 descr
= IPv6RouteListCopyDescription(routes1
);
9673 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9677 routes2
= make_IPv6RouteList(test
->test
, kDirectionBackwards
,
9679 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
9680 if (routes2
!= NULL
) {
9681 descr
= IPv6RouteListCopyDescription(routes2
);
9682 SCPrint(TRUE
, stdout
, CFSTR("Routes are %@\n"), descr
);
9686 if ((routes1
!= NULL
&& routes2
== NULL
)
9687 || (routes1
== NULL
&& routes2
!= NULL
)) {
9688 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
9689 (routes1
!= NULL
) ? "not " : "",
9690 (routes2
!= NULL
) ? "not " : "");
9692 else if (routes1
!= NULL
&& routes2
!= NULL
) {
9693 /* check if they are different */
9694 if (routes1
->count
!= routes2
->count
) {
9695 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
9696 routes1
->count
, routes2
->count
);
9698 else if (bcmp(routes1
, routes2
,
9699 IPv6RouteListComputeSize(routes1
->count
)) != 0) {
9700 fprintf(stderr
, "routes1 and routes2 are different\n");
9703 printf("routes1 and routes2 are the same\n");
9707 if (routes1
!= NULL
) {
9710 if (routes2
!= NULL
) {
9713 printf(EMPHASIS_CHARS
"> RouteList Build '%s': %s <"
9714 EMPHASIS_CHARS
"\n",
9715 test
->name
, ret
? "PASSED" : "FAILED");
9720 apply_test(IPv6RouteTestRef old_test
, IPv6RouteTestRef new_test
)
9722 IPv6RouteListRef new_routes
;
9723 IPv6RouteListRef old_routes
;
9725 printf("\n" EMPHASIS_CHARS
"> Apply '%s', '%s' Begin <"
9726 EMPHASIS_CHARS
"\n",
9727 old_test
->name
, new_test
->name
);
9729 old_routes
= make_IPv6RouteList(old_test
->test
, kDirectionForwards
,
9731 new_routes
= make_IPv6RouteList(new_test
->test
, kDirectionForwards
,
9733 if (old_routes
== NULL
) {
9734 printf("No Old Routes\n");
9737 printf("Old routes ('%s') = ", old_test
->name
);
9738 IPv6RouteListPrint(old_routes
);
9741 /* apply the old routes */
9742 IPv6RouteListApply(NULL
, old_routes
, -1);
9743 if (new_routes
== NULL
) {
9744 printf("No New Routes\n");
9747 printf("New Routes ('%s') = ", new_test
->name
);
9748 IPv6RouteListPrint(new_routes
);
9751 /* apply the new routes */
9752 IPv6RouteListApply(old_routes
, new_routes
, -1);
9753 if (old_routes
!= NULL
) {
9756 if (new_routes
!= NULL
) {
9759 printf(EMPHASIS_CHARS
"> Apply '%s', '%s' End <"
9760 EMPHASIS_CHARS
"\n",
9761 old_test
->name
, new_test
->name
);
9766 main(int argc
, char **argv
)
9768 IPv6RouteTestRef
* test
;
9771 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
9772 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
9774 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
9776 S_scopedroute_v6
= (argc
< 3);
9777 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
9778 if (routelist_build_test(*test
) == FALSE
) {
9779 fprintf(stderr
, "%s failed\n", (*test
)->name
);
9783 for (test
= ipv6_tests
; *test
!= NULL
; test
++) {
9784 IPv6RouteTestRef
* test2
;
9786 for (test2
= test
+ 1; *test2
!= NULL
; test2
++) {
9787 apply_test(*test
, *test2
);
9788 apply_test(*test2
, *test
);
9795 printf("\nChecking for leaks\n");
9796 sprintf(cmd
, "leaks %d 2>&1", getpid());
9804 #endif /* TEST_IPV6_ROUTELIST */