2 * Copyright (c) 2000-2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 * - decides which interface will be made the "primary" interface,
27 * that is, the one with the default route assigned
31 * Modification History
33 * July 19, 2000 Dieter Siegmund (dieter@apple.com)
36 * November 15, 2000 Dieter Siegmund (dieter@apple.com)
37 * - changed to use new configuration model
39 * March 19, 2001 Dieter Siegmund (dieter@apple.com)
40 * - use service state instead of interface state
42 * July 16, 2001 Allan Nathanson (ajn@apple.com)
43 * - update to public SystemConfiguration.framework APIs
45 * August 28, 2001 Dieter Siegmund (dieter@apple.com)
46 * - specify the interface name when installing the default route
47 * - this ensures that default traffic goes to the highest priority
48 * service when multiple interfaces are configured to be on the same subnet
50 * September 16, 2002 Dieter Siegmund (dieter@apple.com)
51 * - don't elect a link-local service to be primary unless it's the only
52 * one that's available
54 * July 16, 2003 Dieter Siegmund (dieter@apple.com)
55 * - modifications to support IPv6
56 * - don't elect a service to be primary if it doesn't have a default route
58 * July 29, 2003 Dieter Siegmund (dieter@apple.com)
59 * - support installing a default route to a router that's not on our subnet
61 * March 22, 2004 Allan Nathanson (ajn@apple.com)
62 * - create expanded DNS configuration
64 * June 20, 2006 Allan Nathanson (ajn@apple.com)
65 * - add SMB configuration
67 * December 5, 2007 Dieter Siegmund (dieter@apple.com)
68 * - added support for multiple scoped routes
75 #include <sys/fcntl.h>
76 #include <sys/ioctl.h>
77 #include <sys/types.h>
78 #include <sys/socket.h>
79 #include <net/route.h>
81 #include <net/if_dl.h>
82 #include <netinet/in.h>
83 #include <netinet/icmp6.h>
84 #include <netinet6/in6_var.h>
85 #include <netinet6/nd6.h>
86 #include <arpa/inet.h>
87 #include <sys/sysctl.h>
90 #include <mach/mach_time.h>
92 #include <SystemConfiguration/SystemConfiguration.h>
93 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
94 #include <SystemConfiguration/SCValidation.h>
95 #include <SystemConfiguration/SCPrivate.h> /* for SCLog() */
98 #include <dnsinfo_create.h>
99 #endif /* !TARGET_OS_IPHONE */
102 #ifndef kDNSServiceCompMulticastDNS
103 #define kDNSServiceCompMulticastDNS "MulticastDNS"
105 #ifndef kDNSServiceCompPrivateDNS
106 #define kDNSServiceCompPrivateDNS "PrivateDNS"
108 #include <network_information.h>
109 #include "network_information_priv.h"
112 kProtocolFlagsNone
= 0x0,
113 kProtocolFlagsIPv4
= 0x1,
114 kProtocolFlagsIPv6
= 0x2
116 typedef uint8_t ProtocolFlags
;
119 kDebugFlag1
= 0x00000001,
120 kDebugFlag2
= 0x00000002,
121 kDebugFlag4
= 0x00000004,
122 kDebugFlag8
= 0x00000008,
123 kDebugFlagDefault
= kDebugFlag1
,
124 kDebugFlagAll
= 0xffffffff
127 #ifdef TEST_IPV4_ROUTELIST
128 #define ROUTELIST_DEBUG(a, f) { if ((S_IPMonitor_debug & (f)) != 0) printf a ;}
130 #define ROUTELIST_DEBUG(a, f)
133 #include "set-hostname.h"
134 #include "dns-configuration.h"
135 #include "proxy-configuration.h"
136 #if !TARGET_OS_IPHONE
137 #include "smb-configuration.h"
138 #endif /* !TARGET_OS_IPHONE */
140 #define PPP_PREFIX "ppp"
142 #define IP_FORMAT "%d.%d.%d.%d"
143 #define IP_CH(ip) ((u_char *)(ip))
144 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
147 * IPv4 Route management
150 typedef uint32_t RouteFlags
;
153 kRouteIsDirectToInterfaceFlag
= 0x00000001,
154 kRouteIsNotSubnetLocalFlag
= 0x00000002,
155 kRouteIsScopedFlag
= 0x00000004,
161 struct in_addr gateway
;
162 char ifname
[IFNAMSIZ
];
163 unsigned int ifindex
;
167 } IPv4Route
, *IPv4RouteRef
;
172 IPv4Route list
[1]; /* variable length */
173 } IPv4RouteList
, *IPv4RouteListRef
;
176 kIPv4RouteListAddRouteCommand
,
177 kIPv4RouteListRemoveRouteCommand
181 char ifname
[IFNAMSIZ
];
184 struct in6_addr iaddr6
;
185 } IPv6RankedE
, *IPv6RankedERef
;
191 } IPv6RankedList
, *IPv6RankedListRef
;
196 * A 32-bit value to encode the relative rank of a service.
198 * The top 8 bits are used to hold the rank assertion (first, last, never, default),
199 * the bottom 24 bits are used to store the service index i.e. the position within
200 * the service order array.
202 #define RANK_ASSERTION_MAKE(r) ((Rank)(r) << 24)
203 #define kRankAssertionFirst RANK_ASSERTION_MAKE(0)
204 #define kRankAssertionDefault RANK_ASSERTION_MAKE(1)
205 #define kRankAssertionLast RANK_ASSERTION_MAKE(2)
206 #define kRankAssertionNever RANK_ASSERTION_MAKE(3)
207 #define kRankAssertionMask RANK_ASSERTION_MAKE(0xff)
208 #define RANK_ASSERTION_MASK(r) ((Rank)(r) & kRankAssertionMask)
210 #define RANK_INDEX_MAKE(r) ((Rank)(r))
211 #define kRankIndexMask RANK_INDEX_MAKE(0xffffff)
212 #define RANK_INDEX_MASK(r) ((Rank)(r) & kRankIndexMask)
214 static __inline__ Rank
215 RankMake(uint32_t service_index
, Rank primary_rank
)
217 return (RANK_INDEX_MASK(service_index
) | RANK_ASSERTION_MASK(primary_rank
));
220 static __inline__ Rank
221 PrimaryRankGetRankAssertion(CFStringRef primaryRank
)
223 if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankNever
)) {
224 return kRankAssertionNever
;
225 } else if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankFirst
)) {
226 return kRankAssertionFirst
;
227 } else if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankLast
)) {
228 return kRankAssertionLast
;
230 return kRankAssertionDefault
;
233 typedef uint32_t IPv4RouteListApplyCommand
;
235 typedef void IPv4RouteListApplyCallBackFunc(IPv4RouteListApplyCommand cmd
,
236 IPv4RouteRef route
, void * arg
);
237 typedef IPv4RouteListApplyCallBackFunc
* IPv4RouteListApplyCallBackFuncPtr
;
239 /* SCDynamicStore session */
240 static SCDynamicStoreRef S_session
= NULL
;
242 /* debug output flags */
243 static uint32_t S_IPMonitor_debug
= 0;
245 /* are we netbooted? If so, don't touch the default route */
246 static boolean_t S_netboot
= FALSE
;
248 /* is scoped routing enabled? */
250 static boolean_t S_scopedroute
= FALSE
;
251 static boolean_t S_scopedroute_v6
= FALSE
;
252 #endif /* RTF_IFSCOPE */
254 /* dictionary to hold per-service state: key is the serviceID */
255 static CFMutableDictionaryRef S_service_state_dict
= NULL
;
256 static CFMutableDictionaryRef S_ipv4_service_rank_dict
= NULL
;
257 static CFMutableDictionaryRef S_ipv6_service_rank_dict
= NULL
;
259 /* dictionary to hold per-interface rank information */
260 static CFMutableDictionaryRef S_if_rank_dict
= NULL
;
262 /* if set, a PPP interface overrides the primary */
263 static boolean_t S_ppp_override_primary
= FALSE
;
265 /* the current primary serviceID's */
266 static CFStringRef S_primary_ipv4
= NULL
;
267 static CFStringRef S_primary_ipv6
= NULL
;
268 static CFStringRef S_primary_dns
= NULL
;
269 static CFStringRef S_primary_proxies
= NULL
;
271 static CFStringRef S_state_global_ipv4
= NULL
;
272 static CFStringRef S_state_global_ipv6
= NULL
;
273 static CFStringRef S_state_global_dns
= NULL
;
274 static CFStringRef S_state_global_proxies
= NULL
;
275 static CFStringRef S_state_service_prefix
= NULL
;
276 static CFStringRef S_setup_global_ipv4
= NULL
;
277 static CFStringRef S_setup_service_prefix
= NULL
;
279 static CFStringRef S_multicast_resolvers
= NULL
;
280 static CFStringRef S_private_resolvers
= NULL
;
282 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
284 static const struct in_addr S_ip_zeros
= { 0 };
285 static const struct in6_addr S_ip6_zeros
= IN6ADDR_ANY_INIT
;
287 static boolean_t S_append_state
= FALSE
;
289 static nwi_state_t S_nwi_state
= NULL
;
291 #if !TARGET_OS_IPHONE
292 static CFStringRef S_primary_smb
= NULL
;
293 static CFStringRef S_state_global_smb
= NULL
;
294 #endif /* !TARGET_OS_IPHONE */
296 #if !TARGET_OS_IPHONE
297 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
298 #endif /* !TARGET_OS_IPHONE */
301 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
302 #endif //KERN_NETBOOT
305 ** entityType*, GetEntityChanges*
306 ** - definitions for the entity types we handle
313 #if !TARGET_OS_IPHONE
315 #endif /* !TARGET_OS_IPHONE */
317 kEntityTypeServiceOptions
= 31
319 typedef uint32_t EntityType
;
321 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
322 &kSCEntNetIPv4
, /* 0 */
323 &kSCEntNetIPv6
, /* 1 */
324 &kSCEntNetDNS
, /* 2 */
325 &kSCEntNetProxies
, /* 3 */
326 #if !TARGET_OS_IPHONE
327 &kSCEntNetSMB
, /* 4 */
328 #endif /* !TARGET_OS_IPHONE */
331 #ifndef kSCEntNetIPv4RouteList
332 #define kSCEntNetIPv4RouteList CFSTR("IPv4RouteList")
335 #ifndef kSCEntNetIPv4ServiceDict
336 #define kSCEntNetIPv4ServiceDict CFSTR("IPv4ServiceDict")
339 static IPv4RouteListRef
340 ipv4_dict_get_routelist(CFDictionaryRef ipv4_dict
)
343 IPv4RouteListRef routes_list
= NULL
;
345 if (isA_CFDictionary(ipv4_dict
) == NULL
) {
349 routes
= CFDictionaryGetValue(ipv4_dict
, kSCEntNetIPv4RouteList
);
351 if (routes
!= NULL
) {
352 routes_list
= (IPv4RouteListRef
)(void*)CFDataGetBytePtr(routes
);
354 return (routes_list
);
358 ipv4_dict_get_ifname(CFDictionaryRef ipv4_dict
)
360 CFDictionaryRef ipv4_service_dict
= NULL
;
362 if (isA_CFDictionary(ipv4_dict
) == NULL
) {
366 ipv4_service_dict
= CFDictionaryGetValue(ipv4_dict
,
367 kSCEntNetIPv4ServiceDict
);
369 if (isA_CFDictionary(ipv4_service_dict
) == NULL
) {
373 return CFDictionaryGetValue(ipv4_service_dict
, kSCPropInterfaceName
);
376 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
377 CFDictionaryRef state_dict
,
378 CFDictionaryRef setup_dict
,
379 CFDictionaryRef info
);
380 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
382 static GetEntityChangesFunc get_ipv4_changes
;
383 static GetEntityChangesFunc get_ipv6_changes
;
384 static GetEntityChangesFunc get_dns_changes
;
385 static GetEntityChangesFunc get_proxies_changes
;
386 #if !TARGET_OS_IPHONE
387 static GetEntityChangesFunc get_smb_changes
;
388 #endif /* !TARGET_OS_IPHONE */
391 my_CFRelease(void * t
);
394 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
397 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
399 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
400 get_ipv4_changes
, /* 0 */
401 get_ipv6_changes
, /* 1 */
402 get_dns_changes
, /* 2 */
403 get_proxies_changes
,/* 3 */
404 #if !TARGET_OS_IPHONE
405 get_smb_changes
, /* 4 */
406 #endif /* !TARGET_OS_IPHONE */
411 ** - mechanism to do an atomic update of the SCDynamicStore
412 ** when the content needs to be changed across multiple functions
415 CFMutableArrayRef notify
;
416 CFMutableArrayRef remove
;
417 CFMutableDictionaryRef set
;
418 } keyChangeList
, * keyChangeListRef
;
421 my_CFStringCopyComponent(CFStringRef path
, CFStringRef separator
,
422 CFIndex component_index
)
425 CFStringRef component
= NULL
;
427 arr
= CFStringCreateArrayBySeparatingStrings(NULL
, path
, separator
);
431 if (CFArrayGetCount(arr
) <= component_index
) {
434 component
= CFRetain(CFArrayGetValueAtIndex(arr
, component_index
));
442 keyChangeListInit(keyChangeListRef keys
)
444 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
445 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
446 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
447 &kCFTypeDictionaryKeyCallBacks
,
448 &kCFTypeDictionaryValueCallBacks
);
453 keyChangeListFree(keyChangeListRef keys
)
455 my_CFRelease(&keys
->notify
);
456 my_CFRelease(&keys
->remove
);
457 my_CFRelease(&keys
->set
);
462 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
464 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
469 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
471 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
472 CFDictionaryRemoveValue(keys
->set
, key
);
477 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
479 my_CFArrayRemoveValue(keys
->remove
, key
);
480 CFDictionarySetValue(keys
->set
, key
, value
);
485 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
,
486 CFStringRef network_change_msg
)
488 CFArrayRef notify
= keys
->notify
;
489 CFArrayRef remove
= keys
->remove
;
490 CFDictionaryRef set
= keys
->set
;
493 if (CFArrayGetCount(notify
) == 0) {
496 if (CFArrayGetCount(remove
) == 0) {
499 if (CFDictionaryGetCount(set
) == 0) {
502 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
505 if (S_IPMonitor_debug
& kDebugFlag1
) {
507 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: Setting:\n%@"),
510 if (remove
!= NULL
) {
511 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: Removing:\n%@"),
514 if (notify
!= NULL
) {
515 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: Notifying:\n%@"),
519 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
521 status
= notify_post("com.apple.system.config.network_change");
522 if (status
== NOTIFY_STATUS_OK
) {
523 if (CFStringGetLength(network_change_msg
) != 0) {
524 SCLog(TRUE
, LOG_NOTICE
, CFSTR("network changed:%@"), network_change_msg
);
526 SCLog(TRUE
, LOG_NOTICE
, CFSTR("network changed."));
529 SCLog(TRUE
, LOG_NOTICE
,
530 CFSTR("IPMonitor: notify_post() failed: error=%ld"), status
);
544 mib
[1] = KERN_NETBOOT
;
545 len
= sizeof(netboot
);
546 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
550 static __inline__
int
553 return (socket(AF_INET6
, SOCK_DGRAM
, 0));
558 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
560 struct in6_defrouter dr
;
561 struct sockaddr_in6
* sin6
;
563 bzero(&dr
, sizeof(dr
));
565 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
566 sin6
->sin6_family
= AF_INET6
;
567 sin6
->sin6_addr
= *addr
;
569 dr
.if_index
= if_index
;
570 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
574 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
576 struct in6_defrouter dr
;
577 struct sockaddr_in6
* sin6
;
579 bzero(&dr
, sizeof(dr
));
581 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
582 sin6
->sin6_family
= AF_INET6
;
583 sin6
->sin6_addr
= *addr
;
584 dr
.if_index
= if_index
;
585 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
587 #endif /* SIOCDRADD_IN6 */
591 S_is_scoped_routing_enabled()
594 size_t len
= sizeof(scopedroute
);
596 if ((sysctlbyname("net.inet.ip.scopedroute",
599 && (errno
!= ENOENT
)) {
600 SCLog(TRUE
, LOG_ERR
, CFSTR("sysctlbyname() failed: %s"), strerror(errno
));
602 return (scopedroute
);
606 S_is_scoped_v6_routing_enabled()
608 int scopedroute_v6
= 0;
609 size_t len
= sizeof(scopedroute_v6
);
611 if ((sysctlbyname("net.inet6.ip6.scopedroute",
612 &scopedroute_v6
, &len
,
614 && (errno
!= ENOENT
)) {
615 SCLog(TRUE
, LOG_ERR
, CFSTR("sysctlbyname() failed: %s"), strerror(errno
));
617 return (scopedroute_v6
);
619 #endif /* RTF_IFSCOPE */
622 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
624 CFIndex n
= CFArrayGetCount(arr
);
626 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
629 CFArrayAppendValue(arr
, new);
634 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
638 i
= CFArrayGetFirstIndexOfValue(arr
,
639 CFRangeMake(0, CFArrayGetCount(arr
)),
641 if (i
!= kCFNotFound
) {
642 CFArrayRemoveValueAtIndex(arr
, i
);
648 my_CFRelease(void * t
)
650 void * * obj
= (void * *)t
;
659 static CFDictionaryRef
660 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
662 if (isA_CFDictionary(dict
) == NULL
) {
665 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
669 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
671 if (isA_CFDictionary(dict
) == NULL
) {
674 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
678 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, int addr_size
)
682 if (isA_CFString(str
) == NULL
) {
688 if (addr_size
< sizeof(struct in_addr
)) {
693 if (addr_size
< sizeof(struct in6_addr
)) {
700 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
701 if (inet_pton(family
, buf
, addr
) == 1) {
705 bzero(addr
, addr_size
);
710 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
712 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
716 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
718 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
721 static CF_RETURNS_RETAINED CFStringRef
722 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
724 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
725 kSCDynamicStoreDomainSetup
,
730 static CF_RETURNS_RETAINED CFStringRef
731 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
733 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
734 kSCDynamicStoreDomainState
,
739 static CFDictionaryRef
740 get_service_setup_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
743 CFStringRef setup_key
;
744 CFDictionaryRef setup_dict
;
746 setup_key
= setup_service_key(serviceID
, entity
);
747 setup_dict
= my_CFDictionaryGetDictionary(services_info
, setup_key
);
748 my_CFRelease(&setup_key
);
752 static CFDictionaryRef
753 get_service_state_entity(CFDictionaryRef services_info
, CFStringRef serviceID
,
756 CFStringRef state_key
;
757 CFDictionaryRef state_dict
;
759 state_key
= state_service_key(serviceID
, entity
);
760 state_dict
= my_CFDictionaryGetDictionary(services_info
, state_key
);
761 my_CFRelease(&state_key
);
766 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
770 ip_list
= CFDictionaryGetValue(dict
, prop
);
771 if (isA_CFArray(ip_list
) != NULL
772 && CFArrayGetCount(ip_list
) > 0
773 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
780 get_override_primary(CFDictionaryRef dict
)
784 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
785 if (isA_CFNumber(override
) != NULL
) {
788 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
793 else if (isA_CFBoolean(override
) != NULL
) {
794 if (CFBooleanGetValue(override
)) {
805 static __inline__
struct in_addr
806 subnet_addr(struct in_addr addr
, struct in_addr mask
)
810 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
814 static __inline__
int
815 in_addr_cmp(struct in_addr a
, struct in_addr b
)
817 return (uint32_cmp(ntohl(a
.s_addr
), ntohl(b
.s_addr
)));
820 static __inline__
int
821 RouteFlagsCompare(RouteFlags a
, RouteFlags b
)
823 return (uint32_cmp(a
, b
));
827 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
829 Rank rank_assertion
= RANK_ASSERTION_MASK(r
->rank
);
831 CFStringAppendFormat(str
, NULL
,
832 CFSTR("Dest " IP_FORMAT
835 " Ifp %s Ifa " IP_FORMAT
),
838 IP_LIST(&r
->gateway
),
839 (r
->ifname
[0] != '\0') ? r
->ifname
: "<none>",
841 if ((r
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
842 CFStringAppend(str
, CFSTR(" [non-local]"));
844 else if ((r
->flags
& kRouteIsDirectToInterfaceFlag
) != 0) {
845 CFStringAppend(str
, CFSTR(" [direct]"));
848 switch(rank_assertion
) {
849 case kRankAssertionFirst
:
850 CFStringAppend(str
, CFSTR(" [first]"));
852 case kRankAssertionLast
:
853 CFStringAppend(str
, CFSTR(" [last]"));
855 case kRankAssertionNever
:
856 CFStringAppend(str
, CFSTR(" [never]"));
862 if ((r
->flags
& kRouteIsScopedFlag
) != 0) {
863 CFStringAppend(str
, CFSTR(" [SCOPED]"));
869 IPv4RouteCopyDescription(IPv4RouteRef r
)
871 CFMutableStringRef str
;
873 str
= CFStringCreateMutable(NULL
, 0);
874 IPv4RouteCopyDescriptionWithString(r
, str
);
878 static __inline__
void
879 IPv4RoutePrint(IPv4RouteRef route
)
881 CFStringRef str
= IPv4RouteCopyDescription(route
);
883 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
888 static __inline__
void
889 IPv4RouteLog(IPv4RouteRef route
)
891 CFStringRef str
= IPv4RouteCopyDescription(route
);
893 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@"), str
);
899 IPv4RouteCompare(IPv4RouteRef a
, Rank a_rank
,
900 IPv4RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
905 cmp
= in_addr_cmp(a
->dest
, b
->dest
);
907 cmp
= in_addr_cmp(a
->mask
, b
->mask
);
909 int name_cmp
= strcmp(a
->ifname
, b
->ifname
);
916 cmp
= RankCompare(a_rank
, b_rank
);
923 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
937 a_str
= IPv4RouteCopyDescription(a
);
938 b_str
= IPv4RouteCopyDescription(b
);
939 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@ rank %u %c %@ rank %u"),
940 a_str
, a_rank
, ch
, b_str
, b_rank
);
947 static CFMutableStringRef
948 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
952 CFMutableStringRef str
;
954 str
= CFStringCreateMutable(NULL
, 0);
955 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
957 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
958 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
959 IPv4RouteCopyDescriptionWithString(r
, str
);
961 CFStringAppend(str
, CFSTR("\n}"));
965 static __inline__
void
966 IPv4RouteListPrint(IPv4RouteListRef routes
)
968 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
970 SCPrint(TRUE
, stdout
, CFSTR("%@\n"), str
);
975 static __inline__
void
976 IPv4RouteListLog(IPv4RouteListRef routes
)
978 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
980 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@"), str
);
985 static __inline__
unsigned int
986 IPv4RouteListComputeSize(unsigned int n
)
988 return (offsetof(IPv4RouteList
, list
[n
]));
992 IPv4RouteListFindRoute(IPv4RouteListRef routes
, IPv4RouteRef route
)
995 IPv4RouteRef scan_result
= NULL
;
998 for (i
= 0, scan
= routes
->list
; i
< routes
->count
; i
++, scan
++) {
999 if ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
1000 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
1001 && (strcmp(scan
->ifname
, route
->ifname
) == 0)
1002 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
1003 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)
1004 && (scan
->flags
== route
->flags
)) {
1009 return (scan_result
);
1013 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
1014 IPv4RouteListApplyCallBackFuncPtr func
, void * arg
)
1019 if (old_routes
== new_routes
&& old_routes
== NULL
) {
1020 /* both old and new are NULL, so there's nothing to do */
1023 if (old_routes
!= NULL
) {
1024 for (i
= 0, scan
= old_routes
->list
;
1025 i
< old_routes
->count
;
1027 IPv4RouteRef new_route
= NULL
;
1029 if (new_routes
!= NULL
) {
1030 new_route
= IPv4RouteListFindRoute(new_routes
, scan
);
1032 if (new_route
== NULL
) {
1034 (*func
)(kIPv4RouteListRemoveRouteCommand
, scan
, arg
);
1039 if (new_routes
!= NULL
) {
1040 for (i
= 0, scan
= new_routes
->list
;
1041 i
< new_routes
->count
;
1043 IPv4RouteRef old_route
= NULL
;
1045 if (old_routes
!= NULL
) {
1046 old_route
= IPv4RouteListFindRoute(old_routes
, scan
);
1048 if (old_route
== NULL
) {
1050 (*func
)(kIPv4RouteListAddRouteCommand
, scan
, arg
);
1059 * Function: IPv4RouteListAddRoute
1062 * Add the given IPv4Route to the list of routes, eliminating lower-ranked
1063 * duplicates on the same interface, and marking any lower ranked duplicates
1064 * on other interfaces with kRouteIsScopedFlag.
1066 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1069 * Route list updated with the given route, possibly a different pointer,
1070 * due to using realloc'd memory.
1079 static IPv4RouteListRef
1080 IPv4RouteListAddRoute(IPv4RouteListRef routes
, int init_size
,
1081 IPv4RouteRef this_route
, Rank this_rank
)
1084 IPv4RouteRef first_scan
= NULL
;
1085 int scope_which
= kScopeNone
;
1089 if (routes
== NULL
) {
1090 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(init_size
));
1091 routes
->size
= init_size
;
1094 for (i
= 0, scan
= routes
->list
; i
< routes
->count
;
1097 boolean_t same_dest
;
1099 cmp
= IPv4RouteCompare(this_route
, this_rank
, scan
, scan
->rank
, &same_dest
);
1101 if (same_dest
== TRUE
&& first_scan
== NULL
) {
1107 if (same_dest
== TRUE
1108 && (first_scan
->flags
& kRouteIsScopedFlag
) == 0) {
1109 if ((scan
->flags
& kRouteIsScopedFlag
) != 0) {
1110 ROUTELIST_DEBUG(("Hit 1: set scope on self\n"),
1112 scope_which
= kScopeThis
;
1115 ROUTELIST_DEBUG(("Hit 2: set scope on next\n"),
1117 scope_which
= kScopeNext
;
1120 /* remember our insertion point, but keep going to find a dup */
1124 else if (cmp
== 0) {
1127 /* this route is a duplicate */
1128 ROUTELIST_DEBUG(("Hit 3: removing [%d]\n", i
), kDebugFlag8
);
1130 if (i
== routes
->count
) {
1131 /* last slot, decrementing gets rid of it */
1134 bcopy(routes
->list
+ i
+ 1,
1136 sizeof(routes
->list
[0]) * (routes
->count
- i
));
1140 /* resolve conflict using rank */
1141 if (this_rank
< scan
->rank
) {
1142 boolean_t is_scoped
= FALSE
;
1144 if (scan
->flags
& kRouteIsScopedFlag
) {
1147 ROUTELIST_DEBUG(("Hit 4:replacing [%d] rank %u < %u\n", i
,
1149 scan
->rank
), kDebugFlag8
);
1150 *scan
= *this_route
;
1151 scan
->rank
= this_rank
;
1153 /* preserve whether route was scoped */
1154 ROUTELIST_DEBUG(("Hit 5: preserved scope\n"), kDebugFlag8
);
1155 scan
->flags
|= kRouteIsScopedFlag
;
1162 if (same_dest
== TRUE
) {
1163 if (scope_which
== kScopeNone
) {
1164 ROUTELIST_DEBUG(("Hit 10: set scope on self\n"),
1166 scope_which
= kScopeThis
;
1169 #ifdef TEST_IPV4_ROUTELIST
1170 else if (where
!= -1) {
1171 /* not possible because we maintain a sorted list */
1172 ROUTELIST_DEBUG(("Hit 11: moved past routes - can't happen\n"),
1176 #endif /* TEST_IPV4_ROUTELIST */
1179 if (routes
->size
== routes
->count
) {
1181 IPv4RouteListRef new_routes
;
1184 /* double the size */
1185 old_size
= routes
->size
;
1186 how_many
= old_size
* 2;
1187 new_routes
= (IPv4RouteListRef
)
1188 realloc(routes
, IPv4RouteListComputeSize(how_many
));
1189 if (new_routes
== NULL
) {
1193 ROUTELIST_DEBUG(("increasing size from %d to %d\n", old_size
,
1194 how_many
), kDebugFlag8
);
1195 new_routes
->size
= how_many
;
1196 routes
= new_routes
;
1199 /* add it to the end */
1200 where
= routes
->count
;
1203 /* insert it at [where] */
1204 bcopy(routes
->list
+ where
,
1205 routes
->list
+ where
+ 1,
1206 sizeof(routes
->list
[0]) * (routes
->count
- where
));
1208 /* copy the route */
1209 routes
->list
[where
] = *this_route
;
1210 routes
->list
[where
].rank
= this_rank
;
1213 switch (scope_which
) {
1215 routes
->list
[where
].flags
|= kRouteIsScopedFlag
;
1218 routes
->list
[where
+ 1].flags
|= kRouteIsScopedFlag
;
1230 * Function: IPv4RouteListAddRouteList
1233 * Invoke IPv4RouteListAddRoute for each route in the given list.
1236 * See IPv4RouteListAddRoute for more information.
1238 static IPv4RouteListRef
1239 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
1240 IPv4RouteListRef service_routes
, Rank rank
)
1245 for (i
= 0, scan
= service_routes
->list
;
1246 i
< service_routes
->count
; i
++, scan
++) {
1247 routes
= IPv4RouteListAddRoute(routes
, init_size
, scan
, rank
);
1253 plist_get_cstring(CFDictionaryRef dict
, CFStringRef prop_name
,
1254 char * buf
, int buf_size
)
1258 val
= CFDictionaryGetValue(dict
, prop_name
);
1259 if (isA_CFString(val
) == NULL
) {
1262 if (CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingASCII
)
1270 * Function: IPv4RouteListCreateWithDictionary
1273 * Given the service ipv4 entity dictionary, generate the list of routes.
1274 * Currently, this includes just the default route and subnet route,
1275 * if the service has a subnet mask.
1278 * If the passed in route_list is NULL or too small, this routine
1279 * allocates malloc'd memory to hold the routes.
1281 static IPv4RouteListRef
1282 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
1283 CFDictionaryRef dict
,
1284 CFStringRef primaryRank
)
1286 struct in_addr addr
= { 0 };
1287 RouteFlags flags
= 0;
1288 unsigned int ifindex
;
1290 struct in_addr mask
= { 0 };
1292 boolean_t add_default
= FALSE
;
1293 boolean_t add_subnet
= FALSE
;
1295 struct in_addr subnet
= { 0 };
1296 struct in_addr router
= { 0 };
1297 Rank rank
= kRankAssertionDefault
;
1302 if (plist_get_cstring(dict
, kSCPropInterfaceName
, ifn
, sizeof(ifn
))
1306 #ifdef TEST_IPV4_ROUTELIST
1308 #else /* TEST_IPV4_ROUTELIST */
1309 ifindex
= if_nametoindex(ifn
);
1311 /* interface doesn't exist */
1314 #endif /* TEST_IPV4_ROUTELIST */
1315 if (cfstring_to_ip(CFDictionaryGetValue(dict
, kSCPropNetIPv4Router
),
1317 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
1319 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
1320 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
1322 subnet
= subnet_addr(addr
, mask
);
1323 /* ignore link-local subnets, let IPConfiguration handle them for now */
1324 if (ntohl(subnet
.s_addr
) != IN_LINKLOCALNETNUM
) {
1329 if (addr
.s_addr
== 0) {
1330 /* thanks for playing */
1333 if (router
.s_addr
== 0) {
1334 flags
|= kRouteIsDirectToInterfaceFlag
;
1335 rank
= kRankAssertionLast
;
1339 * If the router address is our address and the subnet mask is
1340 * not 255.255.255.255, assume all routes are local to the interface.
1342 if (addr
.s_addr
== router
.s_addr
1343 && mask
.s_addr
!= INADDR_BROADCAST
) {
1344 flags
|= kRouteIsDirectToInterfaceFlag
;
1346 if (primaryRank
!= NULL
) {
1347 rank
= PrimaryRankGetRankAssertion(primaryRank
);
1348 } else if (get_override_primary(dict
)) {
1349 rank
= kRankAssertionFirst
;
1353 if (rank
== kRankAssertionNever
) {
1354 flags
|= kRouteIsScopedFlag
;
1357 if (add_subnet
&& (flags
& kRouteIsDirectToInterfaceFlag
) == 0
1358 && subnet
.s_addr
!= subnet_addr(router
, mask
).s_addr
) {
1359 flags
|= kRouteIsNotSubnetLocalFlag
;
1362 if (strncmp(ifn
, "lo0", sizeof(ifn
)) != 0) {
1367 if (routes
== NULL
|| routes
->size
< n
) {
1368 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
1371 bzero(routes
, IPv4RouteListComputeSize(n
));
1374 /* start at the beginning */
1378 /* add the default route */
1379 r
->ifindex
= ifindex
;
1380 strlcpy(r
->ifname
, ifn
, sizeof(r
->ifname
));
1383 if ((flags
& kRouteIsDirectToInterfaceFlag
) == 0) {
1384 r
->gateway
= router
;
1390 if (r
->rank
== kRankAssertionNever
) {
1391 r
->flags
|= kRouteIsScopedFlag
;
1396 /* add the subnet route */
1398 r
->ifindex
= ifindex
;
1402 strlcpy(r
->ifname
, ifn
, sizeof(r
->ifname
));
1405 if (r
->rank
== kRankAssertionNever
) {
1406 r
->flags
|= kRouteIsScopedFlag
;
1414 * Function: parse_component
1416 * Given a string 'key' and a string prefix 'prefix',
1417 * return the next component in the slash '/' separated
1421 * 1. key = "a/b/c" prefix = "a/"
1423 * 2. key = "a/b/c" prefix = "a/b/"
1426 static CF_RETURNS_RETAINED CFStringRef
1427 parse_component(CFStringRef key
, CFStringRef prefix
)
1429 CFMutableStringRef comp
;
1432 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
1435 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
1439 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
1440 range
= CFStringFind(comp
, CFSTR("/"), 0);
1441 if (range
.location
== kCFNotFound
) {
1444 range
.length
= CFStringGetLength(comp
) - range
.location
;
1445 CFStringDelete(comp
, range
);
1449 static CFMutableDictionaryRef
1450 service_dict_copy(CFStringRef serviceID
)
1452 CFDictionaryRef d
= NULL
;
1453 CFMutableDictionaryRef service_dict
;
1455 /* create a modifyable dictionary, a copy or a new one */
1456 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
1459 = CFDictionaryCreateMutable(NULL
, 0,
1460 &kCFTypeDictionaryKeyCallBacks
,
1461 &kCFTypeDictionaryValueCallBacks
);
1464 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
1466 return (service_dict
);
1470 dump_service_entity(CFStringRef serviceID
, CFStringRef entity
,
1471 CFStringRef operation
, CFTypeRef val
)
1474 CFDataRef route_list
;
1475 CFMutableStringRef this_val
= NULL
;
1477 if (CFEqual(entity
, kSCEntNetIPv4
) && isA_CFDictionary(val
) != NULL
) {
1478 CFDictionaryRef service_dict
= NULL
;
1480 route_list
= CFDictionaryGetValue(val
, kSCEntNetIPv4RouteList
);
1481 if (route_list
!= NULL
) {
1482 /* ALIGN: CF should align to at least 8-byte boundaries */
1483 this_val
= IPv4RouteListCopyDescription((IPv4RouteListRef
)
1484 (void *)CFDataGetBytePtr(route_list
));
1487 service_dict
= CFDictionaryGetValue(val
, kSCEntNetIPv4ServiceDict
);
1489 if (service_dict
!= NULL
&& isA_CFDictionary(service_dict
) != NULL
) {
1490 if (this_val
== NULL
) {
1491 this_val
= CFStringCreateMutable(NULL
, 0);
1493 CFStringAppendFormat(this_val
, NULL
, CFSTR("\n <IPv4Dictionary>: %@"), service_dict
);
1498 val
= CFSTR("<none>");
1500 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: serviceID %@ %@ %@ value = %@"),
1501 serviceID
, operation
, entity
, val
);
1502 my_CFRelease(&this_val
);
1507 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
1510 boolean_t changed
= FALSE
;
1512 CFMutableDictionaryRef service_dict
;
1514 service_dict
= service_dict_copy(serviceID
);
1515 old_val
= CFDictionaryGetValue(service_dict
, entity
);
1516 if (new_val
== NULL
) {
1517 if (old_val
!= NULL
) {
1518 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1519 dump_service_entity(serviceID
, entity
, CFSTR("Removed:"),
1522 CFDictionaryRemoveValue(service_dict
, entity
);
1527 if (old_val
== NULL
|| CFEqual(new_val
, old_val
) == FALSE
) {
1528 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1529 dump_service_entity(serviceID
, entity
,
1530 CFSTR("Changed: old"), old_val
);
1531 dump_service_entity(serviceID
, entity
,
1532 CFSTR("Changed: new"), new_val
);
1534 CFDictionarySetValue(service_dict
, entity
, new_val
);
1538 if (CFDictionaryGetCount(service_dict
) == 0) {
1539 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
1542 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
1544 my_CFRelease(&service_dict
);
1548 static CFDictionaryRef
1549 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
1551 CFDictionaryRef service_dict
;
1553 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
1554 if (service_dict
== NULL
) {
1557 return (CFDictionaryGetValue(service_dict
, entity
));
1560 #ifndef kSCPropNetHostname
1561 #define kSCPropNetHostname CFSTR("Hostname")
1566 copy_dhcp_hostname(CFStringRef serviceID
)
1568 CFDictionaryRef dict
= NULL
;
1569 CFStringRef hostname
= NULL
;
1570 CFDictionaryRef service_dict
= NULL
;
1572 dict
= service_dict_get(serviceID
, kSCEntNetIPv4
);
1574 if (dict
== NULL
|| isA_CFDictionary(dict
) == NULL
) {
1579 CFDictionaryGetValue(dict
, kSCEntNetIPv4ServiceDict
);
1581 if (service_dict
== NULL
1582 || isA_CFDictionary(service_dict
) == NULL
) {
1587 CFDictionaryGetValue(service_dict
, kSCPropNetHostname
);
1589 if (hostname
!= NULL
) {
1597 ipv6_service_dict_set(CFStringRef serviceID
, CFDictionaryRef new_val
)
1599 #ifdef SIOCDRADD_IN6
1602 CFStringRef new_router
= NULL
;
1603 char ntopbuf
[INET6_ADDRSTRLEN
];
1604 CFDictionaryRef old_val
= NULL
;
1605 CFStringRef old_router
= NULL
;
1606 struct in6_addr router_ip
;
1610 old_val
= service_dict_get(serviceID
, kSCEntNetIPv6
);
1611 if (old_val
!= NULL
) {
1612 plist_get_cstring(old_val
, kSCPropInterfaceName
, ifn
, sizeof(ifn
));
1613 old_router
= CFDictionaryGetValue(old_val
, kSCPropNetIPv6Router
);
1615 if (ifn
[0] == '\0') {
1617 || plist_get_cstring(new_val
, kSCPropInterfaceName
,
1618 ifn
, sizeof(ifn
)) == FALSE
) {
1619 /* no InterfaceName property, ignore it */
1623 if_index
= if_nametoindex(ifn
);
1624 if (if_index
== 0) {
1627 s
= inet6_dgram_socket();
1630 "IPMonitor: ipv6_service_dict_set: socket failed, %s",
1634 if (new_val
!= NULL
) {
1635 new_router
= CFDictionaryGetValue(new_val
, kSCPropNetIPv6Router
);
1637 if (old_router
!= NULL
1638 && (new_router
== NULL
|| CFEqual(old_router
, new_router
) == FALSE
)) {
1639 /* remove the old Router */
1640 if (cfstring_to_ip6(old_router
, &router_ip
)) {
1641 if (IN6_IS_ADDR_LINKLOCAL(&router_ip
) ||
1642 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip
)) {
1644 router_ip
.__u6_addr
.__u6_addr16
[1] = htons(if_index
);
1646 if (siocdrdel_in6(s
, if_index
, &router_ip
) < 0) {
1647 if (errno
!= EINVAL
) {
1649 "IPMonitor: siocdrdel_in6(%s, %s) failed, %s",
1651 inet_ntop(AF_INET6
, &router_ip
,
1652 ntopbuf
, sizeof(ntopbuf
)),
1656 else if (S_IPMonitor_debug
& kDebugFlag1
) {
1658 "IPMonitor: %s removed default route %s",
1660 inet_ntop(AF_INET6
, &router_ip
,
1661 ntopbuf
, sizeof(ntopbuf
)));
1665 /* add the new Router */
1666 if (cfstring_to_ip6(new_router
, &router_ip
)) {
1667 if (IN6_IS_ADDR_LINKLOCAL(&router_ip
) ||
1668 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip
)) {
1670 router_ip
.__u6_addr
.__u6_addr16
[1] = htons(if_index
);
1672 if (siocdradd_in6(s
, if_index
, &router_ip
, 0) < 0) {
1673 if (errno
!= EINVAL
) {
1675 "IPMonitor: siocdradd_in6(%s, %s) failed, %s",
1677 inet_ntop(AF_INET6
, &router_ip
,
1678 ntopbuf
, sizeof(ntopbuf
)),
1682 else if (S_IPMonitor_debug
& kDebugFlag1
) {
1684 "IPMonitor: %s added default route %s",
1686 inet_ntop(AF_INET6
, &router_ip
,
1687 ntopbuf
, sizeof(ntopbuf
)));
1693 #endif /* SIOCDRADD_IN6 */
1694 return (service_dict_set(serviceID
, kSCEntNetIPv6
, new_val
));
1697 #define ALLOW_EMPTY_STRING 0x1
1699 static CF_RETURNS_RETAINED CFTypeRef
1700 sanitize_prop(CFTypeRef val
, uint32_t flags
)
1703 if (isA_CFString(val
)) {
1704 CFMutableStringRef str
;
1706 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
1707 CFStringTrimWhitespace(str
);
1708 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
1722 merge_array_prop(CFMutableDictionaryRef dict
,
1724 CFDictionaryRef state_dict
,
1725 CFDictionaryRef setup_dict
,
1729 CFMutableArrayRef merge_prop
;
1730 CFArrayRef setup_prop
= NULL
;
1731 CFArrayRef state_prop
= NULL
;
1733 if (setup_dict
!= NULL
) {
1734 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
1736 if (state_dict
!= NULL
) {
1737 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
1740 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
1744 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1745 if (setup_prop
!= NULL
) {
1749 n
= CFArrayGetCount(setup_prop
);
1750 for (i
= 0; i
< n
; i
++) {
1753 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
1754 val
= sanitize_prop(val
, flags
);
1756 CFArrayAppendValue(merge_prop
, val
);
1761 if (state_prop
!= NULL
1762 && (setup_prop
== NULL
|| S_append_state
)) {
1765 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
1767 n
= CFArrayGetCount(state_prop
);
1768 for (i
= 0; i
< n
; i
++) {
1771 val
= CFArrayGetValueAtIndex(state_prop
, i
);
1772 val
= sanitize_prop(val
, flags
);
1774 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
1775 CFArrayAppendValue(merge_prop
, val
);
1781 if (CFArrayGetCount(merge_prop
) > 0) {
1782 CFDictionarySetValue(dict
, key
, merge_prop
);
1784 CFRelease(merge_prop
);
1789 pick_prop(CFMutableDictionaryRef dict
,
1791 CFDictionaryRef state_dict
,
1792 CFDictionaryRef setup_dict
,
1795 CFTypeRef val
= NULL
;
1797 if (setup_dict
!= NULL
) {
1798 val
= CFDictionaryGetValue(setup_dict
, key
);
1799 val
= sanitize_prop(val
, flags
);
1801 if (val
== NULL
&& state_dict
!= NULL
) {
1802 val
= CFDictionaryGetValue(state_dict
, key
);
1803 val
= sanitize_prop(val
, flags
);
1806 CFDictionarySetValue(dict
, key
, val
);
1814 ** GetEntityChangesFunc functions
1817 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
1818 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
1820 CFDictionaryRef aggregated_dict
= NULL
;
1821 boolean_t changed
= FALSE
;
1822 CFMutableDictionaryRef dict
= NULL
;
1823 CFStringRef primaryRank
= NULL
;
1826 IPv4RouteListRef routes
;
1827 /* ALIGN: force align */
1828 uint32_t routes_buf
[roundup(IPv4RouteListComputeSize(R_STATIC
), sizeof(uint32_t))];
1829 CFDataRef routes_data
= NULL
;
1830 CFDictionaryRef service_options
;
1832 if (state_dict
== NULL
) {
1835 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
1836 if (service_options
!= NULL
) {
1837 primaryRank
= CFDictionaryGetValue(service_options
, kSCPropNetServicePrimaryRank
);
1839 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
1840 if (setup_dict
!= NULL
) {
1842 struct in_addr router_ip
;
1844 router
= CFDictionaryGetValue(setup_dict
,
1845 kSCPropNetIPv4Router
);
1847 && cfstring_to_ip(router
, &router_ip
)) {
1848 CFDictionarySetValue(dict
,
1849 kSCPropNetIPv4Router
,
1853 routes
= (IPv4RouteListRef
)(void *)routes_buf
;
1854 routes
->size
= R_STATIC
;
1856 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, primaryRank
);
1858 routes_data
= CFDataCreate(NULL
,
1860 IPv4RouteListComputeSize(r
->count
));
1866 SCLog(TRUE
, LOG_NOTICE
,
1867 CFSTR("IPMonitor: %@ invalid IPv4 dictionary = %@"),
1872 if (routes_data
!= NULL
) {
1873 CFStringRef keys
[2];
1874 CFTypeRef values
[2];
1876 keys
[0] = kSCEntNetIPv4ServiceDict
;
1878 keys
[1] = kSCEntNetIPv4RouteList
;
1879 values
[1] = routes_data
;
1881 aggregated_dict
= CFDictionaryCreate(NULL
,
1884 sizeof(keys
)/sizeof(keys
[0]),
1885 &kCFTypeDictionaryKeyCallBacks
,
1886 &kCFTypeDictionaryValueCallBacks
);
1889 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, aggregated_dict
);
1890 if (routes_data
== NULL
) {
1891 /* clean up the rank too */
1892 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
1894 my_CFRelease(&dict
);
1895 my_CFRelease(&aggregated_dict
);
1896 my_CFRelease(&routes_data
);
1901 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
1902 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
1904 struct in6_addr addr
;
1906 boolean_t changed
= FALSE
;
1907 CFMutableDictionaryRef dict
= NULL
;
1908 CFDictionaryRef new_dict
= NULL
;
1909 CFStringRef router
= NULL
;
1910 struct in6_addr router_ip
;
1911 boolean_t valid_ip
= FALSE
;
1913 if (state_dict
== NULL
) {
1916 addrs
= isA_CFArray(CFDictionaryGetValue(state_dict
,
1917 kSCPropNetIPv6Addresses
));
1918 if (addrs
!= NULL
&& CFArrayGetCount(addrs
) > 0) {
1919 valid_ip
= cfstring_to_ip6(CFArrayGetValueAtIndex(addrs
, 0), &addr
);
1921 if (valid_ip
== FALSE
) {
1922 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1923 SCLog(TRUE
, LOG_NOTICE
,
1924 CFSTR("IPMonitor: %@ has no valid IPv6 address, ignoring"),
1929 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
1930 if (setup_dict
!= NULL
) {
1931 router
= CFDictionaryGetValue(setup_dict
,
1932 kSCPropNetIPv6Router
);
1933 if (router
!= NULL
&& cfstring_to_ip6(router
, &router_ip
)) {
1934 CFDictionarySetValue(dict
,
1935 kSCPropNetIPv6Router
,
1940 router
= CFDictionaryGetValue(dict
,
1941 kSCPropNetIPv6Router
);
1943 && cfstring_to_ip6(router
, &router_ip
) == FALSE
) {
1944 CFDictionaryRemoveValue(dict
, kSCPropNetIPv6Router
);
1950 changed
= ipv6_service_dict_set(serviceID
, new_dict
);
1951 if (new_dict
== NULL
) {
1952 /* clean up the rank too */
1953 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
1955 my_CFRelease(&new_dict
);
1960 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
1961 CFMutableArrayRef out_servers
, CFStringRef interface
)
1966 count
= CFArrayGetCount(in_servers
);
1967 for (i
= 0; i
< count
; i
++) {
1968 CFStringRef addr
= CFArrayGetValueAtIndex(in_servers
, i
);
1969 struct in6_addr ipv6_addr
;
1970 struct in_addr ip_addr
;
1972 if (cfstring_to_ip(addr
, &ip_addr
)) {
1974 if ((active_protos
& kProtocolFlagsIPv4
) == 0
1975 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
1976 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1978 "IPMonitor: no IPv4 connectivity, "
1979 "ignoring DNS server address " IP_FORMAT
,
1987 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
1989 if ((active_protos
& kProtocolFlagsIPv6
) == 0
1990 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
1991 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1992 char ntopbuf
[INET6_ADDRSTRLEN
];
1995 "IPMonitor: no IPv6 connectivity, "
1996 "ignoring DNS server address %s",
1997 inet_ntop(AF_INET6
, &ipv6_addr
,
1998 ntopbuf
, sizeof(ntopbuf
)));
2003 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr
) ||
2004 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr
))
2005 && (interface
!= NULL
)
2006 && (CFStringFind(addr
, CFSTR("%"), 0).location
== kCFNotFound
)) {
2007 // append interface name to IPv6 link local address
2008 addr
= CFStringCreateWithFormat(NULL
, NULL
,
2017 /* bad IP address */
2018 SCLog(TRUE
, LOG_NOTICE
,
2019 CFSTR("IPMonitor: ignoring bad DNS server address '%@'"),
2024 /* DNS server is valid and one we want */
2025 CFArrayAppendValue(out_servers
, addr
);
2032 merge_dns_servers(CFMutableDictionaryRef new_dict
,
2033 CFArrayRef state_servers
,
2034 CFArrayRef setup_servers
,
2036 ProtocolFlags active_protos
,
2037 CFStringRef interface
)
2039 CFMutableArrayRef dns_servers
;
2040 Boolean have_dns_setup
= FALSE
;
2042 if (state_servers
== NULL
&& setup_servers
== NULL
) {
2043 /* no DNS servers */
2046 dns_servers
= CFArrayCreateMutable(NULL
, 0,
2047 &kCFTypeArrayCallBacks
);
2048 if (setup_servers
!= NULL
) {
2049 accumulate_dns_servers(setup_servers
, active_protos
,
2050 dns_servers
, interface
);
2051 if (CFArrayGetCount(dns_servers
) > 0) {
2052 have_dns_setup
= TRUE
;
2055 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
2056 && state_servers
!= NULL
) {
2057 accumulate_dns_servers(state_servers
, active_protos
,
2062 * Here, we determine whether or not we want all queries for this DNS
2063 * configuration to be bound to the associated network interface.
2065 * For dynamically derived network configurations (i.e. from State:)
2066 * this would be the preferred option using the argument "Hey, the
2067 * server told us to use these servers on this network so let's not
2070 * But, when a DNS configuration has been provided by the user/admin
2071 * via the Network pref pane (i.e. from Setup:) we opt to not force
2072 * binding of the outbound queries. The simplest example why we take
2073 * this stance is with a multi-homing configuration. Consider a system
2074 * with one network service associated with "en0" and a second service
2075 * associated with "en1". The "en0" service has been set higher in
2076 * the network service order so it would be primary but the user/admin
2077 * wants the DNS queries to go to a server only accessible via "en1".
2078 * Without this exception we would take the DNS server addresses from
2079 * the Network pref pane (for "en0") and have the queries bound to
2080 * "en0" where they'd never reach their intended destination (via
2081 * "en1"). So, our exception to the rule is that we will not bind
2082 * user/admin configurations to any specific network interface.
2084 * We also add an exception to the "follow the dynamically derived
2085 * network configuration" path for on-the-fly (no Setup: content)
2088 if (CFArrayGetCount(dns_servers
) != 0) {
2089 CFDictionarySetValue(new_dict
,
2090 kSCPropNetDNSServerAddresses
, dns_servers
);
2091 if (have_setup
&& !have_dns_setup
) {
2092 CFDictionarySetValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
, kCFBooleanTrue
);
2096 my_CFRelease(&dns_servers
);
2102 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2103 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2105 ProtocolFlags active_protos
= kProtocolFlagsNone
;
2106 boolean_t changed
= FALSE
;
2108 Boolean have_setup
= FALSE
;
2109 CFStringRef interface
= NULL
;
2110 CFDictionaryRef ipv4
;
2111 CFDictionaryRef ipv6
;
2118 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
2119 { kSCPropNetDNSSortList
, 0, FALSE
},
2120 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
2121 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
2123 CFMutableDictionaryRef new_dict
= NULL
;
2124 const CFStringRef pick_list
[] = {
2125 kSCPropNetDNSDomainName
,
2126 kSCPropNetDNSOptions
,
2127 kSCPropNetDNSSearchOrder
,
2128 kSCPropNetDNSServerPort
,
2129 kSCPropNetDNSServerTimeout
,
2131 IPv4RouteListRef routes
= NULL
;
2133 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
2134 /* there is no DNS content */
2138 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
2139 routes
= ipv4_dict_get_routelist(ipv4
);
2141 if (routes
!= NULL
) {
2142 if (get_service_setup_entity(info
, serviceID
, kSCEntNetIPv4
) != NULL
) {
2146 active_protos
|= kProtocolFlagsIPv4
;
2148 interface
= ipv4_dict_get_ifname(ipv4
);
2151 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
2154 get_service_setup_entity(info
, serviceID
, kSCEntNetIPv6
) != NULL
) {
2158 active_protos
|= kProtocolFlagsIPv6
;
2160 if (interface
== NULL
) {
2161 interface
= CFDictionaryGetValue(ipv6
,
2162 kSCPropInterfaceName
);
2167 if (active_protos
== kProtocolFlagsNone
) {
2168 /* there is no IPv4 nor IPv6 */
2169 if (state_dict
== NULL
) {
2170 /* ... and no DNS content that we care about */
2176 /* merge DNS configuration */
2177 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
2178 &kCFTypeDictionaryKeyCallBacks
,
2179 &kCFTypeDictionaryValueCallBacks
);
2181 if (active_protos
== kProtocolFlagsNone
) {
2182 merge_dns_servers(new_dict
,
2183 my_CFDictionaryGetArray(state_dict
,
2184 kSCPropNetDNSServerAddresses
),
2187 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
,
2191 merge_dns_servers(new_dict
,
2192 my_CFDictionaryGetArray(state_dict
,
2193 kSCPropNetDNSServerAddresses
),
2194 my_CFDictionaryGetArray(setup_dict
,
2195 kSCPropNetDNSServerAddresses
),
2201 for (i
= 0; i
< sizeof(merge_list
)/sizeof(merge_list
[0]); i
++) {
2202 merge_array_prop(new_dict
,
2206 merge_list
[i
].flags
,
2207 merge_list
[i
].append
);
2210 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2218 if (active_protos
== kProtocolFlagsNone
) {
2219 /* there is no IPv4 nor IPv6, only supplemental DNS */
2220 if (CFDictionaryContainsKey(new_dict
,
2221 kSCPropNetDNSSupplementalMatchDomains
)) {
2222 /* only keep State: supplemental */
2223 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
2224 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
2225 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
2226 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
2232 if (CFDictionaryGetCount(new_dict
) == 0) {
2233 my_CFRelease(&new_dict
);
2237 if (interface
!= NULL
) {
2238 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
2241 if (S_append_state
) {
2243 * ensure any specified domain name (e.g. the domain returned by
2244 * a DHCP server) is in the search list.
2246 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
2247 if (isA_CFString(domain
)) {
2250 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
2251 if (isA_CFArray(search
) &&
2252 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
2253 CFMutableArrayRef new_search
;
2255 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
2256 CFArrayAppendValue(new_search
, domain
);
2257 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
2258 my_CFRelease(&new_search
);
2264 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
2265 my_CFRelease(&new_dict
);
2270 merge_dict(const void *key
, const void *value
, void *context
)
2272 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
2274 CFDictionarySetValue(dict
, key
, value
);
2278 #define PROXY_AUTO_DISCOVERY_URL 252
2280 static CF_RETURNS_RETAINED CFStringRef
2281 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
2283 CFStringRef urlString
= NULL
;
2285 if (isA_CFDictionary(dhcp_options
)) {
2288 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
2291 const UInt8
*urlBytes
;
2294 urlBytes
= CFDataGetBytePtr(data
);
2295 urlLen
= CFDataGetLength(data
);
2296 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
2297 // remove trailing NUL
2305 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
2307 urlString
= CFURLGetString(url
);
2308 if (urlString
!= NULL
) {
2309 CFRetain(urlString
);
2319 static CF_RETURNS_RETAINED CFStringRef
2323 CFStringRef urlString
= NULL
;
2325 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
2327 urlString
= CFURLGetString(url
);
2328 if (urlString
!= NULL
) {
2329 CFRetain(urlString
);
2338 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2339 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2341 ProtocolFlags active_protos
= kProtocolFlagsNone
;
2342 boolean_t changed
= FALSE
;
2343 CFStringRef interface
= NULL
;
2344 CFDictionaryRef ipv4
;
2345 CFDictionaryRef ipv6
;
2346 CFMutableDictionaryRef new_dict
= NULL
;
2352 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
2353 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
2356 CFStringRef key1
; /* an "enable" key */
2360 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
2361 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
2362 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
2363 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
2364 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
2365 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
2366 { kSCPropNetProxiesProxyAutoConfigEnable
,
2367 kSCPropNetProxiesProxyAutoConfigURLString
,
2368 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
2369 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2373 IPv4RouteListRef routes
= NULL
;
2375 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
2376 /* there is no proxy content */
2380 ipv4
= service_dict_get(serviceID
, kSCEntNetIPv4
);
2381 routes
= ipv4_dict_get_routelist(ipv4
);
2383 if (routes
!= NULL
) {
2384 active_protos
|= kProtocolFlagsIPv4
;
2386 interface
= ipv4_dict_get_ifname(ipv4
);
2389 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
2391 active_protos
|= kProtocolFlagsIPv6
;
2393 if (interface
== NULL
) {
2394 interface
= CFDictionaryGetValue(ipv6
,
2395 kSCPropInterfaceName
);
2399 if (active_protos
== kProtocolFlagsNone
) {
2400 /* there is no IPv4 nor IPv6 */
2401 if (state_dict
== NULL
) {
2402 /* ... and no proxy content that we care about */
2408 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
2410 CFMutableDictionaryRef setup_copy
;
2413 * Merge the per-service "Setup:" and "State:" proxy information with
2414 * the "Setup:" information always taking precedence. Additionally,
2415 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
2416 * Port) is defined than all of the values for that group will be
2417 * used. That is, we don't allow mixing some of the values from
2418 * the "Setup:" keys and others from the "State:" keys.
2420 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
2422 for (i
= 0; i
< sizeof(merge_list
)/sizeof(merge_list
[0]); i
++) {
2423 merge_array_prop(new_dict
,
2427 merge_list
[i
].flags
,
2428 merge_list
[i
].append
);
2431 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
2432 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2433 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
2435 * if a "Setup:" enabled key has been provided than we want to
2436 * ignore all of the "State:" keys
2438 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
2439 if (pick_list
[i
].key2
!= NULL
) {
2440 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
2442 if (pick_list
[i
].key3
!= NULL
) {
2443 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
2445 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
2446 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
2447 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
2449 * if a "Setup:" enabled key has not been provided and we have
2450 * some" "State:" keys than we remove all of of "Setup:" keys
2452 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
2453 if (pick_list
[i
].key2
!= NULL
) {
2454 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
2456 if (pick_list
[i
].key3
!= NULL
) {
2457 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
2462 /* merge the "Setup:" keys */
2463 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
2464 CFRelease(setup_copy
);
2466 else if (setup_dict
!= NULL
) {
2467 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
2469 else if (state_dict
!= NULL
) {
2470 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
2473 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
2474 CFRelease(new_dict
);
2478 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
2479 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
2483 if (new_dict
!= NULL
) {
2484 CFDictionaryRef dhcp_options
;
2486 CFNumberRef wpad
= NULL
;
2487 int wpadEnabled
= 0;
2488 CFStringRef wpadURL
= NULL
;
2490 if (CFDictionaryGetValueIfPresent(new_dict
,
2491 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2492 (const void **)&num
) &&
2493 isA_CFNumber(num
)) {
2494 /* if we have a WPAD key */
2496 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
2497 /* if we don't like the enabled key/value */
2505 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
2506 if (!isA_CFNumber(num
) ||
2507 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
2508 /* if we don't like the enabled key/value */
2515 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
2516 if (pacURL
!= NULL
) {
2517 if (!isA_CFString(pacURL
)) {
2518 /* if we don't like the PAC URL */
2524 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
2525 if (!isA_CFString(pacJS
)) {
2526 /* if we don't have (or like) the PAC JavaScript */
2534 * we already have a PAC URL so disable WPAD.
2541 * if WPAD is enabled and we don't already have a PAC URL then
2542 * we check for a DHCP provided URL. If not available, we use
2543 * a PAC URL pointing to a well-known file (wpad.dat) on a
2544 * well-known host (wpad.<domain>).
2546 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
2547 wpadURL
= wpadURL_dhcp(dhcp_options
);
2548 if (wpadURL
== NULL
) {
2549 wpadURL
= wpadURL_dns();
2551 if (wpadURL
== NULL
) {
2552 wpadEnabled
= 0; /* if we don't have a WPAD URL */
2557 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
2558 CFDictionarySetValue(new_dict
,
2559 kSCPropNetProxiesProxyAutoConfigEnable
,
2562 CFDictionarySetValue(new_dict
,
2563 kSCPropNetProxiesProxyAutoConfigURLString
,
2570 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
2571 CFDictionarySetValue(new_dict
,
2572 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2579 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
2580 my_CFRelease(&new_dict
);
2584 #if !TARGET_OS_IPHONE
2586 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2587 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2589 boolean_t changed
= FALSE
;
2591 CFMutableDictionaryRef new_dict
= NULL
;
2592 const CFStringRef pick_list
[] = {
2593 kSCPropNetSMBNetBIOSName
,
2594 kSCPropNetSMBNetBIOSNodeType
,
2595 #ifdef ADD_NETBIOS_SCOPE
2596 kSCPropNetSMBNetBIOSScope
,
2597 #endif // ADD_NETBIOS_SCOPE
2598 kSCPropNetSMBWorkgroup
,
2601 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
) {
2602 /* there is no IPv4 */
2606 if (state_dict
== NULL
&& setup_dict
== NULL
) {
2607 /* there is no SMB */
2611 /* merge SMB configuration */
2612 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
2613 &kCFTypeDictionaryKeyCallBacks
,
2614 &kCFTypeDictionaryValueCallBacks
);
2616 merge_array_prop(new_dict
,
2617 kSCPropNetSMBWINSAddresses
,
2622 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2630 if (CFDictionaryGetCount(new_dict
) == 0) {
2631 my_CFRelease(&new_dict
);
2636 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
2637 my_CFRelease(&new_dict
);
2640 #endif /* !TARGET_OS_IPHONE */
2643 services_info_get_interface(CFDictionaryRef services_info
,
2644 CFStringRef serviceID
)
2646 CFStringRef interface
= NULL
;
2647 CFDictionaryRef ipv4_dict
;
2649 ipv4_dict
= get_service_state_entity(services_info
, serviceID
,
2651 if (isA_CFDictionary(ipv4_dict
) != NULL
) {
2652 interface
= CFDictionaryGetValue(ipv4_dict
, kSCPropInterfaceName
);
2655 CFDictionaryRef ipv6_dict
;
2657 ipv6_dict
= get_service_state_entity(services_info
, serviceID
,
2659 if (isA_CFDictionary(ipv6_dict
) != NULL
) {
2660 interface
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
2667 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
2668 CFDictionaryRef setup_options
, CFDictionaryRef services_info
)
2670 boolean_t changed
= FALSE
;
2671 CFMutableDictionaryRef new_dict
= NULL
;
2672 CFStringRef new_rank
= NULL
;
2673 CFStringRef setup_rank
= NULL
;
2674 CFStringRef state_rank
= NULL
;
2678 * Check "PrimaryRank" setting
2680 * Note 1: Rank setting in setup/state option overwrites the
2681 * Rank setting in interface
2682 * Within each rank setting, the following precedence is defined:
2684 * Note 2: Rank Never > Rank Last > Rank First > Rank None
2686 if (isA_CFDictionary(setup_options
)) {
2687 setup_rank
= CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
2688 setup_rank
= isA_CFString(setup_rank
);
2690 if (isA_CFDictionary(state_options
)) {
2691 state_rank
= CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
2692 state_rank
= isA_CFString(state_rank
);
2695 if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankNever
)) ||
2696 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankNever
))) {
2697 new_rank
= kSCValNetServicePrimaryRankNever
;
2699 else if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankLast
)) ||
2700 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankLast
))) {
2701 new_rank
= kSCValNetServicePrimaryRankLast
;
2703 else if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankFirst
)) ||
2704 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankFirst
))) {
2705 new_rank
= kSCValNetServicePrimaryRankFirst
;
2708 /* This corresponds to Note 1 */
2709 if (setup_rank
== NULL
&& state_rank
== NULL
) {
2710 /* Fetch the interface associated with the service */
2711 CFStringRef interface
;
2713 interface
= services_info_get_interface(services_info
, serviceID
);
2715 /* Get the rank on that interface */
2716 if (interface
!= NULL
) {
2717 new_rank
= CFDictionaryGetValue(S_if_rank_dict
, interface
);
2718 if (S_IPMonitor_debug
& kDebugFlag1
) {
2719 SCLog(TRUE
, LOG_NOTICE
,
2720 CFSTR("serviceID %@ interface %@ rank = %@"),
2721 serviceID
, interface
,
2722 (new_rank
!= NULL
) ? new_rank
: CFSTR("<none>"));
2727 if (new_rank
!= NULL
) {
2728 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
2729 &kCFTypeDictionaryKeyCallBacks
,
2730 &kCFTypeDictionaryValueCallBacks
);
2731 CFDictionarySetValue(new_dict
, kSCPropNetServicePrimaryRank
, new_rank
);
2734 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
2735 my_CFRelease(&new_dict
);
2740 if_rank_key_copy(CFStringRef ifname
)
2742 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2743 kSCDynamicStoreDomainState
,
2749 if_rank_set(CFStringRef ifname
, CFDictionaryRef rank_dict
)
2751 CFStringRef rank
= NULL
;
2753 if (isA_CFDictionary(rank_dict
) != NULL
) {
2754 rank
= CFDictionaryGetValue(rank_dict
, kSCPropNetServicePrimaryRank
);
2755 rank
= isA_CFString(rank
);
2758 /* specific rank is asserted */
2760 if (S_IPMonitor_debug
& kDebugFlag1
) {
2761 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Interface %@ asserted rank %@"),
2764 CFDictionarySetValue(S_if_rank_dict
, ifname
, rank
);
2766 if (S_IPMonitor_debug
& kDebugFlag1
) {
2767 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Interface %@ removed rank."),
2770 CFDictionaryRemoveValue(S_if_rank_dict
, ifname
);
2776 if_rank_apply(const void * key
, const void * value
, void * context
)
2779 CFDictionaryRef rank_dict
= (CFDictionaryRef
)value
;
2781 /* State:/Network/Interface/<ifname>/Service, <ifname> is at index 3 */
2782 ifname
= my_CFStringCopyComponent(key
, CFSTR("/"), 3);
2783 if (ifname
!= NULL
) {
2784 if_rank_set(ifname
, rank_dict
);
2791 if_rank_dict_init(void)
2793 CFDictionaryRef info
;
2794 CFStringRef pattern
;
2795 CFArrayRef patterns
;
2798 = CFDictionaryCreateMutable(NULL
, 0,
2799 &kCFTypeDictionaryKeyCallBacks
,
2800 &kCFTypeDictionaryValueCallBacks
);
2801 pattern
= if_rank_key_copy(kSCCompAnyRegex
);
2802 patterns
= CFArrayCreate(NULL
,
2803 (const void **)&pattern
, 1,
2804 &kCFTypeArrayCallBacks
);
2806 info
= SCDynamicStoreCopyMultiple(S_session
, NULL
, patterns
);
2807 CFRelease(patterns
);
2809 CFDictionaryApplyFunction(info
, if_rank_apply
, NULL
);
2817 add_service_keys(CFStringRef serviceID
, CFMutableArrayRef keys
, CFMutableArrayRef patterns
)
2822 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
2826 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
2827 key
= setup_service_key(serviceID
, *entityTypeNames
[i
]);
2828 CFArrayAppendValue(keys
, key
);
2830 key
= state_service_key(serviceID
, *entityTypeNames
[i
]);
2831 CFArrayAppendValue(keys
, key
);
2835 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
2836 CFArrayAppendValue(patterns
, key
);
2839 key
= setup_service_key(serviceID
, NULL
);
2840 CFArrayAppendValue(patterns
, key
);
2842 key
= state_service_key(serviceID
, NULL
);
2843 CFArrayAppendValue(patterns
, key
);
2850 static CFDictionaryRef
2851 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
,
2852 CFArrayRef if_rank_list
)
2855 CFMutableArrayRef get_keys
;
2856 CFMutableArrayRef get_patterns
;
2858 CFDictionaryRef info
;
2861 count
= CFArrayGetCount(service_list
);
2862 get_keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2863 get_patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2865 CFArrayAppendValue(get_keys
, S_setup_global_ipv4
);
2866 CFArrayAppendValue(get_keys
, S_multicast_resolvers
);
2867 CFArrayAppendValue(get_keys
, S_private_resolvers
);
2869 for (s
= 0; s
< count
; s
++) {
2870 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
2872 add_service_keys(serviceID
, get_keys
, get_patterns
);
2875 if_count
= (if_rank_list
!= NULL
)
2876 ? CFArrayGetCount(if_rank_list
) : 0;
2877 for (s
= 0; s
< if_count
; s
++) {
2878 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_list
, s
);
2881 key
= if_rank_key_copy(ifname
);
2882 CFArrayAppendValue(get_keys
, key
);
2886 info
= SCDynamicStoreCopyMultiple(session
, get_keys
, get_patterns
);
2887 my_CFRelease(&get_keys
);
2888 my_CFRelease(&get_patterns
);
2892 static int rtm_seq
= 0;
2895 route_open_socket(void)
2899 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
2900 SCLog(TRUE
, LOG_NOTICE
,
2901 CFSTR("IPMonitor: route_open_socket: socket failed, %s"),
2908 * Define: ROUTE_MSG_ADDRS_SPACE
2910 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2911 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2912 * someone changes the code and doesn't think to modify this.
2914 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2915 + 2 * sizeof(struct sockaddr_dl) \
2918 struct rt_msghdr hdr
;
2919 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2923 ipv4_route(int sockfd
,
2924 int cmd
, struct in_addr gateway
, struct in_addr netaddr
,
2925 struct in_addr netmask
, char * ifname
, unsigned int ifindex
,
2926 struct in_addr ifa
, RouteFlags flags
)
2928 boolean_t default_route
= (netaddr
.s_addr
== 0);
2933 struct sockaddr_in
* in_p
;
2934 struct sockaddr_dl
* dl_p
;
2938 if (default_route
&& S_netboot
) {
2942 if (ifname
== NULL
) {
2943 /* this should not happen, but rather than crash, return an error */
2945 "IPMonitor: ipv4_route ifname is NULL on network address %s",
2946 inet_ntoa(netaddr
));
2949 memset(&rtmsg
, 0, sizeof(rtmsg
));
2950 rtmsg
.hdr
.rtm_type
= cmd
;
2951 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2952 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2954 = RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
| RTA_IFP
| RTA_IFA
;
2956 && (flags
& kRouteIsDirectToInterfaceFlag
) == 0) {
2957 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_GATEWAY
| RTF_STATIC
;
2960 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_CLONING
| RTF_STATIC
;
2962 if ((flags
& kRouteIsScopedFlag
) != 0) {
2964 if (!S_scopedroute
) {
2968 /* specifically asked for a scoped route, yet no index supplied */
2970 "IPMonitor: ipv4_route index is 0 on %s-scoped route %s",
2971 ifname
, inet_ntoa(netaddr
));
2974 rtmsg
.hdr
.rtm_index
= ifindex
;
2975 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2976 #else /* RTF_IFSCOPE */
2978 #endif /* RTF_IFSCOPE */
2981 rtaddr
.ptr
= rtmsg
.addrs
;
2984 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2985 rtaddr
.in_p
->sin_family
= AF_INET
;
2986 rtaddr
.in_p
->sin_addr
= netaddr
;
2987 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2990 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2991 /* gateway is an IP address */
2992 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2993 rtaddr
.in_p
->sin_family
= AF_INET
;
2994 rtaddr
.in_p
->sin_addr
= gateway
;
2995 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2998 /* gateway is the interface itself */
2999 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3000 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3001 rtaddr
.dl_p
->sdl_nlen
= strlen(ifname
);
3002 bcopy(ifname
, rtaddr
.dl_p
->sdl_data
, rtaddr
.dl_p
->sdl_nlen
);
3003 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3007 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
3008 rtaddr
.in_p
->sin_family
= AF_INET
;
3009 rtaddr
.in_p
->sin_addr
= netmask
;
3010 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3012 /* interface name */
3013 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
3014 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
3015 rtaddr
.dl_p
->sdl_nlen
= strlen(ifname
);
3016 bcopy(ifname
, rtaddr
.dl_p
->sdl_data
, rtaddr
.dl_p
->sdl_nlen
);
3017 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
3019 /* interface address */
3020 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
3021 rtaddr
.in_p
->sin_family
= AF_INET
;
3022 rtaddr
.in_p
->sin_addr
= ifa
;
3023 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
3025 len
= sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
);
3026 rtmsg
.hdr
.rtm_msglen
= len
;
3027 if (write(sockfd
, &rtmsg
, len
) == -1) {
3034 ipv6_route(int cmd
, struct in6_addr gateway
, struct in6_addr netaddr
,
3035 struct in6_addr netmask
, char * ifname
, boolean_t is_direct
)
3037 boolean_t default_route
;
3039 boolean_t ret
= TRUE
;
3041 struct rt_msghdr hdr
;
3042 struct sockaddr_in6 dst
;
3043 struct sockaddr_in6 gway
;
3044 struct sockaddr_in6 mask
;
3045 struct sockaddr_dl ifp
;
3048 struct in6_addr zeroes
= IN6ADDR_ANY_INIT
;
3050 default_route
= (bcmp(&zeroes
, &netaddr
, sizeof(netaddr
)) == 0);
3052 if ((IN6_IS_ADDR_LINKLOCAL(&gateway
) ||
3053 IN6_IS_ADDR_MC_LINKLOCAL(&gateway
)) &&
3055 unsigned int index
= if_nametoindex(ifname
);
3057 /* add the scope id to the link local address */
3058 gateway
.__u6_addr
.__u6_addr16
[1] = (uint16_t)htons(index
);
3060 sockfd
= route_open_socket();
3064 memset(&rtmsg
, 0, sizeof(rtmsg
));
3065 rtmsg
.hdr
.rtm_type
= cmd
;
3066 if (default_route
) {
3068 /* if router is directly reachable, don't set the gateway flag */
3069 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
3072 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_GATEWAY
| RTF_STATIC
;
3076 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_CLONING
| RTF_STATIC
;
3078 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
3079 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
3080 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
;
3081 rtmsg
.dst
.sin6_len
= sizeof(rtmsg
.dst
);
3082 rtmsg
.dst
.sin6_family
= AF_INET6
;
3083 rtmsg
.dst
.sin6_addr
= netaddr
;
3084 rtmsg
.gway
.sin6_len
= sizeof(rtmsg
.gway
);
3085 rtmsg
.gway
.sin6_family
= AF_INET6
;
3086 rtmsg
.gway
.sin6_addr
= gateway
;
3087 rtmsg
.mask
.sin6_len
= sizeof(rtmsg
.mask
);
3088 rtmsg
.mask
.sin6_family
= AF_INET6
;
3089 rtmsg
.mask
.sin6_addr
= netmask
;
3091 len
= sizeof(rtmsg
);
3093 rtmsg
.ifp
.sdl_len
= sizeof(rtmsg
.ifp
);
3094 rtmsg
.ifp
.sdl_family
= AF_LINK
;
3095 rtmsg
.ifp
.sdl_nlen
= strlen(ifname
);
3096 rtmsg
.hdr
.rtm_addrs
|= RTA_IFP
;
3097 bcopy(ifname
, rtmsg
.ifp
.sdl_data
, rtmsg
.ifp
.sdl_nlen
);
3100 /* no ifp information */
3101 len
-= sizeof(rtmsg
.ifp
);
3103 rtmsg
.hdr
.rtm_msglen
= len
;
3104 if (write(sockfd
, &rtmsg
, len
) == -1) {
3105 if ((cmd
== RTM_ADD
) && (errno
== EEXIST
)) {
3106 /* no sense complaining about a route that already exists */
3108 else if ((cmd
== RTM_DELETE
) && (errno
== ESRCH
)) {
3109 /* no sense complaining about a route that isn't there */
3112 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3113 SCLog(TRUE
, LOG_NOTICE
,
3114 CFSTR("IPMonitor ipv6_route: write routing"
3115 " socket failed, %s"), strerror(errno
));
3126 ipv6_default_route_delete(void)
3128 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3129 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: IPv6 route delete default"));
3131 return (ipv6_route(RTM_DELETE
, S_ip6_zeros
, S_ip6_zeros
, S_ip6_zeros
,
3136 ipv6_default_route_add(struct in6_addr router
, char * ifname
,
3137 boolean_t is_direct
)
3139 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3140 char ntopbuf
[INET6_ADDRSTRLEN
];
3142 SCLog(TRUE
, LOG_NOTICE
,
3143 CFSTR("IPMonitor: IPv6 route add default"
3144 " %s interface %s direct %d"),
3145 inet_ntop(AF_INET6
, &router
, ntopbuf
, sizeof(ntopbuf
)),
3148 return (ipv6_route(RTM_ADD
, router
, S_ip6_zeros
, S_ip6_zeros
,
3149 ifname
, is_direct
));
3154 multicast_route_delete(int sockfd
)
3156 struct in_addr gateway
= { htonl(INADDR_LOOPBACK
) };
3157 struct in_addr netaddr
= { htonl(INADDR_UNSPEC_GROUP
) };
3158 struct in_addr netmask
= { htonl(IN_CLASSD_NET
) };
3160 return (ipv4_route(sockfd
, RTM_DELETE
, gateway
, netaddr
, netmask
, "lo0", 0,
3165 multicast_route_add(int sockfd
)
3167 struct in_addr gateway
= { htonl(INADDR_LOOPBACK
) };
3168 struct in_addr netaddr
= { htonl(INADDR_UNSPEC_GROUP
) };
3169 struct in_addr netmask
= { htonl(IN_CLASSD_NET
) };
3171 return (ipv4_route(sockfd
, RTM_ADD
, gateway
, netaddr
, netmask
, "lo0", 0,
3177 set_ipv6_default_interface(char * ifname
)
3179 struct in6_ndifreq ndifreq
;
3182 bzero((char *)&ndifreq
, sizeof(ndifreq
));
3183 if (ifname
!= NULL
) {
3184 strlcpy(ndifreq
.ifname
, ifname
, sizeof(ndifreq
.ifname
));
3185 ndifreq
.ifindex
= if_nametoindex(ifname
);
3187 strlcpy(ndifreq
.ifname
, "lo0", sizeof(ndifreq
.ifname
));
3188 ndifreq
.ifindex
= 0;
3191 sock
= inet6_dgram_socket();
3193 SCLog(TRUE
, LOG_ERR
,
3194 CFSTR("IPMonitor: set_ipv6_default_interface: socket failed, %s"),
3198 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
3199 SCLog(TRUE
, LOG_ERR
,
3200 CFSTR("IPMonitor: set_ipv6_default_interface: ioctl(SIOCSDEFIFACE_IN6) failed, %s"),
3206 #endif /* RTF_IFSCOPE */
3209 set_ipv6_router(struct in6_addr
* router
, char * ifname
, boolean_t is_direct
)
3211 /* assign the new default route, ensure local multicast route available */
3212 (void)ipv6_default_route_delete();
3213 if (router
!= NULL
) {
3214 (void)ipv6_default_route_add(*router
, ifname
, is_direct
);
3219 #if !TARGET_OS_IPHONE
3220 static __inline__
void
3223 (void)unlink(VAR_RUN_RESOLV_CONF
);
3227 set_dns(CFArrayRef val_search_domains
,
3228 CFStringRef val_domain_name
,
3229 CFArrayRef val_servers
,
3230 CFArrayRef val_sortlist
)
3232 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
3234 /* publish new resolv.conf */
3239 SCPrint(TRUE
, f
, CFSTR("#\n"));
3240 SCPrint(TRUE
, f
, CFSTR("# Mac OS X Notice\n"));
3241 SCPrint(TRUE
, f
, CFSTR("#\n"));
3242 SCPrint(TRUE
, f
, CFSTR("# This file is not used by the host name and address resolution\n"));
3243 SCPrint(TRUE
, f
, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
3244 SCPrint(TRUE
, f
, CFSTR("# this Mac OS X system.\n"));
3245 SCPrint(TRUE
, f
, CFSTR("#\n"));
3246 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
3247 SCPrint(TRUE
, f
, CFSTR("#\n"));
3249 if (isA_CFArray(val_search_domains
)) {
3250 SCPrint(TRUE
, f
, CFSTR("search"));
3251 n
= CFArrayGetCount(val_search_domains
);
3252 for (i
= 0; i
< n
; i
++) {
3255 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
3256 if (isA_CFString(domain
)) {
3257 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
3260 SCPrint(TRUE
, f
, CFSTR("\n"));
3262 else if (isA_CFString(val_domain_name
)) {
3263 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
3266 if (isA_CFArray(val_servers
)) {
3267 n
= CFArrayGetCount(val_servers
);
3268 for (i
= 0; i
< n
; i
++) {
3269 CFStringRef nameserver
;
3271 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
3272 if (isA_CFString(nameserver
)) {
3273 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
3278 if (isA_CFArray(val_sortlist
)) {
3279 SCPrint(TRUE
, f
, CFSTR("sortlist"));
3280 n
= CFArrayGetCount(val_sortlist
);
3281 for (i
= 0; i
< n
; i
++) {
3282 CFStringRef address
;
3284 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
3285 if (isA_CFString(address
)) {
3286 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
3289 SCPrint(TRUE
, f
, CFSTR("\n"));
3293 rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
3297 #endif /* !TARGET_OS_IPHONE */
3300 router_is_our_ipv6_address(CFStringRef router
, CFArrayRef addr_list
)
3303 CFIndex n
= CFArrayGetCount(addr_list
);
3306 (void)cfstring_to_ip6(router
, &r
);
3307 for (i
= 0; i
< n
; i
++) {
3310 if (cfstring_to_ip6(CFArrayGetValueAtIndex(addr_list
, i
), &ip
)
3311 && bcmp(&r
, &ip
, sizeof(r
)) == 0) {
3318 static IPv4RouteListRef
3319 service_dict_get_ipv4_routelist(CFDictionaryRef service_dict
)
3321 CFDictionaryRef dict
;
3322 IPv4RouteListRef routes
= NULL
;
3324 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
3326 routes
= ipv4_dict_get_routelist(dict
);
3330 typedef struct apply_ipv4_route_context
{
3331 IPv4RouteListRef old
;
3332 IPv4RouteListRef
new;
3334 } apply_ipv4_route_context_t
;
3336 /* add/remove a router/32 subnet */
3338 ipv4_route_gateway(int sockfd
, int cmd
, char * ifn_p
,
3339 IPv4RouteRef def_route
)
3341 struct in_addr mask
;
3343 mask
.s_addr
= htonl(INADDR_BROADCAST
);
3344 return (ipv4_route(sockfd
, cmd
, def_route
->ifa
,
3345 def_route
->gateway
, mask
, ifn_p
, def_route
->ifindex
,
3346 def_route
->ifa
, def_route
->flags
));
3350 * Function: apply_ipv4_route
3352 * Callback function that adds/removes the specified route.
3355 apply_ipv4_route(IPv4RouteListApplyCommand cmd
, IPv4RouteRef route
, void * arg
)
3357 apply_ipv4_route_context_t
*context
= (apply_ipv4_route_context_t
*)arg
;
3361 ifn_p
= route
->ifname
;
3363 case kIPv4RouteListAddRouteCommand
:
3364 if ((route
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
3365 retval
= ipv4_route_gateway(context
->sockfd
, RTM_ADD
,
3367 if (retval
== EEXIST
) {
3368 /* delete and add again */
3369 (void)ipv4_route_gateway(context
->sockfd
, RTM_DELETE
,
3371 retval
= ipv4_route_gateway(context
->sockfd
, RTM_ADD
,
3375 SCLog(TRUE
, LOG_NOTICE
,
3376 CFSTR("IPMonitor apply_ipv4_route failed to add"
3377 " %s/32 route, %s"),
3378 inet_ntoa(route
->gateway
), strerror(retval
));
3380 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3381 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Added IPv4 Route %s/32"),
3382 inet_ntoa(route
->gateway
));
3385 retval
= ipv4_route(context
->sockfd
,
3386 RTM_ADD
, route
->gateway
,
3387 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
3388 route
->ifa
, route
->flags
);
3389 if (retval
== EEXIST
) {
3390 /* delete and add again */
3391 (void)ipv4_route(context
->sockfd
,
3392 RTM_DELETE
, route
->gateway
,
3393 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
3394 route
->ifa
, route
->flags
);
3395 retval
= ipv4_route(context
->sockfd
,
3396 RTM_ADD
, route
->gateway
,
3397 route
->dest
, route
->mask
,
3398 ifn_p
, route
->ifindex
,
3399 route
->ifa
, route
->flags
);
3402 SCLog(TRUE
, LOG_NOTICE
,
3403 CFSTR("IPMonitor apply_ipv4_route failed to add"
3404 " route, %s:"), strerror(retval
));
3405 IPv4RouteLog(route
);
3407 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3408 SCLog(TRUE
, LOG_NOTICE
,
3409 CFSTR("Added IPv4 route new[%d] = "),
3410 route
- context
->new->list
);
3411 IPv4RouteLog(route
);
3414 case kIPv4RouteListRemoveRouteCommand
:
3415 retval
= ipv4_route(context
->sockfd
,
3416 RTM_DELETE
, route
->gateway
,
3417 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
3418 route
->ifa
, route
->flags
);
3420 if (retval
!= ESRCH
) {
3421 SCLog(TRUE
, LOG_NOTICE
,
3422 CFSTR("IPMonitor apply_ipv4_route failed to remove"
3423 " route, %s: "), strerror(retval
));
3424 IPv4RouteLog(route
);
3427 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3428 SCLog(TRUE
, LOG_NOTICE
,
3429 CFSTR("Removed IPv4 route old[%d] = "),
3430 route
- context
->old
->list
);
3431 IPv4RouteLog(route
);
3433 if ((route
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
3434 retval
= ipv4_route_gateway(context
->sockfd
, RTM_DELETE
,
3437 SCLog(TRUE
, LOG_NOTICE
,
3438 CFSTR("IPMonitor apply_ipv4_route failed to remove"
3439 " %s/32 route, %s: "),
3440 inet_ntoa(route
->gateway
), strerror(retval
));
3442 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3443 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Removed IPv4 Route %s/32"),
3444 inet_ntoa(route
->gateway
));
3455 * Function: update_ipv4
3458 * Update the IPv4 configuration based on the latest information.
3459 * Publish the State:/Network/Global/IPv4 information, and update the
3460 * IPv4 routing table. IPv4RouteListApply() invokes our callback,
3461 * apply_ipv4_route(), to install/remove the routes.
3464 update_ipv4(CFStringRef primary
,
3465 IPv4RouteListRef new_routelist
,
3466 keyChangeListRef keys
)
3468 apply_ipv4_route_context_t context
;
3471 if (new_routelist
!= NULL
&& primary
!= NULL
) {
3472 char * ifn_p
= NULL
;
3474 CFMutableDictionaryRef dict
= NULL
;
3476 dict
= CFDictionaryCreateMutable(NULL
, 0,
3477 &kCFTypeDictionaryKeyCallBacks
,
3478 &kCFTypeDictionaryValueCallBacks
);
3479 /* the first entry is the default route */
3480 r
= new_routelist
->list
;
3481 if (r
->gateway
.s_addr
!= 0) {
3484 router
= CFStringCreateWithCString(NULL
,
3485 inet_ntoa(r
->gateway
),
3486 kCFStringEncodingASCII
);
3487 if (router
!= NULL
) {
3488 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, router
);
3492 if (r
->ifname
[0] != '\0') {
3495 if (ifn_p
!= NULL
) {
3496 CFStringRef ifname_cf
;
3498 ifname_cf
= CFStringCreateWithCString(NULL
,
3500 kCFStringEncodingASCII
);
3501 if (ifname_cf
!= NULL
) {
3502 CFDictionarySetValue(dict
,
3503 kSCDynamicStorePropNetPrimaryInterface
,
3505 CFRelease(ifname_cf
);
3508 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
3510 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
3514 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
3518 bzero(&context
, sizeof(context
));
3519 context
.sockfd
= route_open_socket();
3520 if (context
.sockfd
!= -1) {
3521 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3522 if (S_ipv4_routelist
== NULL
) {
3523 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Old Routes = <none>"));
3526 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Old Routes = "));
3527 IPv4RouteListLog(S_ipv4_routelist
);
3529 if (new_routelist
== NULL
) {
3530 SCLog(TRUE
, LOG_NOTICE
, CFSTR("New Routes = <none>"));
3533 SCLog(TRUE
, LOG_NOTICE
, CFSTR("New Routes = "));
3534 IPv4RouteListLog(new_routelist
);
3537 context
.old
= S_ipv4_routelist
;
3538 context
.new = new_routelist
;
3539 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
,
3540 &apply_ipv4_route
, (void *)&context
);
3541 if (new_routelist
!= NULL
) {
3542 (void)multicast_route_delete(context
.sockfd
);
3545 (void)multicast_route_add(context
.sockfd
);
3547 close(context
.sockfd
);
3549 if (S_ipv4_routelist
!= NULL
) {
3550 free(S_ipv4_routelist
);
3552 S_ipv4_routelist
= new_routelist
;
3557 update_ipv6(CFDictionaryRef services_info
,
3558 CFStringRef primary
,
3559 keyChangeListRef keys
)
3561 CFDictionaryRef ipv6_dict
= NULL
;
3563 if (primary
!= NULL
) {
3564 CFDictionaryRef service_dict
;
3566 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3567 if (service_dict
!= NULL
) {
3568 ipv6_dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
3571 if (ipv6_dict
!= NULL
) {
3573 CFMutableDictionaryRef dict
= NULL
;
3574 CFStringRef if_name
= NULL
;
3575 char ifn
[IFNAMSIZ
] = { '\0' };
3576 char * ifn_p
= NULL
;
3577 boolean_t is_direct
= FALSE
;
3578 CFStringRef val_router
= NULL
;
3580 dict
= CFDictionaryCreateMutable(NULL
, 0,
3581 &kCFTypeDictionaryKeyCallBacks
,
3582 &kCFTypeDictionaryValueCallBacks
);
3583 val_router
= CFDictionaryGetValue(ipv6_dict
, kSCPropNetIPv6Router
);
3584 addrs
= CFDictionaryGetValue(ipv6_dict
,
3585 kSCPropNetIPv6Addresses
);
3586 if (val_router
!= NULL
) {
3587 /* no router if router is one of our IP addresses */
3588 is_direct
= router_is_our_ipv6_address(val_router
, addrs
);
3589 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
,
3593 val_router
= CFArrayGetValueAtIndex(addrs
, 0);
3596 if_name
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
3598 CFDictionarySetValue(dict
,
3599 kSCDynamicStorePropNetPrimaryInterface
,
3601 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
3602 kCFStringEncodingASCII
)) {
3606 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
3608 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
3612 if (S_scopedroute_v6
) {
3613 set_ipv6_default_interface(ifn_p
);
3615 #endif /* RTF_IFSCOPE */
3616 { /* route add default ... */
3617 struct in6_addr router
;
3619 (void)cfstring_to_ip6(val_router
, &router
);
3620 set_ipv6_router(&router
, ifn_p
, is_direct
);
3624 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
3626 if (S_scopedroute_v6
) {
3627 set_ipv6_default_interface(NULL
);
3629 #endif /* RTF_IFSCOPE */
3630 { /* route delete default ... */
3631 set_ipv6_router(NULL
, NULL
, FALSE
);
3638 update_dns(CFDictionaryRef services_info
,
3639 CFStringRef primary
,
3640 keyChangeListRef keys
)
3642 CFDictionaryRef dict
= NULL
;
3644 if (primary
!= NULL
) {
3645 CFDictionaryRef service_dict
;
3647 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3648 if (service_dict
!= NULL
) {
3649 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
3653 #if !TARGET_OS_IPHONE
3655 #endif /* !TARGET_OS_IPHONE */
3656 keyChangeListRemoveValue(keys
, S_state_global_dns
);
3659 CFMutableDictionaryRef new_dict
;
3661 #if !TARGET_OS_IPHONE
3662 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
3663 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
3664 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
3665 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
3666 #endif /* !TARGET_OS_IPHONE */
3667 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
3668 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
3669 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
3670 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
3671 CFDictionaryRemoveValue(new_dict
, DNS_CONFIGURATION_SCOPED_QUERY_KEY
);
3672 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
3673 CFRelease(new_dict
);
3679 update_dnsinfo(CFDictionaryRef services_info
,
3680 CFStringRef primary
,
3681 keyChangeListRef keys
,
3682 CFArrayRef service_order
)
3685 CFDictionaryRef dict
= NULL
;
3686 CFArrayRef multicastResolvers
;
3687 CFArrayRef privateResolvers
;
3689 multicastResolvers
= CFDictionaryGetValue(services_info
, S_multicast_resolvers
);
3690 privateResolvers
= CFDictionaryGetValue(services_info
, S_private_resolvers
);
3692 if (primary
!= NULL
) {
3693 CFDictionaryRef service_dict
;
3695 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3696 if (service_dict
!= NULL
) {
3697 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
3701 changed
= dns_configuration_set(dict
,
3702 S_service_state_dict
,
3707 keyChangeListNotifyKey(keys
, S_state_global_dns
);
3713 update_proxies(CFDictionaryRef services_info
,
3714 CFStringRef primary
,
3715 keyChangeListRef keys
,
3716 CFArrayRef service_order
)
3718 CFDictionaryRef dict
= NULL
;
3719 CFDictionaryRef new_dict
;
3721 if (primary
!= NULL
) {
3722 CFDictionaryRef service_dict
;
3724 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3725 if (service_dict
!= NULL
) {
3726 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
3730 new_dict
= proxy_configuration_update(dict
,
3731 S_service_state_dict
,
3733 if (new_dict
== NULL
) {
3734 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
3737 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
3738 CFRelease(new_dict
);
3743 #if !TARGET_OS_IPHONE
3745 update_smb(CFDictionaryRef services_info
,
3746 CFStringRef primary
,
3747 keyChangeListRef keys
)
3749 CFDictionaryRef dict
= NULL
;
3751 if (primary
!= NULL
) {
3752 CFDictionaryRef service_dict
;
3754 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3755 if (service_dict
!= NULL
) {
3756 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
3760 keyChangeListRemoveValue(keys
, S_state_global_smb
);
3763 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
3768 #endif /* !TARGET_OS_IPHONE */
3771 get_service_rank(CFArrayRef order
, int n_order
, CFStringRef serviceID
)
3774 Rank rank
= kRankIndexMask
;
3776 if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
3777 for (i
= 0; i
< n_order
; i
++) {
3778 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
3783 if (CFEqual(serviceID
, s
)) {
3793 ** Service election:
3796 * Function: rank_dict_get_service_rank
3798 * Retrieve the service rank in the given dictionary.
3801 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
3804 Rank rank_val
= RankMake(kRankIndexMask
, kRankAssertionDefault
);
3806 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
3808 CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
);
3814 * Function: rank_dict_set_service_rank
3816 * Save the results of ranking the service so we can look it up later without
3817 * repeating all of the ranking code.
3820 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
3821 CFStringRef serviceID
, Rank rank_val
)
3825 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
3827 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
3833 typedef struct election_info
{
3837 CFStringRef serviceID
;
3838 CFDictionaryRef service_dict
;
3842 typedef boolean_t
election_func_t(void * context
, election_info_t
* info
);
3845 * Function: elect_ipv4
3847 * This function builds the list of IPv4 routes that should be active.
3848 * As elect_new_primary() invokes us with each service, we build up the
3849 * result in the passed in context, a pointer to an IPv4RouteListRef.
3852 elect_ipv4(void * context
, election_info_t
* info
)
3855 IPv4RouteListRef
* routes_p
= (IPv4RouteListRef
*)context
;
3856 IPv4RouteListRef service_routes
;
3858 service_routes
= service_dict_get_ipv4_routelist(info
->service_dict
);
3860 info
->rank
= get_service_rank(info
->order
, info
->n_order
,
3863 if (service_routes
== NULL
) {
3867 primary_rank
= RANK_ASSERTION_MASK(service_routes
->list
->rank
);
3869 if (S_ppp_override_primary
3870 && (strncmp(PPP_PREFIX
, service_routes
->list
->ifname
,
3871 sizeof(PPP_PREFIX
) - 1) == 0)) {
3872 /* PPP override: make ppp* look the best */
3873 /* Hack: should use interface type, not interface name */
3874 primary_rank
= kRankAssertionFirst
;
3877 info
->rank
= RankMake(info
->rank
, primary_rank
);
3879 if (routes_p
!= NULL
) {
3880 *routes_p
= IPv4RouteListAddRouteList(*routes_p
,
3881 info
->n_services
* 3,
3886 if (primary_rank
== kRankAssertionNever
) {
3887 /* never elect as primary */
3890 if (strncmp(service_routes
->list
->ifname
, "lo0",
3891 sizeof(service_routes
->list
->ifname
)) == 0) {
3892 /* never elect as primary */
3896 rank_dict_set_service_rank(S_ipv4_service_rank_dict
,
3897 info
->serviceID
, info
->rank
);
3902 IPv6RankedListInsertE(IPv6RankedListRef list
, IPv6RankedERef e
)
3905 int elems_to_be_moved
= 0;
3907 if (list
== NULL
) return;
3909 for (idx
= 0; idx
< list
->count
; idx
++) {
3910 if ((e
->rank
< list
->elem
[idx
].rank
)) {
3915 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
3916 SCLog(TRUE
, LOG_NOTICE
,
3917 CFSTR("IPv6RankedListInsertE: %s flags 0x%x at index %d with rank %u."),
3918 e
->ifname
, e
->flags
, idx
, e
->rank
);
3921 elems_to_be_moved
= list
->count
- idx
;
3922 if (elems_to_be_moved
> 0) {
3923 bcopy((void*) &list
->elem
[idx
], (void*) &list
->elem
[idx
+1],
3924 elems_to_be_moved
* sizeof(*e
));
3927 bcopy((void*) e
, (void*) &list
->elem
[idx
], sizeof(*e
));
3933 elect_ipv6(void * context
, election_info_t
* info
)
3935 CFStringRef if_name
;
3936 CFStringRef primaryRankStr
= NULL
;
3937 CFDictionaryRef proto_dict
;
3939 CFDictionaryRef service_options
;
3940 IPv6RankedListRef list
= (IPv6RankedListRef
) context
;
3942 CFDictionaryRef dns_dict
= NULL
;
3943 Rank primary_rank
= kRankAssertionDefault
;
3945 memset((void *)&elem
, 0, sizeof(elem
));
3947 info
->rank
= get_service_rank(info
->order
, info
->n_order
, info
->serviceID
);
3949 proto_dict
= CFDictionaryGetValue(info
->service_dict
, kSCEntNetIPv6
);
3950 if (proto_dict
== NULL
) {
3953 if_name
= CFDictionaryGetValue(proto_dict
, kSCPropInterfaceName
);
3954 if (if_name
== NULL
) {
3955 /* we need an interface name */
3958 if (CFEqual(if_name
, CFSTR("lo0"))) {
3959 /* don't ever elect loopback */
3962 CFStringGetCString(if_name
, elem
.ifname
,
3963 sizeof(elem
.ifname
),
3964 kCFStringEncodingASCII
);
3965 router
= CFDictionaryGetValue(proto_dict
,
3966 kSCPropNetIPv6Router
);
3967 if (router
== NULL
) {
3968 /* can't become primary without a router */
3972 dns_dict
= service_dict_get(info
->serviceID
, kSCEntNetDNS
);
3973 if (dns_dict
!= NULL
) {
3974 elem
.flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
3977 service_options
= service_dict_get(info
->serviceID
, kSCEntNetService
);
3978 if (service_options
!= NULL
) {
3979 primaryRankStr
= CFDictionaryGetValue(service_options
, kSCPropNetServicePrimaryRank
);
3980 if (primaryRankStr
!= NULL
) {
3981 primary_rank
= PrimaryRankGetRankAssertion(primaryRankStr
);
3983 if (primary_rank
== kRankAssertionNever
) {
3984 elem
.flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
3985 elem
.rank
= RankMake(info
->rank
, primary_rank
);
3986 IPv6RankedListInsertE(list
, &elem
);
3991 if (get_override_primary(proto_dict
)) {
3992 primary_rank
= kRankAssertionFirst
;
3994 else if (S_ppp_override_primary
3995 && CFStringHasPrefix(if_name
, CFSTR(PPP_PREFIX
))) {
3996 /* PPP override: make ppp* look the best */
3997 /* Hack: should use interface type, not interface name */
3998 primary_rank
= kRankAssertionFirst
;
4001 elem
.rank
= RankMake(info
->rank
, primary_rank
);
4002 info
->rank
= RankMake(info
->rank
, primary_rank
);
4004 (void)cfstring_to_ip6(router
, &elem
.iaddr6
);
4006 IPv6RankedListInsertE(list
, &elem
);
4007 rank_dict_set_service_rank(S_ipv6_service_rank_dict
,
4008 info
->serviceID
, info
->rank
);
4013 update_nwi_state_ipv6(nwi_state_t state
, IPv6RankedListRef list
)
4017 if (state
== NULL
) {
4021 /* A null list indicates to clear the ifstates */
4022 nwi_state_clear(state
, AF_INET6
);
4028 for (idx
= 0 ; idx
< list
->count
; idx
++) {
4029 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
4030 char ntopbuf
[INET6_ADDRSTRLEN
];
4032 inet_ntop(AF_INET6
, &list
->elem
[idx
].iaddr6
, ntopbuf
,
4035 SCLog(TRUE
, LOG_NOTICE
,
4036 CFSTR("Inserting v6 [%s] into list with flags 0x%x"
4037 " with rank %lu ipv6 addr %s"),
4038 list
->elem
[idx
].ifname
, list
->elem
[idx
].flags
,
4039 list
->elem
[idx
].rank
, ntopbuf
);
4042 nwi_insert_ifstate(state
, list
->elem
[idx
].ifname
,
4043 AF_INET6
, list
->elem
[idx
].flags
,
4044 list
->elem
[idx
].rank
,
4045 (void *)&list
->elem
[idx
].iaddr6
);
4047 nwi_state_set_last(state
, AF_INET6
);
4051 interface_has_dns(const void * * values
, int values_count
,
4052 const char * ifname
)
4054 boolean_t has_dns
= FALSE
;
4056 CFStringRef ifname_cf
;
4058 ifname_cf
= CFStringCreateWithCString(NULL
, ifname
, kCFStringEncodingASCII
);
4059 for (i
= 0; i
< values_count
; i
++) {
4060 CFDictionaryRef dns_dict
;
4062 dns_dict
= CFDictionaryGetValue(values
[i
], kSCEntNetDNS
);
4063 if (dns_dict
!= NULL
) {
4064 CFStringRef this_ifname
;
4066 this_ifname
= CFDictionaryGetValue(dns_dict
, kSCPropInterfaceName
);
4067 if (this_ifname
!= NULL
4068 && CFEqual(this_ifname
, ifname_cf
)) {
4074 CFRelease(ifname_cf
);
4079 update_nwi_state_ipv4(nwi_state_t state
, IPv4RouteListRef route
)
4083 const void * * values
;
4084 const void * values_buf
[10];
4087 if (state
== NULL
) {
4090 nwi_state_clear(state
, AF_INET
);
4091 if (route
== NULL
) {
4094 values_count
= CFDictionaryGetCount(S_service_state_dict
);
4095 if (values_count
== 0) {
4098 if (values_count
<= (sizeof(values_buf
) / sizeof(values_buf
[0]))) {
4099 values
= values_buf
;
4102 values
= (const void * *)malloc(sizeof(*values
) * values_count
);
4104 CFDictionaryGetKeysAndValues(S_service_state_dict
,
4105 NULL
, (const void * *)values
);
4106 for (idx
= 0, r
= route
->list
; idx
< route
->count
; idx
++, r
++) {
4107 uint64_t flags
= 0ULL;
4109 if (r
->dest
.s_addr
!= 0) {
4110 /* we're done, no more default routes */
4113 if (RANK_ASSERTION_MASK(r
->rank
) == kRankAssertionNever
) {
4114 flags
|= NWI_IFSTATE_FLAGS_NOT_IN_LIST
;
4117 if (interface_has_dns(values
, values_count
, r
->ifname
)) {
4118 flags
|= NWI_IFSTATE_FLAGS_HAS_DNS
;
4120 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
4121 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Inserting v4 [%s] with flags 0x%x primary_rank %u"),
4122 r
->ifname
, flags
, r
->rank
);
4124 nwi_insert_ifstate(state
, r
->ifname
, AF_INET
, flags
, r
->rank
,
4127 if (values
!= values_buf
) {
4130 nwi_state_set_last(state
, AF_INET
);
4135 * Function: elect_new_primary
4137 * Walk the list of services, passing each service dictionary to "elect_func".
4138 * "elect_func" returns rank information about the service that let us
4139 * determine the new primary.
4142 elect_new_primary(election_func_t
* elect_func
, void * context
,
4143 CFArrayRef order
, int n_order
)
4147 election_info_t info
;
4149 #define N_KEYS_VALUES_STATIC 10
4150 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
4151 CFStringRef new_primary
= NULL
;
4155 count
= CFDictionaryGetCount(S_service_state_dict
);
4156 if (count
<= N_KEYS_VALUES_STATIC
) {
4157 keys
= keys_values_buf
;
4160 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
4162 values
= keys
+ count
;
4163 CFDictionaryGetKeysAndValues(S_service_state_dict
,
4164 (const void * *)keys
,
4165 (const void * *)values
);
4167 info
.n_services
= count
;
4169 info
.n_order
= n_order
;
4171 for (i
= 0; i
< count
; i
++) {
4172 boolean_t found_new_primary
= FALSE
;
4174 info
.serviceID
= (CFStringRef
)keys
[i
];
4175 info
.service_dict
= (CFDictionaryRef
)values
[i
];
4176 info
.rank
= kRankAssertionMask
;
4178 if ((*elect_func
)(context
, &info
) == FALSE
) {
4181 if (new_primary
== NULL
4182 || (info
.rank
< new_rank
)) {
4183 found_new_primary
= TRUE
;
4186 if (found_new_primary
) {
4187 new_primary
= info
.serviceID
;
4188 new_rank
= info
.rank
;
4191 if (new_primary
!= NULL
) {
4192 CFRetain(new_primary
);
4194 if (keys
!= keys_values_buf
) {
4197 return (new_primary
);
4201 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
4203 uint32_t changed
= 0;
4206 /* update service options first (e.g. rank) */
4207 if (get_rank_changes(serviceID
,
4208 get_service_state_entity(services_info
, serviceID
,
4210 get_service_setup_entity(services_info
, serviceID
,
4213 changed
|= (1 << kEntityTypeServiceOptions
);
4215 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
4216 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
4217 GetEntityChangesFuncRef func
= entityChangeFunc
[i
];
4218 if ((*func
)(serviceID
,
4219 get_service_state_entity(services_info
, serviceID
,
4220 *entityTypeNames
[i
]),
4221 get_service_setup_entity(services_info
, serviceID
,
4222 *entityTypeNames
[i
]),
4224 changed
|= (1 << i
);
4231 service_order_get(CFDictionaryRef services_info
)
4233 CFArrayRef order
= NULL
;
4234 CFDictionaryRef ipv4_dict
;
4236 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
4237 S_setup_global_ipv4
);
4238 if (ipv4_dict
!= NULL
) {
4239 CFNumberRef ppp_override
;
4242 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
4243 order
= isA_CFArray(order
);
4245 /* get ppp override primary */
4246 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
4247 kSCPropNetPPPOverridePrimary
);
4248 ppp_override
= isA_CFNumber(ppp_override
);
4249 if (ppp_override
!= NULL
) {
4250 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
4252 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
4255 S_ppp_override_primary
= FALSE
;
4261 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
4262 const char * entity
)
4264 boolean_t changed
= FALSE
;
4265 CFStringRef primary
= *primary_p
;
4267 if (new_primary
!= NULL
) {
4268 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
4269 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4270 SCLog(TRUE
, LOG_NOTICE
,
4271 CFSTR("IPMonitor: %@ is still primary %s"),
4272 new_primary
, entity
);
4276 my_CFRelease(primary_p
);
4277 *primary_p
= CFRetain(new_primary
);
4278 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4279 SCLog(TRUE
, LOG_NOTICE
,
4280 CFSTR("IPMonitor: %@ is the new primary %s"),
4281 new_primary
, entity
);
4286 else if (primary
!= NULL
) {
4287 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4288 SCLog(TRUE
, LOG_NOTICE
,
4289 CFSTR("IPMonitor: %@ is no longer primary %s"),
4292 my_CFRelease(primary_p
);
4299 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
4302 if (service_dict_get(serviceID
, entity
) == NULL
) {
4303 return (RankMake(kRankIndexMask
, kRankAssertionDefault
));
4305 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
4308 static __inline__
unsigned int
4309 IPv6RankedListComputeSize(int n
)
4311 return (offsetof(IPv6RankedList
, elem
[n
]));
4316 update_interface_rank(CFDictionaryRef services_info
, CFStringRef ifname
)
4318 CFStringRef if_rank_key
;
4319 CFDictionaryRef rank_dict
;
4321 if_rank_key
= if_rank_key_copy(ifname
);
4322 rank_dict
= CFDictionaryGetValue(services_info
, if_rank_key
);
4323 CFRelease(if_rank_key
);
4324 if_rank_set(ifname
, rank_dict
);
4329 append_serviceIDs_for_interface(CFMutableArrayRef services_changed
,
4335 #define N_KEYS_VALUES_STATIC 10
4336 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
4339 count
= CFDictionaryGetCount(S_service_state_dict
);
4340 if (count
<= N_KEYS_VALUES_STATIC
) {
4341 keys
= keys_values_buf
;
4343 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
4345 values
= keys
+ count
;
4346 CFDictionaryGetKeysAndValues(S_service_state_dict
,
4347 (const void * *)keys
,
4348 (const void * *)values
);
4350 for (i
= 0; i
< count
; i
++) {
4351 CFDictionaryRef ipv4
= NULL
;
4352 CFStringRef interface
= NULL
;
4353 CFStringRef serviceID
;
4354 CFDictionaryRef service_dict
;
4356 serviceID
= (CFStringRef
)keys
[i
];
4357 service_dict
= (CFDictionaryRef
)values
[i
];
4359 /* check if this is a ipv4 dictionary */
4360 ipv4
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
4362 interface
= ipv4_dict_get_ifname(ipv4
);
4363 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
4364 if (S_IPMonitor_debug
& kDebugFlag1
) {
4365 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Found ipv4 service %@ on interface %@."),
4369 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
4372 CFDictionaryRef proto_dict
;
4374 /* check if this is a ipv6 dictionary */
4375 proto_dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
4376 if (proto_dict
== NULL
) {
4379 interface
= CFDictionaryGetValue(proto_dict
, kSCPropInterfaceName
);
4380 if (interface
!= NULL
&& CFEqual(interface
, ifname
)) {
4381 if (S_IPMonitor_debug
& kDebugFlag1
) {
4382 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Found ipv6 service %@ on interface %@."),
4386 my_CFArrayAppendUniqueValue(services_changed
, serviceID
);
4391 if (keys
!= keys_values_buf
) {
4396 static const CFStringRef
*statusEntityNames
[] = {
4403 add_status_keys(CFMutableArrayRef patterns
)
4407 for (i
= 0; i
< sizeof(statusEntityNames
)/sizeof(statusEntityNames
[0]); i
++) {
4408 CFStringRef pattern
;
4410 pattern
= state_service_key(kSCCompAnyRegex
, *statusEntityNames
[i
]);
4411 CFArrayAppendValue(patterns
, pattern
);
4421 get_changed_str(CFStringRef serviceID
, CFStringRef entity
, CFDictionaryRef old_dict
)
4423 CFDictionaryRef new_dict
= NULL
;
4425 if (serviceID
!= NULL
) {
4426 new_dict
= service_dict_get(serviceID
, entity
);
4429 if (old_dict
== NULL
) {
4430 if (new_dict
!= NULL
) {
4434 if (new_dict
== NULL
) {
4436 } else if (!CFEqual(old_dict
, new_dict
)) {
4443 static CF_RETURNS_RETAINED CFStringRef
4444 generate_log_changes(nwi_state_t changes_state
,
4445 boolean_t dns_changed
,
4446 boolean_t dnsinfo_changed
,
4447 CFDictionaryRef old_primary_dns
,
4448 boolean_t proxy_changed
,
4449 CFDictionaryRef old_primary_proxy
,
4450 boolean_t smb_changed
,
4451 CFDictionaryRef old_primary_smb
4455 CFMutableStringRef log_output
;
4458 log_output
= CFStringCreateMutable(NULL
, 0);
4460 if (changes_state
!= NULL
) {
4461 for (idx
= 0; idx
< sizeof(nwi_af_list
)/sizeof(nwi_af_list
[0]); idx
++) {
4462 CFMutableStringRef changes
= NULL
;
4463 CFMutableStringRef primary_str
= NULL
;
4465 scan
= nwi_state_get_first_ifstate(changes_state
, nwi_af_list
[idx
]);
4467 while (scan
!= NULL
) {
4468 const char * changed_str
;
4470 changed_str
= nwi_ifstate_get_diff_str(scan
);
4471 if (changed_str
!= NULL
) {
4473 const char * addr_str
;
4474 char ntopbuf
[INET6_ADDRSTRLEN
];
4476 address
= (void *)nwi_ifstate_get_address(scan
);
4477 addr_str
= inet_ntop(scan
->af
, address
,
4478 ntopbuf
, sizeof(ntopbuf
));
4480 if (primary_str
== NULL
) {
4481 primary_str
= CFStringCreateMutable(NULL
, 0);
4482 CFStringAppendFormat(primary_str
, NULL
, CFSTR("%s%s:%s"),
4483 nwi_ifstate_get_ifname(scan
),
4484 changed_str
, addr_str
);
4486 if (changes
== NULL
) {
4487 changes
= CFStringCreateMutable(NULL
, 0);
4489 CFStringAppendFormat(changes
, NULL
, CFSTR(", %s"),
4490 nwi_ifstate_get_ifname(scan
));
4491 if (strcmp(changed_str
, "") != 0) {
4492 CFStringAppendFormat(changes
, NULL
, CFSTR("%s:%s"),
4493 changed_str
, addr_str
);
4497 scan
= nwi_ifstate_get_next(scan
, scan
->af
);
4500 if (primary_str
!= NULL
) {
4501 CFStringAppendFormat(log_output
, NULL
, CFSTR(" %s(%@"),
4502 nwi_af_list
[idx
] == AF_INET
? "v4" : "v6",
4505 if (changes
!= NULL
&& CFStringGetLength(changes
) != 0) {
4506 CFStringAppendFormat(log_output
, NULL
, CFSTR("%@"),
4509 CFStringAppendFormat(log_output
, NULL
, CFSTR(")"));
4511 my_CFRelease(&primary_str
);
4512 my_CFRelease(&changes
);
4517 if (dns_changed
|| dnsinfo_changed
) {
4520 str
= get_changed_str(S_primary_dns
, kSCEntNetDNS
, old_primary_dns
);
4521 if ((strcmp(str
, "") == 0) && dnsinfo_changed
) {
4522 str
= "*"; // dnsinfo change w/no change to primary
4524 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS%s"), str
);
4525 } else if (S_primary_dns
!= NULL
){
4526 CFStringAppendFormat(log_output
, NULL
, CFSTR(" DNS"));
4529 if (proxy_changed
) {
4532 str
= get_changed_str(S_primary_proxies
, kSCEntNetProxies
, old_primary_proxy
);
4533 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy%s"), str
);
4534 } else if (S_primary_proxies
!= NULL
) {
4535 CFStringAppendFormat(log_output
, NULL
, CFSTR(" Proxy"));
4538 #if !TARGET_OS_IPHONE
4542 str
= get_changed_str(S_primary_smb
, kSCEntNetSMB
, old_primary_smb
);
4543 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB%s"), str
);
4544 } else if (S_primary_smb
!= NULL
) {
4545 CFStringAppendFormat(log_output
, NULL
, CFSTR(" SMB"));
4547 #endif // !TARGET_OS_IPHONE
4553 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
4557 nwi_state_t changes_state
= NULL
;
4558 boolean_t dns_changed
= FALSE
;
4559 boolean_t dnsinfo_changed
= FALSE
;
4560 boolean_t global_ipv4_changed
= FALSE
;
4561 boolean_t global_ipv6_changed
= FALSE
;
4563 CFMutableArrayRef if_rank_changes
= NULL
;
4566 CFStringRef network_change_msg
;
4568 int n_service_order
= 0;
4569 nwi_state_t old_nwi_state
= NULL
;
4570 CFDictionaryRef old_primary_dns
= NULL
;
4571 CFDictionaryRef old_primary_proxy
= NULL
;
4572 #if !TARGET_OS_IPHONE
4573 CFDictionaryRef old_primary_smb
= NULL
;
4574 #endif // !TARGET_OS_IPHONE
4575 boolean_t proxies_changed
= FALSE
;
4576 CFArrayRef service_order
;
4577 CFMutableArrayRef service_changes
= NULL
;
4578 CFDictionaryRef services_info
= NULL
;
4579 #if !TARGET_OS_IPHONE
4580 boolean_t smb_changed
= FALSE
;
4581 #endif // !TARGET_OS_IPHONE
4583 count
= CFArrayGetCount(changed_keys
);
4588 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4589 SCLog(TRUE
, LOG_NOTICE
,
4590 CFSTR("IPMonitor: changes %@ (%d)"), changed_keys
, count
);
4593 if (S_primary_dns
!= NULL
) {
4594 old_primary_dns
= service_dict_get(S_primary_dns
, kSCEntNetDNS
);
4595 if (old_primary_dns
!= NULL
) {
4596 old_primary_dns
= CFDictionaryCreateCopy(NULL
, old_primary_dns
);
4600 if (S_primary_proxies
!= NULL
) {
4601 old_primary_proxy
= service_dict_get(S_primary_proxies
, kSCEntNetProxies
);
4602 if (old_primary_proxy
!= NULL
) {
4603 old_primary_proxy
= CFDictionaryCreateCopy(NULL
, old_primary_proxy
);
4607 #if !TARGET_OS_IPHONE
4608 if (S_primary_smb
!= NULL
) {
4609 old_primary_smb
= service_dict_get(S_primary_smb
, kSCEntNetSMB
);
4610 if (old_primary_smb
!= NULL
) {
4611 old_primary_smb
= CFDictionaryCreateCopy(NULL
, old_primary_smb
);
4614 #endif // !TARGET_OS_IPHONE
4616 keyChangeListInit(&keys
);
4617 service_changes
= CFArrayCreateMutable(NULL
, 0,
4618 &kCFTypeArrayCallBacks
);
4620 for (i
= 0; i
< count
; i
++) {
4621 CFStringRef change
= CFArrayGetValueAtIndex(changed_keys
, i
);
4622 if (CFEqual(change
, S_setup_global_ipv4
)) {
4623 global_ipv4_changed
= TRUE
;
4624 global_ipv6_changed
= TRUE
;
4626 else if (CFEqual(change
, S_multicast_resolvers
)) {
4627 dnsinfo_changed
= TRUE
;
4629 else if (CFEqual(change
, S_private_resolvers
)) {
4630 dnsinfo_changed
= TRUE
;
4632 #if !TARGET_OS_IPHONE
4633 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
4634 dnsinfo_changed
= TRUE
;
4636 #endif /* !TARGET_OS_IPHONE */
4637 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
4639 boolean_t status_changed
= FALSE
;
4641 for (i
= 0; i
< sizeof(statusEntityNames
)/sizeof(statusEntityNames
[0]); i
++) {
4642 if (CFStringHasSuffix(change
, *statusEntityNames
[i
])) {
4643 status_changed
= TRUE
;
4644 dnsinfo_changed
= TRUE
;
4649 if (!status_changed
) {
4650 CFStringRef serviceID
= parse_component(change
,
4651 S_state_service_prefix
);
4653 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
4654 CFRelease(serviceID
);
4658 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
4659 CFStringRef serviceID
= parse_component(change
,
4660 S_setup_service_prefix
);
4662 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
4663 CFRelease(serviceID
);
4666 else if (CFStringHasSuffix(change
, kSCEntNetService
)) {
4667 CFStringRef ifname
= my_CFStringCopyComponent(change
, CFSTR("/"), 3);
4669 if (ifname
!= NULL
) {
4670 if (if_rank_changes
== NULL
) {
4671 if_rank_changes
= CFArrayCreateMutable(NULL
, 0,
4672 &kCFTypeArrayCallBacks
);
4674 my_CFArrayAppendUniqueValue(if_rank_changes
, ifname
);
4680 /* determine which serviceIDs are impacted by the interface rank changes */
4681 if (if_rank_changes
!= NULL
) {
4682 n
= CFArrayGetCount(if_rank_changes
);
4683 for (i
= 0; i
< n
; i
++) {
4684 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
4686 if (S_IPMonitor_debug
& kDebugFlag1
) {
4687 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Interface rank changed %@"),
4690 append_serviceIDs_for_interface(service_changes
, ifname
);
4694 /* grab a snapshot of everything we need */
4695 services_info
= services_info_copy(session
, service_changes
,
4697 service_order
= service_order_get(services_info
);
4698 if (service_order
!= NULL
) {
4699 n_service_order
= CFArrayGetCount(service_order
);
4700 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4701 SCLog(TRUE
, LOG_NOTICE
,
4702 CFSTR("IPMonitor: service_order %@ "), service_order
);
4706 if (if_rank_changes
!= NULL
) {
4707 for (i
= 0; i
< n
; i
++) {
4708 CFStringRef ifname
= CFArrayGetValueAtIndex(if_rank_changes
, i
);
4709 update_interface_rank(services_info
, ifname
);
4713 n
= CFArrayGetCount(service_changes
);
4714 for (i
= 0; i
< n
; i
++) {
4716 CFStringRef serviceID
;
4718 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
4719 changes
= service_changed(services_info
, serviceID
);
4720 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
4721 /* if __Service__ (e.g. PrimaryRank) changed */
4722 global_ipv4_changed
= TRUE
;
4723 global_ipv6_changed
= TRUE
;
4726 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
4727 global_ipv4_changed
= TRUE
;
4728 dnsinfo_changed
= TRUE
;
4729 proxies_changed
= TRUE
;
4731 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
4732 global_ipv6_changed
= TRUE
;
4733 dnsinfo_changed
= TRUE
;
4734 proxies_changed
= TRUE
;
4737 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
4738 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
4739 update_dns(services_info
, serviceID
, &keys
);
4741 dnsinfo_changed
= TRUE
;
4743 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
4744 proxies_changed
= TRUE
;
4746 #if !TARGET_OS_IPHONE
4747 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
4748 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
4749 update_smb(services_info
, serviceID
, &keys
);
4752 #endif /* !TARGET_OS_IPHONE */
4755 /* ensure S_nwi_state can hold as many services as we have currently */
4756 n_services
= CFDictionaryGetCount(S_service_state_dict
);
4757 old_nwi_state
= nwi_state_copy_priv(S_nwi_state
);
4758 S_nwi_state
= nwi_state_new(S_nwi_state
, n_services
);
4760 if (global_ipv4_changed
|| dnsinfo_changed
) {
4761 IPv4RouteListRef new_routelist
= NULL
;
4762 CFStringRef new_primary
;
4764 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4765 SCLog(TRUE
, LOG_NOTICE
,
4766 CFSTR("IPMonitor: IPv4 service election"));
4768 new_primary
= elect_new_primary(&elect_ipv4
, &new_routelist
,
4769 service_order
, n_service_order
);
4770 update_nwi_state_ipv4(S_nwi_state
, new_routelist
);
4772 if (global_ipv4_changed
) {
4773 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
4774 update_ipv4(S_primary_ipv4
, new_routelist
, &keys
);
4776 else if (new_routelist
!= NULL
) {
4777 free(new_routelist
);
4778 new_routelist
= NULL
;
4780 my_CFRelease(&new_primary
);
4782 if (global_ipv6_changed
|| dnsinfo_changed
) {
4783 IPv6RankedListRef list
= NULL
;
4784 CFStringRef new_primary
;
4787 size
= (n_services
!= 0)
4788 ? (sizeof(IPv6RankedList
)
4789 + IPv6RankedListComputeSize(n_services
)) : 0;
4792 list
= malloc(size
);
4799 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4800 SCLog(TRUE
, LOG_NOTICE
,
4801 CFSTR("IPMonitor: IPv6 service election"));
4803 new_primary
= elect_new_primary(&elect_ipv6
, list
,
4804 service_order
, n_service_order
);
4805 update_nwi_state_ipv6(S_nwi_state
, list
);
4807 if (global_ipv6_changed
) {
4808 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
4809 update_ipv6(services_info
, S_primary_ipv6
, &keys
);
4815 my_CFRelease(&new_primary
);
4817 if (global_ipv4_changed
|| global_ipv6_changed
) {
4818 CFStringRef new_primary_dns
= NULL
;
4819 CFStringRef new_primary_proxies
= NULL
;
4820 #if !TARGET_OS_IPHONE
4821 CFStringRef new_primary_smb
= NULL
;
4822 #endif /* !TARGET_OS_IPHONE */
4824 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
4825 /* decide between IPv4 and IPv6 */
4826 if (rank_service_entity(S_ipv4_service_rank_dict
,
4827 S_primary_ipv4
, kSCEntNetDNS
)
4828 <= rank_service_entity(S_ipv6_service_rank_dict
,
4829 S_primary_ipv6
, kSCEntNetDNS
)) {
4830 new_primary_dns
= S_primary_ipv4
;
4833 new_primary_dns
= S_primary_ipv6
;
4835 if (rank_service_entity(S_ipv4_service_rank_dict
,
4836 S_primary_ipv4
, kSCEntNetProxies
)
4837 <= rank_service_entity(S_ipv6_service_rank_dict
,
4838 S_primary_ipv6
, kSCEntNetProxies
)) {
4839 new_primary_proxies
= S_primary_ipv4
;
4842 new_primary_proxies
= S_primary_ipv6
;
4844 #if !TARGET_OS_IPHONE
4845 if (rank_service_entity(S_ipv4_service_rank_dict
,
4846 S_primary_ipv4
, kSCEntNetSMB
)
4847 <= rank_service_entity(S_ipv6_service_rank_dict
,
4848 S_primary_ipv6
, kSCEntNetSMB
)) {
4849 new_primary_smb
= S_primary_ipv4
;
4852 new_primary_smb
= S_primary_ipv6
;
4854 #endif /* !TARGET_OS_IPHONE */
4857 else if (S_primary_ipv6
!= NULL
) {
4858 new_primary_dns
= S_primary_ipv6
;
4859 new_primary_proxies
= S_primary_ipv6
;
4860 #if !TARGET_OS_IPHONE
4861 new_primary_smb
= S_primary_ipv6
;
4862 #endif /* !TARGET_OS_IPHONE */
4864 else if (S_primary_ipv4
!= NULL
) {
4865 new_primary_dns
= S_primary_ipv4
;
4866 new_primary_proxies
= S_primary_ipv4
;
4867 #if !TARGET_OS_IPHONE
4868 new_primary_smb
= S_primary_ipv4
;
4869 #endif /* !TARGET_OS_IPHONE */
4872 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
4873 update_dns(services_info
, S_primary_dns
, &keys
);
4875 dnsinfo_changed
= TRUE
;
4877 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
, "Proxies")) {
4878 proxies_changed
= TRUE
;
4880 #if !TARGET_OS_IPHONE
4881 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
4882 update_smb(services_info
, S_primary_smb
, &keys
);
4885 #endif /* !TARGET_OS_IPHONE */
4887 if (dnsinfo_changed
|| global_ipv4_changed
|| global_ipv6_changed
) {
4888 if (S_nwi_state
!= NULL
) {
4889 S_nwi_state
->generation_count
= mach_absolute_time();
4890 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
4891 syslog(LOG_NOTICE
, "Updating network information");
4892 _nwi_state_dump(LOG_NOTICE
, S_nwi_state
);
4894 if (_nwi_state_store(S_nwi_state
) == FALSE
) {
4895 SCLog(TRUE
, LOG_ERR
, CFSTR("Notifying nwi_state_store failed"));
4899 if (dnsinfo_changed
) {
4900 update_dnsinfo(services_info
, S_primary_dns
, &keys
, service_order
);
4902 if (proxies_changed
||
4903 (dnsinfo_changed
&& (G_supplemental_proxies_follow_dns
!= NULL
) && CFBooleanGetValue(G_supplemental_proxies_follow_dns
))
4905 // if proxy change OR supplemental Proxies follow supplemental DNS
4906 update_proxies(services_info
, S_primary_proxies
, &keys
, service_order
);
4907 proxies_changed
= TRUE
;
4909 my_CFRelease(&service_changes
);
4910 my_CFRelease(&services_info
);
4911 my_CFRelease(&if_rank_changes
);
4912 changes_state
= nwi_state_diff(old_nwi_state
, S_nwi_state
);
4913 if ((S_IPMonitor_debug
& kDebugFlag2
) != 0) {
4914 syslog(LOG_NOTICE
, "network information diffs: ");
4915 _nwi_state_dump(LOG_NOTICE
, changes_state
);
4917 network_change_msg
=
4918 generate_log_changes(changes_state
,
4924 #if !TARGET_OS_IPHONE
4927 #else // !TARGET_OS_IPHONE
4930 #endif // !TARGET_OS_IPHONE
4932 keyChangeListApplyToStore(&keys
, session
, network_change_msg
);
4933 my_CFRelease(&old_primary_dns
);
4934 my_CFRelease(&old_primary_proxy
);
4935 #if !TARGET_OS_IPHONE
4936 my_CFRelease(&old_primary_smb
);
4937 #endif // !TARGET_OS_IPHONE
4938 my_CFRelease(&network_change_msg
);
4940 if (changes_state
!= NULL
) {
4941 nwi_state_release(changes_state
);
4943 if (old_nwi_state
!= NULL
) {
4944 nwi_state_release(old_nwi_state
);
4946 keyChangeListFree(&keys
);
4953 CFMutableArrayRef keys
= NULL
;
4954 CFStringRef pattern
;
4955 CFMutableArrayRef patterns
= NULL
;
4956 CFRunLoopSourceRef rls
= NULL
;
4958 if (S_is_network_boot() != 0) {
4963 if (S_is_scoped_routing_enabled() != 0) {
4964 S_scopedroute
= TRUE
;
4967 if (S_is_scoped_v6_routing_enabled() != 0) {
4968 S_scopedroute_v6
= TRUE
;
4970 #endif /* RTF_IFSCOPE */
4972 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
4973 IPMonitorNotify
, NULL
);
4974 if (S_session
== NULL
) {
4975 SCLog(TRUE
, LOG_ERR
,
4976 CFSTR("IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s"),
4977 SCErrorString(SCError()));
4981 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4982 kSCDynamicStoreDomainState
,
4985 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4986 kSCDynamicStoreDomainState
,
4989 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4990 kSCDynamicStoreDomainState
,
4992 S_state_global_proxies
4993 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4994 kSCDynamicStoreDomainState
,
4996 #if !TARGET_OS_IPHONE
4998 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4999 kSCDynamicStoreDomainState
,
5001 #endif /* !TARGET_OS_IPHONE */
5003 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5004 kSCDynamicStoreDomainSetup
,
5006 S_state_service_prefix
5007 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5008 kSCDynamicStoreDomainState
,
5011 S_setup_service_prefix
5012 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5013 kSCDynamicStoreDomainSetup
,
5016 S_service_state_dict
5017 = CFDictionaryCreateMutable(NULL
, 0,
5018 &kCFTypeDictionaryKeyCallBacks
,
5019 &kCFTypeDictionaryValueCallBacks
);
5021 S_ipv4_service_rank_dict
5022 = CFDictionaryCreateMutable(NULL
, 0,
5023 &kCFTypeDictionaryKeyCallBacks
,
5024 &kCFTypeDictionaryValueCallBacks
);
5026 S_ipv6_service_rank_dict
5027 = CFDictionaryCreateMutable(NULL
, 0,
5028 &kCFTypeDictionaryKeyCallBacks
,
5029 &kCFTypeDictionaryValueCallBacks
);
5031 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5032 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5034 /* register for State: and Setup: per-service notifications */
5035 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
5037 /* register for State: per-service PPP/VPN/IPSec status notifications */
5038 add_status_keys(patterns
);
5040 /* register for interface rank notifications */
5041 pattern
= if_rank_key_copy(kSCCompAnyRegex
);
5042 CFArrayAppendValue(patterns
, pattern
);
5045 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
5046 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
5048 /* add notifier for multicast DNS configuration (Bonjour/.local) */
5049 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
5050 kSCDynamicStoreDomainState
,
5052 CFSTR(kDNSServiceCompMulticastDNS
));
5053 CFArrayAppendValue(keys
, S_multicast_resolvers
);
5055 /* add notifier for private DNS configuration (Back to My Mac) */
5056 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
5057 kSCDynamicStoreDomainState
,
5059 CFSTR(kDNSServiceCompPrivateDNS
));
5060 CFArrayAppendValue(keys
, S_private_resolvers
);
5062 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
5063 SCLog(TRUE
, LOG_ERR
,
5064 CFSTR("IPMonitor ip_plugin_init "
5065 "SCDynamicStoreSetNotificationKeys failed: %s"),
5066 SCErrorString(SCError()));
5070 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
5072 SCLog(TRUE
, LOG_ERR
,
5073 CFSTR("IPMonitor ip_plugin_init "
5074 "SCDynamicStoreCreateRunLoopSource failed: %s"),
5075 SCErrorString(SCError()));
5079 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
5082 /* initialize dns configuration */
5083 (void)dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
5084 #if !TARGET_OS_IPHONE
5086 #endif /* !TARGET_OS_IPHONE */
5087 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
5089 #if !TARGET_OS_IPHONE
5090 /* initialize SMB configuration */
5091 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
5092 #endif /* !TARGET_OS_IPHONE */
5094 if_rank_dict_init();
5097 my_CFRelease(&keys
);
5098 my_CFRelease(&patterns
);
5106 /* initialize multicast route */
5107 update_ipv4(NULL
, NULL
, NULL
);
5112 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
5116 boolean_t ret
= def
;
5118 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
5120 ret
= CFBooleanGetValue(b
);
5127 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
5129 CFDictionaryRef info_dict
;
5131 info_dict
= CFBundleGetInfoDictionary(bundle
);
5132 if (info_dict
!= NULL
) {
5134 = S_get_plist_boolean(info_dict
,
5135 CFSTR("AppendStateArrayToSetupArray"),
5138 if (bundleVerbose
) {
5139 S_IPMonitor_debug
= kDebugFlagDefault
;
5142 dns_configuration_init(bundle
);
5143 proxy_configuration_init(bundle
);
5146 #if !TARGET_OS_IPHONE
5147 if (S_session
!= NULL
) {
5148 dns_configuration_monitor(S_session
, IPMonitorNotify
);
5150 #endif /* !TARGET_OS_IPHONE */
5152 load_hostname((S_IPMonitor_debug
& kDebugFlag1
) != 0);
5153 #if !TARGET_OS_IPHONE
5154 load_smb_configuration((S_IPMonitor_debug
& kDebugFlag1
) != 0);
5155 #endif /* !TARGET_OS_IPHONE */
5162 #pragma mark Standalone test code
5165 #ifdef TEST_IPMONITOR
5166 #include "dns-configuration.c"
5167 #include "set-hostname.c"
5170 main(int argc
, char **argv
)
5174 S_IPMonitor_debug
= kDebugFlag1
;
5176 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
5179 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
5186 #endif /* TEST_IPMONITOR */
5188 #ifdef TEST_IPV4_ROUTELIST
5189 #include "dns-configuration.c"
5190 #include "set-hostname.c"
5192 struct ipv4_service_contents
{
5196 const char * router
;
5197 const char * ifname
;
5199 const CFStringRef
*primaryRank
;
5203 * addr mask dest router ifname pri primaryRank
5205 struct ipv4_service_contents en0_10
= {
5206 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, NULL
5209 struct ipv4_service_contents en0_15
= {
5210 "10.0.0.19", "255.255.255.0", NULL
, "10.0.0.1", "en0", 15, NULL
5213 struct ipv4_service_contents en0_30
= {
5214 "10.0.0.11", "255.255.255.0", NULL
, "10.0.0.1", "en0", 30, NULL
5217 struct ipv4_service_contents en0_40
= {
5218 "10.0.0.12", "255.255.255.0", NULL
, "10.0.0.1", "en0", 40, NULL
5221 struct ipv4_service_contents en0_50
= {
5222 "10.0.0.13", "255.255.255.0", NULL
, "10.0.0.1", "en0", 50, NULL
5225 struct ipv4_service_contents en0_110
= {
5226 "192.168.2.10", "255.255.255.0", NULL
, "192.168.2.1", "en0", 110, NULL
5229 struct ipv4_service_contents en0_1
= {
5230 "17.202.40.191", "255.255.252.0", NULL
, "17.202.20.1", "en0", 1, NULL
5233 struct ipv4_service_contents en1_20
= {
5234 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, NULL
5237 struct ipv4_service_contents en1_2
= {
5238 "17.202.42.24", "255.255.252.0", NULL
, "17.202.20.1", "en1", 2, NULL
5241 struct ipv4_service_contents en1_125
= {
5242 "192.168.2.20", "255.255.255.0", NULL
, "192.168.2.1", "en1", 125, NULL
5245 struct ipv4_service_contents fw0_25
= {
5246 "192.168.2.30", "255.255.255.0", NULL
, "192.168.2.1", "fw0", 25, NULL
5249 struct ipv4_service_contents fw0_21
= {
5250 "192.168.3.30", "255.255.255.0", NULL
, "192.168.3.1", "fw0", 21, NULL
5253 struct ipv4_service_contents ppp0_0_1
= {
5254 "17.219.156.22", NULL
, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
5257 struct ipv4_service_contents en0_test6
= {
5258 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 2, NULL
5260 struct ipv4_service_contents en1_test6
= {
5261 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 3, NULL
5263 struct ipv4_service_contents en2_test6
= {
5264 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
5267 struct ipv4_service_contents en0_test7
= {
5268 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 3, NULL
5270 struct ipv4_service_contents en1_test7
= {
5271 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 2, NULL
5273 struct ipv4_service_contents en2_test7
= {
5274 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
5276 struct ipv4_service_contents fw0_test6_and_7
= {
5277 "169.254.11.33", "255.255.0.0", NULL
, NULL
, "fw0", 0x0ffffff, NULL
5280 struct ipv4_service_contents en0_10_last
= {
5281 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
5284 struct ipv4_service_contents en0_10_never
= {
5285 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
5288 struct ipv4_service_contents en1_20_first
= {
5289 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
5292 struct ipv4_service_contents en1_20_never
= {
5293 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
5296 struct ipv4_service_contents
* test1
[] = {
5307 struct ipv4_service_contents
* test2
[] = {
5317 struct ipv4_service_contents
* test3
[] = {
5331 struct ipv4_service_contents
* test4
[] = {
5340 struct ipv4_service_contents
* test5
[] = {
5350 struct ipv4_service_contents
* test6
[] = {
5358 struct ipv4_service_contents
* test7
[] = {
5366 struct ipv4_service_contents
* test8
[] = {
5372 struct ipv4_service_contents
* test9
[] = {
5379 struct ipv4_service_contents
* test10
[] = {
5386 struct ipv4_service_contents
* test11
[] = {
5393 struct ipv4_service_contents
* test12
[] = {
5399 struct ipv4_service_contents
* test13
[] = {
5405 struct ipv4_service_contents
* test14
[] = {
5411 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
5414 CFStringRef prop_val
;
5419 prop_val
= CFStringCreateWithCString(NULL
,
5421 kCFStringEncodingASCII
);
5422 CFDictionarySetValue(dict
, prop_name
, prop_val
);
5423 CFRelease(prop_val
);
5428 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
5432 CFStringRef prop_val
;
5437 prop_val
= CFStringCreateWithCString(NULL
,
5439 kCFStringEncodingASCII
);
5440 array
= CFArrayCreate(NULL
,
5441 (const void **)&prop_val
, 1,
5442 &kCFTypeArrayCallBacks
);
5443 CFRelease(prop_val
);
5444 CFDictionarySetValue(dict
, prop_name
, array
);
5449 static CFDictionaryRef
5450 make_IPv4_dict(struct ipv4_service_contents
* t
)
5452 CFMutableDictionaryRef dict
;
5454 dict
= CFDictionaryCreateMutable(NULL
, 0,
5455 &kCFTypeDictionaryKeyCallBacks
,
5456 &kCFTypeDictionaryValueCallBacks
);
5457 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
5458 dict_add_string_as_array(dict
, kSCPropNetIPv4SubnetMasks
, t
->mask
);
5459 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
5460 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
5461 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
5465 static IPv4RouteListRef
5466 make_IPv4RouteList(struct ipv4_service_contents
* * this_test
)
5469 IPv4RouteListRef routes
;
5470 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
5471 IPv4RouteListRef ret
= NULL
;
5472 struct ipv4_service_contents
* * scan_test
;
5474 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
5475 CFDictionaryRef dict
;
5477 dict
= make_IPv4_dict(*scan_test
);
5479 fprintf(stderr
, "make_IPv4_dict failed\n");
5482 routes
= (IPv4RouteListRef
)routes_buf
;
5483 routes
->size
= R_STATIC
;
5485 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
5486 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
5488 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
5492 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
, kRankAssertionDefault
);
5494 if ((*scan_test
)->primaryRank
!= NULL
) {
5495 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
,
5496 PrimaryRankGetRankAssertion(*(*scan_test
)->primaryRank
));
5499 if ((*scan_test
)->router
== NULL
) {
5500 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
,
5501 PrimaryRankGetRankAssertion(kSCValNetServicePrimaryRankLast
));
5504 ret
= IPv4RouteListAddRouteList(ret
, 1, r
, (*scan_test
)->rank
);
5514 * Function: run_test
5516 * Runs through the given set of routes first in the forward direction,
5517 * then again backwards. We should end up with exactly the same set of
5518 * routes at the end.
5521 run_test(const char * name
, struct ipv4_service_contents
* * this_test
)
5524 boolean_t ret
= FALSE
;
5526 IPv4RouteListRef routes
;
5527 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
5528 IPv4RouteListRef routes1
= NULL
, routes2
= NULL
;
5529 struct ipv4_service_contents
* * scan_test
;
5531 printf("\nStarting test %s\n", name
);
5532 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
5533 CFDictionaryRef dict
;
5535 dict
= make_IPv4_dict(*scan_test
);
5537 fprintf(stderr
, "make_IPv4_dict failed\n");
5540 routes
= (IPv4RouteListRef
)routes_buf
;
5541 routes
->size
= R_STATIC
;
5543 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
5544 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
5546 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
5549 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
5550 descr
= IPv4RouteListCopyDescription(r
);
5551 SCLog(TRUE
, LOG_NOTICE
, CFSTR("test: Adding %@"), descr
);
5555 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
, kRankAssertionDefault
);
5557 if ((*scan_test
)->primaryRank
!= NULL
) {
5558 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
,
5559 PrimaryRankGetRankAssertion(*(*scan_test
)->primaryRank
));
5562 routes1
= IPv4RouteListAddRouteList(routes1
, 1, r
, (*scan_test
)->rank
);
5568 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
5569 if (routes1
!= NULL
) {
5570 descr
= IPv4RouteListCopyDescription(routes1
);
5571 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Routes are %@"), descr
);
5575 for (scan_test
--; scan_test
>= this_test
; scan_test
--) {
5576 CFDictionaryRef dict
;
5578 dict
= make_IPv4_dict(*scan_test
);
5580 fprintf(stderr
, "make_IPv4_dict failed\n");
5583 routes
= (IPv4RouteListRef
)routes_buf
;
5584 routes
->size
= R_STATIC
;
5586 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
5587 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
5589 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
5592 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
5593 descr
= IPv4RouteListCopyDescription(r
);
5594 SCLog(TRUE
, LOG_NOTICE
, CFSTR("test: Adding %@"), descr
);
5597 if ((*scan_test
)->primaryRank
!= NULL
) {
5598 (*scan_test
)->rank
= RankMake((*scan_test
)->rank
,
5599 PrimaryRankGetRankAssertion(*(*scan_test
)->primaryRank
));
5602 routes2
= IPv4RouteListAddRouteList(routes2
, 1, r
, (*scan_test
)->rank
);
5608 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
5609 if (routes2
!= NULL
) {
5610 descr
= IPv4RouteListCopyDescription(routes2
);
5611 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Routes are %@"), descr
);
5615 if ((routes1
!= NULL
&& routes2
== NULL
)
5616 || (routes1
== NULL
&& routes2
!= NULL
)) {
5617 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
5618 (routes1
!= NULL
) ? "not " : "",
5619 (routes2
!= NULL
) ? "not " : "");
5621 else if (routes1
!= NULL
&& routes2
!= NULL
) {
5622 /* check if they are different */
5623 if (routes1
->count
!= routes2
->count
) {
5624 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
5625 routes1
->count
, routes2
->count
);
5627 else if (bcmp(routes1
, routes2
,
5628 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
5629 fprintf(stderr
, "routes1 and routes2 are different\n");
5632 printf("routes1 and routes2 are the same\n");
5636 if (routes1
!= NULL
) {
5639 if (routes2
!= NULL
) {
5645 typedef struct compare_context
{
5646 IPv4RouteListRef old
;
5647 IPv4RouteListRef
new;
5648 } compare_context_t
;
5651 compare_callback(IPv4RouteListApplyCommand cmd
, IPv4RouteRef route
, void * arg
)
5653 compare_context_t
* context
= (compare_context_t
*)arg
;
5656 case kIPv4RouteListAddRouteCommand
:
5657 printf("Add new[%ld] = ", route
- context
->new->list
);
5658 IPv4RoutePrint(route
);
5660 case kIPv4RouteListRemoveRouteCommand
:
5661 printf("Remove old[%ld] = ", route
- context
->old
->list
);
5662 IPv4RoutePrint(route
);
5671 compare_tests(struct ipv4_service_contents
* * old_test
,
5672 struct ipv4_service_contents
* * new_test
)
5674 IPv4RouteListRef new_routes
;
5675 IPv4RouteListRef old_routes
;
5676 compare_context_t context
;
5678 old_routes
= make_IPv4RouteList(old_test
);
5679 new_routes
= make_IPv4RouteList(new_test
);
5681 if (old_routes
== NULL
) {
5682 printf("No Old Routes\n");
5685 printf("Old Routes = ");
5686 IPv4RouteListPrint(old_routes
);
5688 if (new_routes
== NULL
) {
5689 printf("No New Routes\n");
5692 printf("New Routes = ");
5693 IPv4RouteListPrint(new_routes
);
5695 context
.old
= old_routes
;
5696 context
.new = new_routes
;
5697 IPv4RouteListApply(old_routes
, new_routes
, compare_callback
, &context
);
5698 if (old_routes
!= NULL
) {
5701 if (new_routes
!= NULL
) {
5709 main(int argc
, char **argv
)
5712 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
5714 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
5716 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
5719 if (run_test("test1", test1
) == FALSE
) {
5720 fprintf(stderr
, "test1 failed\n");
5723 if (run_test("test2", test2
) == FALSE
) {
5724 fprintf(stderr
, "test2 failed\n");
5727 if (run_test("test3", test4
) == FALSE
) {
5728 fprintf(stderr
, "test3 failed\n");
5731 if (run_test("test4", test4
) == FALSE
) {
5732 fprintf(stderr
, "test4 failed\n");
5735 if (run_test("test5", test5
) == FALSE
) {
5736 fprintf(stderr
, "test5 failed\n");
5740 printf("\nCompare 1 to 2:\n");
5741 compare_tests(test1
, test2
);
5743 printf("\nCompare 2 to 1:\n");
5744 compare_tests(test2
, test1
);
5746 printf("\nCompare 1 to 1:\n");
5747 compare_tests(test1
, test1
);
5749 printf("\nCompare 1 to 3:\n");
5750 compare_tests(test1
, test3
);
5752 printf("\nCompare 3 to 1:\n");
5753 compare_tests(test3
, test1
);
5755 printf("\nCompare 2 to 3:\n");
5756 compare_tests(test2
, test3
);
5758 printf("\nCompare 3 to 2:\n");
5759 compare_tests(test3
, test2
);
5761 printf("\nCompare 3 to 4:\n");
5762 compare_tests(test3
, test4
);
5764 printf("\nCompare 5 to 4:\n");
5765 compare_tests(test5
, test4
);
5767 printf("\nCompare 6 to 7:\n");
5768 compare_tests(test6
, test7
);
5770 printf("\nCompare 7 to 6:\n");
5771 compare_tests(test7
, test6
);
5773 printf("\nCompare 8 to 9:\n");
5774 compare_tests(test8
, test9
);
5776 printf("\nCompare 8 to 10:\n");
5777 compare_tests(test8
, test10
);
5779 printf("\nCompare 8 to 11:\n");
5780 compare_tests(test8
, test11
);
5782 printf("\nCompare 12 to 13:\n");
5783 compare_tests(test12
, test13
);
5785 printf("\nCompare 13 to 14:\n");
5786 compare_tests(test13
, test14
);
5788 printf("\nChecking for leaks\n");
5790 sprintf(cmd
, "leaks %d 2>&1", getpid());
5798 #endif /* TEST_IPV4_ROUTELIST */