2 * Copyright (c) 2000-2011 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>
91 #include <SystemConfiguration/SystemConfiguration.h>
92 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
93 #include <SystemConfiguration/SCValidation.h>
94 #include <SystemConfiguration/SCPrivate.h> /* for SCLog() */
97 #include <dnsinfo_create.h>
98 #endif /* !TARGET_OS_IPHONE */
101 #ifndef kDNSServiceCompMulticastDNS
102 #define kDNSServiceCompMulticastDNS "MulticastDNS"
104 #ifndef kDNSServiceCompPrivateDNS
105 #define kDNSServiceCompPrivateDNS "PrivateDNS"
109 kProtocolFlagsNone
= 0x0,
110 kProtocolFlagsIPv4
= 0x1,
111 kProtocolFlagsIPv6
= 0x2
113 typedef uint8_t ProtocolFlags
;
116 kDebugFlag1
= 0x00000001,
117 kDebugFlag2
= 0x00000002,
118 kDebugFlag4
= 0x00000004,
119 kDebugFlag8
= 0x00000008,
120 kDebugFlagDefault
= kDebugFlag1
,
121 kDebugFlagAll
= 0xffffffff
124 #ifdef TEST_IPV4_ROUTELIST
125 #define ROUTELIST_DEBUG(a, f) { if ((S_IPMonitor_debug & (f)) != 0) printf a ;}
127 #define ROUTELIST_DEBUG(a, f)
130 #include "set-hostname.h"
131 #include "dns-configuration.h"
132 #include "proxy-configuration.h"
133 #if !TARGET_OS_IPHONE
134 #include "smb-configuration.h"
135 #endif /* !TARGET_OS_IPHONE */
137 #define PPP_PREFIX "ppp"
139 #define IP_FORMAT "%d.%d.%d.%d"
140 #define IP_CH(ip) ((u_char *)(ip))
141 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
143 typedef uint32_t Rank
;
151 * IPv4 Route management
154 typedef uint32_t RouteFlags
;
157 kRouteIsDirectToInterfaceFlag
= 0x00000001,
158 kRouteIsNotSubnetLocalFlag
= 0x00000002,
159 kRouteChooseFirstFlag
= 0x00000004,
160 kRouteChooseLastFlag
= 0x00000008,
161 kRouteChooseNeverFlag
= 0x00000010,
162 kRouteIsScopedFlag
= 0x00000020,
163 kRouteWantScopedFlag
= (kRouteChooseNeverFlag
|kRouteIsScopedFlag
),
169 struct in_addr gateway
;
170 char ifname
[IFNAMSIZ
];
171 unsigned int ifindex
;
175 } IPv4Route
, *IPv4RouteRef
;
180 IPv4Route list
[1]; /* variable length */
181 } IPv4RouteList
, *IPv4RouteListRef
;
184 kIPv4RouteListAddRouteCommand
,
185 kIPv4RouteListRemoveRouteCommand
188 typedef uint32_t IPv4RouteListApplyCommand
;
190 typedef void IPv4RouteListApplyCallBackFunc(IPv4RouteListApplyCommand cmd
,
191 IPv4RouteRef route
, void * arg
);
192 typedef IPv4RouteListApplyCallBackFunc
* IPv4RouteListApplyCallBackFuncPtr
;
194 /* SCDynamicStore session */
195 static SCDynamicStoreRef S_session
= NULL
;
197 /* debug output flags */
198 static uint32_t S_IPMonitor_debug
= 0;
200 /* are we netbooted? If so, don't touch the default route */
201 static boolean_t S_netboot
= FALSE
;
203 /* is scoped routing enabled? */
205 static boolean_t S_scopedroute
= FALSE
;
206 static boolean_t S_scopedroute_v6
= FALSE
;
207 #endif /* RTF_IFSCOPE */
209 /* dictionary to hold per-service state: key is the serviceID */
210 static CFMutableDictionaryRef S_service_state_dict
= NULL
;
211 static CFMutableDictionaryRef S_ipv4_service_rank_dict
= NULL
;
212 static CFMutableDictionaryRef S_ipv6_service_rank_dict
= NULL
;
214 /* if set, a PPP interface overrides the primary */
215 static boolean_t S_ppp_override_primary
= FALSE
;
217 /* the current primary serviceID's */
218 static CFStringRef S_primary_ipv4
= NULL
;
219 static CFStringRef S_primary_ipv6
= NULL
;
220 static CFStringRef S_primary_dns
= NULL
;
221 static CFStringRef S_primary_proxies
= NULL
;
223 static CFStringRef S_state_global_ipv4
= NULL
;
224 static CFStringRef S_state_global_ipv6
= NULL
;
225 static CFStringRef S_state_global_dns
= NULL
;
226 static CFStringRef S_state_global_proxies
= NULL
;
227 static CFStringRef S_state_service_prefix
= NULL
;
228 static CFStringRef S_setup_global_ipv4
= NULL
;
229 static CFStringRef S_setup_service_prefix
= NULL
;
231 static CFStringRef S_multicast_resolvers
= NULL
;
232 static CFStringRef S_private_resolvers
= NULL
;
234 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
236 static const struct in_addr S_ip_zeros
= { 0 };
237 static const struct in6_addr S_ip6_zeros
= IN6ADDR_ANY_INIT
;
239 static boolean_t S_append_state
= FALSE
;
241 #if !TARGET_OS_IPHONE
242 static CFStringRef S_primary_smb
= NULL
;
243 static CFStringRef S_state_global_smb
= NULL
;
244 #endif /* !TARGET_OS_IPHONE */
246 #if !TARGET_OS_IPHONE
247 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
248 #endif /* !TARGET_OS_IPHONE */
251 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
255 ** entityType*, GetEntityChanges*
256 ** - definitions for the entity types we handle
263 #if !TARGET_OS_IPHONE
265 #endif /* !TARGET_OS_IPHONE */
267 kEntityTypeServiceOptions
= 31
269 typedef uint32_t EntityType
;
271 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
272 &kSCEntNetIPv4
, /* 0 */
273 &kSCEntNetIPv6
, /* 1 */
274 &kSCEntNetDNS
, /* 2 */
275 &kSCEntNetProxies
, /* 3 */
276 #if !TARGET_OS_IPHONE
277 &kSCEntNetSMB
, /* 4 */
278 #endif /* !TARGET_OS_IPHONE */
281 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
282 CFDictionaryRef state_dict
,
283 CFDictionaryRef setup_dict
,
284 CFDictionaryRef info
);
285 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
287 static GetEntityChangesFunc get_ipv4_changes
;
288 static GetEntityChangesFunc get_ipv6_changes
;
289 static GetEntityChangesFunc get_dns_changes
;
290 static GetEntityChangesFunc get_proxies_changes
;
291 #if !TARGET_OS_IPHONE
292 static GetEntityChangesFunc get_smb_changes
;
293 #endif /* !TARGET_OS_IPHONE */
296 my_CFRelease(void * t
);
299 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
302 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
304 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
305 get_ipv4_changes
, /* 0 */
306 get_ipv6_changes
, /* 1 */
307 get_dns_changes
, /* 2 */
308 get_proxies_changes
,/* 3 */
309 #if !TARGET_OS_IPHONE
310 get_smb_changes
, /* 4 */
311 #endif /* !TARGET_OS_IPHONE */
316 ** - mechanism to do an atomic update of the SCDynamicStore
317 ** when the content needs to be changed across multiple functions
320 CFMutableArrayRef notify
;
321 CFMutableArrayRef remove
;
322 CFMutableDictionaryRef set
;
323 } keyChangeList
, * keyChangeListRef
;
326 keyChangeListInit(keyChangeListRef keys
)
328 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
329 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
330 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
331 &kCFTypeDictionaryKeyCallBacks
,
332 &kCFTypeDictionaryValueCallBacks
);
337 keyChangeListFree(keyChangeListRef keys
)
339 my_CFRelease(&keys
->notify
);
340 my_CFRelease(&keys
->remove
);
341 my_CFRelease(&keys
->set
);
346 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
348 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
353 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
355 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
356 CFDictionaryRemoveValue(keys
->set
, key
);
361 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
363 my_CFArrayRemoveValue(keys
->remove
, key
);
364 CFDictionarySetValue(keys
->set
, key
, value
);
369 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
371 CFArrayRef notify
= keys
->notify
;
372 CFArrayRef remove
= keys
->remove
;
373 CFDictionaryRef set
= keys
->set
;
376 if (CFArrayGetCount(notify
) == 0) {
379 if (CFArrayGetCount(remove
) == 0) {
382 if (CFDictionaryGetCount(set
) == 0) {
385 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
388 if (S_IPMonitor_debug
& kDebugFlag1
) {
390 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: Setting:\n%@"),
393 if (remove
!= NULL
) {
394 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: Removing:\n%@"),
397 if (notify
!= NULL
) {
398 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: Notifying:\n%@"),
402 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
404 status
= notify_post("com.apple.system.config.network_change");
405 if (status
== NOTIFY_STATUS_OK
) {
406 SCLog(TRUE
, LOG_NOTICE
, CFSTR("network configuration changed."));
408 SCLog(TRUE
, LOG_NOTICE
,
409 CFSTR("IPMonitor: notify_post() failed: error=%ld"), status
);
423 mib
[1] = KERN_NETBOOT
;
424 len
= sizeof(netboot
);
425 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
429 static __inline__
int
432 return (socket(AF_INET6
, SOCK_DGRAM
, 0));
437 siocdradd_in6(int s
, int if_index
, const struct in6_addr
* addr
, u_char flags
)
439 struct in6_defrouter dr
;
440 struct sockaddr_in6
* sin6
;
442 bzero(&dr
, sizeof(dr
));
444 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
445 sin6
->sin6_family
= AF_INET6
;
446 sin6
->sin6_addr
= *addr
;
448 dr
.if_index
= if_index
;
449 return (ioctl(s
, SIOCDRADD_IN6
, &dr
));
453 siocdrdel_in6(int s
, int if_index
, const struct in6_addr
* addr
)
455 struct in6_defrouter dr
;
456 struct sockaddr_in6
* sin6
;
458 bzero(&dr
, sizeof(dr
));
460 sin6
->sin6_len
= sizeof(struct sockaddr_in6
);
461 sin6
->sin6_family
= AF_INET6
;
462 sin6
->sin6_addr
= *addr
;
463 dr
.if_index
= if_index
;
464 return (ioctl(s
, SIOCDRDEL_IN6
, &dr
));
466 #endif /* SIOCDRADD_IN6 */
470 S_is_scoped_routing_enabled()
473 size_t len
= sizeof(scopedroute
);
475 if ((sysctlbyname("net.inet.ip.scopedroute",
478 && (errno
!= ENOENT
)) {
479 SCLog(TRUE
, LOG_ERR
, CFSTR("sysctlbyname() failed: %s"), strerror(errno
));
481 return (scopedroute
);
485 S_is_scoped_v6_routing_enabled()
487 int scopedroute_v6
= 0;
488 size_t len
= sizeof(scopedroute_v6
);
490 if ((sysctlbyname("net.inet6.ip6.scopedroute",
491 &scopedroute_v6
, &len
,
493 && (errno
!= ENOENT
)) {
494 SCLog(TRUE
, LOG_ERR
, CFSTR("sysctlbyname() failed: %s"), strerror(errno
));
496 return (scopedroute_v6
);
498 #endif /* RTF_IFSCOPE */
501 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
503 CFIndex n
= CFArrayGetCount(arr
);
505 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
508 CFArrayAppendValue(arr
, new);
513 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
517 i
= CFArrayGetFirstIndexOfValue(arr
,
518 CFRangeMake(0, CFArrayGetCount(arr
)),
520 if (i
!= kCFNotFound
) {
521 CFArrayRemoveValueAtIndex(arr
, i
);
527 my_CFRelease(void * t
)
529 void * * obj
= (void * *)t
;
538 static CFDictionaryRef
539 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
541 if (isA_CFDictionary(dict
) == NULL
) {
544 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
548 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
550 if (isA_CFDictionary(dict
) == NULL
) {
553 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
557 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, int addr_size
)
561 if (isA_CFString(str
) == NULL
) {
567 if (addr_size
< sizeof(struct in_addr
)) {
572 if (addr_size
< sizeof(struct in6_addr
)) {
579 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
580 if (inet_pton(family
, buf
, addr
) == 1) {
584 bzero(addr
, addr_size
);
589 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
591 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
595 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
597 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
601 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
603 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
604 kSCDynamicStoreDomainSetup
,
610 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
612 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
613 kSCDynamicStoreDomainState
,
618 static CFDictionaryRef
619 get_service_setup_entity(CFDictionaryRef service_info
, CFStringRef serviceID
,
622 CFStringRef setup_key
;
623 CFDictionaryRef setup_dict
;
625 setup_key
= setup_service_key(serviceID
, entity
);
626 setup_dict
= my_CFDictionaryGetDictionary(service_info
, setup_key
);
627 my_CFRelease(&setup_key
);
631 static CFDictionaryRef
632 get_service_state_entity(CFDictionaryRef service_info
, CFStringRef serviceID
,
635 CFStringRef state_key
;
636 CFDictionaryRef state_dict
;
638 state_key
= state_service_key(serviceID
, entity
);
639 state_dict
= my_CFDictionaryGetDictionary(service_info
, state_key
);
640 my_CFRelease(&state_key
);
645 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
649 ip_list
= CFDictionaryGetValue(dict
, prop
);
650 if (isA_CFArray(ip_list
) != NULL
651 && CFArrayGetCount(ip_list
) > 0
652 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
659 get_override_primary(CFDictionaryRef dict
)
663 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
664 if (isA_CFNumber(override
) != NULL
) {
667 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
672 else if (isA_CFBoolean(override
) != NULL
) {
673 if (CFBooleanGetValue(override
)) {
684 static __inline__
struct in_addr
685 subnet_addr(struct in_addr addr
, struct in_addr mask
)
689 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
693 static __inline__
int
694 uint32_cmp(uint32_t a
, uint32_t b
)
710 static __inline__
int
711 in_addr_cmp(struct in_addr a
, struct in_addr b
)
713 return (uint32_cmp(ntohl(a
.s_addr
), ntohl(b
.s_addr
)));
716 static __inline__
int
717 RankCompare(Rank a
, Rank b
)
719 return (uint32_cmp(a
, b
));
722 static __inline__
int
723 RouteFlagsCompare(RouteFlags a
, RouteFlags b
)
725 return (uint32_cmp(a
, b
));
729 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
731 CFStringAppendFormat(str
, NULL
,
732 CFSTR("Dest " IP_FORMAT
735 " Ifp %s Ifa " IP_FORMAT
),
738 IP_LIST(&r
->gateway
),
739 (r
->ifname
[0] != '\0') ? r
->ifname
: "<none>",
741 if ((r
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
742 CFStringAppend(str
, CFSTR(" [non-local]"));
744 else if ((r
->flags
& kRouteIsDirectToInterfaceFlag
) != 0) {
745 CFStringAppend(str
, CFSTR(" [direct]"));
747 if ((r
->flags
& kRouteChooseFirstFlag
) != 0) {
748 CFStringAppend(str
, CFSTR(" [first]"));
750 if ((r
->flags
& kRouteChooseLastFlag
) != 0) {
751 CFStringAppend(str
, CFSTR(" [last]"));
753 if ((r
->flags
& kRouteChooseNeverFlag
) != 0) {
754 CFStringAppend(str
, CFSTR(" [never]"));
756 if ((r
->flags
& kRouteIsScopedFlag
) != 0) {
757 CFStringAppend(str
, CFSTR(" [SCOPED]"));
759 else if ((r
->flags
& kRouteWantScopedFlag
) != 0) {
760 CFStringAppend(str
, CFSTR(" [SCOPED*]"));
766 IPv4RouteCopyDescription(IPv4RouteRef r
)
768 CFMutableStringRef str
;
770 str
= CFStringCreateMutable(NULL
, 0);
771 IPv4RouteCopyDescriptionWithString(r
, str
);
775 static __inline__
void
776 IPv4RoutePrint(IPv4RouteRef route
)
778 CFStringRef str
= IPv4RouteCopyDescription(route
);
780 SCPrint(TRUE
, stdout
, CFSTR("%@"), str
);
785 static __inline__
void
786 IPv4RouteLog(IPv4RouteRef route
)
788 CFStringRef str
= IPv4RouteCopyDescription(route
);
790 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@"), str
);
796 IPv4RouteCompare(IPv4RouteRef a
, Rank a_rank
,
797 IPv4RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
802 cmp
= in_addr_cmp(a
->dest
, b
->dest
);
804 cmp
= in_addr_cmp(a
->mask
, b
->mask
);
806 int name_cmp
= strcmp(a
->ifname
, b
->ifname
);
812 boolean_t a_never
= (a
->flags
& kRouteChooseNeverFlag
) != 0;
813 boolean_t b_never
= (b
->flags
& kRouteChooseNeverFlag
) != 0;
816 if (a_never
!= b_never
) {
825 boolean_t a_last
= (a
->flags
& kRouteChooseLastFlag
) != 0;
826 boolean_t b_last
= (b
->flags
& kRouteChooseLastFlag
) != 0;
828 if (a_last
!= b_last
) {
837 boolean_t a_first
= (a
->flags
& kRouteChooseFirstFlag
) != 0;
838 boolean_t b_first
= (b
->flags
& kRouteChooseFirstFlag
) != 0;
840 if (a_first
!= b_first
) {
849 cmp
= RankCompare(a_rank
, b_rank
);
859 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
873 a_str
= IPv4RouteCopyDescription(a
);
874 b_str
= IPv4RouteCopyDescription(b
);
875 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@ rank %u %c %@ rank %u"),
876 a_str
, a_rank
, ch
, b_str
, b_rank
);
884 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
888 CFMutableStringRef str
;
890 str
= CFStringCreateMutable(NULL
, 0);
891 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
893 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
894 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
895 IPv4RouteCopyDescriptionWithString(r
, str
);
897 CFStringAppend(str
, CFSTR("\n}\n"));
901 static __inline__
void
902 IPv4RouteListPrint(IPv4RouteListRef routes
)
904 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
906 SCPrint(TRUE
, stdout
, CFSTR("%@"), str
);
911 static __inline__
void
912 IPv4RouteListLog(IPv4RouteListRef routes
)
914 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
916 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@"), str
);
921 static __inline__
unsigned int
922 IPv4RouteListComputeSize(unsigned int n
)
924 return (offsetof(IPv4RouteList
, list
[n
]));
928 IPv4RouteListFindRoute(IPv4RouteListRef routes
, IPv4RouteRef route
)
931 IPv4RouteRef scan_result
= NULL
;
934 for (i
= 0, scan
= routes
->list
; i
< routes
->count
; i
++, scan
++) {
935 if ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
936 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
937 && (strcmp(scan
->ifname
, route
->ifname
) == 0)
938 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
939 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)) {
941 * So far, the routes look the same. If the flags
942 * are also equiv than we've found a match.
947 s_flags
= scan
->flags
;
948 if ((s_flags
& kRouteWantScopedFlag
) != 0) {
949 s_flags
|= kRouteWantScopedFlag
;
951 r_flags
= route
->flags
;
952 if ((r_flags
& kRouteWantScopedFlag
) != 0) {
953 r_flags
|= kRouteWantScopedFlag
;
955 if (s_flags
== r_flags
) {
961 return (scan_result
);
965 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
966 IPv4RouteListApplyCallBackFuncPtr func
, void * arg
)
971 if (old_routes
== new_routes
&& old_routes
== NULL
) {
972 /* both old and new are NULL, so there's nothing to do */
975 if (old_routes
!= NULL
) {
976 for (i
= 0, scan
= old_routes
->list
;
977 i
< old_routes
->count
;
979 IPv4RouteRef new_route
= NULL
;
981 if (new_routes
!= NULL
) {
982 new_route
= IPv4RouteListFindRoute(new_routes
, scan
);
984 if (new_route
== NULL
) {
986 (*func
)(kIPv4RouteListRemoveRouteCommand
, scan
, arg
);
991 if (new_routes
!= NULL
) {
992 for (i
= 0, scan
= new_routes
->list
;
993 i
< new_routes
->count
;
995 IPv4RouteRef old_route
= NULL
;
997 if (old_routes
!= NULL
) {
998 old_route
= IPv4RouteListFindRoute(old_routes
, scan
);
1000 if (old_route
== NULL
) {
1002 (*func
)(kIPv4RouteListAddRouteCommand
, scan
, arg
);
1011 * Function: IPv4RouteListAddRoute
1014 * Add the given IPv4Route to the list of routes, eliminating lower-ranked
1015 * duplicates on the same interface, and marking any lower ranked duplicates
1016 * on other interfaces with kRouteIsScopedFlag.
1018 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1021 * Route list updated with the given route, possibly a different pointer,
1022 * due to using realloc'd memory.
1031 static IPv4RouteListRef
1032 IPv4RouteListAddRoute(IPv4RouteListRef routes
, int init_size
,
1033 IPv4RouteRef this_route
, Rank this_rank
)
1036 int scope_which
= kScopeNone
;
1040 if (routes
== NULL
) {
1041 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(init_size
));
1042 routes
->size
= init_size
;
1045 for (i
= 0, scan
= routes
->list
; i
< routes
->count
;
1048 boolean_t same_dest
;
1050 cmp
= IPv4RouteCompare(this_route
, this_rank
, scan
, scan
->rank
, &same_dest
);
1053 if (same_dest
== TRUE
) {
1054 if ((scan
->flags
& kRouteIsScopedFlag
) != 0) {
1055 ROUTELIST_DEBUG(("Hit 1: set scope on self\n"),
1057 scope_which
= kScopeThis
;
1060 ROUTELIST_DEBUG(("Hit 2: set scope on next\n"),
1062 scope_which
= kScopeNext
;
1065 /* remember our insertion point, but keep going to find a dup */
1069 else if (cmp
== 0) {
1072 /* this route is a duplicate */
1073 ROUTELIST_DEBUG(("Hit 3: removing [%d]\n", i
), kDebugFlag8
);
1075 if (i
== routes
->count
) {
1076 /* last slot, decrementing gets rid of it */
1079 bcopy(routes
->list
+ i
+ 1,
1081 sizeof(routes
->list
[0]) * (routes
->count
- i
));
1085 /* resolve conflict using rank */
1086 if (this_rank
< scan
->rank
) {
1087 boolean_t is_scoped
= FALSE
;
1089 if (scan
->flags
& kRouteIsScopedFlag
) {
1092 ROUTELIST_DEBUG(("Hit 4:replacing [%d] rank %u < %u\n", i
,
1094 scan
->rank
), kDebugFlag8
);
1095 *scan
= *this_route
;
1096 scan
->rank
= this_rank
;
1098 /* preserve whether route was scoped */
1099 ROUTELIST_DEBUG(("Hit 5: preserved scope\n"), kDebugFlag8
);
1100 scan
->flags
|= kRouteIsScopedFlag
;
1107 if (same_dest
== TRUE
) {
1108 if (scope_which
== kScopeNone
) {
1109 ROUTELIST_DEBUG(("Hit 10: set scope on self\n"),
1111 scope_which
= kScopeThis
;
1114 #ifdef TEST_IPV4_ROUTELIST
1115 else if (where
!= -1) {
1116 /* not possible because we maintain a sorted list */
1117 ROUTELIST_DEBUG(("Hit 11: moved past routes - can't happen\n"),
1121 #endif /* TEST_IPV4_ROUTELIST */
1124 if (routes
->size
== routes
->count
) {
1126 IPv4RouteListRef new_routes
;
1129 /* double the size */
1130 old_size
= routes
->size
;
1131 how_many
= old_size
* 2;
1132 new_routes
= (IPv4RouteListRef
)
1133 realloc(routes
, IPv4RouteListComputeSize(how_many
));
1134 if (new_routes
== NULL
) {
1138 ROUTELIST_DEBUG(("increasing size from %d to %d\n", old_size
,
1139 how_many
), kDebugFlag8
);
1140 new_routes
->size
= how_many
;
1141 routes
= new_routes
;
1144 /* add it to the end */
1145 where
= routes
->count
;
1148 /* insert it at [where] */
1149 bcopy(routes
->list
+ where
,
1150 routes
->list
+ where
+ 1,
1151 sizeof(routes
->list
[0]) * (routes
->count
- where
));
1153 /* copy the route */
1154 routes
->list
[where
] = *this_route
;
1155 routes
->list
[where
].rank
= this_rank
;
1158 switch (scope_which
) {
1160 routes
->list
[where
].flags
|= kRouteIsScopedFlag
;
1163 routes
->list
[where
+ 1].flags
|= kRouteIsScopedFlag
;
1175 * Function: IPv4RouteListAddRouteList
1178 * Invoke IPv4RouteListAddRoute for each route in the given list.
1181 * See IPv4RouteListAddRoute for more information.
1183 static IPv4RouteListRef
1184 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
1185 IPv4RouteListRef service_routes
, Rank rank
)
1190 for (i
= 0, scan
= service_routes
->list
;
1191 i
< service_routes
->count
; i
++, scan
++) {
1192 routes
= IPv4RouteListAddRoute(routes
, init_size
, scan
, rank
);
1198 plist_get_cstring(CFDictionaryRef dict
, CFStringRef prop_name
,
1199 char * buf
, int buf_size
)
1203 val
= CFDictionaryGetValue(dict
, prop_name
);
1204 if (isA_CFString(val
) == NULL
) {
1207 if (CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingASCII
)
1215 * Function: IPv4RouteListCreateWithDictionary
1218 * Given the service ipv4 entity dictionary, generate the list of routes.
1219 * Currently, this includes just the default route and subnet route,
1220 * if the service has a subnet mask.
1223 * If the passed in route_list is NULL or too small, this routine
1224 * allocates malloc'd memory to hold the routes.
1226 static IPv4RouteListRef
1227 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
1228 CFDictionaryRef dict
,
1229 CFStringRef primaryRank
)
1231 struct in_addr addr
= { 0 };
1232 RouteFlags flags
= 0;
1233 unsigned int ifindex
;
1235 struct in_addr mask
= { 0 };
1237 boolean_t add_default
= FALSE
;
1238 boolean_t add_subnet
= FALSE
;
1240 struct in_addr subnet
= { 0 };
1241 struct in_addr router
= { 0 };
1246 if (plist_get_cstring(dict
, kSCPropInterfaceName
, ifn
, sizeof(ifn
))
1250 #ifdef TEST_IPV4_ROUTELIST
1252 #else /* TEST_IPV4_ROUTELIST */
1253 ifindex
= if_nametoindex(ifn
);
1255 /* interface doesn't exist */
1258 #endif /* TEST_IPV4_ROUTELIST */
1259 if (cfstring_to_ip(CFDictionaryGetValue(dict
, kSCPropNetIPv4Router
),
1261 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
1263 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
1264 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
1266 subnet
= subnet_addr(addr
, mask
);
1267 /* ignore link-local subnets, let IPConfiguration handle them for now */
1268 if (ntohl(subnet
.s_addr
) != IN_LINKLOCALNETNUM
) {
1273 if (addr
.s_addr
== 0) {
1274 /* thanks for playing */
1277 if (router
.s_addr
== 0) {
1278 flags
|= kRouteIsDirectToInterfaceFlag
| kRouteChooseLastFlag
;
1282 * If the router address is our address and the subnet mask is
1283 * not 255.255.255.255, assume all routes are local to the interface.
1285 if (addr
.s_addr
== router
.s_addr
1286 && mask
.s_addr
!= INADDR_BROADCAST
) {
1287 flags
|= kRouteIsDirectToInterfaceFlag
;
1289 if (primaryRank
!= NULL
) {
1290 if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankNever
)) {
1291 flags
|= kRouteChooseNeverFlag
;
1292 } else if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankFirst
)) {
1293 flags
|= kRouteChooseFirstFlag
;
1294 } else if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankLast
)) {
1295 flags
|= kRouteChooseLastFlag
;
1297 } else if (get_override_primary(dict
)) {
1298 flags
|= kRouteChooseFirstFlag
;
1301 if (add_subnet
&& (flags
& kRouteIsDirectToInterfaceFlag
) == 0
1302 && subnet
.s_addr
!= subnet_addr(router
, mask
).s_addr
) {
1303 flags
|= kRouteIsNotSubnetLocalFlag
;
1306 if (strncmp(ifn
, "lo0", sizeof(ifn
)) != 0) {
1311 if (routes
== NULL
|| routes
->size
< n
) {
1312 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
1315 bzero(routes
, IPv4RouteListComputeSize(n
));
1318 /* start at the beginning */
1322 /* add the default route */
1323 r
->ifindex
= ifindex
;
1324 strlcpy(r
->ifname
, ifn
, sizeof(r
->ifname
));
1327 if ((flags
& kRouteIsDirectToInterfaceFlag
) == 0) {
1328 r
->gateway
= router
;
1336 /* add the subnet route */
1338 r
->ifindex
= ifindex
;
1342 strlcpy(r
->ifname
, ifn
, sizeof(r
->ifname
));
1344 r
->flags
= flags
& (kRouteChooseFirstFlag
|kRouteChooseLastFlag
|kRouteChooseNeverFlag
);
1351 * Function: parse_component
1353 * Given a string 'key' and a string prefix 'prefix',
1354 * return the next component in the slash '/' separated
1358 * 1. key = "a/b/c" prefix = "a/"
1360 * 2. key = "a/b/c" prefix = "a/b/"
1364 parse_component(CFStringRef key
, CFStringRef prefix
)
1366 CFMutableStringRef comp
;
1369 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
1372 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
1376 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
1377 range
= CFStringFind(comp
, CFSTR("/"), 0);
1378 if (range
.location
== kCFNotFound
) {
1381 range
.length
= CFStringGetLength(comp
) - range
.location
;
1382 CFStringDelete(comp
, range
);
1386 static CFMutableDictionaryRef
1387 service_dict_copy(CFStringRef serviceID
)
1389 CFDictionaryRef d
= NULL
;
1390 CFMutableDictionaryRef service_dict
;
1392 /* create a modifyable dictionary, a copy or a new one */
1393 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
1396 = CFDictionaryCreateMutable(NULL
, 0,
1397 &kCFTypeDictionaryKeyCallBacks
,
1398 &kCFTypeDictionaryValueCallBacks
);
1401 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
1403 return (service_dict
);
1407 dump_service_entity(CFStringRef serviceID
, CFStringRef entity
,
1408 CFStringRef operation
, CFTypeRef val
)
1410 CFStringRef this_val
= NULL
;
1412 if (isA_CFData(val
) && CFEqual(entity
, kSCEntNetIPv4
)) {
1413 this_val
= IPv4RouteListCopyDescription((IPv4RouteListRef
)
1414 CFDataGetBytePtr(val
));
1415 if (this_val
!= NULL
) {
1420 val
= CFSTR("<none>");
1422 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: serviceID %@ %@ %@ value = %@"),
1423 serviceID
, operation
, entity
, val
);
1424 my_CFRelease(&this_val
);
1429 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
1432 boolean_t changed
= FALSE
;
1434 CFMutableDictionaryRef service_dict
;
1436 service_dict
= service_dict_copy(serviceID
);
1437 old_val
= CFDictionaryGetValue(service_dict
, entity
);
1438 if (new_val
== NULL
) {
1439 if (old_val
!= NULL
) {
1440 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1441 dump_service_entity(serviceID
, entity
, CFSTR("Removed:"),
1444 CFDictionaryRemoveValue(service_dict
, entity
);
1449 if (old_val
== NULL
|| CFEqual(new_val
, old_val
) == FALSE
) {
1450 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1451 dump_service_entity(serviceID
, entity
,
1452 CFSTR("Changed: old"), old_val
);
1453 dump_service_entity(serviceID
, entity
,
1454 CFSTR("Changed: new"), new_val
);
1456 CFDictionarySetValue(service_dict
, entity
, new_val
);
1460 if (CFDictionaryGetCount(service_dict
) == 0) {
1461 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
1464 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
1466 my_CFRelease(&service_dict
);
1470 static CFDictionaryRef
1471 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
1473 CFDictionaryRef service_dict
;
1475 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
1476 if (service_dict
== NULL
) {
1479 return (CFDictionaryGetValue(service_dict
, entity
));
1483 ipv6_service_dict_set(CFStringRef serviceID
, CFDictionaryRef new_val
)
1485 #ifdef SIOCDRADD_IN6
1488 CFStringRef new_router
= NULL
;
1489 char ntopbuf
[INET6_ADDRSTRLEN
];
1490 CFDictionaryRef old_val
= NULL
;
1491 CFStringRef old_router
= NULL
;
1492 struct in6_addr router_ip
;
1496 old_val
= service_dict_get(serviceID
, kSCEntNetIPv6
);
1497 if (old_val
!= NULL
) {
1498 plist_get_cstring(old_val
, kSCPropInterfaceName
, ifn
, sizeof(ifn
));
1499 old_router
= CFDictionaryGetValue(old_val
, kSCPropNetIPv6Router
);
1501 if (ifn
[0] == '\0') {
1503 || plist_get_cstring(new_val
, kSCPropInterfaceName
,
1504 ifn
, sizeof(ifn
)) == FALSE
) {
1505 /* no InterfaceName property, ignore it */
1509 if_index
= if_nametoindex(ifn
);
1510 if (if_index
== 0) {
1513 s
= inet6_dgram_socket();
1516 "IPMonitor: ipv6_service_dict_set: socket failed, %s",
1520 if (new_val
!= NULL
) {
1521 new_router
= CFDictionaryGetValue(new_val
, kSCPropNetIPv6Router
);
1523 if (old_router
!= NULL
1524 && (new_router
== NULL
|| CFEqual(old_router
, new_router
) == FALSE
)) {
1525 /* remove the old Router */
1526 if (cfstring_to_ip6(old_router
, &router_ip
)) {
1527 if (IN6_IS_ADDR_LINKLOCAL(&router_ip
)) {
1529 router_ip
.__u6_addr
.__u6_addr16
[1] = htons(if_index
);
1531 if (siocdrdel_in6(s
, if_index
, &router_ip
) < 0) {
1532 if (errno
!= EINVAL
) {
1534 "IPMonitor: siocdrdel_in6(%s, %s) failed, %s",
1536 inet_ntop(AF_INET6
, &router_ip
,
1537 ntopbuf
, sizeof(ntopbuf
)),
1541 else if (S_IPMonitor_debug
& kDebugFlag1
) {
1543 "IPMonitor: %s removed default route %s",
1545 inet_ntop(AF_INET6
, &router_ip
,
1546 ntopbuf
, sizeof(ntopbuf
)));
1550 /* add the new Router */
1551 if (cfstring_to_ip6(new_router
, &router_ip
)) {
1552 if (IN6_IS_ADDR_LINKLOCAL(&router_ip
)) {
1554 router_ip
.__u6_addr
.__u6_addr16
[1] = htons(if_index
);
1556 if (siocdradd_in6(s
, if_index
, &router_ip
, 0) < 0) {
1557 if (errno
!= EINVAL
) {
1559 "IPMonitor: siocdradd_in6(%s, %s) failed, %s",
1561 inet_ntop(AF_INET6
, &router_ip
,
1562 ntopbuf
, sizeof(ntopbuf
)),
1566 else if (S_IPMonitor_debug
& kDebugFlag1
) {
1568 "IPMonitor: %s added default route %s",
1570 inet_ntop(AF_INET6
, &router_ip
,
1571 ntopbuf
, sizeof(ntopbuf
)));
1577 #endif /* SIOCDRADD_IN6 */
1578 return (service_dict_set(serviceID
, kSCEntNetIPv6
, new_val
));
1581 #define ALLOW_EMPTY_STRING 0x1
1584 sanitize_prop(CFTypeRef val
, uint32_t flags
)
1587 if (isA_CFString(val
)) {
1588 CFMutableStringRef str
;
1590 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
1591 CFStringTrimWhitespace(str
);
1592 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
1606 merge_array_prop(CFMutableDictionaryRef dict
,
1608 CFDictionaryRef state_dict
,
1609 CFDictionaryRef setup_dict
,
1613 CFMutableArrayRef merge_prop
;
1614 CFArrayRef setup_prop
= NULL
;
1615 CFArrayRef state_prop
= NULL
;
1617 if (setup_dict
!= NULL
) {
1618 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
1620 if (state_dict
!= NULL
) {
1621 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
1624 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
1628 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1629 if (setup_prop
!= NULL
) {
1633 n
= CFArrayGetCount(setup_prop
);
1634 for (i
= 0; i
< n
; i
++) {
1637 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
1638 val
= sanitize_prop(val
, flags
);
1640 CFArrayAppendValue(merge_prop
, val
);
1645 if (state_prop
!= NULL
1646 && (setup_prop
== NULL
|| S_append_state
)) {
1649 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
1651 n
= CFArrayGetCount(state_prop
);
1652 for (i
= 0; i
< n
; i
++) {
1655 val
= CFArrayGetValueAtIndex(state_prop
, i
);
1656 val
= sanitize_prop(val
, flags
);
1658 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
1659 CFArrayAppendValue(merge_prop
, val
);
1665 if (CFArrayGetCount(merge_prop
) > 0) {
1666 CFDictionarySetValue(dict
, key
, merge_prop
);
1668 CFRelease(merge_prop
);
1673 pick_prop(CFMutableDictionaryRef dict
,
1675 CFDictionaryRef state_dict
,
1676 CFDictionaryRef setup_dict
,
1679 CFTypeRef val
= NULL
;
1681 if (setup_dict
!= NULL
) {
1682 val
= CFDictionaryGetValue(setup_dict
, key
);
1683 val
= sanitize_prop(val
, flags
);
1685 if (val
== NULL
&& state_dict
!= NULL
) {
1686 val
= CFDictionaryGetValue(state_dict
, key
);
1687 val
= sanitize_prop(val
, flags
);
1690 CFDictionarySetValue(dict
, key
, val
);
1698 ** GetEntityChangesFunc functions
1701 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
1702 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
1704 boolean_t changed
= FALSE
;
1705 CFMutableDictionaryRef dict
= NULL
;
1706 CFStringRef primaryRank
= NULL
;
1709 IPv4RouteListRef routes
;
1710 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
1711 CFDataRef routes_data
= NULL
;
1712 CFDictionaryRef service_options
;
1714 if (state_dict
== NULL
) {
1717 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
1718 if (service_options
!= NULL
) {
1719 primaryRank
= CFDictionaryGetValue(service_options
, kSCPropNetServicePrimaryRank
);
1721 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
1722 if (setup_dict
!= NULL
) {
1724 struct in_addr router_ip
;
1726 router
= CFDictionaryGetValue(setup_dict
,
1727 kSCPropNetIPv4Router
);
1729 && cfstring_to_ip(router
, &router_ip
)) {
1730 CFDictionarySetValue(dict
,
1731 kSCPropNetIPv4Router
,
1735 routes
= (IPv4RouteListRef
)routes_buf
;
1736 routes
->size
= R_STATIC
;
1738 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, primaryRank
);
1740 routes_data
= CFDataCreate(NULL
,
1742 IPv4RouteListComputeSize(r
->count
));
1748 SCLog(TRUE
, LOG_NOTICE
,
1749 CFSTR("IPMonitor: %@ invalid IPv4 dictionary = %@"),
1754 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, routes_data
);
1755 if (routes_data
== NULL
) {
1756 /* clean up the rank too */
1757 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
1759 my_CFRelease(&dict
);
1760 my_CFRelease(&routes_data
);
1765 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
1766 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
1768 struct in6_addr addr
;
1770 boolean_t changed
= FALSE
;
1771 CFMutableDictionaryRef dict
= NULL
;
1772 CFDictionaryRef new_dict
= NULL
;
1773 CFStringRef router
= NULL
;
1774 struct in6_addr router_ip
;
1775 boolean_t valid_ip
= FALSE
;
1777 if (state_dict
== NULL
) {
1780 addrs
= isA_CFArray(CFDictionaryGetValue(state_dict
,
1781 kSCPropNetIPv6Addresses
));
1782 if (addrs
!= NULL
&& CFArrayGetCount(addrs
) > 0) {
1783 valid_ip
= cfstring_to_ip6(CFArrayGetValueAtIndex(addrs
, 0), &addr
);
1785 if (valid_ip
== FALSE
) {
1786 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1787 SCLog(TRUE
, LOG_NOTICE
,
1788 CFSTR("IPMonitor: %@ has no valid IPv6 address, ignoring"),
1793 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
1794 if (setup_dict
!= NULL
) {
1795 router
= CFDictionaryGetValue(setup_dict
,
1796 kSCPropNetIPv6Router
);
1797 if (router
!= NULL
&& cfstring_to_ip6(router
, &router_ip
)) {
1798 CFDictionarySetValue(dict
,
1799 kSCPropNetIPv6Router
,
1804 router
= CFDictionaryGetValue(dict
,
1805 kSCPropNetIPv6Router
);
1807 && cfstring_to_ip6(router
, &router_ip
) == FALSE
) {
1808 CFDictionaryRemoveValue(dict
, kSCPropNetIPv6Router
);
1814 changed
= ipv6_service_dict_set(serviceID
, new_dict
);
1815 if (new_dict
== NULL
) {
1816 /* clean up the rank too */
1817 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
1819 my_CFRelease(&new_dict
);
1824 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
1825 CFMutableArrayRef out_servers
)
1830 count
= CFArrayGetCount(in_servers
);
1831 for (i
= 0; i
< count
; i
++) {
1832 CFStringRef addr
= CFArrayGetValueAtIndex(in_servers
, i
);
1833 struct in6_addr ipv6_addr
;
1834 struct in_addr ip_addr
;
1836 if (cfstring_to_ip(addr
, &ip_addr
)) {
1838 if ((active_protos
& kProtocolFlagsIPv4
) == 0
1839 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
1840 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1842 "IPMonitor: no IPv4 connectivity, "
1843 "ignoring DNS server address " IP_FORMAT
,
1849 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
1851 if ((active_protos
& kProtocolFlagsIPv6
) == 0
1852 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
1853 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1854 char ntopbuf
[INET6_ADDRSTRLEN
];
1857 "IPMonitor: no IPv6 connectivity, "
1858 "ignoring DNS server address %s",
1859 inet_ntop(AF_INET6
, &ipv6_addr
,
1860 ntopbuf
, sizeof(ntopbuf
)));
1866 /* bad IP address */
1867 SCLog(TRUE
, LOG_NOTICE
,
1868 CFSTR("IPMonitor: ignoring bad DNS server address '%@'"),
1872 /* DNS server is valid and one we want */
1873 CFArrayAppendValue(out_servers
, addr
);
1879 merge_dns_servers(CFMutableDictionaryRef new_dict
,
1880 CFArrayRef state_servers
,
1881 CFArrayRef setup_servers
,
1882 ProtocolFlags active_protos
)
1884 CFMutableArrayRef dns_servers
;
1886 if (state_servers
== NULL
&& setup_servers
== NULL
) {
1887 /* no DNS servers */
1890 dns_servers
= CFArrayCreateMutable(NULL
, 0,
1891 &kCFTypeArrayCallBacks
);
1892 if (setup_servers
!= NULL
) {
1893 accumulate_dns_servers(setup_servers
, active_protos
,
1896 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
1897 && state_servers
!= NULL
) {
1898 accumulate_dns_servers(state_servers
, active_protos
,
1901 if (CFArrayGetCount(dns_servers
) != 0) {
1902 CFDictionarySetValue(new_dict
,
1903 kSCPropNetDNSServerAddresses
, dns_servers
);
1905 my_CFRelease(&dns_servers
);
1911 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
1912 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
1914 ProtocolFlags active_protos
= kProtocolFlagsNone
;
1915 boolean_t changed
= FALSE
;
1917 CFStringRef interface
= NULL
;
1919 CFDictionaryRef ipv6
;
1926 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
1927 { kSCPropNetDNSSortList
, 0, FALSE
},
1928 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
1929 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
1931 CFMutableDictionaryRef new_dict
= NULL
;
1932 CFStringRef pick_list
[] = {
1933 kSCPropNetDNSDomainName
,
1934 kSCPropNetDNSOptions
,
1935 kSCPropNetDNSSearchOrder
,
1936 kSCPropNetDNSServerPort
,
1937 kSCPropNetDNSServerTimeout
,
1940 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
1941 /* there is no DNS content */
1945 ipv4
= (CFDataRef
)service_dict_get(serviceID
, kSCEntNetIPv4
);
1947 IPv4RouteListRef routes
;
1949 active_protos
|= kProtocolFlagsIPv4
;
1951 routes
= (IPv4RouteListRef
)CFDataGetBytePtr(ipv4
);
1952 if (routes
->count
> 0) {
1953 interface
= CFStringCreateWithCString(NULL
,
1954 routes
->list
[0].ifname
,
1955 kCFStringEncodingASCII
);
1959 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
1961 active_protos
|= kProtocolFlagsIPv6
;
1963 if ((interface
== NULL
) &&
1964 CFDictionaryGetValueIfPresent(ipv6
,
1965 kSCPropInterfaceName
,
1966 (const void **)&interface
)) {
1967 CFRetain(interface
);
1971 if (active_protos
== kProtocolFlagsNone
) {
1972 /* there is no IPv4 nor IPv6 */
1973 if (state_dict
== NULL
) {
1974 /* ... and no DNS content that we care about */
1980 /* merge DNS configuration */
1981 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
1982 &kCFTypeDictionaryKeyCallBacks
,
1983 &kCFTypeDictionaryValueCallBacks
);
1985 if (active_protos
== kProtocolFlagsNone
) {
1986 merge_dns_servers(new_dict
,
1987 my_CFDictionaryGetArray(state_dict
,
1988 kSCPropNetDNSServerAddresses
),
1990 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
);
1993 merge_dns_servers(new_dict
,
1994 my_CFDictionaryGetArray(state_dict
,
1995 kSCPropNetDNSServerAddresses
),
1996 my_CFDictionaryGetArray(setup_dict
,
1997 kSCPropNetDNSServerAddresses
),
2001 for (i
= 0; i
< sizeof(merge_list
)/sizeof(merge_list
[0]); i
++) {
2002 merge_array_prop(new_dict
,
2006 merge_list
[i
].flags
,
2007 merge_list
[i
].append
);
2010 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2018 if (active_protos
== kProtocolFlagsNone
) {
2019 /* there is no IPv4 nor IPv6, only supplemental DNS */
2020 if (CFDictionaryContainsKey(new_dict
,
2021 kSCPropNetDNSSupplementalMatchDomains
)) {
2022 /* only keep State: supplemental */
2023 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
2024 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
2025 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
2026 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
2032 if (CFDictionaryGetCount(new_dict
) == 0) {
2033 my_CFRelease(&new_dict
);
2037 if (interface
!= NULL
) {
2038 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
2041 if (S_append_state
) {
2043 * ensure any specified domain name (e.g. the domain returned by
2044 * a DHCP server) is in the search list.
2046 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
2047 if (isA_CFString(domain
)) {
2050 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
2051 if (isA_CFArray(search
) &&
2052 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
2053 CFMutableArrayRef new_search
;
2055 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
2056 CFArrayAppendValue(new_search
, domain
);
2057 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
2058 my_CFRelease(&new_search
);
2064 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
2065 my_CFRelease(&new_dict
);
2066 my_CFRelease(&interface
);
2071 merge_dict(const void *key
, const void *value
, void *context
)
2073 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
2075 CFDictionarySetValue(dict
, key
, value
);
2079 #define PROXY_AUTO_DISCOVERY_URL 252
2082 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
2084 CFStringRef urlString
= NULL
;
2086 if (isA_CFDictionary(dhcp_options
)) {
2089 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
2092 const UInt8
*urlBytes
;
2095 urlBytes
= CFDataGetBytePtr(data
);
2096 urlLen
= CFDataGetLength(data
);
2097 while ((urlLen
> 0) && (urlBytes
[urlLen
- 1] == 0)) {
2098 // remove trailing NUL
2106 url
= CFURLCreateWithBytes(NULL
, urlBytes
, urlLen
, kCFStringEncodingUTF8
, NULL
);
2108 urlString
= CFURLGetString(url
);
2109 if (urlString
!= NULL
) {
2110 CFRetain(urlString
);
2124 CFStringRef urlString
= NULL
;
2126 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
2128 urlString
= CFURLGetString(url
);
2129 if (urlString
!= NULL
) {
2130 CFRetain(urlString
);
2139 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2140 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2142 ProtocolFlags active_protos
= kProtocolFlagsNone
;
2143 boolean_t changed
= FALSE
;
2144 CFStringRef interface
= NULL
;
2146 CFDictionaryRef ipv6
;
2147 CFMutableDictionaryRef new_dict
= NULL
;
2153 { kSCPropNetProxiesSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
2154 { kSCPropNetProxiesSupplementalMatchOrders
, 0, TRUE
},
2157 CFStringRef key1
; /* an "enable" key */
2161 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
2162 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
2163 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
2164 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
2165 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
2166 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
2167 { kSCPropNetProxiesProxyAutoConfigEnable
,
2168 kSCPropNetProxiesProxyAutoConfigURLString
,
2169 kSCPropNetProxiesProxyAutoConfigJavaScript
, },
2170 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2175 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
2176 /* there is no proxy content */
2180 ipv4
= (CFDataRef
)service_dict_get(serviceID
, kSCEntNetIPv4
);
2182 IPv4RouteListRef routes
;
2184 active_protos
|= kProtocolFlagsIPv4
;
2186 routes
= (IPv4RouteListRef
)CFDataGetBytePtr(ipv4
);
2187 if (routes
->count
> 0) {
2188 interface
= CFStringCreateWithCString(NULL
,
2189 routes
->list
[0].ifname
,
2190 kCFStringEncodingASCII
);
2194 ipv6
= service_dict_get(serviceID
, kSCEntNetIPv6
);
2196 active_protos
|= kProtocolFlagsIPv6
;
2198 if ((interface
== NULL
) &&
2199 CFDictionaryGetValueIfPresent(ipv6
,
2200 kSCPropInterfaceName
,
2201 (const void **)&interface
)) {
2202 CFRetain(interface
);
2206 if (active_protos
== kProtocolFlagsNone
) {
2207 /* there is no IPv4 nor IPv6 */
2208 if (state_dict
== NULL
) {
2209 /* ... and no proxy content that we care about */
2215 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
2217 CFMutableDictionaryRef setup_copy
;
2220 * Merge the per-service "Setup:" and "State:" proxy information with
2221 * the "Setup:" information always taking precedence. Additionally,
2222 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
2223 * Port) is defined than all of the values for that group will be
2224 * used. That is, we don't allow mixing some of the values from
2225 * the "Setup:" keys and others from the "State:" keys.
2227 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
2229 for (i
= 0; i
< sizeof(merge_list
)/sizeof(merge_list
[0]); i
++) {
2230 merge_array_prop(new_dict
,
2234 merge_list
[i
].flags
,
2235 merge_list
[i
].append
);
2238 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
2239 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2240 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
2242 * if a "Setup:" enabled key has been provided than we want to
2243 * ignore all of the "State:" keys
2245 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
2246 if (pick_list
[i
].key2
!= NULL
) {
2247 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
2249 if (pick_list
[i
].key3
!= NULL
) {
2250 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
2252 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
2253 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
2254 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
2256 * if a "Setup:" enabled key has not been provided and we have
2257 * some" "State:" keys than we remove all of of "Setup:" keys
2259 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
2260 if (pick_list
[i
].key2
!= NULL
) {
2261 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
2263 if (pick_list
[i
].key3
!= NULL
) {
2264 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
2269 /* merge the "Setup:" keys */
2270 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
2271 CFRelease(setup_copy
);
2273 else if (setup_dict
!= NULL
) {
2274 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
2276 else if (state_dict
!= NULL
) {
2277 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
2280 if ((new_dict
!= NULL
) && (CFDictionaryGetCount(new_dict
) == 0)) {
2281 CFRelease(new_dict
);
2285 if ((new_dict
!= NULL
) && (interface
!= NULL
)) {
2286 CFDictionarySetValue(new_dict
, kSCPropInterfaceName
, interface
);
2290 if (new_dict
!= NULL
) {
2291 CFDictionaryRef dhcp_options
;
2293 CFNumberRef wpad
= NULL
;
2294 int wpadEnabled
= 0;
2295 CFStringRef wpadURL
= NULL
;
2297 if (CFDictionaryGetValueIfPresent(new_dict
,
2298 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2299 (const void **)&num
) &&
2300 isA_CFNumber(num
)) {
2301 /* if we have a WPAD key */
2303 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
2304 /* if we don't like the enabled key/value */
2312 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
2313 if (!isA_CFNumber(num
) ||
2314 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
2315 /* if we don't like the enabled key/value */
2322 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
2323 if (pacURL
!= NULL
) {
2324 if (!isA_CFString(pacURL
)) {
2325 /* if we don't like the PAC URL */
2331 pacJS
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
2332 if (!isA_CFString(pacJS
)) {
2333 /* if we don't have (or like) the PAC JavaScript */
2341 * we already have a PAC URL so disable WPAD.
2348 * if WPAD is enabled and we don't already have a PAC URL then
2349 * we check for a DHCP provided URL. If not available, we use
2350 * a PAC URL pointing to a well-known file (wpad.dat) on a
2351 * well-known host (wpad.<domain>).
2353 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
2354 wpadURL
= wpadURL_dhcp(dhcp_options
);
2355 if (wpadURL
== NULL
) {
2356 wpadURL
= wpadURL_dns();
2358 if (wpadURL
== NULL
) {
2359 wpadEnabled
= 0; /* if we don't have a WPAD URL */
2364 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
2365 CFDictionarySetValue(new_dict
,
2366 kSCPropNetProxiesProxyAutoConfigEnable
,
2369 CFDictionarySetValue(new_dict
,
2370 kSCPropNetProxiesProxyAutoConfigURLString
,
2377 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
2378 CFDictionarySetValue(new_dict
,
2379 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2386 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
2387 my_CFRelease(&new_dict
);
2388 my_CFRelease(&interface
);
2392 #if !TARGET_OS_IPHONE
2394 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2395 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2397 boolean_t changed
= FALSE
;
2399 CFMutableDictionaryRef new_dict
= NULL
;
2400 CFStringRef pick_list
[] = {
2401 kSCPropNetSMBNetBIOSName
,
2402 kSCPropNetSMBNetBIOSNodeType
,
2403 #ifdef ADD_NETBIOS_SCOPE
2404 kSCPropNetSMBNetBIOSScope
,
2405 #endif // ADD_NETBIOS_SCOPE
2406 kSCPropNetSMBWorkgroup
,
2409 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
) {
2410 /* there is no IPv4 */
2414 if (state_dict
== NULL
&& setup_dict
== NULL
) {
2415 /* there is no SMB */
2419 /* merge SMB configuration */
2420 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
2421 &kCFTypeDictionaryKeyCallBacks
,
2422 &kCFTypeDictionaryValueCallBacks
);
2424 merge_array_prop(new_dict
,
2425 kSCPropNetSMBWINSAddresses
,
2430 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2438 if (CFDictionaryGetCount(new_dict
) == 0) {
2439 my_CFRelease(&new_dict
);
2444 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
2445 my_CFRelease(&new_dict
);
2448 #endif /* !TARGET_OS_IPHONE */
2451 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
2452 CFDictionaryRef setup_options
, CFDictionaryRef info
)
2454 boolean_t changed
= FALSE
;
2455 CFMutableDictionaryRef new_dict
= NULL
;
2456 CFStringRef new_rank
= NULL
;
2457 CFStringRef setup_rank
= NULL
;
2458 CFStringRef state_rank
= NULL
;
2462 * Check "PrimaryRank" setting
2464 * Note: Rank Never > Rank Last > Rank First > Rank None
2466 if (isA_CFDictionary(setup_options
)) {
2467 setup_rank
= CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
2468 setup_rank
= isA_CFString(setup_rank
);
2470 if (isA_CFDictionary(state_options
)) {
2471 state_rank
= CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
2472 state_rank
= isA_CFString(state_rank
);
2475 if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankNever
)) ||
2476 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankNever
))) {
2477 new_rank
= kSCValNetServicePrimaryRankNever
;
2479 else if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankLast
)) ||
2480 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankLast
))) {
2481 new_rank
= kSCValNetServicePrimaryRankLast
;
2483 else if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankFirst
)) ||
2484 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankFirst
))) {
2485 new_rank
= kSCValNetServicePrimaryRankFirst
;
2488 if (new_rank
!= NULL
) {
2489 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
2490 &kCFTypeDictionaryKeyCallBacks
,
2491 &kCFTypeDictionaryValueCallBacks
);
2492 CFDictionarySetValue(new_dict
, kSCPropNetServicePrimaryRank
, new_rank
);
2495 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
2496 my_CFRelease(&new_dict
);
2501 add_service_keys(CFStringRef serviceID
, CFMutableArrayRef keys
, CFMutableArrayRef patterns
)
2506 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
2510 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
2511 key
= setup_service_key(serviceID
, *entityTypeNames
[i
]);
2512 CFArrayAppendValue(keys
, key
);
2514 key
= state_service_key(serviceID
, *entityTypeNames
[i
]);
2515 CFArrayAppendValue(keys
, key
);
2519 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
2520 CFArrayAppendValue(patterns
, key
);
2523 key
= setup_service_key(serviceID
, NULL
);
2524 CFArrayAppendValue(patterns
, key
);
2526 key
= state_service_key(serviceID
, NULL
);
2527 CFArrayAppendValue(patterns
, key
);
2534 static CFDictionaryRef
2535 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
2538 CFMutableArrayRef get_keys
;
2539 CFMutableArrayRef get_patterns
;
2540 CFDictionaryRef info
;
2543 count
= CFArrayGetCount(service_list
);
2544 get_keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2545 get_patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2547 CFArrayAppendValue(get_keys
, S_setup_global_ipv4
);
2548 CFArrayAppendValue(get_keys
, S_multicast_resolvers
);
2549 CFArrayAppendValue(get_keys
, S_private_resolvers
);
2551 for (s
= 0; s
< count
; s
++) {
2552 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
2554 add_service_keys(serviceID
, get_keys
, get_patterns
);
2557 info
= SCDynamicStoreCopyMultiple(session
, get_keys
, get_patterns
);
2558 my_CFRelease(&get_keys
);
2559 my_CFRelease(&get_patterns
);
2563 static int rtm_seq
= 0;
2566 route_open_socket(void)
2570 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
)) == -1) {
2571 SCLog(TRUE
, LOG_NOTICE
,
2572 CFSTR("IPMonitor: route_open_socket: socket failed, %s"),
2579 * Define: ROUTE_MSG_ADDRS_SPACE
2581 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2582 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2583 * someone changes the code and doesn't think to modify this.
2585 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2586 + 2 * sizeof(struct sockaddr_dl) \
2589 struct rt_msghdr hdr
;
2590 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2594 ipv4_route(int sockfd
,
2595 int cmd
, struct in_addr gateway
, struct in_addr netaddr
,
2596 struct in_addr netmask
, char * ifname
, unsigned int ifindex
,
2597 struct in_addr ifa
, RouteFlags flags
)
2599 boolean_t default_route
= (netaddr
.s_addr
== 0);
2604 struct sockaddr_in
* in_p
;
2605 struct sockaddr_dl
* dl_p
;
2609 if (default_route
&& S_netboot
) {
2613 if (ifname
== NULL
) {
2614 /* this should not happen, but rather than crash, return an error */
2616 "IPMonitor: ipv4_route ifname is NULL on network address %s",
2617 inet_ntoa(netaddr
));
2620 memset(&rtmsg
, 0, sizeof(rtmsg
));
2621 rtmsg
.hdr
.rtm_type
= cmd
;
2622 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2623 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2625 = RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
| RTA_IFP
| RTA_IFA
;
2627 && (flags
& kRouteIsDirectToInterfaceFlag
) == 0) {
2628 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_GATEWAY
| RTF_STATIC
;
2631 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_CLONING
| RTF_STATIC
;
2633 if ((flags
& kRouteWantScopedFlag
) != 0) {
2635 if (!S_scopedroute
) {
2639 /* specifically asked for a scoped route, yet no index supplied */
2641 "IPMonitor: ipv4_route index is 0 on %s-scoped route %s",
2642 ifname
, inet_ntoa(netaddr
));
2645 rtmsg
.hdr
.rtm_index
= ifindex
;
2646 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2647 #else /* RTF_IFSCOPE */
2649 #endif /* RTF_IFSCOPE */
2652 rtaddr
.ptr
= rtmsg
.addrs
;
2655 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2656 rtaddr
.in_p
->sin_family
= AF_INET
;
2657 rtaddr
.in_p
->sin_addr
= netaddr
;
2658 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2661 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2662 /* gateway is an IP address */
2663 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2664 rtaddr
.in_p
->sin_family
= AF_INET
;
2665 rtaddr
.in_p
->sin_addr
= gateway
;
2666 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2669 /* gateway is the interface itself */
2670 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2671 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2672 rtaddr
.dl_p
->sdl_nlen
= strlen(ifname
);
2673 bcopy(ifname
, rtaddr
.dl_p
->sdl_data
, rtaddr
.dl_p
->sdl_nlen
);
2674 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2678 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2679 rtaddr
.in_p
->sin_family
= AF_INET
;
2680 rtaddr
.in_p
->sin_addr
= netmask
;
2681 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2683 /* interface name */
2684 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2685 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2686 rtaddr
.dl_p
->sdl_nlen
= strlen(ifname
);
2687 bcopy(ifname
, rtaddr
.dl_p
->sdl_data
, rtaddr
.dl_p
->sdl_nlen
);
2688 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2690 /* interface address */
2691 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2692 rtaddr
.in_p
->sin_family
= AF_INET
;
2693 rtaddr
.in_p
->sin_addr
= ifa
;
2694 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2696 len
= sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
);
2697 rtmsg
.hdr
.rtm_msglen
= len
;
2698 if (write(sockfd
, &rtmsg
, len
) == -1) {
2705 ipv6_route(int cmd
, struct in6_addr gateway
, struct in6_addr netaddr
,
2706 struct in6_addr netmask
, char * ifname
, boolean_t is_direct
)
2708 boolean_t default_route
;
2710 boolean_t ret
= TRUE
;
2712 struct rt_msghdr hdr
;
2713 struct sockaddr_in6 dst
;
2714 struct sockaddr_in6 gway
;
2715 struct sockaddr_in6 mask
;
2716 struct sockaddr_dl ifp
;
2719 struct in6_addr zeroes
= IN6ADDR_ANY_INIT
;
2721 default_route
= (bcmp(&zeroes
, &netaddr
, sizeof(netaddr
)) == 0);
2723 if (IN6_IS_ADDR_LINKLOCAL(&gateway
) && ifname
!= NULL
) {
2724 unsigned int index
= if_nametoindex(ifname
);
2726 /* add the scope id to the link local address */
2727 gateway
.__u6_addr
.__u6_addr16
[1] = (uint16_t)htons(index
);
2729 sockfd
= route_open_socket();
2733 memset(&rtmsg
, 0, sizeof(rtmsg
));
2734 rtmsg
.hdr
.rtm_type
= cmd
;
2735 if (default_route
) {
2737 /* if router is directly reachable, don't set the gateway flag */
2738 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2741 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_GATEWAY
| RTF_STATIC
;
2745 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_CLONING
| RTF_STATIC
;
2747 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2748 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2749 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
;
2750 rtmsg
.dst
.sin6_len
= sizeof(rtmsg
.dst
);
2751 rtmsg
.dst
.sin6_family
= AF_INET6
;
2752 rtmsg
.dst
.sin6_addr
= netaddr
;
2753 rtmsg
.gway
.sin6_len
= sizeof(rtmsg
.gway
);
2754 rtmsg
.gway
.sin6_family
= AF_INET6
;
2755 rtmsg
.gway
.sin6_addr
= gateway
;
2756 rtmsg
.mask
.sin6_len
= sizeof(rtmsg
.mask
);
2757 rtmsg
.mask
.sin6_family
= AF_INET6
;
2758 rtmsg
.mask
.sin6_addr
= netmask
;
2760 len
= sizeof(rtmsg
);
2762 rtmsg
.ifp
.sdl_len
= sizeof(rtmsg
.ifp
);
2763 rtmsg
.ifp
.sdl_family
= AF_LINK
;
2764 rtmsg
.ifp
.sdl_nlen
= strlen(ifname
);
2765 rtmsg
.hdr
.rtm_addrs
|= RTA_IFP
;
2766 bcopy(ifname
, rtmsg
.ifp
.sdl_data
, rtmsg
.ifp
.sdl_nlen
);
2769 /* no ifp information */
2770 len
-= sizeof(rtmsg
.ifp
);
2772 rtmsg
.hdr
.rtm_msglen
= len
;
2773 if (write(sockfd
, &rtmsg
, len
) == -1) {
2774 if ((cmd
== RTM_ADD
) && (errno
== EEXIST
)) {
2775 /* no sense complaining about a route that already exists */
2777 else if ((cmd
== RTM_DELETE
) && (errno
== ESRCH
)) {
2778 /* no sense complaining about a route that isn't there */
2781 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2782 SCLog(TRUE
, LOG_NOTICE
,
2783 CFSTR("IPMonitor ipv6_route: write routing"
2784 " socket failed, %s"), strerror(errno
));
2795 ipv6_default_route_delete(void)
2797 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2798 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: IPv6 route delete default"));
2800 return (ipv6_route(RTM_DELETE
, S_ip6_zeros
, S_ip6_zeros
, S_ip6_zeros
,
2805 ipv6_default_route_add(struct in6_addr router
, char * ifname
,
2806 boolean_t is_direct
)
2808 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2809 char ntopbuf
[INET6_ADDRSTRLEN
];
2811 SCLog(TRUE
, LOG_NOTICE
,
2812 CFSTR("IPMonitor: IPv6 route add default"
2813 " %s interface %s direct %d"),
2814 inet_ntop(AF_INET6
, &router
, ntopbuf
, sizeof(ntopbuf
)),
2817 return (ipv6_route(RTM_ADD
, router
, S_ip6_zeros
, S_ip6_zeros
,
2818 ifname
, is_direct
));
2823 multicast_route_delete(int sockfd
)
2825 struct in_addr gateway
= { htonl(INADDR_LOOPBACK
) };
2826 struct in_addr netaddr
= { htonl(INADDR_UNSPEC_GROUP
) };
2827 struct in_addr netmask
= { htonl(IN_CLASSD_NET
) };
2829 return (ipv4_route(sockfd
, RTM_DELETE
, gateway
, netaddr
, netmask
, "lo0", 0,
2834 multicast_route_add(int sockfd
)
2836 struct in_addr gateway
= { htonl(INADDR_LOOPBACK
) };
2837 struct in_addr netaddr
= { htonl(INADDR_UNSPEC_GROUP
) };
2838 struct in_addr netmask
= { htonl(IN_CLASSD_NET
) };
2840 return (ipv4_route(sockfd
, RTM_ADD
, gateway
, netaddr
, netmask
, "lo0", 0,
2846 set_ipv6_default_interface(char * ifname
)
2848 struct in6_ndifreq ndifreq
;
2851 bzero((char *)&ndifreq
, sizeof(ndifreq
));
2852 if (ifname
!= NULL
) {
2853 strlcpy(ndifreq
.ifname
, ifname
, sizeof(ndifreq
.ifname
));
2854 ndifreq
.ifindex
= if_nametoindex(ifname
);
2856 strlcpy(ndifreq
.ifname
, "lo0", sizeof(ndifreq
.ifname
));
2857 ndifreq
.ifindex
= 0;
2860 sock
= inet6_dgram_socket();
2862 SCLog(TRUE
, LOG_ERR
,
2863 CFSTR("IPMonitor: set_ipv6_default_interface: socket failed, %s"),
2867 if (ioctl(sock
, SIOCSDEFIFACE_IN6
, (caddr_t
)&ndifreq
) == -1) {
2868 SCLog(TRUE
, LOG_ERR
,
2869 CFSTR("IPMonitor: set_ipv6_default_interface: ioctl(SIOCSDEFIFACE_IN6) failed, %s"),
2875 #endif /* RTF_IFSCOPE */
2878 set_ipv6_router(struct in6_addr
* router
, char * ifname
, boolean_t is_direct
)
2880 /* assign the new default route, ensure local multicast route available */
2881 (void)ipv6_default_route_delete();
2882 if (router
!= NULL
) {
2883 (void)ipv6_default_route_add(*router
, ifname
, is_direct
);
2888 #if !TARGET_OS_IPHONE
2889 static __inline__
void
2892 (void)unlink(VAR_RUN_RESOLV_CONF
);
2896 set_dns(CFArrayRef val_search_domains
,
2897 CFStringRef val_domain_name
,
2898 CFArrayRef val_servers
,
2899 CFArrayRef val_sortlist
)
2901 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
2903 /* publish new resolv.conf */
2908 SCPrint(TRUE
, f
, CFSTR("#\n"));
2909 SCPrint(TRUE
, f
, CFSTR("# Mac OS X Notice\n"));
2910 SCPrint(TRUE
, f
, CFSTR("#\n"));
2911 SCPrint(TRUE
, f
, CFSTR("# This file is not used by the host name and address resolution\n"));
2912 SCPrint(TRUE
, f
, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
2913 SCPrint(TRUE
, f
, CFSTR("# this Mac OS X system.\n"));
2914 SCPrint(TRUE
, f
, CFSTR("#\n"));
2915 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
2916 SCPrint(TRUE
, f
, CFSTR("#\n"));
2918 if (isA_CFArray(val_search_domains
)) {
2919 SCPrint(TRUE
, f
, CFSTR("search"));
2920 n
= CFArrayGetCount(val_search_domains
);
2921 for (i
= 0; i
< n
; i
++) {
2924 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
2925 if (isA_CFString(domain
)) {
2926 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
2929 SCPrint(TRUE
, f
, CFSTR("\n"));
2931 else if (isA_CFString(val_domain_name
)) {
2932 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
2935 if (isA_CFArray(val_servers
)) {
2936 n
= CFArrayGetCount(val_servers
);
2937 for (i
= 0; i
< n
; i
++) {
2938 CFStringRef nameserver
;
2940 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
2941 if (isA_CFString(nameserver
)) {
2942 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
2947 if (isA_CFArray(val_sortlist
)) {
2948 SCPrint(TRUE
, f
, CFSTR("sortlist"));
2949 n
= CFArrayGetCount(val_sortlist
);
2950 for (i
= 0; i
< n
; i
++) {
2951 CFStringRef address
;
2953 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
2954 if (isA_CFString(address
)) {
2955 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
2958 SCPrint(TRUE
, f
, CFSTR("\n"));
2962 rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
2966 #endif /* !TARGET_OS_IPHONE */
2969 router_is_our_ipv6_address(CFStringRef router
, CFArrayRef addr_list
)
2972 CFIndex n
= CFArrayGetCount(addr_list
);
2975 (void)cfstring_to_ip6(router
, &r
);
2976 for (i
= 0; i
< n
; i
++) {
2979 if (cfstring_to_ip6(CFArrayGetValueAtIndex(addr_list
, i
), &ip
)
2980 && bcmp(&r
, &ip
, sizeof(r
)) == 0) {
2987 static IPv4RouteListRef
2988 service_dict_get_ipv4_routelist(CFDictionaryRef service_dict
)
2991 IPv4RouteListRef routes
= NULL
;
2993 data
= (CFDataRef
)CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
2995 routes
= (IPv4RouteListRef
)CFDataGetBytePtr(data
);
3000 typedef struct apply_ipv4_route_context
{
3001 IPv4RouteListRef old
;
3002 IPv4RouteListRef
new;
3004 } apply_ipv4_route_context_t
;
3006 /* add/remove a router/32 subnet */
3008 ipv4_route_gateway(int sockfd
, int cmd
, char * ifn_p
,
3009 IPv4RouteRef def_route
)
3011 struct in_addr mask
;
3013 mask
.s_addr
= htonl(INADDR_BROADCAST
);
3014 return (ipv4_route(sockfd
, cmd
, def_route
->ifa
,
3015 def_route
->gateway
, mask
, ifn_p
, def_route
->ifindex
,
3017 (def_route
->flags
& kRouteWantScopedFlag
)));
3021 * Function: apply_ipv4_route
3023 * Callback function that adds/removes the specified route.
3026 apply_ipv4_route(IPv4RouteListApplyCommand cmd
, IPv4RouteRef route
, void * arg
)
3028 apply_ipv4_route_context_t
*context
= (apply_ipv4_route_context_t
*)arg
;
3032 ifn_p
= route
->ifname
;
3034 case kIPv4RouteListAddRouteCommand
:
3035 if ((route
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
3036 retval
= ipv4_route_gateway(context
->sockfd
, RTM_ADD
,
3038 if (retval
== EEXIST
) {
3039 /* delete and add again */
3040 (void)ipv4_route_gateway(context
->sockfd
, RTM_DELETE
,
3042 retval
= ipv4_route_gateway(context
->sockfd
, RTM_ADD
,
3046 SCLog(TRUE
, LOG_NOTICE
,
3047 CFSTR("IPMonitor apply_ipv4_route failed to add"
3048 " %s/32 route, %s"),
3049 inet_ntoa(route
->gateway
), strerror(retval
));
3051 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3052 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Added IPv4 Route %s/32"),
3053 inet_ntoa(route
->gateway
));
3056 retval
= ipv4_route(context
->sockfd
,
3057 RTM_ADD
, route
->gateway
,
3058 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
3059 route
->ifa
, route
->flags
);
3060 if (retval
== EEXIST
) {
3061 /* delete and add again */
3062 (void)ipv4_route(context
->sockfd
,
3063 RTM_DELETE
, route
->gateway
,
3064 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
3065 route
->ifa
, route
->flags
);
3066 retval
= ipv4_route(context
->sockfd
,
3067 RTM_ADD
, route
->gateway
,
3068 route
->dest
, route
->mask
,
3069 ifn_p
, route
->ifindex
,
3070 route
->ifa
, route
->flags
);
3073 SCLog(TRUE
, LOG_NOTICE
,
3074 CFSTR("IPMonitor apply_ipv4_route failed to add"
3075 " route, %s:"), strerror(retval
));
3076 IPv4RouteLog(route
);
3078 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3079 SCLog(TRUE
, LOG_NOTICE
,
3080 CFSTR("Added IPv4 route new[%d] = "),
3081 route
- context
->new->list
);
3082 IPv4RouteLog(route
);
3085 case kIPv4RouteListRemoveRouteCommand
:
3086 retval
= ipv4_route(context
->sockfd
,
3087 RTM_DELETE
, route
->gateway
,
3088 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
3089 route
->ifa
, route
->flags
);
3091 if (retval
!= ESRCH
) {
3092 SCLog(TRUE
, LOG_NOTICE
,
3093 CFSTR("IPMonitor apply_ipv4_route failed to remove"
3094 " route, %s: "), strerror(retval
));
3095 IPv4RouteLog(route
);
3098 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3099 SCLog(TRUE
, LOG_NOTICE
,
3100 CFSTR("Removed IPv4 route old[%d] = "),
3101 route
- context
->old
->list
);
3102 IPv4RouteLog(route
);
3104 if ((route
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
3105 retval
= ipv4_route_gateway(context
->sockfd
, RTM_DELETE
,
3108 SCLog(TRUE
, LOG_NOTICE
,
3109 CFSTR("IPMonitor apply_ipv4_route failed to remove"
3110 " %s/32 route, %s: "),
3111 inet_ntoa(route
->gateway
), strerror(retval
));
3113 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3114 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Removed IPv4 Route %s/32"),
3115 inet_ntoa(route
->gateway
));
3126 * Function: update_ipv4
3129 * Update the IPv4 configuration based on the latest information.
3130 * Publish the State:/Network/Global/IPv4 information, and update the
3131 * IPv4 routing table. IPv4RouteListApply() invokes our callback,
3132 * apply_ipv4_route(), to install/remove the routes.
3135 update_ipv4(CFStringRef primary
,
3136 IPv4RouteListRef new_routelist
,
3137 keyChangeListRef keys
)
3139 apply_ipv4_route_context_t context
;
3142 if (new_routelist
!= NULL
&& primary
!= NULL
) {
3143 char * ifn_p
= NULL
;
3145 CFMutableDictionaryRef dict
= NULL
;
3147 dict
= CFDictionaryCreateMutable(NULL
, 0,
3148 &kCFTypeDictionaryKeyCallBacks
,
3149 &kCFTypeDictionaryValueCallBacks
);
3150 /* the first entry is the default route */
3151 r
= new_routelist
->list
;
3152 if (r
->gateway
.s_addr
!= 0) {
3155 router
= CFStringCreateWithCString(NULL
,
3156 inet_ntoa(r
->gateway
),
3157 kCFStringEncodingASCII
);
3158 if (router
!= NULL
) {
3159 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, router
);
3163 if (r
->ifname
[0] != '\0') {
3166 if (ifn_p
!= NULL
) {
3167 CFStringRef ifname_cf
;
3169 ifname_cf
= CFStringCreateWithCString(NULL
,
3171 kCFStringEncodingASCII
);
3172 if (ifname_cf
!= NULL
) {
3173 CFDictionarySetValue(dict
,
3174 kSCDynamicStorePropNetPrimaryInterface
,
3176 CFRelease(ifname_cf
);
3179 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
3181 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
3185 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
3189 bzero(&context
, sizeof(context
));
3190 context
.sockfd
= route_open_socket();
3191 if (context
.sockfd
!= -1) {
3192 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3193 if (S_ipv4_routelist
== NULL
) {
3194 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Old Routes = <none>"));
3197 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Old Routes = "));
3198 IPv4RouteListLog(S_ipv4_routelist
);
3200 if (new_routelist
== NULL
) {
3201 SCLog(TRUE
, LOG_NOTICE
, CFSTR("New Routes = <none>"));
3204 SCLog(TRUE
, LOG_NOTICE
, CFSTR("New Routes = "));
3205 IPv4RouteListLog(new_routelist
);
3208 context
.old
= S_ipv4_routelist
;
3209 context
.new = new_routelist
;
3210 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
,
3211 &apply_ipv4_route
, (void *)&context
);
3212 if (new_routelist
!= NULL
) {
3213 (void)multicast_route_delete(context
.sockfd
);
3216 (void)multicast_route_add(context
.sockfd
);
3218 close(context
.sockfd
);
3220 if (S_ipv4_routelist
!= NULL
) {
3221 free(S_ipv4_routelist
);
3223 S_ipv4_routelist
= new_routelist
;
3228 update_ipv6(CFDictionaryRef service_info
,
3229 CFStringRef primary
,
3230 keyChangeListRef keys
)
3232 CFDictionaryRef ipv6_dict
= NULL
;
3234 if (primary
!= NULL
) {
3235 CFDictionaryRef service_dict
;
3237 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3238 if (service_dict
!= NULL
) {
3239 ipv6_dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
3242 if (ipv6_dict
!= NULL
) {
3244 CFMutableDictionaryRef dict
= NULL
;
3245 CFStringRef if_name
= NULL
;
3246 char ifn
[IFNAMSIZ
] = { '\0' };
3247 char * ifn_p
= NULL
;
3248 boolean_t is_direct
= FALSE
;
3249 CFStringRef val_router
= NULL
;
3251 dict
= CFDictionaryCreateMutable(NULL
, 0,
3252 &kCFTypeDictionaryKeyCallBacks
,
3253 &kCFTypeDictionaryValueCallBacks
);
3254 val_router
= CFDictionaryGetValue(ipv6_dict
, kSCPropNetIPv6Router
);
3255 addrs
= CFDictionaryGetValue(ipv6_dict
,
3256 kSCPropNetIPv6Addresses
);
3257 if (val_router
!= NULL
) {
3258 /* no router if router is one of our IP addresses */
3259 is_direct
= router_is_our_ipv6_address(val_router
, addrs
);
3260 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
,
3264 val_router
= CFArrayGetValueAtIndex(addrs
, 0);
3267 if_name
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
3269 CFDictionarySetValue(dict
,
3270 kSCDynamicStorePropNetPrimaryInterface
,
3272 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
3273 kCFStringEncodingASCII
)) {
3277 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
3279 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
3283 if (S_scopedroute_v6
) {
3284 set_ipv6_default_interface(ifn_p
);
3286 #endif /* RTF_IFSCOPE */
3287 { /* route add default ... */
3288 struct in6_addr router
;
3290 (void)cfstring_to_ip6(val_router
, &router
);
3291 set_ipv6_router(&router
, ifn_p
, is_direct
);
3295 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
3297 if (S_scopedroute_v6
) {
3298 set_ipv6_default_interface(NULL
);
3300 #endif /* RTF_IFSCOPE */
3301 { /* route delete default ... */
3302 set_ipv6_router(NULL
, NULL
, FALSE
);
3309 update_dns(CFDictionaryRef service_info
,
3310 CFStringRef primary
,
3311 keyChangeListRef keys
)
3313 CFDictionaryRef dict
= NULL
;
3315 if (primary
!= NULL
) {
3316 CFDictionaryRef service_dict
;
3318 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3319 if (service_dict
!= NULL
) {
3320 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
3324 #if !TARGET_OS_IPHONE
3326 #endif /* !TARGET_OS_IPHONE */
3327 keyChangeListRemoveValue(keys
, S_state_global_dns
);
3330 CFMutableDictionaryRef new_dict
;
3332 #if !TARGET_OS_IPHONE
3333 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
3334 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
3335 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
3336 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
3337 #endif /* !TARGET_OS_IPHONE */
3338 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
3339 CFDictionaryRemoveValue(new_dict
, kSCPropInterfaceName
);
3340 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchDomains
);
3341 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSupplementalMatchOrders
);
3342 keyChangeListSetValue(keys
, S_state_global_dns
, new_dict
);
3343 CFRelease(new_dict
);
3349 update_dnsinfo(CFDictionaryRef service_info
,
3350 CFStringRef primary
,
3351 keyChangeListRef keys
,
3352 CFArrayRef service_order
)
3354 CFDictionaryRef dict
= NULL
;
3355 CFArrayRef multicastResolvers
;
3356 CFArrayRef privateResolvers
;
3358 multicastResolvers
= CFDictionaryGetValue(service_info
, S_multicast_resolvers
);
3359 privateResolvers
= CFDictionaryGetValue(service_info
, S_private_resolvers
);
3361 if (primary
!= NULL
) {
3362 CFDictionaryRef service_dict
;
3364 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3365 if (service_dict
!= NULL
) {
3366 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
3370 dns_configuration_set(dict
,
3371 S_service_state_dict
,
3375 keyChangeListNotifyKey(keys
, S_state_global_dns
);
3380 update_proxies(CFDictionaryRef service_info
,
3381 CFStringRef primary
,
3382 keyChangeListRef keys
,
3383 CFArrayRef service_order
)
3385 CFDictionaryRef dict
= NULL
;
3386 CFDictionaryRef new_dict
;
3388 if (primary
!= NULL
) {
3389 CFDictionaryRef service_dict
;
3391 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3392 if (service_dict
!= NULL
) {
3393 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
3397 new_dict
= proxy_configuration_update(dict
,
3398 S_service_state_dict
,
3400 if (new_dict
== NULL
) {
3401 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
3404 keyChangeListSetValue(keys
, S_state_global_proxies
, new_dict
);
3405 CFRelease(new_dict
);
3410 #if !TARGET_OS_IPHONE
3412 update_smb(CFDictionaryRef service_info
,
3413 CFStringRef primary
,
3414 keyChangeListRef keys
)
3416 CFDictionaryRef dict
= NULL
;
3418 if (primary
!= NULL
) {
3419 CFDictionaryRef service_dict
;
3421 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3422 if (service_dict
!= NULL
) {
3423 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
3427 keyChangeListRemoveValue(keys
, S_state_global_smb
);
3430 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
3435 #endif /* !TARGET_OS_IPHONE */
3438 get_service_rank(CFArrayRef order
, int n_order
, CFStringRef serviceID
)
3441 Rank rank
= kRankLast
;
3443 if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
3444 for (i
= 0; i
< n_order
; i
++) {
3445 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
3450 if (CFEqual(serviceID
, s
)) {
3460 ** Service election:
3463 * Function: rank_dict_get_service_rank
3465 * Retrieve the service rank in the given dictionary.
3468 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
3471 Rank rank_val
= kRankLast
;
3473 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
3475 CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
);
3481 * Function: rank_dict_set_service_rank
3483 * Save the results of ranking the service so we can look it up later without
3484 * repeating all of the ranking code.
3487 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
3488 CFStringRef serviceID
, Rank rank_val
)
3492 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
3494 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
3500 typedef struct election_info
{
3504 CFStringRef serviceID
;
3505 CFDictionaryRef service_dict
;
3507 boolean_t choose_last
;
3510 typedef boolean_t
election_func_t(void * context
, election_info_t
* info
);
3513 * Function: elect_ipv4
3515 * This function builds the list of IPv4 routes that should be active.
3516 * As elect_new_primary() invokes us with each service, we build up the
3517 * result in the passed in context, a pointer to an IPv4RouteListRef.
3520 elect_ipv4(void * context
, election_info_t
* info
)
3522 IPv4RouteListRef
* routes_p
= (IPv4RouteListRef
*)context
;
3523 IPv4RouteListRef service_routes
;
3525 service_routes
= service_dict_get_ipv4_routelist(info
->service_dict
);
3526 if (service_routes
== NULL
) {
3529 if ((service_routes
->list
->flags
& kRouteChooseFirstFlag
) != 0) {
3530 info
->service_rank
= kRankFirst
;
3532 else if (S_ppp_override_primary
3533 && (strncmp(PPP_PREFIX
, service_routes
->list
->ifname
,
3534 sizeof(PPP_PREFIX
) - 1) == 0)) {
3535 /* PPP override: make ppp* look the best */
3536 /* Hack: should use interface type, not interface name */
3537 info
->service_rank
= kRankFirst
;
3540 info
->service_rank
= get_service_rank(info
->order
, info
->n_order
,
3542 if ((service_routes
->list
->flags
& kRouteChooseLastFlag
) != 0) {
3543 info
->choose_last
= TRUE
;
3546 if (routes_p
!= NULL
) {
3547 *routes_p
= IPv4RouteListAddRouteList(*routes_p
,
3548 info
->n_services
* 3,
3550 info
->service_rank
);
3552 if ((service_routes
->list
->flags
& kRouteChooseNeverFlag
) != 0) {
3553 /* never elect as primary */
3556 if (strncmp(service_routes
->list
->ifname
, "lo0",
3557 sizeof(service_routes
->list
->ifname
)) == 0) {
3558 /* never elect as primary */
3562 rank_dict_set_service_rank(S_ipv4_service_rank_dict
,
3563 info
->serviceID
, info
->service_rank
);
3568 elect_ipv6(void * context
, election_info_t
* info
)
3570 CFStringRef if_name
;
3571 CFStringRef primaryRank
= NULL
;
3572 CFDictionaryRef proto_dict
;
3574 CFDictionaryRef service_options
;
3576 proto_dict
= CFDictionaryGetValue(info
->service_dict
, kSCEntNetIPv6
);
3577 if (proto_dict
== NULL
) {
3580 service_options
= service_dict_get(info
->serviceID
, kSCEntNetService
);
3581 if (service_options
!= NULL
) {
3582 primaryRank
= CFDictionaryGetValue(service_options
, kSCPropNetServicePrimaryRank
);
3583 if ((primaryRank
!= NULL
)
3584 && CFEqual(primaryRank
, kSCValNetServicePrimaryRankNever
)) {
3588 if_name
= CFDictionaryGetValue(proto_dict
, kSCPropInterfaceName
);
3589 if (if_name
!= NULL
&& CFEqual(if_name
, CFSTR("lo0"))) {
3590 /* never elect as primary */
3593 router
= CFDictionaryGetValue(proto_dict
,
3594 kSCPropNetIPv6Router
);
3595 if (router
== NULL
) {
3596 info
->choose_last
= TRUE
;
3597 info
->service_rank
= kRankLast
;
3599 else if ((primaryRank
!= NULL
)
3600 && CFEqual(primaryRank
, kSCValNetServicePrimaryRankFirst
)) {
3601 info
->service_rank
= kRankFirst
;
3603 else if (get_override_primary(proto_dict
)) {
3604 info
->service_rank
= kRankFirst
;
3606 else if (S_ppp_override_primary
3608 && CFStringHasPrefix(if_name
, CFSTR(PPP_PREFIX
))) {
3609 /* PPP override: make ppp* look the best */
3610 /* Hack: should use interface type, not interface name */
3611 info
->service_rank
= kRankFirst
;
3614 info
->service_rank
= get_service_rank(info
->order
, info
->n_order
,
3618 rank_dict_set_service_rank(S_ipv6_service_rank_dict
,
3619 info
->serviceID
, info
->service_rank
);
3624 * Function: elect_new_primary
3626 * Walk the list of services, passing each service dictionary to "elect_func".
3627 * "elect_func" returns rank information about the service that let us
3628 * determine the new primary.
3631 elect_new_primary(election_func_t
* elect_func
, void * context
,
3632 CFArrayRef order
, int n_order
)
3636 election_info_t info
;
3638 #define N_KEYS_VALUES_STATIC 10
3639 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
3640 CFStringRef new_primary
= NULL
;
3641 Rank new_primary_rank
= kRankLast
;
3642 boolean_t new_primary_choose_last
= FALSE
;
3645 count
= CFDictionaryGetCount(S_service_state_dict
);
3646 if (count
<= N_KEYS_VALUES_STATIC
) {
3647 keys
= keys_values_buf
;
3650 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
3652 values
= keys
+ count
;
3653 CFDictionaryGetKeysAndValues(S_service_state_dict
,
3654 (const void * *)keys
,
3655 (const void * *)values
);
3657 info
.n_services
= count
;
3659 info
.n_order
= n_order
;
3660 for (i
= 0; i
< count
; i
++) {
3661 boolean_t found_new_primary
= FALSE
;
3663 info
.serviceID
= (CFStringRef
)keys
[i
];
3664 info
.service_dict
= (CFDictionaryRef
)values
[i
];
3665 info
.service_rank
= kRankLast
;
3666 info
.choose_last
= FALSE
;
3668 if ((*elect_func
)(context
, &info
) == FALSE
) {
3671 if (new_primary
== NULL
) {
3672 found_new_primary
= TRUE
;
3674 else if (info
.choose_last
== new_primary_choose_last
) {
3675 found_new_primary
= (info
.service_rank
< new_primary_rank
);
3677 else if (new_primary_choose_last
) {
3678 found_new_primary
= TRUE
;
3680 if (found_new_primary
) {
3681 new_primary
= info
.serviceID
;
3682 new_primary_rank
= info
.service_rank
;
3683 new_primary_choose_last
= info
.choose_last
;
3686 if (new_primary
!= NULL
) {
3687 CFRetain(new_primary
);
3689 if (keys
!= keys_values_buf
) {
3692 return (new_primary
);
3696 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
3698 uint32_t changed
= 0;
3701 /* update service options first (e.g. rank) */
3702 if (get_rank_changes(serviceID
,
3703 get_service_state_entity(services_info
, serviceID
,
3705 get_service_setup_entity(services_info
, serviceID
,
3708 changed
|= (1 << kEntityTypeServiceOptions
);
3710 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
3711 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
3712 GetEntityChangesFuncRef func
= entityChangeFunc
[i
];
3713 if ((*func
)(serviceID
,
3714 get_service_state_entity(services_info
, serviceID
,
3715 *entityTypeNames
[i
]),
3716 get_service_setup_entity(services_info
, serviceID
,
3717 *entityTypeNames
[i
]),
3719 changed
|= (1 << i
);
3726 service_order_get(CFDictionaryRef services_info
)
3728 CFArrayRef order
= NULL
;
3729 CFDictionaryRef ipv4_dict
;
3731 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
3732 S_setup_global_ipv4
);
3733 if (ipv4_dict
!= NULL
) {
3734 CFNumberRef ppp_override
;
3737 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
3738 order
= isA_CFArray(order
);
3740 /* get ppp override primary */
3741 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
3742 kSCPropNetPPPOverridePrimary
);
3743 ppp_override
= isA_CFNumber(ppp_override
);
3744 if (ppp_override
!= NULL
) {
3745 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
3747 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
3750 S_ppp_override_primary
= FALSE
;
3756 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
3757 const char * entity
)
3759 boolean_t changed
= FALSE
;
3760 CFStringRef primary
= *primary_p
;
3762 if (new_primary
!= NULL
) {
3763 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
3764 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3765 SCLog(TRUE
, LOG_NOTICE
,
3766 CFSTR("IPMonitor: %@ is still primary %s"),
3767 new_primary
, entity
);
3771 my_CFRelease(primary_p
);
3772 *primary_p
= CFRetain(new_primary
);
3773 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3774 SCLog(TRUE
, LOG_NOTICE
,
3775 CFSTR("IPMonitor: %@ is the new primary %s"),
3776 new_primary
, entity
);
3781 else if (primary
!= NULL
) {
3782 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3783 SCLog(TRUE
, LOG_NOTICE
,
3784 CFSTR("IPMonitor: %@ is no longer primary %s"),
3787 my_CFRelease(primary_p
);
3794 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
3797 if (service_dict_get(serviceID
, entity
) == NULL
) {
3800 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
3804 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
3808 boolean_t dnsinfo_changed
= FALSE
;
3809 boolean_t global_ipv4_changed
= FALSE
;
3810 boolean_t global_ipv6_changed
= FALSE
;
3814 int n_service_order
= 0;
3815 boolean_t proxies_changed
= FALSE
;
3816 CFArrayRef service_order
;
3817 CFMutableArrayRef service_changes
= NULL
;
3818 CFDictionaryRef services_info
= NULL
;
3820 count
= CFArrayGetCount(changed_keys
);
3825 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3826 SCLog(TRUE
, LOG_NOTICE
,
3827 CFSTR("IPMonitor: changes %@ (%d)"), changed_keys
, count
);
3830 keyChangeListInit(&keys
);
3831 service_changes
= CFArrayCreateMutable(NULL
, 0,
3832 &kCFTypeArrayCallBacks
);
3833 for (i
= 0; i
< count
; i
++) {
3834 CFStringRef change
= CFArrayGetValueAtIndex(changed_keys
, i
);
3835 if (CFEqual(change
, S_setup_global_ipv4
)) {
3836 global_ipv4_changed
= TRUE
;
3837 global_ipv6_changed
= TRUE
;
3839 else if (CFEqual(change
, S_multicast_resolvers
)) {
3840 dnsinfo_changed
= TRUE
;
3842 else if (CFEqual(change
, S_private_resolvers
)) {
3843 dnsinfo_changed
= TRUE
;
3845 #if !TARGET_OS_IPHONE
3846 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
3847 dnsinfo_changed
= TRUE
;
3849 #endif /* !TARGET_OS_IPHONE */
3850 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
3851 CFStringRef serviceID
= parse_component(change
,
3852 S_state_service_prefix
);
3854 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
3855 CFRelease(serviceID
);
3858 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
3859 CFStringRef serviceID
= parse_component(change
,
3860 S_setup_service_prefix
);
3862 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
3863 CFRelease(serviceID
);
3868 /* grab a snapshot of everything we need */
3869 services_info
= services_info_copy(session
, service_changes
);
3870 service_order
= service_order_get(services_info
);
3871 if (service_order
!= NULL
) {
3872 n_service_order
= CFArrayGetCount(service_order
);
3873 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3874 SCLog(TRUE
, LOG_NOTICE
,
3875 CFSTR("IPMonitor: service_order %@ "), service_order
);
3878 n
= CFArrayGetCount(service_changes
);
3879 for (i
= 0; i
< n
; i
++) {
3881 CFStringRef serviceID
;
3883 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
3884 changes
= service_changed(services_info
, serviceID
);
3885 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
3886 /* if __Service__ (e.g. PrimaryRank) changed */
3887 global_ipv4_changed
= TRUE
;
3888 global_ipv6_changed
= TRUE
;
3891 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
3892 global_ipv4_changed
= TRUE
;
3893 dnsinfo_changed
= TRUE
;
3894 proxies_changed
= TRUE
;
3896 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
3897 global_ipv6_changed
= TRUE
;
3898 dnsinfo_changed
= TRUE
;
3899 proxies_changed
= TRUE
;
3902 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
3903 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
3904 update_dns(services_info
, serviceID
, &keys
);
3906 dnsinfo_changed
= TRUE
;
3908 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
3909 proxies_changed
= TRUE
;
3911 #if !TARGET_OS_IPHONE
3912 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
3913 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
3914 update_smb(services_info
, serviceID
, &keys
);
3917 #endif /* !TARGET_OS_IPHONE */
3920 if (global_ipv4_changed
) {
3921 IPv4RouteListRef new_routelist
= NULL
;
3922 CFStringRef new_primary
;
3924 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3925 SCLog(TRUE
, LOG_NOTICE
,
3926 CFSTR("IPMonitor: IPv4 service election"));
3928 new_primary
= elect_new_primary(&elect_ipv4
, &new_routelist
,
3929 service_order
, n_service_order
);
3930 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
3931 update_ipv4(S_primary_ipv4
, new_routelist
, &keys
);
3932 my_CFRelease(&new_primary
);
3934 if (global_ipv6_changed
) {
3935 CFStringRef new_primary
;
3937 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3938 SCLog(TRUE
, LOG_NOTICE
,
3939 CFSTR("IPMonitor: IPv6 service election"));
3941 new_primary
= elect_new_primary(&elect_ipv6
, NULL
,
3942 service_order
, n_service_order
);
3943 (void)set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6");
3944 update_ipv6(services_info
, S_primary_ipv6
, &keys
);
3945 my_CFRelease(&new_primary
);
3947 if (global_ipv4_changed
|| global_ipv6_changed
) {
3948 CFStringRef new_primary_dns
= NULL
;
3949 CFStringRef new_primary_proxies
= NULL
;
3950 #if !TARGET_OS_IPHONE
3951 CFStringRef new_primary_smb
= NULL
;
3952 #endif /* !TARGET_OS_IPHONE */
3954 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
3955 /* decide between IPv4 and IPv6 */
3956 if (rank_service_entity(S_ipv4_service_rank_dict
,
3957 S_primary_ipv4
, kSCEntNetDNS
)
3958 <= rank_service_entity(S_ipv6_service_rank_dict
,
3959 S_primary_ipv6
, kSCEntNetDNS
)) {
3960 new_primary_dns
= S_primary_ipv4
;
3963 new_primary_dns
= S_primary_ipv6
;
3965 if (rank_service_entity(S_ipv4_service_rank_dict
,
3966 S_primary_ipv4
, kSCEntNetProxies
)
3967 <= rank_service_entity(S_ipv6_service_rank_dict
,
3968 S_primary_ipv6
, kSCEntNetProxies
)) {
3969 new_primary_proxies
= S_primary_ipv4
;
3972 new_primary_proxies
= S_primary_ipv6
;
3974 #if !TARGET_OS_IPHONE
3975 if (rank_service_entity(S_ipv4_service_rank_dict
,
3976 S_primary_ipv4
, kSCEntNetSMB
)
3977 <= rank_service_entity(S_ipv6_service_rank_dict
,
3978 S_primary_ipv6
, kSCEntNetSMB
)) {
3979 new_primary_smb
= S_primary_ipv4
;
3982 new_primary_smb
= S_primary_ipv6
;
3984 #endif /* !TARGET_OS_IPHONE */
3987 else if (S_primary_ipv6
!= NULL
) {
3988 new_primary_dns
= S_primary_ipv6
;
3989 new_primary_proxies
= S_primary_ipv6
;
3990 #if !TARGET_OS_IPHONE
3991 new_primary_smb
= S_primary_ipv6
;
3992 #endif /* !TARGET_OS_IPHONE */
3994 else if (S_primary_ipv4
!= NULL
) {
3995 new_primary_dns
= S_primary_ipv4
;
3996 new_primary_proxies
= S_primary_ipv4
;
3997 #if !TARGET_OS_IPHONE
3998 new_primary_smb
= S_primary_ipv4
;
3999 #endif /* !TARGET_OS_IPHONE */
4002 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
4003 update_dns(services_info
, S_primary_dns
, &keys
);
4004 dnsinfo_changed
= TRUE
;
4006 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
, "Proxies")) {
4007 proxies_changed
= TRUE
;
4009 #if !TARGET_OS_IPHONE
4010 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
4011 update_smb(services_info
, S_primary_smb
, &keys
);
4013 #endif /* !TARGET_OS_IPHONE */
4015 if (dnsinfo_changed
) {
4016 update_dnsinfo(services_info
, S_primary_dns
, &keys
, service_order
);
4018 if (proxies_changed
|| dnsinfo_changed
) { // note: supplemental Proxies may follow supplemental DNS
4019 update_proxies(services_info
, S_primary_proxies
, &keys
, service_order
);
4021 my_CFRelease(&service_changes
);
4022 my_CFRelease(&services_info
);
4023 keyChangeListApplyToStore(&keys
, session
);
4024 keyChangeListFree(&keys
);
4031 CFMutableArrayRef keys
= NULL
;
4032 CFMutableArrayRef patterns
= NULL
;
4033 CFRunLoopSourceRef rls
= NULL
;
4035 if (S_is_network_boot() != 0) {
4040 if (S_is_scoped_routing_enabled() != 0) {
4041 S_scopedroute
= TRUE
;
4044 if (S_is_scoped_v6_routing_enabled() != 0) {
4045 S_scopedroute_v6
= TRUE
;
4047 #endif /* RTF_IFSCOPE */
4049 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
4050 IPMonitorNotify
, NULL
);
4051 if (S_session
== NULL
) {
4052 SCLog(TRUE
, LOG_ERR
,
4053 CFSTR("IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s"),
4054 SCErrorString(SCError()));
4058 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4059 kSCDynamicStoreDomainState
,
4062 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4063 kSCDynamicStoreDomainState
,
4066 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4067 kSCDynamicStoreDomainState
,
4069 S_state_global_proxies
4070 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4071 kSCDynamicStoreDomainState
,
4073 #if !TARGET_OS_IPHONE
4075 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4076 kSCDynamicStoreDomainState
,
4078 #endif /* !TARGET_OS_IPHONE */
4080 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4081 kSCDynamicStoreDomainSetup
,
4083 S_state_service_prefix
4084 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@/"),
4085 kSCDynamicStoreDomainState
,
4088 S_setup_service_prefix
4089 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@/"),
4090 kSCDynamicStoreDomainSetup
,
4093 S_service_state_dict
4094 = CFDictionaryCreateMutable(NULL
, 0,
4095 &kCFTypeDictionaryKeyCallBacks
,
4096 &kCFTypeDictionaryValueCallBacks
);
4098 S_ipv4_service_rank_dict
4099 = CFDictionaryCreateMutable(NULL
, 0,
4100 &kCFTypeDictionaryKeyCallBacks
,
4101 &kCFTypeDictionaryValueCallBacks
);
4103 S_ipv6_service_rank_dict
4104 = CFDictionaryCreateMutable(NULL
, 0,
4105 &kCFTypeDictionaryKeyCallBacks
,
4106 &kCFTypeDictionaryValueCallBacks
);
4108 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4109 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4111 /* register for State: and Setup: per-service notifications */
4112 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
4114 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
4115 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
4117 /* add notifier for multicast DNS configuration (Bonjour/.local) */
4118 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
4119 kSCDynamicStoreDomainState
,
4121 CFSTR(kDNSServiceCompMulticastDNS
));
4122 CFArrayAppendValue(keys
, S_multicast_resolvers
);
4124 /* add notifier for private DNS configuration (Back to My Mac) */
4125 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
4126 kSCDynamicStoreDomainState
,
4128 CFSTR(kDNSServiceCompPrivateDNS
));
4129 CFArrayAppendValue(keys
, S_private_resolvers
);
4131 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
4132 SCLog(TRUE
, LOG_ERR
,
4133 CFSTR("IPMonitor ip_plugin_init "
4134 "SCDynamicStoreSetNotificationKeys failed: %s"),
4135 SCErrorString(SCError()));
4139 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
4141 SCLog(TRUE
, LOG_ERR
,
4142 CFSTR("IPMonitor ip_plugin_init "
4143 "SCDynamicStoreCreateRunLoopSource failed: %s"),
4144 SCErrorString(SCError()));
4148 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
4151 /* initialize dns configuration */
4152 dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
4153 #if !TARGET_OS_IPHONE
4155 #endif /* !TARGET_OS_IPHONE */
4156 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
4158 #if !TARGET_OS_IPHONE
4159 /* initialize SMB configuration */
4160 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
4161 #endif /* !TARGET_OS_IPHONE */
4164 my_CFRelease(&keys
);
4165 my_CFRelease(&patterns
);
4173 /* initialize multicast route */
4174 update_ipv4(NULL
, NULL
, NULL
);
4179 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
4183 boolean_t ret
= def
;
4185 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
4187 ret
= CFBooleanGetValue(b
);
4194 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
4196 CFDictionaryRef info_dict
;
4198 info_dict
= CFBundleGetInfoDictionary(bundle
);
4199 if (info_dict
!= NULL
) {
4201 = S_get_plist_boolean(info_dict
,
4202 CFSTR("AppendStateArrayToSetupArray"),
4205 if (bundleVerbose
) {
4206 S_IPMonitor_debug
= kDebugFlagDefault
;
4209 dns_configuration_init(bundle
);
4210 proxy_configuration_init(bundle
);
4213 #if !TARGET_OS_IPHONE
4214 if (S_session
!= NULL
) {
4215 dns_configuration_monitor(S_session
, IPMonitorNotify
);
4217 #endif /* !TARGET_OS_IPHONE */
4219 load_hostname((S_IPMonitor_debug
& kDebugFlag1
) != 0);
4220 #if !TARGET_OS_IPHONE
4221 load_smb_configuration((S_IPMonitor_debug
& kDebugFlag1
) != 0);
4222 #endif /* !TARGET_OS_IPHONE */
4228 #ifdef TEST_IPMONITOR
4229 #include "dns-configuration.c"
4230 #include "set-hostname.c"
4233 main(int argc
, char **argv
)
4237 S_IPMonitor_debug
= kDebugFlag1
;
4239 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
4242 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
4249 #endif /* TEST_IPMONITOR */
4251 #ifdef TEST_IPV4_ROUTELIST
4252 #include "dns-configuration.c"
4253 #include "set-hostname.c"
4255 struct ipv4_service_contents
{
4259 const char * router
;
4260 const char * ifname
;
4262 const CFStringRef
*primaryRank
;
4266 * addr mask dest router ifname pri primaryRank
4268 struct ipv4_service_contents en0_10
= {
4269 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, NULL
4272 struct ipv4_service_contents en0_15
= {
4273 "10.0.0.19", "255.255.255.0", NULL
, "10.0.0.1", "en0", 15, NULL
4276 struct ipv4_service_contents en0_30
= {
4277 "10.0.0.11", "255.255.255.0", NULL
, "10.0.0.1", "en0", 30, NULL
4280 struct ipv4_service_contents en0_40
= {
4281 "10.0.0.12", "255.255.255.0", NULL
, "10.0.0.1", "en0", 40, NULL
4284 struct ipv4_service_contents en0_50
= {
4285 "10.0.0.13", "255.255.255.0", NULL
, "10.0.0.1", "en0", 50, NULL
4288 struct ipv4_service_contents en0_110
= {
4289 "192.168.2.10", "255.255.255.0", NULL
, "192.168.2.1", "en0", 110, NULL
4292 struct ipv4_service_contents en0_1
= {
4293 "17.202.40.191", "255.255.252.0", NULL
, "17.202.20.1", "en0", 1, NULL
4296 struct ipv4_service_contents en1_20
= {
4297 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, NULL
4300 struct ipv4_service_contents en1_2
= {
4301 "17.202.42.24", "255.255.252.0", NULL
, "17.202.20.1", "en1", 2, NULL
4304 struct ipv4_service_contents en1_125
= {
4305 "192.168.2.20", "255.255.255.0", NULL
, "192.168.2.1", "en1", 125, NULL
4308 struct ipv4_service_contents fw0_25
= {
4309 "192.168.2.30", "255.255.255.0", NULL
, "192.168.2.1", "fw0", 25, NULL
4312 struct ipv4_service_contents fw0_21
= {
4313 "192.168.3.30", "255.255.255.0", NULL
, "192.168.3.1", "fw0", 21, NULL
4316 struct ipv4_service_contents ppp0_0_1
= {
4317 "17.219.156.22", NULL
, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
4320 struct ipv4_service_contents en0_test6
= {
4321 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 2, NULL
4323 struct ipv4_service_contents en1_test6
= {
4324 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 3, NULL
4326 struct ipv4_service_contents en2_test6
= {
4327 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
4330 struct ipv4_service_contents en0_test7
= {
4331 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 3, NULL
4333 struct ipv4_service_contents en1_test7
= {
4334 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 2, NULL
4336 struct ipv4_service_contents en2_test7
= {
4337 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
4339 struct ipv4_service_contents fw0_test6_and_7
= {
4340 "169.254.11.33", "255.255.0.0", NULL
, NULL
, "fw0", UINT_MAX
, NULL
4343 struct ipv4_service_contents en0_10_last
= {
4344 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
4347 struct ipv4_service_contents en0_10_never
= {
4348 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
4351 struct ipv4_service_contents en1_20_first
= {
4352 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
4355 struct ipv4_service_contents en1_20_never
= {
4356 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
4359 struct ipv4_service_contents
* test1
[] = {
4370 struct ipv4_service_contents
* test2
[] = {
4380 struct ipv4_service_contents
* test3
[] = {
4394 struct ipv4_service_contents
* test4
[] = {
4403 struct ipv4_service_contents
* test5
[] = {
4413 struct ipv4_service_contents
* test6
[] = {
4421 struct ipv4_service_contents
* test7
[] = {
4429 struct ipv4_service_contents
* test8
[] = {
4435 struct ipv4_service_contents
* test9
[] = {
4442 struct ipv4_service_contents
* test10
[] = {
4449 struct ipv4_service_contents
* test11
[] = {
4456 struct ipv4_service_contents
* test12
[] = {
4462 struct ipv4_service_contents
* test13
[] = {
4468 struct ipv4_service_contents
* test14
[] = {
4474 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
4477 CFStringRef prop_val
;
4482 prop_val
= CFStringCreateWithCString(NULL
,
4484 kCFStringEncodingASCII
);
4485 CFDictionarySetValue(dict
, prop_name
, prop_val
);
4486 CFRelease(prop_val
);
4491 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
4495 CFStringRef prop_val
;
4500 prop_val
= CFStringCreateWithCString(NULL
,
4502 kCFStringEncodingASCII
);
4503 array
= CFArrayCreate(NULL
,
4504 (const void **)&prop_val
, 1,
4505 &kCFTypeArrayCallBacks
);
4506 CFRelease(prop_val
);
4507 CFDictionarySetValue(dict
, prop_name
, array
);
4512 static CFDictionaryRef
4513 make_IPv4_dict(struct ipv4_service_contents
* t
)
4515 CFMutableDictionaryRef dict
;
4517 dict
= CFDictionaryCreateMutable(NULL
, 0,
4518 &kCFTypeDictionaryKeyCallBacks
,
4519 &kCFTypeDictionaryValueCallBacks
);
4520 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
4521 dict_add_string_as_array(dict
, kSCPropNetIPv4SubnetMasks
, t
->mask
);
4522 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
4523 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
4524 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
4528 static IPv4RouteListRef
4529 make_IPv4RouteList(struct ipv4_service_contents
* * this_test
)
4532 IPv4RouteListRef routes
;
4533 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
4534 IPv4RouteListRef ret
= NULL
;
4535 struct ipv4_service_contents
* * scan_test
;
4537 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
4538 CFDictionaryRef dict
;
4540 dict
= make_IPv4_dict(*scan_test
);
4542 fprintf(stderr
, "make_IPv4_dict failed\n");
4545 routes
= (IPv4RouteListRef
)routes_buf
;
4546 routes
->size
= R_STATIC
;
4548 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
4549 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
4551 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
4554 ret
= IPv4RouteListAddRouteList(ret
, 1, r
, (*scan_test
)->rank
);
4564 * Function: run_test
4566 * Runs through the given set of routes first in the forward direction,
4567 * then again backwards. We should end up with exactly the same set of
4568 * routes at the end.
4571 run_test(const char * name
, struct ipv4_service_contents
* * this_test
)
4574 boolean_t ret
= FALSE
;
4576 IPv4RouteListRef routes
;
4577 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
4578 IPv4RouteListRef routes1
= NULL
, routes2
= NULL
;
4579 struct ipv4_service_contents
* * scan_test
;
4581 printf("\nStarting test %s\n", name
);
4582 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
4583 CFDictionaryRef dict
;
4585 dict
= make_IPv4_dict(*scan_test
);
4587 fprintf(stderr
, "make_IPv4_dict failed\n");
4590 routes
= (IPv4RouteListRef
)routes_buf
;
4591 routes
->size
= R_STATIC
;
4593 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
4594 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
4596 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
4599 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4600 descr
= IPv4RouteListCopyDescription(r
);
4601 SCLog(TRUE
, LOG_NOTICE
, CFSTR("test: Adding %@"), descr
);
4605 routes1
= IPv4RouteListAddRouteList(routes1
, 1, r
, (*scan_test
)->rank
);
4611 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4612 if (routes1
!= NULL
) {
4613 descr
= IPv4RouteListCopyDescription(routes1
);
4614 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Routes are %@"), descr
);
4618 for (scan_test
--; scan_test
>= this_test
; scan_test
--) {
4619 CFDictionaryRef dict
;
4621 dict
= make_IPv4_dict(*scan_test
);
4623 fprintf(stderr
, "make_IPv4_dict failed\n");
4626 routes
= (IPv4RouteListRef
)routes_buf
;
4627 routes
->size
= R_STATIC
;
4629 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
4630 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
4632 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
4635 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4636 descr
= IPv4RouteListCopyDescription(r
);
4637 SCLog(TRUE
, LOG_NOTICE
, CFSTR("test: Adding %@"), descr
);
4640 routes2
= IPv4RouteListAddRouteList(routes2
, 1, r
, (*scan_test
)->rank
);
4646 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4647 if (routes2
!= NULL
) {
4648 descr
= IPv4RouteListCopyDescription(routes2
);
4649 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Routes are %@"), descr
);
4653 if ((routes1
!= NULL
&& routes2
== NULL
)
4654 || (routes1
== NULL
&& routes2
!= NULL
)) {
4655 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
4656 (routes1
!= NULL
) ? "not " : "",
4657 (routes2
!= NULL
) ? "not " : "");
4659 else if (routes1
!= NULL
&& routes2
!= NULL
) {
4660 /* check if they are different */
4661 if (routes1
->count
!= routes2
->count
) {
4662 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
4663 routes1
->count
, routes2
->count
);
4665 else if (bcmp(routes1
, routes2
,
4666 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
4667 fprintf(stderr
, "routes1 and routes2 are different\n");
4670 printf("routes1 and routes2 are the same\n");
4674 if (routes1
!= NULL
) {
4677 if (routes2
!= NULL
) {
4683 typedef struct compare_context
{
4684 IPv4RouteListRef old
;
4685 IPv4RouteListRef
new;
4686 } compare_context_t
;
4689 compare_callback(IPv4RouteListApplyCommand cmd
, IPv4RouteRef route
, void * arg
)
4691 compare_context_t
* context
= (compare_context_t
*)arg
;
4694 case kIPv4RouteListAddRouteCommand
:
4695 printf("Add new[%ld] = ", route
- context
->new->list
);
4696 IPv4RoutePrint(route
);
4699 case kIPv4RouteListRemoveRouteCommand
:
4700 printf("Remove old[%ld] = ", route
- context
->old
->list
);
4701 IPv4RoutePrint(route
);
4711 compare_tests(struct ipv4_service_contents
* * old_test
,
4712 struct ipv4_service_contents
* * new_test
)
4714 IPv4RouteListRef new_routes
;
4715 IPv4RouteListRef old_routes
;
4716 compare_context_t context
;
4718 old_routes
= make_IPv4RouteList(old_test
);
4719 new_routes
= make_IPv4RouteList(new_test
);
4721 if (old_routes
== NULL
) {
4722 printf("No Old Routes\n");
4725 printf("Old Routes = ");
4726 IPv4RouteListPrint(old_routes
);
4728 if (new_routes
== NULL
) {
4729 printf("No New Routes\n");
4732 printf("New Routes = ");
4733 IPv4RouteListPrint(new_routes
);
4735 context
.old
= old_routes
;
4736 context
.new = new_routes
;
4737 IPv4RouteListApply(old_routes
, new_routes
, compare_callback
, &context
);
4738 if (old_routes
!= NULL
) {
4741 if (new_routes
!= NULL
) {
4749 main(int argc
, char **argv
)
4752 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
4754 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
4756 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
4759 if (run_test("test1", test1
) == FALSE
) {
4760 fprintf(stderr
, "test1 failed\n");
4763 if (run_test("test2", test2
) == FALSE
) {
4764 fprintf(stderr
, "test2 failed\n");
4767 if (run_test("test3", test4
) == FALSE
) {
4768 fprintf(stderr
, "test3 failed\n");
4771 if (run_test("test4", test4
) == FALSE
) {
4772 fprintf(stderr
, "test4 failed\n");
4775 if (run_test("test5", test5
) == FALSE
) {
4776 fprintf(stderr
, "test5 failed\n");
4780 printf("\nCompare 1 to 2:\n");
4781 compare_tests(test1
, test2
);
4783 printf("\nCompare 2 to 1:\n");
4784 compare_tests(test2
, test1
);
4786 printf("\nCompare 1 to 1:\n");
4787 compare_tests(test1
, test1
);
4789 printf("\nCompare 1 to 3:\n");
4790 compare_tests(test1
, test3
);
4792 printf("\nCompare 3 to 1:\n");
4793 compare_tests(test3
, test1
);
4795 printf("\nCompare 2 to 3:\n");
4796 compare_tests(test2
, test3
);
4798 printf("\nCompare 3 to 2:\n");
4799 compare_tests(test3
, test2
);
4801 printf("\nCompare 3 to 4:\n");
4802 compare_tests(test3
, test4
);
4804 printf("\nCompare 5 to 4:\n");
4805 compare_tests(test5
, test4
);
4807 printf("\nCompare 6 to 7:\n");
4808 compare_tests(test6
, test7
);
4810 printf("\nCompare 7 to 6:\n");
4811 compare_tests(test7
, test6
);
4813 printf("\nCompare 8 to 9:\n");
4814 compare_tests(test8
, test9
);
4816 printf("\nCompare 8 to 10:\n");
4817 compare_tests(test8
, test10
);
4819 printf("\nCompare 8 to 11:\n");
4820 compare_tests(test8
, test11
);
4822 printf("\nCompare 12 to 13:\n");
4823 compare_tests(test12
, test13
);
4825 printf("\nCompare 13 to 14:\n");
4826 compare_tests(test13
, test14
);
4828 printf("\nChecking for leaks\n");
4830 sprintf(cmd
, "leaks %d 2>&1", getpid());
4838 #endif /* TEST_IPV4_ROUTELIST */