2 * Copyright (c) 2000-2009 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/types.h>
77 #include <sys/socket.h>
78 #include <net/route.h>
80 #include <net/if_dl.h>
81 #include <netinet/in.h>
82 #include <arpa/inet.h>
83 #include <sys/sysctl.h>
87 #include <SystemConfiguration/SystemConfiguration.h>
88 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
89 #include <SystemConfiguration/SCValidation.h>
90 #include <SystemConfiguration/SCPrivate.h> /* for SCLog() */
93 #include <dnsinfo_create.h>
94 #endif /* !TARGET_OS_IPHONE */
97 #ifndef kDNSServiceCompMulticastDNS
98 #define kDNSServiceCompMulticastDNS "MulticastDNS"
100 #ifndef kDNSServiceCompPrivateDNS
101 #define kDNSServiceCompPrivateDNS "PrivateDNS"
105 kProtocolFlagsNone
= 0x0,
106 kProtocolFlagsIPv4
= 0x1,
107 kProtocolFlagsIPv6
= 0x2
109 typedef uint8_t ProtocolFlags
;
112 kDebugFlag1
= 0x00000001,
113 kDebugFlag2
= 0x00000002,
114 kDebugFlag4
= 0x00000004,
115 kDebugFlag8
= 0x00000008,
116 kDebugFlagDefault
= kDebugFlag1
,
117 kDebugFlagAll
= 0xffffffff
120 #ifdef TEST_IPV4_ROUTELIST
121 #define ROUTELIST_DEBUG(a, f) { if ((S_IPMonitor_debug & (f)) != 0) printf a ;}
123 #define ROUTELIST_DEBUG(a, f)
126 #include "set-hostname.h"
127 #include "dns-configuration.h"
128 #if !TARGET_OS_IPHONE
129 #include "smb-configuration.h"
130 #endif /* !TARGET_OS_IPHONE */
132 #define PPP_PREFIX "ppp"
134 #define IP_FORMAT "%d.%d.%d.%d"
135 #define IP_CH(ip) ((u_char *)(ip))
136 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
138 typedef uint32_t Rank
;
146 * IPv4 Route management
149 typedef uint32_t RouteFlags
;
152 kRouteIsDirectToInterfaceFlag
= 0x00000001,
153 kRouteIsNotSubnetLocalFlag
= 0x00000002,
154 kRouteChooseFirstFlag
= 0x00000004,
155 kRouteChooseLastFlag
= 0x00000008,
156 kRouteChooseNeverFlag
= 0x00000010,
157 kRouteIsScopedFlag
= 0x00000020,
158 kRouteWantScopedFlag
= (kRouteChooseNeverFlag
|kRouteIsScopedFlag
),
164 struct in_addr gateway
;
165 char ifname
[IFNAMSIZ
];
166 unsigned int ifindex
;
170 } IPv4Route
, *IPv4RouteRef
;
175 IPv4Route list
[1]; /* variable length */
176 } IPv4RouteList
, *IPv4RouteListRef
;
179 kIPv4RouteListAddRouteCommand
,
180 kIPv4RouteListRemoveRouteCommand
183 typedef uint32_t IPv4RouteListApplyCommand
;
185 typedef void IPv4RouteListApplyCallBackFunc(IPv4RouteListApplyCommand cmd
,
186 IPv4RouteRef route
, void * arg
);
187 typedef IPv4RouteListApplyCallBackFunc
* IPv4RouteListApplyCallBackFuncPtr
;
189 /* SCDynamicStore session */
190 static SCDynamicStoreRef S_session
= NULL
;
192 /* debug output flags */
193 static uint32_t S_IPMonitor_debug
= 0;
195 /* are we netbooted? If so, don't touch the default route */
196 static boolean_t S_netboot
= FALSE
;
198 /* is scoped routing enabled? */
200 static boolean_t S_scopedroute
= FALSE
;
201 #endif /* RTF_IFSCOPE */
203 /* dictionary to hold per-service state: key is the serviceID */
204 static CFMutableDictionaryRef S_service_state_dict
= NULL
;
205 static CFMutableDictionaryRef S_ipv4_service_rank_dict
= NULL
;
206 static CFMutableDictionaryRef S_ipv6_service_rank_dict
= NULL
;
208 /* if set, a PPP interface overrides the primary */
209 static boolean_t S_ppp_override_primary
= FALSE
;
211 /* the current primary serviceID's */
212 static CFStringRef S_primary_ipv4
= NULL
;
213 static CFStringRef S_primary_ipv6
= NULL
;
214 static CFStringRef S_primary_dns
= NULL
;
215 static CFStringRef S_primary_proxies
= NULL
;
217 static CFStringRef S_state_global_ipv4
= NULL
;
218 static CFStringRef S_state_global_ipv6
= NULL
;
219 static CFStringRef S_state_global_dns
= NULL
;
220 static CFStringRef S_state_global_proxies
= NULL
;
221 static CFStringRef S_state_service_prefix
= NULL
;
222 static CFStringRef S_setup_global_ipv4
= NULL
;
223 static CFStringRef S_setup_global_proxies
= NULL
;
224 static CFStringRef S_setup_service_prefix
= NULL
;
226 static CFStringRef S_multicast_resolvers
= NULL
;
227 static CFStringRef S_private_resolvers
= NULL
;
229 static IPv4RouteListRef S_ipv4_routelist
= NULL
;
231 static const struct in_addr S_ip_zeros
= { 0 };
232 static const struct in6_addr S_ip6_zeros
= IN6ADDR_ANY_INIT
;
234 static boolean_t S_append_state
= FALSE
;
236 #if !TARGET_OS_IPHONE
237 static CFStringRef S_primary_smb
= NULL
;
238 static CFStringRef S_state_global_smb
= NULL
;
239 static CFStringRef S_setup_global_smb
= NULL
;
240 #endif /* !TARGET_OS_IPHONE */
242 #if !TARGET_OS_IPHONE
243 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
244 #endif /* !TARGET_OS_IPHONE */
247 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
251 ** entityType*, GetEntityChanges*
252 ** - definitions for the entity types we handle
259 #if !TARGET_OS_IPHONE
261 #endif /* !TARGET_OS_IPHONE */
263 kEntityTypeServiceOptions
= 31
265 typedef uint32_t EntityType
;
267 static const CFStringRef
*entityTypeNames
[ENTITY_TYPES_COUNT
] = {
268 &kSCEntNetIPv4
, /* 0 */
269 &kSCEntNetIPv6
, /* 1 */
270 &kSCEntNetDNS
, /* 2 */
271 &kSCEntNetProxies
, /* 3 */
272 #if !TARGET_OS_IPHONE
273 &kSCEntNetSMB
, /* 4 */
274 #endif /* !TARGET_OS_IPHONE */
277 typedef boolean_t
GetEntityChangesFunc(CFStringRef serviceID
,
278 CFDictionaryRef state_dict
,
279 CFDictionaryRef setup_dict
,
280 CFDictionaryRef info
);
281 typedef GetEntityChangesFunc
* GetEntityChangesFuncRef
;
283 static GetEntityChangesFunc get_ipv4_changes
;
284 static GetEntityChangesFunc get_ipv6_changes
;
285 static GetEntityChangesFunc get_dns_changes
;
286 static GetEntityChangesFunc get_proxies_changes
;
287 #if !TARGET_OS_IPHONE
288 static GetEntityChangesFunc get_smb_changes
;
289 #endif /* !TARGET_OS_IPHONE */
292 my_CFRelease(void * t
);
295 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new);
298 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
);
300 static const GetEntityChangesFuncRef entityChangeFunc
[ENTITY_TYPES_COUNT
] = {
301 get_ipv4_changes
, /* 0 */
302 get_ipv6_changes
, /* 1 */
303 get_dns_changes
, /* 2 */
304 get_proxies_changes
,/* 3 */
305 #if !TARGET_OS_IPHONE
306 get_smb_changes
, /* 4 */
307 #endif /* !TARGET_OS_IPHONE */
312 ** - mechanism to do an atomic update of the SCDynamicStore
313 ** when the content needs to be changed across multiple functions
316 CFMutableArrayRef notify
;
317 CFMutableArrayRef remove
;
318 CFMutableDictionaryRef set
;
319 } keyChangeList
, * keyChangeListRef
;
322 keyChangeListInit(keyChangeListRef keys
)
324 keys
->notify
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
325 keys
->remove
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
326 keys
->set
= CFDictionaryCreateMutable(NULL
, 0,
327 &kCFTypeDictionaryKeyCallBacks
,
328 &kCFTypeDictionaryValueCallBacks
);
333 keyChangeListFree(keyChangeListRef keys
)
335 my_CFRelease(&keys
->notify
);
336 my_CFRelease(&keys
->remove
);
337 my_CFRelease(&keys
->set
);
342 keyChangeListNotifyKey(keyChangeListRef keys
, CFStringRef key
)
344 my_CFArrayAppendUniqueValue(keys
->notify
, key
);
349 keyChangeListRemoveValue(keyChangeListRef keys
, CFStringRef key
)
351 my_CFArrayAppendUniqueValue(keys
->remove
, key
);
352 CFDictionaryRemoveValue(keys
->set
, key
);
357 keyChangeListSetValue(keyChangeListRef keys
, CFStringRef key
, CFTypeRef value
)
359 my_CFArrayRemoveValue(keys
->remove
, key
);
360 CFDictionarySetValue(keys
->set
, key
, value
);
365 keyChangeListApplyToStore(keyChangeListRef keys
, SCDynamicStoreRef session
)
367 CFArrayRef notify
= keys
->notify
;
368 CFArrayRef remove
= keys
->remove
;
369 CFDictionaryRef set
= keys
->set
;
372 if (CFArrayGetCount(notify
) == 0) {
375 if (CFArrayGetCount(remove
) == 0) {
378 if (CFDictionaryGetCount(set
) == 0) {
381 if (set
== NULL
&& remove
== NULL
&& notify
== NULL
) {
384 if (S_IPMonitor_debug
& kDebugFlag1
) {
386 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: Setting:\n%@"),
389 if (remove
!= NULL
) {
390 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: Removing:\n%@"),
393 if (notify
!= NULL
) {
394 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: Notifying:\n%@"),
398 (void)SCDynamicStoreSetMultiple(session
, set
, remove
, notify
);
400 status
= notify_post("com.apple.system.config.network_change");
401 if (status
== NOTIFY_STATUS_OK
) {
402 SCLog(TRUE
, LOG_NOTICE
, CFSTR("network configuration changed."));
404 SCLog(TRUE
, LOG_NOTICE
,
405 CFSTR("IPMonitor: notify_post() failed: error=%ld"), status
);
419 mib
[1] = KERN_NETBOOT
;
420 len
= sizeof(netboot
);
421 sysctl(mib
, 2, &netboot
, &len
, NULL
, 0);
427 S_is_scoped_routing_enabled()
430 size_t len
= sizeof(scopedroute
);
432 if ((sysctlbyname("net.inet.ip.scopedroute",
435 && (errno
!= ENOENT
)) {
436 SCLog(TRUE
, LOG_ERR
, CFSTR("sysctlbyname() failed: %s"), strerror(errno
));
438 return (scopedroute
);
440 #endif /* RTF_IFSCOPE */
443 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr
, CFTypeRef
new)
445 CFIndex n
= CFArrayGetCount(arr
);
447 if (CFArrayContainsValue(arr
, CFRangeMake(0, n
), new)) {
450 CFArrayAppendValue(arr
, new);
455 my_CFArrayRemoveValue(CFMutableArrayRef arr
, CFStringRef key
)
459 i
= CFArrayGetFirstIndexOfValue(arr
,
460 CFRangeMake(0, CFArrayGetCount(arr
)),
462 if (i
!= kCFNotFound
) {
463 CFArrayRemoveValueAtIndex(arr
, i
);
469 my_CFRelease(void * t
)
471 void * * obj
= (void * *)t
;
480 static CFDictionaryRef
481 my_CFDictionaryGetDictionary(CFDictionaryRef dict
, CFStringRef key
)
483 if (isA_CFDictionary(dict
) == NULL
) {
486 return (isA_CFDictionary(CFDictionaryGetValue(dict
, key
)));
490 my_CFDictionaryGetArray(CFDictionaryRef dict
, CFStringRef key
)
492 if (isA_CFDictionary(dict
) == NULL
) {
495 return (isA_CFArray(CFDictionaryGetValue(dict
, key
)));
499 cfstring_to_ipvx(int family
, CFStringRef str
, void * addr
, int addr_size
)
503 if (isA_CFString(str
) == NULL
) {
509 if (addr_size
< sizeof(struct in_addr
)) {
514 if (addr_size
< sizeof(struct in6_addr
)) {
521 (void)_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
);
522 if (inet_pton(family
, buf
, addr
) == 1) {
526 bzero(addr
, addr_size
);
531 cfstring_to_ip(CFStringRef str
, struct in_addr
* ip_p
)
533 return (cfstring_to_ipvx(AF_INET
, str
, ip_p
, sizeof(*ip_p
)));
537 cfstring_to_ip6(CFStringRef str
, struct in6_addr
* ip6_p
)
539 return (cfstring_to_ipvx(AF_INET6
, str
, ip6_p
, sizeof(*ip6_p
)));
543 setup_service_key(CFStringRef serviceID
, CFStringRef entity
)
545 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
546 kSCDynamicStoreDomainSetup
,
552 state_service_key(CFStringRef serviceID
, CFStringRef entity
)
554 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
555 kSCDynamicStoreDomainState
,
560 static CFDictionaryRef
561 get_service_setup_entity(CFDictionaryRef service_info
, CFStringRef serviceID
,
564 CFStringRef setup_key
;
565 CFDictionaryRef setup_dict
;
567 setup_key
= setup_service_key(serviceID
, entity
);
568 setup_dict
= my_CFDictionaryGetDictionary(service_info
, setup_key
);
569 my_CFRelease(&setup_key
);
573 static CFDictionaryRef
574 get_service_state_entity(CFDictionaryRef service_info
, CFStringRef serviceID
,
577 CFStringRef state_key
;
578 CFDictionaryRef state_dict
;
580 state_key
= state_service_key(serviceID
, entity
);
581 state_dict
= my_CFDictionaryGetDictionary(service_info
, state_key
);
582 my_CFRelease(&state_key
);
587 dict_get_first_ip(CFDictionaryRef dict
, CFStringRef prop
, struct in_addr
* ip_p
)
591 ip_list
= CFDictionaryGetValue(dict
, prop
);
592 if (isA_CFArray(ip_list
) != NULL
593 && CFArrayGetCount(ip_list
) > 0
594 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list
, 0), ip_p
)) {
601 get_override_primary(CFDictionaryRef dict
)
605 override
= CFDictionaryGetValue(dict
, kSCPropNetOverridePrimary
);
606 if (isA_CFNumber(override
) != NULL
) {
609 CFNumberGetValue((CFNumberRef
)override
, kCFNumberIntType
, &val
);
614 else if (isA_CFBoolean(override
) != NULL
) {
615 if (CFBooleanGetValue(override
)) {
626 static __inline__
struct in_addr
627 subnet_addr(struct in_addr addr
, struct in_addr mask
)
631 net
.s_addr
= addr
.s_addr
& mask
.s_addr
;
635 static __inline__
int
636 uint32_cmp(uint32_t a
, uint32_t b
)
652 static __inline__
int
653 in_addr_cmp(struct in_addr a
, struct in_addr b
)
655 return (uint32_cmp(ntohl(a
.s_addr
), ntohl(b
.s_addr
)));
658 static __inline__
int
659 RankCompare(Rank a
, Rank b
)
661 return (uint32_cmp(a
, b
));
664 static __inline__
int
665 RouteFlagsCompare(RouteFlags a
, RouteFlags b
)
667 return (uint32_cmp(a
, b
));
671 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r
, CFMutableStringRef str
)
673 CFStringAppendFormat(str
, NULL
,
674 CFSTR("Dest " IP_FORMAT
677 " Ifp %s Ifa " IP_FORMAT
),
680 IP_LIST(&r
->gateway
),
681 (r
->ifname
[0] != '\0') ? r
->ifname
: "<none>",
683 if ((r
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
684 CFStringAppend(str
, CFSTR(" [non-local]"));
686 else if ((r
->flags
& kRouteIsDirectToInterfaceFlag
) != 0) {
687 CFStringAppend(str
, CFSTR(" [direct]"));
689 if ((r
->flags
& kRouteChooseFirstFlag
) != 0) {
690 CFStringAppend(str
, CFSTR(" [first]"));
692 if ((r
->flags
& kRouteChooseLastFlag
) != 0) {
693 CFStringAppend(str
, CFSTR(" [last]"));
695 if ((r
->flags
& kRouteChooseNeverFlag
) != 0) {
696 CFStringAppend(str
, CFSTR(" [never]"));
698 if ((r
->flags
& kRouteIsScopedFlag
) != 0) {
699 CFStringAppend(str
, CFSTR(" [SCOPED]"));
701 else if ((r
->flags
& kRouteWantScopedFlag
) != 0) {
702 CFStringAppend(str
, CFSTR(" [SCOPED*]"));
708 IPv4RouteCopyDescription(IPv4RouteRef r
)
710 CFMutableStringRef str
;
712 str
= CFStringCreateMutable(NULL
, 0);
713 IPv4RouteCopyDescriptionWithString(r
, str
);
717 static __inline__
void
718 IPv4RoutePrint(IPv4RouteRef route
)
720 CFStringRef str
= IPv4RouteCopyDescription(route
);
722 SCPrint(TRUE
, stdout
, CFSTR("%@"), str
);
727 static __inline__
void
728 IPv4RouteLog(IPv4RouteRef route
)
730 CFStringRef str
= IPv4RouteCopyDescription(route
);
732 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@"), str
);
738 IPv4RouteCompare(IPv4RouteRef a
, Rank a_rank
,
739 IPv4RouteRef b
, Rank b_rank
, boolean_t
* same_dest
)
744 cmp
= in_addr_cmp(a
->dest
, b
->dest
);
746 cmp
= in_addr_cmp(a
->mask
, b
->mask
);
748 int name_cmp
= strcmp(a
->ifname
, b
->ifname
);
754 boolean_t a_never
= (a
->flags
& kRouteChooseNeverFlag
) != 0;
755 boolean_t b_never
= (b
->flags
& kRouteChooseNeverFlag
) != 0;
758 if (a_never
!= b_never
) {
767 boolean_t a_last
= (a
->flags
& kRouteChooseLastFlag
) != 0;
768 boolean_t b_last
= (b
->flags
& kRouteChooseLastFlag
) != 0;
770 if (a_last
!= b_last
) {
779 boolean_t a_first
= (a
->flags
& kRouteChooseFirstFlag
) != 0;
780 boolean_t b_first
= (b
->flags
& kRouteChooseFirstFlag
) != 0;
782 if (a_first
!= b_first
) {
791 cmp
= RankCompare(a_rank
, b_rank
);
801 if ((S_IPMonitor_debug
& kDebugFlag8
) != 0) {
815 a_str
= IPv4RouteCopyDescription(a
);
816 b_str
= IPv4RouteCopyDescription(b
);
817 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@ rank %u %c %@ rank %u"),
818 a_str
, a_rank
, ch
, b_str
, b_rank
);
826 IPv4RouteListCopyDescription(IPv4RouteListRef routes
)
830 CFMutableStringRef str
;
832 str
= CFStringCreateMutable(NULL
, 0);
833 CFStringAppendFormat(str
, NULL
, CFSTR("<IPv4RouteList[%d]> = {"),
835 for (i
= 0, r
= routes
->list
; i
< routes
->count
; i
++, r
++) {
836 CFStringAppendFormat(str
, NULL
, CFSTR("\n%2d. "), i
);
837 IPv4RouteCopyDescriptionWithString(r
, str
);
839 CFStringAppend(str
, CFSTR("\n}\n"));
843 static __inline__
void
844 IPv4RouteListPrint(IPv4RouteListRef routes
)
846 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
848 SCPrint(TRUE
, stdout
, CFSTR("%@"), str
);
853 static __inline__
void
854 IPv4RouteListLog(IPv4RouteListRef routes
)
856 CFStringRef str
= IPv4RouteListCopyDescription(routes
);
858 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@"), str
);
863 static __inline__
unsigned int
864 IPv4RouteListComputeSize(unsigned int n
)
866 return (offsetof(IPv4RouteList
, list
[n
]));
870 IPv4RouteListFindRoute(IPv4RouteListRef routes
, IPv4RouteRef route
)
873 IPv4RouteRef scan_result
= NULL
;
876 for (i
= 0, scan
= routes
->list
; i
< routes
->count
; i
++, scan
++) {
877 if ((scan
->dest
.s_addr
== route
->dest
.s_addr
)
878 && (scan
->mask
.s_addr
== route
->mask
.s_addr
)
879 && (strcmp(scan
->ifname
, route
->ifname
) == 0)
880 && (scan
->ifa
.s_addr
== route
->ifa
.s_addr
)
881 && (scan
->gateway
.s_addr
== route
->gateway
.s_addr
)) {
883 * So far, the routes look the same. If the flags
884 * are also equiv than we've found a match.
889 s_flags
= scan
->flags
;
890 if ((s_flags
& kRouteWantScopedFlag
) != 0) {
891 s_flags
|= kRouteWantScopedFlag
;
893 r_flags
= route
->flags
;
894 if ((r_flags
& kRouteWantScopedFlag
) != 0) {
895 r_flags
|= kRouteWantScopedFlag
;
897 if (s_flags
== r_flags
) {
903 return (scan_result
);
907 IPv4RouteListApply(IPv4RouteListRef old_routes
, IPv4RouteListRef new_routes
,
908 IPv4RouteListApplyCallBackFuncPtr func
, void * arg
)
913 if (old_routes
== new_routes
&& old_routes
== NULL
) {
914 /* both old and new are NULL, so there's nothing to do */
917 if (old_routes
!= NULL
) {
918 for (i
= 0, scan
= old_routes
->list
;
919 i
< old_routes
->count
;
921 IPv4RouteRef new_route
= NULL
;
923 if (new_routes
!= NULL
) {
924 new_route
= IPv4RouteListFindRoute(new_routes
, scan
);
926 if (new_route
== NULL
) {
928 (*func
)(kIPv4RouteListRemoveRouteCommand
, scan
, arg
);
933 if (new_routes
!= NULL
) {
934 for (i
= 0, scan
= new_routes
->list
;
935 i
< new_routes
->count
;
937 IPv4RouteRef old_route
= NULL
;
939 if (old_routes
!= NULL
) {
940 old_route
= IPv4RouteListFindRoute(old_routes
, scan
);
942 if (old_route
== NULL
) {
944 (*func
)(kIPv4RouteListAddRouteCommand
, scan
, arg
);
953 * Function: IPv4RouteListAddRoute
956 * Add the given IPv4Route to the list of routes, eliminating lower-ranked
957 * duplicates on the same interface, and marking any lower ranked duplicates
958 * on other interfaces with kRouteIsScopedFlag.
960 * This routine assumes that if routes is not NULL, it is malloc'd memory.
963 * Route list updated with the given route, possibly a different pointer,
964 * due to using realloc'd memory.
973 static IPv4RouteListRef
974 IPv4RouteListAddRoute(IPv4RouteListRef routes
, int init_size
,
975 IPv4RouteRef this_route
, Rank this_rank
)
978 int scope_which
= kScopeNone
;
982 if (routes
== NULL
) {
983 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(init_size
));
984 routes
->size
= init_size
;
987 for (i
= 0, scan
= routes
->list
; i
< routes
->count
;
992 cmp
= IPv4RouteCompare(this_route
, this_rank
, scan
, scan
->rank
, &same_dest
);
995 if (same_dest
== TRUE
) {
996 if ((scan
->flags
& kRouteIsScopedFlag
) != 0) {
997 ROUTELIST_DEBUG(("Hit 1: set scope on self\n"),
999 scope_which
= kScopeThis
;
1002 ROUTELIST_DEBUG(("Hit 2: set scope on next\n"),
1004 scope_which
= kScopeNext
;
1007 /* remember our insertion point, but keep going to find a dup */
1011 else if (cmp
== 0) {
1014 /* this route is a duplicate */
1015 ROUTELIST_DEBUG(("Hit 3: removing [%d]\n", i
), kDebugFlag8
);
1017 if (i
== routes
->count
) {
1018 /* last slot, decrementing gets rid of it */
1021 bcopy(routes
->list
+ i
+ 1,
1023 sizeof(routes
->list
[0]) * (routes
->count
- i
));
1027 /* resolve conflict using rank */
1028 if (this_rank
< scan
->rank
) {
1029 boolean_t is_scoped
= FALSE
;
1031 if (scan
->flags
& kRouteIsScopedFlag
) {
1034 ROUTELIST_DEBUG(("Hit 4:replacing [%d] rank %u < %u\n", i
,
1036 scan
->rank
), kDebugFlag8
);
1037 *scan
= *this_route
;
1038 scan
->rank
= this_rank
;
1040 /* preserve whether route was scoped */
1041 ROUTELIST_DEBUG(("Hit 5: preserved scope\n"), kDebugFlag8
);
1042 scan
->flags
|= kRouteIsScopedFlag
;
1049 if (same_dest
== TRUE
) {
1050 if (scope_which
== kScopeNone
) {
1051 ROUTELIST_DEBUG(("Hit 10: set scope on self\n"),
1053 scope_which
= kScopeThis
;
1056 #ifdef TEST_IPV4_ROUTELIST
1057 else if (where
!= -1) {
1058 /* not possible because we maintain a sorted list */
1059 ROUTELIST_DEBUG(("Hit 11: moved past routes - can't happen\n"),
1063 #endif /* TEST_IPV4_ROUTELIST */
1066 if (routes
->size
== routes
->count
) {
1068 IPv4RouteListRef new_routes
;
1071 /* double the size */
1072 old_size
= routes
->size
;
1073 how_many
= old_size
* 2;
1074 new_routes
= (IPv4RouteListRef
)
1075 realloc(routes
, IPv4RouteListComputeSize(how_many
));
1076 if (new_routes
== NULL
) {
1080 ROUTELIST_DEBUG(("increasing size from %d to %d\n", old_size
,
1081 how_many
), kDebugFlag8
);
1082 new_routes
->size
= how_many
;
1083 routes
= new_routes
;
1086 /* add it to the end */
1087 where
= routes
->count
;
1090 /* insert it at [where] */
1091 bcopy(routes
->list
+ where
,
1092 routes
->list
+ where
+ 1,
1093 sizeof(routes
->list
[0]) * (routes
->count
- where
));
1095 /* copy the route */
1096 routes
->list
[where
] = *this_route
;
1097 routes
->list
[where
].rank
= this_rank
;
1100 switch (scope_which
) {
1102 routes
->list
[where
].flags
|= kRouteIsScopedFlag
;
1105 routes
->list
[where
+ 1].flags
|= kRouteIsScopedFlag
;
1117 * Function: IPv4RouteListAddRouteList
1120 * Invoke IPv4RouteListAddRoute for each route in the given list.
1123 * See IPv4RouteListAddRoute for more information.
1125 static IPv4RouteListRef
1126 IPv4RouteListAddRouteList(IPv4RouteListRef routes
, int init_size
,
1127 IPv4RouteListRef service_routes
, Rank rank
)
1132 for (i
= 0, scan
= service_routes
->list
;
1133 i
< service_routes
->count
; i
++, scan
++) {
1134 routes
= IPv4RouteListAddRoute(routes
, init_size
, scan
, rank
);
1140 plist_get_cstring(CFDictionaryRef dict
, CFStringRef prop_name
,
1141 char * buf
, int buf_size
)
1145 val
= CFDictionaryGetValue(dict
, prop_name
);
1146 if (isA_CFString(val
) == NULL
) {
1149 if (CFStringGetCString(val
, buf
, buf_size
, kCFStringEncodingASCII
)
1157 * Function: IPv4RouteListCreateWithDictionary
1160 * Given the service ipv4 entity dictionary, generate the list of routes.
1161 * Currently, this includes just the default route and subnet route,
1162 * if the service has a subnet mask.
1165 * If the passed in route_list is NULL or too small, this routine
1166 * allocates malloc'd memory to hold the routes.
1168 static IPv4RouteListRef
1169 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes
,
1170 CFDictionaryRef dict
,
1171 CFStringRef primaryRank
)
1173 struct in_addr addr
= { 0 };
1174 RouteFlags flags
= 0;
1175 unsigned int ifindex
;
1177 struct in_addr mask
= { 0 };
1180 struct in_addr subnet
= { 0 };
1181 struct in_addr router
= { 0 };
1186 if (plist_get_cstring(dict
, kSCPropInterfaceName
, ifn
, sizeof(ifn
))
1190 #ifdef TEST_IPV4_ROUTELIST
1192 #else /* TEST_IPV4_ROUTELIST */
1193 ifindex
= if_nametoindex(ifn
);
1195 /* interface doesn't exist */
1198 #endif /* TEST_IPV4_ROUTELIST */
1199 if (cfstring_to_ip(CFDictionaryGetValue(dict
, kSCPropNetIPv4Router
),
1201 (void)dict_get_first_ip(dict
, kSCPropNetIPv4DestAddresses
, &router
);
1204 if (dict_get_first_ip(dict
, kSCPropNetIPv4Addresses
, &addr
)
1205 && dict_get_first_ip(dict
, kSCPropNetIPv4SubnetMasks
, &mask
)) {
1207 subnet
= subnet_addr(addr
, mask
);
1208 /* ignore link-local subnets, let IPConfiguration handle them for now */
1209 if (ntohl(subnet
.s_addr
) != IN_LINKLOCALNETNUM
) {
1213 if (addr
.s_addr
== 0) {
1214 /* thanks for playing */
1217 if (router
.s_addr
== 0) {
1218 flags
|= kRouteIsDirectToInterfaceFlag
| kRouteChooseLastFlag
;
1222 * If the router address is our address and the subnet mask is
1223 * not 255.255.255.255, assume all routes are local to the interface.
1225 if (addr
.s_addr
== router
.s_addr
1226 && ifn
[0] != '\0' && mask
.s_addr
!= INADDR_BROADCAST
) {
1227 flags
|= kRouteIsDirectToInterfaceFlag
;
1229 if (primaryRank
!= NULL
) {
1230 if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankNever
)) {
1231 flags
|= kRouteChooseNeverFlag
;
1232 } else if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankFirst
)) {
1233 flags
|= kRouteChooseFirstFlag
;
1234 } else if (CFEqual(primaryRank
, kSCValNetServicePrimaryRankLast
)) {
1235 flags
|= kRouteChooseLastFlag
;
1237 } else if (get_override_primary(dict
)) {
1238 flags
|= kRouteChooseFirstFlag
;
1241 if (n
> 1 && (flags
& kRouteIsDirectToInterfaceFlag
) == 0
1242 && subnet
.s_addr
!= subnet_addr(router
, mask
).s_addr
) {
1243 flags
|= kRouteIsNotSubnetLocalFlag
;
1246 if (routes
== NULL
|| routes
->size
< n
) {
1247 routes
= (IPv4RouteListRef
)malloc(IPv4RouteListComputeSize(n
));
1250 bzero(routes
, IPv4RouteListComputeSize(n
));
1253 /* start at the beginning */
1256 /* add the default route */
1257 r
->ifindex
= ifindex
;
1258 strlcpy(r
->ifname
, ifn
, sizeof(r
->ifname
));
1261 if ((flags
& kRouteIsDirectToInterfaceFlag
) == 0) {
1262 r
->gateway
= router
;
1270 /* add the subnet route */
1272 r
->ifindex
= ifindex
;
1276 strlcpy(r
->ifname
, ifn
, sizeof(r
->ifname
));
1278 r
->flags
= flags
& (kRouteChooseFirstFlag
|kRouteChooseLastFlag
|kRouteChooseNeverFlag
);
1285 * Function: parse_component
1287 * Given a string 'key' and a string prefix 'prefix',
1288 * return the next component in the slash '/' separated
1292 * 1. key = "a/b/c" prefix = "a/"
1294 * 2. key = "a/b/c" prefix = "a/b/"
1298 parse_component(CFStringRef key
, CFStringRef prefix
)
1300 CFMutableStringRef comp
;
1303 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
1306 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
1310 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
1311 range
= CFStringFind(comp
, CFSTR("/"), 0);
1312 if (range
.location
== kCFNotFound
) {
1315 range
.length
= CFStringGetLength(comp
) - range
.location
;
1316 CFStringDelete(comp
, range
);
1320 static CFMutableDictionaryRef
1321 service_dict_copy(CFStringRef serviceID
)
1323 CFDictionaryRef d
= NULL
;
1324 CFMutableDictionaryRef service_dict
;
1326 /* create a modifyable dictionary, a copy or a new one */
1327 d
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
1330 = CFDictionaryCreateMutable(NULL
, 0,
1331 &kCFTypeDictionaryKeyCallBacks
,
1332 &kCFTypeDictionaryValueCallBacks
);
1335 service_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, d
);
1337 return (service_dict
);
1341 dump_service_entity(CFStringRef serviceID
, CFStringRef entity
,
1342 CFStringRef operation
, CFTypeRef val
)
1344 CFStringRef this_val
= NULL
;
1346 if (isA_CFData(val
) && CFEqual(entity
, kSCEntNetIPv4
)) {
1347 this_val
= IPv4RouteListCopyDescription((IPv4RouteListRef
)
1348 CFDataGetBytePtr(val
));
1349 if (this_val
!= NULL
) {
1354 val
= CFSTR("<none>");
1356 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: serviceID %@ %@ %@ value = %@"),
1357 serviceID
, operation
, entity
, val
);
1358 my_CFRelease(&this_val
);
1363 service_dict_set(CFStringRef serviceID
, CFStringRef entity
,
1366 boolean_t changed
= FALSE
;
1368 CFMutableDictionaryRef service_dict
;
1370 service_dict
= service_dict_copy(serviceID
);
1371 old_val
= CFDictionaryGetValue(service_dict
, entity
);
1372 if (new_val
== NULL
) {
1373 if (old_val
!= NULL
) {
1374 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1375 dump_service_entity(serviceID
, entity
, CFSTR("Removed:"),
1378 CFDictionaryRemoveValue(service_dict
, entity
);
1383 if (old_val
== NULL
|| CFEqual(new_val
, old_val
) == FALSE
) {
1384 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1385 dump_service_entity(serviceID
, entity
,
1386 CFSTR("Changed: old"), old_val
);
1387 dump_service_entity(serviceID
, entity
,
1388 CFSTR("Changed: new"), new_val
);
1390 CFDictionarySetValue(service_dict
, entity
, new_val
);
1394 if (CFDictionaryGetCount(service_dict
) == 0) {
1395 CFDictionaryRemoveValue(S_service_state_dict
, serviceID
);
1398 CFDictionarySetValue(S_service_state_dict
, serviceID
, service_dict
);
1400 my_CFRelease(&service_dict
);
1404 static CFDictionaryRef
1405 service_dict_get(CFStringRef serviceID
, CFStringRef entity
)
1407 CFDictionaryRef service_dict
;
1409 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
1410 if (service_dict
== NULL
) {
1413 return (CFDictionaryGetValue(service_dict
, entity
));
1416 #define ALLOW_EMPTY_STRING 0x1
1419 sanitize_prop(CFTypeRef val
, uint32_t flags
)
1422 if (isA_CFString(val
)) {
1423 CFMutableStringRef str
;
1425 str
= CFStringCreateMutableCopy(NULL
, 0, (CFStringRef
)val
);
1426 CFStringTrimWhitespace(str
);
1427 if (!(flags
& ALLOW_EMPTY_STRING
) && (CFStringGetLength(str
) == 0)) {
1441 merge_array_prop(CFMutableDictionaryRef dict
,
1443 CFDictionaryRef state_dict
,
1444 CFDictionaryRef setup_dict
,
1448 CFMutableArrayRef merge_prop
;
1449 CFArrayRef setup_prop
= NULL
;
1450 CFArrayRef state_prop
= NULL
;
1452 if (setup_dict
!= NULL
) {
1453 setup_prop
= isA_CFArray(CFDictionaryGetValue(setup_dict
, key
));
1455 if (state_dict
!= NULL
) {
1456 state_prop
= isA_CFArray(CFDictionaryGetValue(state_dict
, key
));
1459 if ((setup_prop
== NULL
) && (state_prop
== NULL
)) {
1463 merge_prop
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1464 if (setup_prop
!= NULL
) {
1468 n
= CFArrayGetCount(setup_prop
);
1469 for (i
= 0; i
< n
; i
++) {
1472 val
= CFArrayGetValueAtIndex(setup_prop
, i
);
1473 val
= sanitize_prop(val
, flags
);
1475 CFArrayAppendValue(merge_prop
, val
);
1480 if (state_prop
!= NULL
1481 && (setup_prop
== NULL
|| S_append_state
)) {
1484 CFRange setup_range
= CFRangeMake(0, CFArrayGetCount(merge_prop
));
1486 n
= CFArrayGetCount(state_prop
);
1487 for (i
= 0; i
< n
; i
++) {
1490 val
= CFArrayGetValueAtIndex(state_prop
, i
);
1491 val
= sanitize_prop(val
, flags
);
1493 if (append
|| !CFArrayContainsValue(merge_prop
, setup_range
, val
)) {
1494 CFArrayAppendValue(merge_prop
, val
);
1500 if (CFArrayGetCount(merge_prop
) > 0) {
1501 CFDictionarySetValue(dict
, key
, merge_prop
);
1503 CFRelease(merge_prop
);
1508 pick_prop(CFMutableDictionaryRef dict
,
1510 CFDictionaryRef state_dict
,
1511 CFDictionaryRef setup_dict
,
1514 CFTypeRef val
= NULL
;
1516 if (setup_dict
!= NULL
) {
1517 val
= CFDictionaryGetValue(setup_dict
, key
);
1518 val
= sanitize_prop(val
, flags
);
1520 if (val
== NULL
&& state_dict
!= NULL
) {
1521 val
= CFDictionaryGetValue(state_dict
, key
);
1522 val
= sanitize_prop(val
, flags
);
1525 CFDictionarySetValue(dict
, key
, val
);
1533 ** GetEntityChangesFunc functions
1536 get_ipv4_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
1537 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
1539 boolean_t changed
= FALSE
;
1540 CFMutableDictionaryRef dict
= NULL
;
1541 CFStringRef primaryRank
= NULL
;
1544 IPv4RouteListRef routes
;
1545 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
1546 CFDataRef routes_data
= NULL
;
1547 CFDictionaryRef service_options
;
1549 if (state_dict
== NULL
) {
1552 service_options
= service_dict_get(serviceID
, kSCEntNetService
);
1553 if (service_options
!= NULL
) {
1554 primaryRank
= CFDictionaryGetValue(service_options
, kSCPropNetServicePrimaryRank
);
1556 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
1557 if (setup_dict
!= NULL
) {
1559 struct in_addr router_ip
;
1561 router
= CFDictionaryGetValue(setup_dict
,
1562 kSCPropNetIPv4Router
);
1564 && cfstring_to_ip(router
, &router_ip
)) {
1565 CFDictionarySetValue(dict
,
1566 kSCPropNetIPv4Router
,
1570 routes
= (IPv4RouteListRef
)routes_buf
;
1571 routes
->size
= R_STATIC
;
1573 r
= IPv4RouteListCreateWithDictionary(routes
, dict
, primaryRank
);
1575 routes_data
= CFDataCreate(NULL
,
1577 IPv4RouteListComputeSize(r
->count
));
1583 SCLog(TRUE
, LOG_NOTICE
,
1584 CFSTR("IPMonitor: %@ invalid IPv4 dictionary = %@"),
1589 changed
= service_dict_set(serviceID
, kSCEntNetIPv4
, routes_data
);
1590 if (routes_data
== NULL
) {
1591 /* clean up the rank too */
1592 CFDictionaryRemoveValue(S_ipv4_service_rank_dict
, serviceID
);
1594 my_CFRelease(&dict
);
1595 my_CFRelease(&routes_data
);
1600 get_ipv6_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
1601 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
1603 struct in6_addr addr
;
1605 boolean_t changed
= FALSE
;
1606 CFMutableDictionaryRef dict
= NULL
;
1607 CFDictionaryRef new_dict
= NULL
;
1608 CFStringRef router
= NULL
;
1609 struct in6_addr router_ip
;
1610 boolean_t valid_ip
= FALSE
;
1612 if (state_dict
== NULL
) {
1615 addrs
= isA_CFArray(CFDictionaryGetValue(state_dict
,
1616 kSCPropNetIPv6Addresses
));
1617 if (addrs
!= NULL
&& CFArrayGetCount(addrs
) > 0) {
1618 valid_ip
= cfstring_to_ip6(CFArrayGetValueAtIndex(addrs
, 0), &addr
);
1620 if (valid_ip
== FALSE
) {
1621 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1622 SCLog(TRUE
, LOG_NOTICE
,
1623 CFSTR("IPMonitor: %@ has no valid IPv6 address, ignoring"),
1628 dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
1629 if (setup_dict
!= NULL
) {
1630 router
= CFDictionaryGetValue(setup_dict
,
1631 kSCPropNetIPv6Router
);
1632 if (router
!= NULL
&& cfstring_to_ip6(router
, &router_ip
)) {
1633 CFDictionarySetValue(dict
,
1634 kSCPropNetIPv6Router
,
1639 router
= CFDictionaryGetValue(dict
,
1640 kSCPropNetIPv6Router
);
1642 && cfstring_to_ip6(router
, &router_ip
) == FALSE
) {
1643 CFDictionaryRemoveValue(dict
, kSCPropNetIPv6Router
);
1648 changed
= service_dict_set(serviceID
, kSCEntNetIPv6
, new_dict
);
1649 if (new_dict
== NULL
) {
1650 /* clean up the rank too */
1651 CFDictionaryRemoveValue(S_ipv6_service_rank_dict
, serviceID
);
1653 my_CFRelease(&new_dict
);
1658 dns_has_supplemental(CFStringRef serviceID
)
1660 CFDictionaryRef dns_dict
;
1661 CFDictionaryRef service_dict
;
1663 service_dict
= CFDictionaryGetValue(S_service_state_dict
, serviceID
);
1664 if (service_dict
== NULL
) {
1668 dns_dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
1669 if (dns_dict
== NULL
) {
1673 return CFDictionaryContainsKey(dns_dict
, kSCPropNetDNSSupplementalMatchDomains
);
1677 accumulate_dns_servers(CFArrayRef in_servers
, ProtocolFlags active_protos
,
1678 CFMutableArrayRef out_servers
)
1683 count
= CFArrayGetCount(in_servers
);
1684 for (i
= 0; i
< count
; i
++) {
1685 CFStringRef addr
= CFArrayGetValueAtIndex(in_servers
, i
);
1686 struct in6_addr ipv6_addr
;
1687 struct in_addr ip_addr
;
1689 if (cfstring_to_ip(addr
, &ip_addr
)) {
1691 if ((active_protos
& kProtocolFlagsIPv4
) == 0
1692 && ntohl(ip_addr
.s_addr
) != INADDR_LOOPBACK
) {
1693 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1695 "IPMonitor: no IPv4 connectivity, "
1696 "ignoring DNS server address " IP_FORMAT
,
1702 else if (cfstring_to_ip6(addr
, &ipv6_addr
)) {
1704 if ((active_protos
& kProtocolFlagsIPv6
) == 0
1705 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr
)) {
1706 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
1710 inet_ntop(AF_INET6
, &ipv6_addr
, str
, sizeof(str
));
1712 "IPMonitor: no IPv6 connectivity, "
1713 "ignoring DNS server address %s",
1720 /* bad IP address */
1721 SCLog(TRUE
, LOG_NOTICE
,
1722 CFSTR("IPMonitor: ignoring bad DNS server address '%@'"),
1726 /* DNS server is valid and one we want */
1727 CFArrayAppendValue(out_servers
, addr
);
1733 merge_dns_servers(CFMutableDictionaryRef new_dict
,
1734 CFArrayRef state_servers
,
1735 CFArrayRef setup_servers
,
1736 ProtocolFlags active_protos
)
1738 CFMutableArrayRef dns_servers
;
1740 if (state_servers
== NULL
&& setup_servers
== NULL
) {
1741 /* no DNS servers */
1744 dns_servers
= CFArrayCreateMutable(NULL
, 0,
1745 &kCFTypeArrayCallBacks
);
1746 if (setup_servers
!= NULL
) {
1747 accumulate_dns_servers(setup_servers
, active_protos
,
1750 if ((CFArrayGetCount(dns_servers
) == 0 || S_append_state
)
1751 && state_servers
!= NULL
) {
1752 accumulate_dns_servers(state_servers
, active_protos
,
1755 if (CFArrayGetCount(dns_servers
) != 0) {
1756 CFDictionarySetValue(new_dict
,
1757 kSCPropNetDNSServerAddresses
, dns_servers
);
1759 my_CFRelease(&dns_servers
);
1765 get_dns_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
1766 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
1768 ProtocolFlags active_protos
= kProtocolFlagsNone
;
1769 boolean_t changed
= FALSE
;
1777 { kSCPropNetDNSSearchDomains
, 0, FALSE
},
1778 { kSCPropNetDNSSortList
, 0, FALSE
},
1779 { kSCPropNetDNSSupplementalMatchDomains
, ALLOW_EMPTY_STRING
, TRUE
},
1780 { kSCPropNetDNSSupplementalMatchOrders
, 0, TRUE
},
1782 CFMutableDictionaryRef new_dict
= NULL
;
1783 CFStringRef pick_list
[] = {
1784 kSCPropNetDNSDomainName
,
1785 kSCPropNetDNSOptions
,
1786 kSCPropNetDNSSearchOrder
,
1787 kSCPropNetDNSServerPort
,
1788 kSCPropNetDNSServerTimeout
,
1791 if ((state_dict
== NULL
) && (setup_dict
== NULL
)) {
1792 /* there is no DNS */
1796 if (service_dict_get(serviceID
, kSCEntNetIPv4
) != NULL
) {
1797 active_protos
|= kProtocolFlagsIPv4
;
1799 if (service_dict_get(serviceID
, kSCEntNetIPv6
) != NULL
) {
1800 active_protos
|= kProtocolFlagsIPv6
;
1802 /* merge DNS configuration */
1803 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
1804 &kCFTypeDictionaryKeyCallBacks
,
1805 &kCFTypeDictionaryValueCallBacks
);
1806 if (active_protos
== kProtocolFlagsNone
) {
1807 /* there is no IPv4 nor IPv6 */
1808 if (state_dict
== NULL
) {
1809 /* no DNS information at all */
1812 merge_dns_servers(new_dict
,
1813 my_CFDictionaryGetArray(state_dict
,
1814 kSCPropNetDNSServerAddresses
),
1816 kProtocolFlagsIPv4
| kProtocolFlagsIPv6
);
1820 merge_dns_servers(new_dict
,
1821 my_CFDictionaryGetArray(state_dict
,
1822 kSCPropNetDNSServerAddresses
),
1823 my_CFDictionaryGetArray(setup_dict
,
1824 kSCPropNetDNSServerAddresses
),
1828 for (i
= 0; i
< sizeof(merge_list
)/sizeof(merge_list
[0]); i
++) {
1829 merge_array_prop(new_dict
,
1833 merge_list
[i
].flags
,
1834 merge_list
[i
].append
);
1836 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
1844 if (active_protos
== kProtocolFlagsNone
) {
1845 /* there is no IPv4 nor IPv6, only supplemental DNS */
1846 if (CFDictionaryContainsKey(new_dict
,
1847 kSCPropNetDNSSupplementalMatchDomains
)) {
1848 /* only keep State: supplemental */
1849 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSDomainName
);
1850 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchDomains
);
1851 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSearchOrder
);
1852 CFDictionaryRemoveValue(new_dict
, kSCPropNetDNSSortList
);
1857 if (CFDictionaryGetCount(new_dict
) == 0) {
1858 my_CFRelease(&new_dict
);
1862 if (S_append_state
) {
1864 * ensure any specified domain name (e.g. the domain returned by
1865 * a DHCP server) is in the search list.
1867 domain
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSDomainName
);
1868 if (isA_CFString(domain
)) {
1871 search
= CFDictionaryGetValue(new_dict
, kSCPropNetDNSSearchDomains
);
1872 if (isA_CFArray(search
) &&
1873 !CFArrayContainsValue(search
, CFRangeMake(0, CFArrayGetCount(search
)), domain
)) {
1874 CFMutableArrayRef new_search
;
1876 new_search
= CFArrayCreateMutableCopy(NULL
, 0, search
);
1877 CFArrayAppendValue(new_search
, domain
);
1878 CFDictionarySetValue(new_dict
, kSCPropNetDNSSearchDomains
, new_search
);
1879 my_CFRelease(&new_search
);
1885 changed
= service_dict_set(serviceID
, kSCEntNetDNS
, new_dict
);
1886 my_CFRelease(&new_dict
);
1891 merge_dict(const void *key
, const void *value
, void *context
)
1893 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
1895 CFDictionarySetValue(dict
, key
, value
);
1899 #define PROXY_AUTO_DISCOVERY_URL 252
1902 wpadURL_dhcp(CFDictionaryRef dhcp_options
)
1904 CFStringRef urlString
= NULL
;
1906 if (isA_CFDictionary(dhcp_options
)) {
1909 data
= DHCPInfoGetOptionData(dhcp_options
, PROXY_AUTO_DISCOVERY_URL
);
1913 url
= CFURLCreateWithBytes(NULL
,
1914 CFDataGetBytePtr(data
),
1915 CFDataGetLength(data
),
1916 kCFStringEncodingUTF8
,
1919 urlString
= CFURLGetString(url
);
1920 if (urlString
!= NULL
) {
1921 CFRetain(urlString
);
1935 CFStringRef urlString
= NULL
;
1937 url
= CFURLCreateWithString(NULL
, CFSTR("http://wpad/wpad.dat"), NULL
);
1939 urlString
= CFURLGetString(url
);
1940 if (urlString
!= NULL
) {
1941 CFRetain(urlString
);
1950 get_proxies_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
1951 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
1953 boolean_t changed
= FALSE
;
1954 CFMutableDictionaryRef new_dict
= NULL
;
1956 CFStringRef key1
; /* an "enable" key */
1960 { kSCPropNetProxiesFTPEnable
, kSCPropNetProxiesFTPProxy
, kSCPropNetProxiesFTPPort
},
1961 { kSCPropNetProxiesGopherEnable
, kSCPropNetProxiesGopherProxy
, kSCPropNetProxiesGopherPort
},
1962 { kSCPropNetProxiesHTTPEnable
, kSCPropNetProxiesHTTPProxy
, kSCPropNetProxiesHTTPPort
},
1963 { kSCPropNetProxiesHTTPSEnable
, kSCPropNetProxiesHTTPSProxy
, kSCPropNetProxiesHTTPSPort
},
1964 { kSCPropNetProxiesRTSPEnable
, kSCPropNetProxiesRTSPProxy
, kSCPropNetProxiesRTSPPort
},
1965 { kSCPropNetProxiesSOCKSEnable
, kSCPropNetProxiesSOCKSProxy
, kSCPropNetProxiesSOCKSPort
},
1966 { kSCPropNetProxiesProxyAutoConfigEnable
,
1967 kSCPropNetProxiesProxyAutoConfigURLString
,
1969 { kSCPropNetProxiesProxyAutoDiscoveryEnable
,
1974 if ((service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
) &&
1975 (service_dict_get(serviceID
, kSCEntNetIPv6
) == NULL
)) {
1976 /* there is no IPv4 nor IPv6 */
1980 if ((setup_dict
!= NULL
) && (state_dict
!= NULL
)) {
1982 CFMutableDictionaryRef setup_copy
;
1985 * Merge the per-service "Setup:" and "State:" proxy information with
1986 * the "Setup:" information always taking precedence. Additionally,
1987 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
1988 * Port) is defined than all of the values for that group will be
1989 * used. That is, we don't allow mixing some of the values from
1990 * the "Setup:" keys and others from the "State:" keys.
1992 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
1993 setup_copy
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
1994 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
1995 if (CFDictionaryContainsKey(setup_copy
, pick_list
[i
].key1
)) {
1997 * if a "Setup:" enabled key has been provided than we want to
1998 * ignore all of the "State:" keys
2000 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key1
);
2001 if (pick_list
[i
].key2
!= NULL
) {
2002 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key2
);
2004 if (pick_list
[i
].key3
!= NULL
) {
2005 CFDictionaryRemoveValue(new_dict
, pick_list
[i
].key3
);
2007 } else if (CFDictionaryContainsKey(state_dict
, pick_list
[i
].key1
) ||
2008 ((pick_list
[i
].key2
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key2
)) ||
2009 ((pick_list
[i
].key3
!= NULL
) && CFDictionaryContainsKey(state_dict
, pick_list
[i
].key3
))) {
2011 * if a "Setup:" enabled key has not been provided and we have
2012 * some" "State:" keys than we remove all of of "Setup:" keys
2014 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key1
);
2015 if (pick_list
[i
].key2
!= NULL
) {
2016 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key2
);
2018 if (pick_list
[i
].key3
!= NULL
) {
2019 CFDictionaryRemoveValue(setup_copy
, pick_list
[i
].key3
);
2024 /* merge the "Setup:" keys */
2025 CFDictionaryApplyFunction(setup_copy
, merge_dict
, new_dict
);
2026 CFRelease(setup_copy
);
2028 if (CFDictionaryGetCount(new_dict
) == 0) {
2029 CFRelease(new_dict
);
2033 else if (setup_dict
!= NULL
) {
2034 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, setup_dict
);
2036 else if (state_dict
!= NULL
) {
2037 new_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, state_dict
);
2041 if (new_dict
!= NULL
) {
2042 CFDictionaryRef dhcp_options
;
2044 CFNumberRef wpad
= NULL
;
2045 int wpadEnabled
= 0;
2046 CFStringRef wpadURL
= NULL
;
2048 if (CFDictionaryGetValueIfPresent(new_dict
,
2049 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2050 (const void **)&num
) &&
2051 isA_CFNumber(num
)) {
2052 /* if we have a WPAD key */
2054 if (!CFNumberGetValue(num
, kCFNumberIntType
, &wpadEnabled
)) {
2055 /* if we don't like the enabled key/value */
2063 num
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigEnable
);
2064 if (!isA_CFNumber(num
) ||
2065 !CFNumberGetValue(num
, kCFNumberIntType
, &pacEnabled
)) {
2066 /* if we don't like the enabled key/value */
2073 pacURL
= CFDictionaryGetValue(new_dict
, kSCPropNetProxiesProxyAutoConfigURLString
);
2074 if (!isA_CFString(pacURL
)) {
2075 /* if we don't like the PAC URL */
2082 * we already have a PAC URL so disable WPAD.
2089 * if WPAD is enabled and we don't already have a PAC URL then
2090 * we check for a DHCP provided URL. If not available, we use
2091 * a PAC URL pointing to a well-known file (wpad.dat) on a
2092 * well-known host (wpad.<domain>).
2094 dhcp_options
= get_service_state_entity(info
, serviceID
, kSCEntNetDHCP
);
2095 wpadURL
= wpadURL_dhcp(dhcp_options
);
2096 if (wpadURL
== NULL
) {
2097 wpadURL
= wpadURL_dns();
2099 if (wpadURL
== NULL
) {
2100 wpadEnabled
= 0; /* if we don't have a WPAD URL */
2105 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &pacEnabled
);
2106 CFDictionarySetValue(new_dict
,
2107 kSCPropNetProxiesProxyAutoConfigEnable
,
2110 CFDictionarySetValue(new_dict
,
2111 kSCPropNetProxiesProxyAutoConfigURLString
,
2118 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &wpadEnabled
);
2119 CFDictionarySetValue(new_dict
,
2120 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
2127 changed
= service_dict_set(serviceID
, kSCEntNetProxies
, new_dict
);
2128 if (new_dict
!= NULL
) CFRelease(new_dict
);
2132 #if !TARGET_OS_IPHONE
2134 get_smb_changes(CFStringRef serviceID
, CFDictionaryRef state_dict
,
2135 CFDictionaryRef setup_dict
, CFDictionaryRef info
)
2137 boolean_t changed
= FALSE
;
2139 CFMutableDictionaryRef new_dict
= NULL
;
2140 CFStringRef pick_list
[] = {
2141 kSCPropNetSMBNetBIOSName
,
2142 kSCPropNetSMBNetBIOSNodeType
,
2143 kSCPropNetSMBNetBIOSScope
,
2144 kSCPropNetSMBWorkgroup
,
2147 if (service_dict_get(serviceID
, kSCEntNetIPv4
) == NULL
) {
2148 /* there is no IPv4 */
2152 if (state_dict
== NULL
&& setup_dict
== NULL
) {
2153 /* there is no SMB */
2157 /* merge SMB configuration */
2158 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
2159 &kCFTypeDictionaryKeyCallBacks
,
2160 &kCFTypeDictionaryValueCallBacks
);
2162 merge_array_prop(new_dict
,
2163 kSCPropNetSMBWINSAddresses
,
2168 for (i
= 0; i
< sizeof(pick_list
)/sizeof(pick_list
[0]); i
++) {
2176 if (CFDictionaryGetCount(new_dict
) == 0) {
2177 my_CFRelease(&new_dict
);
2182 changed
= service_dict_set(serviceID
, kSCEntNetSMB
, new_dict
);
2183 my_CFRelease(&new_dict
);
2186 #endif /* !TARGET_OS_IPHONE */
2189 get_rank_changes(CFStringRef serviceID
, CFDictionaryRef state_options
,
2190 CFDictionaryRef setup_options
, CFDictionaryRef info
)
2192 boolean_t changed
= FALSE
;
2193 CFMutableDictionaryRef new_dict
= NULL
;
2194 CFStringRef new_rank
= NULL
;
2195 CFStringRef setup_rank
= NULL
;
2196 CFStringRef state_rank
= NULL
;
2200 * Check "PrimaryRank" setting
2202 * Note: Rank Never > Rank Last > Rank First > Rank None
2204 if (isA_CFDictionary(setup_options
)) {
2205 setup_rank
= CFDictionaryGetValue(setup_options
, kSCPropNetServicePrimaryRank
);
2206 setup_rank
= isA_CFString(setup_rank
);
2208 if (isA_CFDictionary(state_options
)) {
2209 state_rank
= CFDictionaryGetValue(state_options
, kSCPropNetServicePrimaryRank
);
2210 state_rank
= isA_CFString(state_rank
);
2213 if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankNever
)) ||
2214 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankNever
))) {
2215 new_rank
= kSCValNetServicePrimaryRankNever
;
2217 else if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankLast
)) ||
2218 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankLast
))) {
2219 new_rank
= kSCValNetServicePrimaryRankLast
;
2221 else if (((setup_rank
!= NULL
) && CFEqual(setup_rank
, kSCValNetServicePrimaryRankFirst
)) ||
2222 ((state_rank
!= NULL
) && CFEqual(state_rank
, kSCValNetServicePrimaryRankFirst
))) {
2223 new_rank
= kSCValNetServicePrimaryRankFirst
;
2226 if (new_rank
!= NULL
) {
2227 new_dict
= CFDictionaryCreateMutable(NULL
, 0,
2228 &kCFTypeDictionaryKeyCallBacks
,
2229 &kCFTypeDictionaryValueCallBacks
);
2230 CFDictionarySetValue(new_dict
, kSCPropNetServicePrimaryRank
, new_rank
);
2233 changed
= service_dict_set(serviceID
, kSCEntNetService
, new_dict
);
2234 my_CFRelease(&new_dict
);
2239 add_service_keys(CFStringRef serviceID
, CFMutableArrayRef keys
, CFMutableArrayRef patterns
)
2244 if (CFEqual(serviceID
, kSCCompAnyRegex
)) {
2248 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
2249 key
= setup_service_key(serviceID
, *entityTypeNames
[i
]);
2250 CFArrayAppendValue(keys
, key
);
2252 key
= state_service_key(serviceID
, *entityTypeNames
[i
]);
2253 CFArrayAppendValue(keys
, key
);
2257 key
= state_service_key(serviceID
, kSCEntNetDHCP
);
2258 CFArrayAppendValue(patterns
, key
);
2261 key
= setup_service_key(serviceID
, NULL
);
2262 CFArrayAppendValue(patterns
, key
);
2264 key
= state_service_key(serviceID
, NULL
);
2265 CFArrayAppendValue(patterns
, key
);
2272 static CFDictionaryRef
2273 services_info_copy(SCDynamicStoreRef session
, CFArrayRef service_list
)
2276 CFMutableArrayRef get_keys
;
2277 CFMutableArrayRef get_patterns
;
2278 CFDictionaryRef info
;
2281 count
= CFArrayGetCount(service_list
);
2282 get_keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2283 get_patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2285 CFArrayAppendValue(get_keys
, S_setup_global_ipv4
);
2286 CFArrayAppendValue(get_keys
, S_setup_global_proxies
);
2287 #if !TARGET_OS_IPHONE
2288 CFArrayAppendValue(get_keys
, S_setup_global_smb
);
2289 #endif /* !TARGET_OS_IPHONE */
2290 CFArrayAppendValue(get_keys
, S_multicast_resolvers
);
2291 CFArrayAppendValue(get_keys
, S_private_resolvers
);
2293 for (s
= 0; s
< count
; s
++) {
2294 CFStringRef serviceID
= CFArrayGetValueAtIndex(service_list
, s
);
2296 add_service_keys(serviceID
, get_keys
, get_patterns
);
2299 info
= SCDynamicStoreCopyMultiple(session
, get_keys
, get_patterns
);
2300 my_CFRelease(&get_keys
);
2301 my_CFRelease(&get_patterns
);
2305 static int rtm_seq
= 0;
2308 ipv4_route_open_socket(void)
2312 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, AF_INET
)) == -1) {
2313 SCLog(TRUE
, LOG_NOTICE
,
2314 CFSTR("IPMonitor: ipv4_route_open_socket: socket failed, %s"),
2321 * Define: ROUTE_MSG_ADDRS_SPACE
2323 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2324 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2325 * someone changes the code and doesn't think to modify this.
2327 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2328 + 2 * sizeof(struct sockaddr_dl) \
2331 struct rt_msghdr hdr
;
2332 char addrs
[ROUTE_MSG_ADDRS_SPACE
];
2336 ipv4_route(int sockfd
,
2337 int cmd
, struct in_addr gateway
, struct in_addr netaddr
,
2338 struct in_addr netmask
, char * ifname
, unsigned int ifindex
,
2339 struct in_addr ifa
, RouteFlags flags
)
2341 boolean_t default_route
= (netaddr
.s_addr
== 0);
2346 struct sockaddr_in
* in_p
;
2347 struct sockaddr_dl
* dl_p
;
2351 if (default_route
&& S_netboot
) {
2355 if (ifname
== NULL
) {
2356 /* this should not happen, but rather than crash, return an error */
2358 "IPMonitor: ipv4_route ifname is NULL on network address %s",
2359 inet_ntoa(netaddr
));
2362 memset(&rtmsg
, 0, sizeof(rtmsg
));
2363 rtmsg
.hdr
.rtm_type
= cmd
;
2364 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2365 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2367 = RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
| RTA_IFP
| RTA_IFA
;
2369 && (flags
& kRouteIsDirectToInterfaceFlag
) == 0) {
2370 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_GATEWAY
| RTF_STATIC
;
2373 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_CLONING
| RTF_STATIC
;
2375 if ((flags
& kRouteWantScopedFlag
) != 0) {
2377 if (!S_scopedroute
) {
2381 /* specifically asked for a scoped route, yet no index supplied */
2383 "IPMonitor: ipv4_route index is 0 on %s-scoped route %s",
2384 ifname
, inet_ntoa(netaddr
));
2387 rtmsg
.hdr
.rtm_index
= ifindex
;
2388 rtmsg
.hdr
.rtm_flags
|= RTF_IFSCOPE
;
2389 #else /* RTF_IFSCOPE */
2391 #endif /* RTF_IFSCOPE */
2394 rtaddr
.ptr
= rtmsg
.addrs
;
2397 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2398 rtaddr
.in_p
->sin_family
= AF_INET
;
2399 rtaddr
.in_p
->sin_addr
= netaddr
;
2400 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2403 if ((rtmsg
.hdr
.rtm_flags
& RTF_GATEWAY
) != 0) {
2404 /* gateway is an IP address */
2405 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2406 rtaddr
.in_p
->sin_family
= AF_INET
;
2407 rtaddr
.in_p
->sin_addr
= gateway
;
2408 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2411 /* gateway is the interface itself */
2412 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2413 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2414 rtaddr
.dl_p
->sdl_nlen
= strlen(ifname
);
2415 bcopy(ifname
, rtaddr
.dl_p
->sdl_data
, rtaddr
.dl_p
->sdl_nlen
);
2416 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2420 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2421 rtaddr
.in_p
->sin_family
= AF_INET
;
2422 rtaddr
.in_p
->sin_addr
= netmask
;
2423 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2425 /* interface name */
2426 rtaddr
.dl_p
->sdl_len
= sizeof(*rtaddr
.dl_p
);
2427 rtaddr
.dl_p
->sdl_family
= AF_LINK
;
2428 rtaddr
.dl_p
->sdl_nlen
= strlen(ifname
);
2429 bcopy(ifname
, rtaddr
.dl_p
->sdl_data
, rtaddr
.dl_p
->sdl_nlen
);
2430 rtaddr
.ptr
+= sizeof(*rtaddr
.dl_p
);
2432 /* interface address */
2433 rtaddr
.in_p
->sin_len
= sizeof(*rtaddr
.in_p
);
2434 rtaddr
.in_p
->sin_family
= AF_INET
;
2435 rtaddr
.in_p
->sin_addr
= ifa
;
2436 rtaddr
.ptr
+= sizeof(*rtaddr
.in_p
);
2438 len
= sizeof(rtmsg
.hdr
) + (rtaddr
.ptr
- (void *)rtmsg
.addrs
);
2439 rtmsg
.hdr
.rtm_msglen
= len
;
2440 if (write(sockfd
, &rtmsg
, len
) == -1) {
2447 ipv6_route(int cmd
, struct in6_addr gateway
, struct in6_addr netaddr
,
2448 struct in6_addr netmask
, char * ifname
, boolean_t is_direct
)
2450 boolean_t default_route
;
2452 boolean_t ret
= TRUE
;
2454 struct rt_msghdr hdr
;
2455 struct sockaddr_in6 dst
;
2456 struct sockaddr_in6 gway
;
2457 struct sockaddr_in6 mask
;
2458 struct sockaddr_dl ifp
;
2461 struct in6_addr zeroes
= IN6ADDR_ANY_INIT
;
2463 default_route
= (bcmp(&zeroes
, &netaddr
, sizeof(netaddr
)) == 0);
2465 if (IN6_IS_ADDR_LINKLOCAL(&gateway
) && ifname
!= NULL
) {
2466 unsigned int index
= if_nametoindex(ifname
);
2468 /* add the scope id to the link local address */
2469 gateway
.__u6_addr
.__u6_addr16
[1] = (uint16_t)htons(index
);
2471 if ((sockfd
= socket(PF_ROUTE
, SOCK_RAW
, AF_INET
)) == -1) {
2472 SCLog(TRUE
, LOG_NOTICE
,
2473 CFSTR("IPMonitor ipv6_route: open routing socket failed, %s"),
2477 memset(&rtmsg
, 0, sizeof(rtmsg
));
2478 rtmsg
.hdr
.rtm_type
= cmd
;
2479 if (default_route
) {
2481 /* if router is directly reachable, don't set the gateway flag */
2482 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_STATIC
;
2485 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_GATEWAY
| RTF_STATIC
;
2489 rtmsg
.hdr
.rtm_flags
= RTF_UP
| RTF_CLONING
| RTF_STATIC
;
2491 rtmsg
.hdr
.rtm_version
= RTM_VERSION
;
2492 rtmsg
.hdr
.rtm_seq
= ++rtm_seq
;
2493 rtmsg
.hdr
.rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
;
2494 rtmsg
.dst
.sin6_len
= sizeof(rtmsg
.dst
);
2495 rtmsg
.dst
.sin6_family
= AF_INET6
;
2496 rtmsg
.dst
.sin6_addr
= netaddr
;
2497 rtmsg
.gway
.sin6_len
= sizeof(rtmsg
.gway
);
2498 rtmsg
.gway
.sin6_family
= AF_INET6
;
2499 rtmsg
.gway
.sin6_addr
= gateway
;
2500 rtmsg
.mask
.sin6_len
= sizeof(rtmsg
.mask
);
2501 rtmsg
.mask
.sin6_family
= AF_INET6
;
2502 rtmsg
.mask
.sin6_addr
= netmask
;
2504 len
= sizeof(rtmsg
);
2506 rtmsg
.ifp
.sdl_len
= sizeof(rtmsg
.ifp
);
2507 rtmsg
.ifp
.sdl_family
= AF_LINK
;
2508 rtmsg
.ifp
.sdl_nlen
= strlen(ifname
);
2509 rtmsg
.hdr
.rtm_addrs
|= RTA_IFP
;
2510 bcopy(ifname
, rtmsg
.ifp
.sdl_data
, rtmsg
.ifp
.sdl_nlen
);
2513 /* no ifp information */
2514 len
-= sizeof(rtmsg
.ifp
);
2516 rtmsg
.hdr
.rtm_msglen
= len
;
2517 if (write(sockfd
, &rtmsg
, len
) == -1) {
2518 if ((cmd
== RTM_ADD
) && (errno
== EEXIST
)) {
2519 /* no sense complaining about a route that already exists */
2521 else if ((cmd
== RTM_DELETE
) && (errno
== ESRCH
)) {
2522 /* no sense complaining about a route that isn't there */
2525 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2526 SCLog(TRUE
, LOG_NOTICE
,
2527 CFSTR("IPMonitor ipv6_route: write routing"
2528 " socket failed, %s"), strerror(errno
));
2539 ipv6_default_route_delete(void)
2541 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2542 SCLog(TRUE
, LOG_NOTICE
, CFSTR("IPMonitor: IPv6 route delete default"));
2544 return (ipv6_route(RTM_DELETE
, S_ip6_zeros
, S_ip6_zeros
, S_ip6_zeros
,
2549 ipv6_default_route_add(struct in6_addr router
, char * ifname
,
2550 boolean_t is_direct
)
2552 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2557 inet_ntop(AF_INET6
, &router
, str
, sizeof(str
));
2558 SCLog(TRUE
, LOG_NOTICE
,
2559 CFSTR("IPMonitor: IPv6 route add default"
2560 " %s interface %s direct %d"),
2561 str
, ifname
, is_direct
);
2563 return (ipv6_route(RTM_ADD
, router
, S_ip6_zeros
, S_ip6_zeros
,
2564 ifname
, is_direct
));
2569 multicast_route_delete(int sockfd
)
2571 struct in_addr gateway
= { htonl(INADDR_LOOPBACK
) };
2572 struct in_addr netaddr
= { htonl(INADDR_UNSPEC_GROUP
) };
2573 struct in_addr netmask
= { htonl(IN_CLASSD_NET
) };
2575 return (ipv4_route(sockfd
, RTM_DELETE
, gateway
, netaddr
, netmask
, "lo0", 0,
2580 multicast_route_add(int sockfd
)
2582 struct in_addr gateway
= { htonl(INADDR_LOOPBACK
) };
2583 struct in_addr netaddr
= { htonl(INADDR_UNSPEC_GROUP
) };
2584 struct in_addr netmask
= { htonl(IN_CLASSD_NET
) };
2586 return (ipv4_route(sockfd
, RTM_ADD
, gateway
, netaddr
, netmask
, "lo0", 0,
2591 set_ipv6_router(struct in6_addr
* router
, char * ifname
, boolean_t is_direct
)
2593 /* assign the new default route, ensure local multicast route available */
2594 (void)ipv6_default_route_delete();
2595 if (router
!= NULL
) {
2596 (void)ipv6_default_route_add(*router
, ifname
, is_direct
);
2601 #if !TARGET_OS_IPHONE
2602 static __inline__
void
2605 (void)unlink(VAR_RUN_RESOLV_CONF
);
2609 set_dns(CFArrayRef val_search_domains
,
2610 CFStringRef val_domain_name
,
2611 CFArrayRef val_servers
,
2612 CFArrayRef val_sortlist
)
2614 FILE * f
= fopen(VAR_RUN_RESOLV_CONF
"-", "w");
2616 /* publish new resolv.conf */
2621 SCPrint(TRUE
, f
, CFSTR("#\n"));
2622 SCPrint(TRUE
, f
, CFSTR("# Mac OS X Notice\n"));
2623 SCPrint(TRUE
, f
, CFSTR("#\n"));
2624 SCPrint(TRUE
, f
, CFSTR("# This file is not used by the host name and address resolution\n"));
2625 SCPrint(TRUE
, f
, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
2626 SCPrint(TRUE
, f
, CFSTR("# this Mac OS X system.\n"));
2627 SCPrint(TRUE
, f
, CFSTR("#\n"));
2628 SCPrint(TRUE
, f
, CFSTR("# This file is automatically generated.\n"));
2629 SCPrint(TRUE
, f
, CFSTR("#\n"));
2631 if (isA_CFString(val_domain_name
)) {
2632 SCPrint(TRUE
, f
, CFSTR("domain %@\n"), val_domain_name
);
2635 if (isA_CFArray(val_search_domains
)) {
2636 SCPrint(TRUE
, f
, CFSTR("search"));
2637 n
= CFArrayGetCount(val_search_domains
);
2638 for (i
= 0; i
< n
; i
++) {
2641 domain
= CFArrayGetValueAtIndex(val_search_domains
, i
);
2642 if (isA_CFString(domain
)) {
2643 SCPrint(TRUE
, f
, CFSTR(" %@"), domain
);
2646 SCPrint(TRUE
, f
, CFSTR("\n"));
2649 if (isA_CFArray(val_servers
)) {
2650 n
= CFArrayGetCount(val_servers
);
2651 for (i
= 0; i
< n
; i
++) {
2652 CFStringRef nameserver
;
2654 nameserver
= CFArrayGetValueAtIndex(val_servers
, i
);
2655 if (isA_CFString(nameserver
)) {
2656 SCPrint(TRUE
, f
, CFSTR("nameserver %@\n"), nameserver
);
2661 if (isA_CFArray(val_sortlist
)) {
2662 SCPrint(TRUE
, f
, CFSTR("sortlist"));
2663 n
= CFArrayGetCount(val_sortlist
);
2664 for (i
= 0; i
< n
; i
++) {
2665 CFStringRef address
;
2667 address
= CFArrayGetValueAtIndex(val_sortlist
, i
);
2668 if (isA_CFString(address
)) {
2669 SCPrint(TRUE
, f
, CFSTR(" %@"), address
);
2672 SCPrint(TRUE
, f
, CFSTR("\n"));
2676 rename(VAR_RUN_RESOLV_CONF
"-", VAR_RUN_RESOLV_CONF
);
2680 #endif /* !TARGET_OS_IPHONE */
2683 router_is_our_ipv6_address(CFStringRef router
, CFArrayRef addr_list
)
2686 CFIndex n
= CFArrayGetCount(addr_list
);
2689 (void)cfstring_to_ip6(router
, &r
);
2690 for (i
= 0; i
< n
; i
++) {
2693 if (cfstring_to_ip6(CFArrayGetValueAtIndex(addr_list
, i
), &ip
)
2694 && bcmp(&r
, &ip
, sizeof(r
)) == 0) {
2701 static IPv4RouteListRef
2702 service_dict_get_ipv4_routelist(CFDictionaryRef service_dict
)
2705 IPv4RouteListRef routes
= NULL
;
2707 data
= (CFDataRef
)CFDictionaryGetValue(service_dict
, kSCEntNetIPv4
);
2709 routes
= (IPv4RouteListRef
)CFDataGetBytePtr(data
);
2714 typedef struct apply_ipv4_route_context
{
2715 IPv4RouteListRef old
;
2716 IPv4RouteListRef
new;
2718 } apply_ipv4_route_context_t
;
2720 /* add/remove a router/32 subnet */
2722 ipv4_route_gateway(int sockfd
, int cmd
, char * ifn_p
,
2723 IPv4RouteRef def_route
)
2725 struct in_addr mask
;
2727 mask
.s_addr
= htonl(INADDR_BROADCAST
);
2728 return (ipv4_route(sockfd
, cmd
, def_route
->ifa
,
2729 def_route
->gateway
, mask
, ifn_p
, def_route
->ifindex
,
2731 (def_route
->flags
& kRouteWantScopedFlag
)));
2735 * Function: apply_ipv4_route
2737 * Callback function that adds/removes the specified route.
2740 apply_ipv4_route(IPv4RouteListApplyCommand cmd
, IPv4RouteRef route
, void * arg
)
2742 apply_ipv4_route_context_t
*context
= (apply_ipv4_route_context_t
*)arg
;
2746 ifn_p
= route
->ifname
;
2748 case kIPv4RouteListAddRouteCommand
:
2749 if ((route
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
2750 retval
= ipv4_route_gateway(context
->sockfd
, RTM_ADD
,
2752 if (retval
== EEXIST
) {
2753 /* delete and add again */
2754 (void)ipv4_route_gateway(context
->sockfd
, RTM_DELETE
,
2756 retval
= ipv4_route_gateway(context
->sockfd
, RTM_ADD
,
2760 SCLog(TRUE
, LOG_NOTICE
,
2761 CFSTR("IPMonitor apply_ipv4_route failed to add"
2762 " %s/32 route, %s"),
2763 inet_ntoa(route
->gateway
), strerror(retval
));
2765 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2766 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Added IPv4 Route %s/32"),
2767 inet_ntoa(route
->gateway
));
2770 retval
= ipv4_route(context
->sockfd
,
2771 RTM_ADD
, route
->gateway
,
2772 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
2773 route
->ifa
, route
->flags
);
2774 if (retval
== EEXIST
) {
2775 /* delete and add again */
2776 (void)ipv4_route(context
->sockfd
,
2777 RTM_DELETE
, route
->gateway
,
2778 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
2779 route
->ifa
, route
->flags
);
2780 retval
= ipv4_route(context
->sockfd
,
2781 RTM_ADD
, route
->gateway
,
2782 route
->dest
, route
->mask
,
2783 ifn_p
, route
->ifindex
,
2784 route
->ifa
, route
->flags
);
2787 SCLog(TRUE
, LOG_NOTICE
,
2788 CFSTR("IPMonitor apply_ipv4_route failed to add"
2789 " route, %s:"), strerror(retval
));
2790 IPv4RouteLog(route
);
2792 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2793 SCLog(TRUE
, LOG_NOTICE
,
2794 CFSTR("Added IPv4 route new[%d] = "),
2795 route
- context
->new->list
);
2796 IPv4RouteLog(route
);
2799 case kIPv4RouteListRemoveRouteCommand
:
2800 retval
= ipv4_route(context
->sockfd
,
2801 RTM_DELETE
, route
->gateway
,
2802 route
->dest
, route
->mask
, ifn_p
, route
->ifindex
,
2803 route
->ifa
, route
->flags
);
2805 if (retval
!= ESRCH
) {
2806 SCLog(TRUE
, LOG_NOTICE
,
2807 CFSTR("IPMonitor apply_ipv4_route failed to remove"
2808 " route, %s: "), strerror(retval
));
2809 IPv4RouteLog(route
);
2812 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2813 SCLog(TRUE
, LOG_NOTICE
,
2814 CFSTR("Removed IPv4 route old[%d] = "),
2815 route
- context
->old
->list
);
2816 IPv4RouteLog(route
);
2818 if ((route
->flags
& kRouteIsNotSubnetLocalFlag
) != 0) {
2819 retval
= ipv4_route_gateway(context
->sockfd
, RTM_DELETE
,
2822 SCLog(TRUE
, LOG_NOTICE
,
2823 CFSTR("IPMonitor apply_ipv4_route failed to remove"
2824 " %s/32 route, %s: "),
2825 inet_ntoa(route
->gateway
), strerror(retval
));
2827 else if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2828 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Removed IPv4 Route %s/32"),
2829 inet_ntoa(route
->gateway
));
2840 * Function: update_ipv4
2843 * Update the IPv4 configuration based on the latest information.
2844 * Publish the State:/Network/Global/IPv4 information, and update the
2845 * IPv4 routing table. IPv4RouteListApply() invokes our callback,
2846 * apply_ipv4_route(), to install/remove the routes.
2849 update_ipv4(CFStringRef primary
,
2850 IPv4RouteListRef new_routelist
,
2851 keyChangeListRef keys
)
2853 apply_ipv4_route_context_t context
;
2856 if (new_routelist
!= NULL
&& primary
!= NULL
) {
2857 char * ifn_p
= NULL
;
2859 CFMutableDictionaryRef dict
= NULL
;
2861 dict
= CFDictionaryCreateMutable(NULL
, 0,
2862 &kCFTypeDictionaryKeyCallBacks
,
2863 &kCFTypeDictionaryValueCallBacks
);
2864 /* the first entry is the default route */
2865 r
= new_routelist
->list
;
2866 if (r
->gateway
.s_addr
!= 0) {
2869 router
= CFStringCreateWithCString(NULL
,
2870 inet_ntoa(r
->gateway
),
2871 kCFStringEncodingASCII
);
2872 if (router
!= NULL
) {
2873 CFDictionarySetValue(dict
, kSCPropNetIPv4Router
, router
);
2877 if (r
->ifname
[0] != '\0') {
2880 if (ifn_p
!= NULL
) {
2881 CFStringRef ifname_cf
;
2883 ifname_cf
= CFStringCreateWithCString(NULL
,
2885 kCFStringEncodingASCII
);
2886 if (ifname_cf
!= NULL
) {
2887 CFDictionarySetValue(dict
,
2888 kSCDynamicStorePropNetPrimaryInterface
,
2890 CFRelease(ifname_cf
);
2893 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
2895 keyChangeListSetValue(keys
, S_state_global_ipv4
, dict
);
2899 keyChangeListRemoveValue(keys
, S_state_global_ipv4
);
2903 bzero(&context
, sizeof(context
));
2904 context
.sockfd
= ipv4_route_open_socket();
2905 if (context
.sockfd
!= -1) {
2906 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
2907 if (S_ipv4_routelist
== NULL
) {
2908 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Old Routes = <none>"));
2911 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Old Routes = "));
2912 IPv4RouteListLog(S_ipv4_routelist
);
2914 if (new_routelist
== NULL
) {
2915 SCLog(TRUE
, LOG_NOTICE
, CFSTR("New Routes = <none>"));
2918 SCLog(TRUE
, LOG_NOTICE
, CFSTR("New Routes = "));
2919 IPv4RouteListLog(new_routelist
);
2922 context
.old
= S_ipv4_routelist
;
2923 context
.new = new_routelist
;
2924 IPv4RouteListApply(S_ipv4_routelist
, new_routelist
,
2925 &apply_ipv4_route
, (void *)&context
);
2926 if (new_routelist
!= NULL
) {
2927 (void)multicast_route_delete(context
.sockfd
);
2930 (void)multicast_route_add(context
.sockfd
);
2932 close(context
.sockfd
);
2934 if (S_ipv4_routelist
!= NULL
) {
2935 free(S_ipv4_routelist
);
2937 S_ipv4_routelist
= new_routelist
;
2942 update_ipv6(CFDictionaryRef service_info
,
2943 CFStringRef primary
,
2944 keyChangeListRef keys
)
2946 CFDictionaryRef ipv6_dict
= NULL
;
2948 if (primary
!= NULL
) {
2949 CFDictionaryRef service_dict
;
2951 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
2952 if (service_dict
!= NULL
) {
2953 ipv6_dict
= CFDictionaryGetValue(service_dict
, kSCEntNetIPv6
);
2956 if (ipv6_dict
!= NULL
) {
2958 CFMutableDictionaryRef dict
= NULL
;
2959 CFStringRef if_name
= NULL
;
2960 char ifn
[IFNAMSIZ
] = { '\0' };
2961 char * ifn_p
= NULL
;
2962 boolean_t is_direct
= FALSE
;
2963 CFStringRef val_router
= NULL
;
2965 dict
= CFDictionaryCreateMutable(NULL
, 0,
2966 &kCFTypeDictionaryKeyCallBacks
,
2967 &kCFTypeDictionaryValueCallBacks
);
2968 val_router
= CFDictionaryGetValue(ipv6_dict
, kSCPropNetIPv6Router
);
2969 addrs
= CFDictionaryGetValue(ipv6_dict
,
2970 kSCPropNetIPv6Addresses
);
2971 if (val_router
!= NULL
) {
2972 /* no router if router is one of our IP addresses */
2973 is_direct
= router_is_our_ipv6_address(val_router
, addrs
);
2974 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
,
2978 val_router
= CFArrayGetValueAtIndex(addrs
, 0);
2981 if_name
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
2983 CFDictionarySetValue(dict
,
2984 kSCDynamicStorePropNetPrimaryInterface
,
2986 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
2987 kCFStringEncodingASCII
)) {
2991 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
2993 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
2996 { /* route add default ... */
2997 struct in6_addr router
;
2999 (void)cfstring_to_ip6(val_router
, &router
);
3000 set_ipv6_router(&router
, ifn_p
, is_direct
);
3004 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
3005 set_ipv6_router(NULL
, NULL
, FALSE
);
3011 update_dns(CFDictionaryRef service_info
,
3012 CFStringRef primary
,
3013 keyChangeListRef keys
)
3015 CFDictionaryRef dict
= NULL
;
3017 if (primary
!= NULL
) {
3018 CFDictionaryRef service_dict
;
3020 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3021 if (service_dict
!= NULL
) {
3022 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
3026 #if !TARGET_OS_IPHONE
3028 #endif /* !TARGET_OS_IPHONE */
3029 keyChangeListRemoveValue(keys
, S_state_global_dns
);
3032 #if !TARGET_OS_IPHONE
3033 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
3034 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
3035 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
3036 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
3037 #endif /* !TARGET_OS_IPHONE */
3038 keyChangeListSetValue(keys
, S_state_global_dns
, dict
);
3044 update_dnsinfo(CFDictionaryRef service_info
,
3045 CFStringRef primary
,
3046 keyChangeListRef keys
,
3047 CFArrayRef service_order
)
3049 CFDictionaryRef dict
= NULL
;
3050 CFArrayRef multicastResolvers
;
3051 CFArrayRef privateResolvers
;
3053 multicastResolvers
= CFDictionaryGetValue(service_info
, S_multicast_resolvers
);
3054 privateResolvers
= CFDictionaryGetValue(service_info
, S_private_resolvers
);
3056 if (primary
!= NULL
) {
3057 CFDictionaryRef service_dict
;
3059 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3060 if (service_dict
!= NULL
) {
3061 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
3065 dns_configuration_set(dict
,
3066 S_service_state_dict
,
3070 keyChangeListNotifyKey(keys
, S_state_global_dns
);
3075 update_proxies(CFDictionaryRef service_info
,
3076 CFStringRef primary
,
3077 keyChangeListRef keys
)
3079 CFDictionaryRef dict
= NULL
;
3081 if (primary
!= NULL
) {
3082 CFDictionaryRef service_dict
;
3084 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3085 if (service_dict
!= NULL
) {
3086 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
3088 dict
= my_CFDictionaryGetDictionary(service_info
,
3089 S_setup_global_proxies
);
3094 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
3097 keyChangeListSetValue(keys
, S_state_global_proxies
, dict
);
3102 #if !TARGET_OS_IPHONE
3104 update_smb(CFDictionaryRef service_info
,
3105 CFStringRef primary
,
3106 keyChangeListRef keys
)
3108 CFDictionaryRef dict
= NULL
;
3110 if (primary
!= NULL
) {
3111 CFDictionaryRef service_dict
;
3113 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3114 if (service_dict
!= NULL
) {
3115 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
3117 dict
= my_CFDictionaryGetDictionary(service_info
,
3118 S_setup_global_smb
);
3123 keyChangeListRemoveValue(keys
, S_state_global_smb
);
3126 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
3131 #endif /* !TARGET_OS_IPHONE */
3134 get_service_rank(CFArrayRef order
, int n_order
, CFStringRef serviceID
)
3137 Rank rank
= kRankLast
;
3139 if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
3140 for (i
= 0; i
< n_order
; i
++) {
3141 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
3146 if (CFEqual(serviceID
, s
)) {
3156 ** Service election:
3159 * Function: rank_dict_get_service_rank
3161 * Retrieve the service rank in the given dictionary.
3164 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
3167 Rank rank_val
= kRankLast
;
3169 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
3171 CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
);
3177 * Function: rank_dict_set_service_rank
3179 * Save the results of ranking the service so we can look it up later without
3180 * repeating all of the ranking code.
3183 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
3184 CFStringRef serviceID
, Rank rank_val
)
3188 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
3190 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
3196 typedef struct election_info
{
3200 CFStringRef serviceID
;
3201 CFDictionaryRef service_dict
;
3203 boolean_t choose_last
;
3206 typedef boolean_t
election_func_t(void * context
, election_info_t
* info
);
3209 * Function: elect_ipv4
3211 * This function builds the list of IPv4 routes that should be active.
3212 * As elect_new_primary() invokes us with each service, we build up the
3213 * result in the passed in context, a pointer to an IPv4RouteListRef.
3216 elect_ipv4(void * context
, election_info_t
* info
)
3218 IPv4RouteListRef
* routes_p
= (IPv4RouteListRef
*)context
;
3219 IPv4RouteListRef service_routes
;
3221 service_routes
= service_dict_get_ipv4_routelist(info
->service_dict
);
3222 if (service_routes
== NULL
) {
3225 if ((service_routes
->list
->flags
& kRouteChooseFirstFlag
) != 0) {
3226 info
->service_rank
= kRankFirst
;
3228 else if (S_ppp_override_primary
3229 && (strncmp(PPP_PREFIX
, service_routes
->list
->ifname
,
3230 sizeof(PPP_PREFIX
) - 1) == 0)) {
3231 /* PPP override: make ppp* look the best */
3232 /* Hack: should use interface type, not interface name */
3233 info
->service_rank
= kRankFirst
;
3236 info
->service_rank
= get_service_rank(info
->order
, info
->n_order
,
3238 if ((service_routes
->list
->flags
& kRouteChooseLastFlag
) != 0) {
3239 info
->choose_last
= TRUE
;
3242 if (routes_p
!= NULL
) {
3243 *routes_p
= IPv4RouteListAddRouteList(*routes_p
,
3244 info
->n_services
* 3,
3246 info
->service_rank
);
3248 if ((service_routes
->list
->flags
& kRouteChooseNeverFlag
) != 0) {
3249 /* never elect as primary */
3252 rank_dict_set_service_rank(S_ipv4_service_rank_dict
,
3253 info
->serviceID
, info
->service_rank
);
3258 elect_ipv6(void * context
, election_info_t
* info
)
3260 CFStringRef if_name
;
3261 CFStringRef primaryRank
= NULL
;
3262 CFDictionaryRef proto_dict
;
3264 CFDictionaryRef service_options
;
3266 proto_dict
= CFDictionaryGetValue(info
->service_dict
, kSCEntNetIPv6
);
3267 if (proto_dict
== NULL
) {
3270 service_options
= service_dict_get(info
->serviceID
, kSCEntNetService
);
3271 if (service_options
!= NULL
) {
3272 primaryRank
= CFDictionaryGetValue(service_options
, kSCPropNetServicePrimaryRank
);
3273 if ((primaryRank
!= NULL
)
3274 && CFEqual(primaryRank
, kSCValNetServicePrimaryRankNever
)) {
3278 router
= CFDictionaryGetValue(proto_dict
,
3279 kSCPropNetIPv6Router
);
3280 if (router
== NULL
) {
3281 info
->choose_last
= TRUE
;
3282 info
->service_rank
= kRankLast
;
3284 else if ((primaryRank
!= NULL
)
3285 && CFEqual(primaryRank
, kSCValNetServicePrimaryRankFirst
)) {
3286 info
->service_rank
= kRankFirst
;
3288 else if (get_override_primary(proto_dict
)) {
3289 info
->service_rank
= kRankFirst
;
3291 else if (S_ppp_override_primary
3292 && CFDictionaryGetValueIfPresent(proto_dict
,
3293 kSCPropInterfaceName
,
3294 (const void **)&if_name
)
3295 && CFStringHasPrefix(if_name
, CFSTR(PPP_PREFIX
))) {
3296 /* PPP override: make ppp* look the best */
3297 /* Hack: should use interface type, not interface name */
3298 info
->service_rank
= kRankFirst
;
3301 info
->service_rank
= get_service_rank(info
->order
, info
->n_order
,
3304 rank_dict_set_service_rank(S_ipv6_service_rank_dict
,
3305 info
->serviceID
, info
->service_rank
);
3310 * Function: elect_new_primary
3312 * Walk the list of services, passing each service dictionary to "elect_func".
3313 * "elect_func" returns rank information about the service that let us
3314 * determine the new primary.
3317 elect_new_primary(election_func_t
* elect_func
, void * context
,
3318 CFArrayRef order
, int n_order
)
3322 election_info_t info
;
3324 #define N_KEYS_VALUES_STATIC 10
3325 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
3326 CFStringRef new_primary
= NULL
;
3327 Rank new_primary_rank
= kRankLast
;
3328 boolean_t new_primary_choose_last
= FALSE
;
3331 count
= CFDictionaryGetCount(S_service_state_dict
);
3332 if (count
<= N_KEYS_VALUES_STATIC
) {
3333 keys
= keys_values_buf
;
3336 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
3338 values
= keys
+ count
;
3339 CFDictionaryGetKeysAndValues(S_service_state_dict
,
3340 (const void * *)keys
,
3341 (const void * *)values
);
3343 info
.n_services
= count
;
3345 info
.n_order
= n_order
;
3346 for (i
= 0; i
< count
; i
++) {
3347 boolean_t found_new_primary
= FALSE
;
3349 info
.serviceID
= (CFStringRef
)keys
[i
];
3350 info
.service_dict
= (CFDictionaryRef
)values
[i
];
3351 info
.service_rank
= kRankLast
;
3352 info
.choose_last
= FALSE
;
3354 if ((*elect_func
)(context
, &info
) == FALSE
) {
3357 if (new_primary
== NULL
) {
3358 found_new_primary
= TRUE
;
3360 else if (info
.choose_last
== new_primary_choose_last
) {
3361 found_new_primary
= (info
.service_rank
< new_primary_rank
);
3363 else if (new_primary_choose_last
) {
3364 found_new_primary
= TRUE
;
3366 if (found_new_primary
) {
3367 new_primary
= info
.serviceID
;
3368 new_primary_rank
= info
.service_rank
;
3369 new_primary_choose_last
= info
.choose_last
;
3372 if (new_primary
!= NULL
) {
3373 CFRetain(new_primary
);
3375 if (keys
!= keys_values_buf
) {
3378 return (new_primary
);
3382 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
3384 uint32_t changed
= 0;
3387 /* update service options first (e.g. rank) */
3388 if (get_rank_changes(serviceID
,
3389 get_service_state_entity(services_info
, serviceID
,
3391 get_service_setup_entity(services_info
, serviceID
,
3394 changed
|= (1 << kEntityTypeServiceOptions
);
3396 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
3397 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
3398 GetEntityChangesFuncRef func
= entityChangeFunc
[i
];
3399 if ((*func
)(serviceID
,
3400 get_service_state_entity(services_info
, serviceID
,
3401 *entityTypeNames
[i
]),
3402 get_service_setup_entity(services_info
, serviceID
,
3403 *entityTypeNames
[i
]),
3405 changed
|= (1 << i
);
3412 service_order_get(CFDictionaryRef services_info
)
3414 CFArrayRef order
= NULL
;
3415 CFDictionaryRef ipv4_dict
;
3417 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
3418 S_setup_global_ipv4
);
3419 if (ipv4_dict
!= NULL
) {
3420 CFNumberRef ppp_override
;
3423 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
3424 order
= isA_CFArray(order
);
3426 /* get ppp override primary */
3427 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
3428 kSCPropNetPPPOverridePrimary
);
3429 ppp_override
= isA_CFNumber(ppp_override
);
3430 if (ppp_override
!= NULL
) {
3431 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
3433 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
3436 S_ppp_override_primary
= FALSE
;
3442 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
3443 const char * entity
)
3445 boolean_t changed
= FALSE
;
3446 CFStringRef primary
= *primary_p
;
3448 if (new_primary
!= NULL
) {
3449 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
3450 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3451 SCLog(TRUE
, LOG_NOTICE
,
3452 CFSTR("IPMonitor: %@ is still primary %s"),
3453 new_primary
, entity
);
3457 my_CFRelease(primary_p
);
3458 *primary_p
= CFRetain(new_primary
);
3459 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3460 SCLog(TRUE
, LOG_NOTICE
,
3461 CFSTR("IPMonitor: %@ is the new primary %s"),
3462 new_primary
, entity
);
3467 else if (primary
!= NULL
) {
3468 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3469 SCLog(TRUE
, LOG_NOTICE
,
3470 CFSTR("IPMonitor: %@ is no longer primary %s"),
3473 my_CFRelease(primary_p
);
3480 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
3483 if (service_dict_get(serviceID
, entity
) == NULL
) {
3486 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
3490 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
3494 boolean_t dnsinfo_changed
= FALSE
;
3495 boolean_t global_ipv4_changed
= FALSE
;
3496 boolean_t global_ipv6_changed
= FALSE
;
3500 int n_service_order
= 0;
3501 CFArrayRef service_order
;
3502 CFMutableArrayRef service_changes
= NULL
;
3503 CFDictionaryRef services_info
= NULL
;
3505 count
= CFArrayGetCount(changed_keys
);
3510 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3511 SCLog(TRUE
, LOG_NOTICE
,
3512 CFSTR("IPMonitor: changes %@ (%d)"), changed_keys
, count
);
3515 keyChangeListInit(&keys
);
3516 service_changes
= CFArrayCreateMutable(NULL
, 0,
3517 &kCFTypeArrayCallBacks
);
3518 for (i
= 0; i
< count
; i
++) {
3519 CFStringRef change
= CFArrayGetValueAtIndex(changed_keys
, i
);
3520 if (CFEqual(change
, S_setup_global_ipv4
)) {
3521 global_ipv4_changed
= TRUE
;
3522 global_ipv6_changed
= TRUE
;
3524 else if (CFEqual(change
, S_setup_global_proxies
)) {
3525 if (S_primary_proxies
!= NULL
) {
3526 my_CFArrayAppendUniqueValue(service_changes
, S_primary_proxies
);
3529 #if !TARGET_OS_IPHONE
3530 else if (CFEqual(change
, S_setup_global_smb
)) {
3531 if (S_primary_smb
!= NULL
) {
3532 my_CFArrayAppendUniqueValue(service_changes
, S_primary_smb
);
3535 #endif /* !TARGET_OS_IPHONE */
3536 else if (CFEqual(change
, S_multicast_resolvers
)) {
3537 dnsinfo_changed
= TRUE
;
3539 else if (CFEqual(change
, S_private_resolvers
)) {
3540 dnsinfo_changed
= TRUE
;
3542 #if !TARGET_OS_IPHONE
3543 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
3544 dnsinfo_changed
= TRUE
;
3546 #endif /* !TARGET_OS_IPHONE */
3547 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
3548 CFStringRef serviceID
= parse_component(change
,
3549 S_state_service_prefix
);
3551 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
3552 CFRelease(serviceID
);
3555 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
3556 CFStringRef serviceID
= parse_component(change
,
3557 S_setup_service_prefix
);
3559 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
3560 CFRelease(serviceID
);
3565 /* grab a snapshot of everything we need */
3566 services_info
= services_info_copy(session
, service_changes
);
3567 service_order
= service_order_get(services_info
);
3568 if (service_order
!= NULL
) {
3569 n_service_order
= CFArrayGetCount(service_order
);
3570 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3571 SCLog(TRUE
, LOG_NOTICE
,
3572 CFSTR("IPMonitor: service_order %@ "), service_order
);
3575 n
= CFArrayGetCount(service_changes
);
3576 for (i
= 0; i
< n
; i
++) {
3578 CFStringRef serviceID
;
3579 Boolean wasSupplemental
;
3581 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
3582 wasSupplemental
= dns_has_supplemental(serviceID
);
3583 changes
= service_changed(services_info
, serviceID
);
3584 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
3585 /* if __Service__ (e.g. PrimaryRank) changed */
3586 global_ipv4_changed
= TRUE
;
3588 else if (S_primary_ipv4
!= NULL
&& CFEqual(S_primary_ipv4
, serviceID
)) {
3589 // if we are looking at the primary [IPv4] service
3590 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
3591 // and something changed for THIS service
3592 global_ipv4_changed
= TRUE
;
3595 else if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
3596 global_ipv4_changed
= TRUE
;
3598 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
3599 // if we are looking at the primary [IPv6] service
3600 if (S_primary_ipv6
!= NULL
&& CFEqual(S_primary_ipv6
, serviceID
)) {
3601 update_ipv6(services_info
, serviceID
, &keys
);
3603 // and something changed for THIS service
3604 global_ipv6_changed
= TRUE
;
3606 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
3607 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
3608 update_dns(services_info
, serviceID
, &keys
);
3609 dnsinfo_changed
= TRUE
;
3611 else if (wasSupplemental
|| dns_has_supplemental(serviceID
)) {
3612 dnsinfo_changed
= TRUE
;
3615 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
3616 if (S_primary_proxies
!= NULL
&& CFEqual(S_primary_proxies
, serviceID
)) {
3617 update_proxies(services_info
, serviceID
, &keys
);
3620 #if !TARGET_OS_IPHONE
3621 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
3622 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
3623 update_smb(services_info
, serviceID
, &keys
);
3626 #endif /* !TARGET_OS_IPHONE */
3629 if (global_ipv4_changed
) {
3630 IPv4RouteListRef new_routelist
= NULL
;
3631 CFStringRef new_primary
;
3633 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3634 SCLog(TRUE
, LOG_NOTICE
,
3635 CFSTR("IPMonitor: IPv4 service election"));
3637 new_primary
= elect_new_primary(&elect_ipv4
, &new_routelist
,
3638 service_order
, n_service_order
);
3639 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
3640 update_ipv4(S_primary_ipv4
, new_routelist
, &keys
);
3641 my_CFRelease(&new_primary
);
3643 if (global_ipv6_changed
) {
3644 CFStringRef new_primary
;
3646 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3647 SCLog(TRUE
, LOG_NOTICE
,
3648 CFSTR("IPMonitor: IPv6 service election"));
3650 new_primary
= elect_new_primary(&elect_ipv6
, NULL
,
3651 service_order
, n_service_order
);
3652 if (set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6")) {
3653 update_ipv6(services_info
, S_primary_ipv6
, &keys
);
3655 my_CFRelease(&new_primary
);
3657 if (global_ipv4_changed
|| global_ipv6_changed
) {
3658 CFStringRef new_primary_dns
= NULL
;
3659 CFStringRef new_primary_proxies
= NULL
;
3660 #if !TARGET_OS_IPHONE
3661 CFStringRef new_primary_smb
= NULL
;
3662 #endif /* !TARGET_OS_IPHONE */
3664 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
3665 /* decide between IPv4 and IPv6 */
3666 if (rank_service_entity(S_ipv4_service_rank_dict
,
3667 S_primary_ipv4
, kSCEntNetDNS
)
3668 <= rank_service_entity(S_ipv6_service_rank_dict
,
3669 S_primary_ipv6
, kSCEntNetDNS
)) {
3670 new_primary_dns
= S_primary_ipv4
;
3673 new_primary_dns
= S_primary_ipv6
;
3675 if (rank_service_entity(S_ipv4_service_rank_dict
,
3676 S_primary_ipv4
, kSCEntNetProxies
)
3677 <= rank_service_entity(S_ipv6_service_rank_dict
,
3678 S_primary_ipv6
, kSCEntNetProxies
)) {
3679 new_primary_proxies
= S_primary_ipv4
;
3682 new_primary_proxies
= S_primary_ipv6
;
3684 #if !TARGET_OS_IPHONE
3685 if (rank_service_entity(S_ipv4_service_rank_dict
,
3686 S_primary_ipv4
, kSCEntNetSMB
)
3687 <= rank_service_entity(S_ipv6_service_rank_dict
,
3688 S_primary_ipv6
, kSCEntNetSMB
)) {
3689 new_primary_smb
= S_primary_ipv4
;
3692 new_primary_smb
= S_primary_ipv6
;
3694 #endif /* !TARGET_OS_IPHONE */
3697 else if (S_primary_ipv6
!= NULL
) {
3698 new_primary_dns
= S_primary_ipv6
;
3699 new_primary_proxies
= S_primary_ipv6
;
3700 #if !TARGET_OS_IPHONE
3701 new_primary_smb
= S_primary_ipv6
;
3702 #endif /* !TARGET_OS_IPHONE */
3704 else if (S_primary_ipv4
!= NULL
) {
3705 new_primary_dns
= S_primary_ipv4
;
3706 new_primary_proxies
= S_primary_ipv4
;
3707 #if !TARGET_OS_IPHONE
3708 new_primary_smb
= S_primary_ipv4
;
3709 #endif /* !TARGET_OS_IPHONE */
3712 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
3713 update_dns(services_info
, S_primary_dns
, &keys
);
3714 dnsinfo_changed
= TRUE
;
3716 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
, "Proxies")) {
3717 update_proxies(services_info
, S_primary_proxies
, &keys
);
3719 #if !TARGET_OS_IPHONE
3720 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
3721 update_smb(services_info
, S_primary_smb
, &keys
);
3723 #endif /* !TARGET_OS_IPHONE */
3725 if (dnsinfo_changed
) {
3726 update_dnsinfo(services_info
, S_primary_dns
, &keys
, service_order
);
3728 my_CFRelease(&service_changes
);
3729 my_CFRelease(&services_info
);
3730 keyChangeListApplyToStore(&keys
, session
);
3731 keyChangeListFree(&keys
);
3738 CFMutableArrayRef keys
= NULL
;
3739 CFMutableArrayRef patterns
= NULL
;
3740 CFRunLoopSourceRef rls
= NULL
;
3742 if (S_is_network_boot() != 0) {
3747 if (S_is_scoped_routing_enabled() != 0) {
3748 S_scopedroute
= TRUE
;
3750 #endif /* RTF_IFSCOPE */
3752 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
3753 IPMonitorNotify
, NULL
);
3754 if (S_session
== NULL
) {
3755 SCLog(TRUE
, LOG_ERR
,
3756 CFSTR("IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s"),
3757 SCErrorString(SCError()));
3761 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3762 kSCDynamicStoreDomainState
,
3765 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3766 kSCDynamicStoreDomainState
,
3769 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3770 kSCDynamicStoreDomainState
,
3772 S_state_global_proxies
3773 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3774 kSCDynamicStoreDomainState
,
3776 #if !TARGET_OS_IPHONE
3778 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3779 kSCDynamicStoreDomainState
,
3781 #endif /* !TARGET_OS_IPHONE */
3783 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3784 kSCDynamicStoreDomainSetup
,
3786 S_setup_global_proxies
3787 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3788 kSCDynamicStoreDomainSetup
,
3790 #if !TARGET_OS_IPHONE
3792 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3793 kSCDynamicStoreDomainSetup
,
3795 #endif /* !TARGET_OS_IPHONE */
3796 S_state_service_prefix
3797 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@/"),
3798 kSCDynamicStoreDomainState
,
3801 S_setup_service_prefix
3802 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@/"),
3803 kSCDynamicStoreDomainSetup
,
3806 S_service_state_dict
3807 = CFDictionaryCreateMutable(NULL
, 0,
3808 &kCFTypeDictionaryKeyCallBacks
,
3809 &kCFTypeDictionaryValueCallBacks
);
3811 S_ipv4_service_rank_dict
3812 = CFDictionaryCreateMutable(NULL
, 0,
3813 &kCFTypeDictionaryKeyCallBacks
,
3814 &kCFTypeDictionaryValueCallBacks
);
3816 S_ipv6_service_rank_dict
3817 = CFDictionaryCreateMutable(NULL
, 0,
3818 &kCFTypeDictionaryKeyCallBacks
,
3819 &kCFTypeDictionaryValueCallBacks
);
3821 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3822 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3824 /* register for State: and Setup: per-service notifications */
3825 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
3827 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
3828 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
3830 /* add notifier for multicast DNS configuration (Bonjour/.local) */
3831 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
3832 kSCDynamicStoreDomainState
,
3834 CFSTR(kDNSServiceCompMulticastDNS
));
3835 CFArrayAppendValue(keys
, S_multicast_resolvers
);
3837 /* add notifier for private DNS configuration (Back to My Mac) */
3838 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
3839 kSCDynamicStoreDomainState
,
3841 CFSTR(kDNSServiceCompPrivateDNS
));
3842 CFArrayAppendValue(keys
, S_private_resolvers
);
3844 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
3845 SCLog(TRUE
, LOG_ERR
,
3846 CFSTR("IPMonitor ip_plugin_init "
3847 "SCDynamicStoreSetNotificationKeys failed: %s"),
3848 SCErrorString(SCError()));
3852 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
3854 SCLog(TRUE
, LOG_ERR
,
3855 CFSTR("IPMonitor ip_plugin_init "
3856 "SCDynamicStoreCreateRunLoopSource failed: %s"),
3857 SCErrorString(SCError()));
3861 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
3864 /* initialize dns configuration */
3865 dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
3866 #if !TARGET_OS_IPHONE
3868 #endif /* !TARGET_OS_IPHONE */
3869 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
3871 #if !TARGET_OS_IPHONE
3872 /* initialize SMB configuration */
3873 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
3874 #endif /* !TARGET_OS_IPHONE */
3877 my_CFRelease(&keys
);
3878 my_CFRelease(&patterns
);
3886 /* initialize multicast route */
3887 update_ipv4(NULL
, NULL
, NULL
);
3892 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
3896 boolean_t ret
= def
;
3898 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
3900 ret
= CFBooleanGetValue(b
);
3907 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
3909 CFDictionaryRef info_dict
;
3911 info_dict
= CFBundleGetInfoDictionary(bundle
);
3912 if (info_dict
!= NULL
) {
3914 = S_get_plist_boolean(info_dict
,
3915 CFSTR("AppendStateArrayToSetupArray"),
3918 if (bundleVerbose
) {
3919 S_IPMonitor_debug
= kDebugFlagDefault
;
3922 dns_configuration_init(bundle
);
3925 #if !TARGET_OS_IPHONE
3926 if (S_session
!= NULL
) {
3927 dns_configuration_monitor(S_session
, IPMonitorNotify
);
3929 #endif /* !TARGET_OS_IPHONE */
3931 load_hostname((S_IPMonitor_debug
& kDebugFlag1
) != 0);
3932 #if !TARGET_OS_IPHONE
3933 load_smb_configuration((S_IPMonitor_debug
& kDebugFlag1
) != 0);
3934 #endif /* !TARGET_OS_IPHONE */
3940 #ifdef TEST_IPMONITOR
3941 #include "dns-configuration.c"
3942 #include "set-hostname.c"
3945 main(int argc
, char **argv
)
3949 S_IPMonitor_debug
= kDebugFlag1
;
3951 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
3954 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
3961 #endif /* TEST_IPMONITOR */
3963 #ifdef TEST_IPV4_ROUTELIST
3964 #include "dns-configuration.c"
3965 #include "set-hostname.c"
3967 struct ipv4_service_contents
{
3971 const char * router
;
3972 const char * ifname
;
3974 const CFStringRef
*primaryRank
;
3978 * addr mask dest router ifname pri primaryRank
3980 struct ipv4_service_contents en0_10
= {
3981 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, NULL
3984 struct ipv4_service_contents en0_15
= {
3985 "10.0.0.19", "255.255.255.0", NULL
, "10.0.0.1", "en0", 15, NULL
3988 struct ipv4_service_contents en0_30
= {
3989 "10.0.0.11", "255.255.255.0", NULL
, "10.0.0.1", "en0", 30, NULL
3992 struct ipv4_service_contents en0_40
= {
3993 "10.0.0.12", "255.255.255.0", NULL
, "10.0.0.1", "en0", 40, NULL
3996 struct ipv4_service_contents en0_50
= {
3997 "10.0.0.13", "255.255.255.0", NULL
, "10.0.0.1", "en0", 50, NULL
4000 struct ipv4_service_contents en0_110
= {
4001 "192.168.2.10", "255.255.255.0", NULL
, "192.168.2.1", "en0", 110, NULL
4004 struct ipv4_service_contents en0_1
= {
4005 "17.202.40.191", "255.255.252.0", NULL
, "17.202.20.1", "en0", 1, NULL
4008 struct ipv4_service_contents en1_20
= {
4009 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, NULL
4012 struct ipv4_service_contents en1_2
= {
4013 "17.202.42.24", "255.255.252.0", NULL
, "17.202.20.1", "en1", 2, NULL
4016 struct ipv4_service_contents en1_125
= {
4017 "192.168.2.20", "255.255.255.0", NULL
, "192.168.2.1", "en1", 125, NULL
4020 struct ipv4_service_contents fw0_25
= {
4021 "192.168.2.30", "255.255.255.0", NULL
, "192.168.2.1", "fw0", 25, NULL
4024 struct ipv4_service_contents fw0_21
= {
4025 "192.168.3.30", "255.255.255.0", NULL
, "192.168.3.1", "fw0", 21, NULL
4028 struct ipv4_service_contents ppp0_0_1
= {
4029 "17.219.156.22", NULL
, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
4032 struct ipv4_service_contents en0_test6
= {
4033 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 2, NULL
4035 struct ipv4_service_contents en1_test6
= {
4036 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 3, NULL
4038 struct ipv4_service_contents en2_test6
= {
4039 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
4042 struct ipv4_service_contents en0_test7
= {
4043 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 3, NULL
4045 struct ipv4_service_contents en1_test7
= {
4046 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 2, NULL
4048 struct ipv4_service_contents en2_test7
= {
4049 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
4051 struct ipv4_service_contents fw0_test6_and_7
= {
4052 "169.254.11.33", "255.255.0.0", NULL
, NULL
, "fw0", UINT_MAX
, NULL
4055 struct ipv4_service_contents en0_10_last
= {
4056 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
4059 struct ipv4_service_contents en0_10_never
= {
4060 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
4063 struct ipv4_service_contents en1_20_first
= {
4064 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
4067 struct ipv4_service_contents en1_20_never
= {
4068 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
4071 struct ipv4_service_contents
* test1
[] = {
4082 struct ipv4_service_contents
* test2
[] = {
4092 struct ipv4_service_contents
* test3
[] = {
4106 struct ipv4_service_contents
* test4
[] = {
4115 struct ipv4_service_contents
* test5
[] = {
4125 struct ipv4_service_contents
* test6
[] = {
4133 struct ipv4_service_contents
* test7
[] = {
4141 struct ipv4_service_contents
* test8
[] = {
4147 struct ipv4_service_contents
* test9
[] = {
4154 struct ipv4_service_contents
* test10
[] = {
4161 struct ipv4_service_contents
* test11
[] = {
4168 struct ipv4_service_contents
* test12
[] = {
4174 struct ipv4_service_contents
* test13
[] = {
4180 struct ipv4_service_contents
* test14
[] = {
4186 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
4189 CFStringRef prop_val
;
4194 prop_val
= CFStringCreateWithCString(NULL
,
4196 kCFStringEncodingASCII
);
4197 CFDictionarySetValue(dict
, prop_name
, prop_val
);
4198 CFRelease(prop_val
);
4203 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
4207 CFStringRef prop_val
;
4212 prop_val
= CFStringCreateWithCString(NULL
,
4214 kCFStringEncodingASCII
);
4215 array
= CFArrayCreate(NULL
,
4216 (const void **)&prop_val
, 1,
4217 &kCFTypeArrayCallBacks
);
4218 CFRelease(prop_val
);
4219 CFDictionarySetValue(dict
, prop_name
, array
);
4224 static CFDictionaryRef
4225 make_IPv4_dict(struct ipv4_service_contents
* t
)
4227 CFMutableDictionaryRef dict
;
4229 dict
= CFDictionaryCreateMutable(NULL
, 0,
4230 &kCFTypeDictionaryKeyCallBacks
,
4231 &kCFTypeDictionaryValueCallBacks
);
4232 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
4233 dict_add_string_as_array(dict
, kSCPropNetIPv4SubnetMasks
, t
->mask
);
4234 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
4235 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
4236 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
4240 static IPv4RouteListRef
4241 make_IPv4RouteList(struct ipv4_service_contents
* * this_test
)
4244 IPv4RouteListRef routes
;
4245 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
4246 IPv4RouteListRef ret
= NULL
;
4247 struct ipv4_service_contents
* * scan_test
;
4249 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
4250 CFDictionaryRef dict
;
4252 dict
= make_IPv4_dict(*scan_test
);
4254 fprintf(stderr
, "make_IPv4_dict failed\n");
4257 routes
= (IPv4RouteListRef
)routes_buf
;
4258 routes
->size
= R_STATIC
;
4260 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
4261 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
4263 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
4266 ret
= IPv4RouteListAddRouteList(ret
, 1, r
, (*scan_test
)->rank
);
4276 * Function: run_test
4278 * Runs through the given set of routes first in the forward direction,
4279 * then again backwards. We should end up with exactly the same set of
4280 * routes at the end.
4283 run_test(const char * name
, struct ipv4_service_contents
* * this_test
)
4286 boolean_t ret
= FALSE
;
4288 IPv4RouteListRef routes
;
4289 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
4290 IPv4RouteListRef routes1
= NULL
, routes2
= NULL
;
4291 struct ipv4_service_contents
* * scan_test
;
4293 printf("\nStarting test %s\n", name
);
4294 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
4295 CFDictionaryRef dict
;
4297 dict
= make_IPv4_dict(*scan_test
);
4299 fprintf(stderr
, "make_IPv4_dict failed\n");
4302 routes
= (IPv4RouteListRef
)routes_buf
;
4303 routes
->size
= R_STATIC
;
4305 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
4306 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
4308 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
4311 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4312 descr
= IPv4RouteListCopyDescription(r
);
4313 SCLog(TRUE
, LOG_NOTICE
, CFSTR("test: Adding %@"), descr
);
4317 routes1
= IPv4RouteListAddRouteList(routes1
, 1, r
, (*scan_test
)->rank
);
4323 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4324 if (routes1
!= NULL
) {
4325 descr
= IPv4RouteListCopyDescription(routes1
);
4326 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Routes are %@"), descr
);
4330 for (scan_test
--; scan_test
>= this_test
; scan_test
--) {
4331 CFDictionaryRef dict
;
4333 dict
= make_IPv4_dict(*scan_test
);
4335 fprintf(stderr
, "make_IPv4_dict failed\n");
4338 routes
= (IPv4RouteListRef
)routes_buf
;
4339 routes
->size
= R_STATIC
;
4341 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
4342 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
4344 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
4347 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4348 descr
= IPv4RouteListCopyDescription(r
);
4349 SCLog(TRUE
, LOG_NOTICE
, CFSTR("test: Adding %@"), descr
);
4352 routes2
= IPv4RouteListAddRouteList(routes2
, 1, r
, (*scan_test
)->rank
);
4358 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4359 if (routes2
!= NULL
) {
4360 descr
= IPv4RouteListCopyDescription(routes2
);
4361 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Routes are %@"), descr
);
4365 if ((routes1
!= NULL
&& routes2
== NULL
)
4366 || (routes1
== NULL
&& routes2
!= NULL
)) {
4367 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
4368 (routes1
!= NULL
) ? "not " : "",
4369 (routes2
!= NULL
) ? "not " : "");
4371 else if (routes1
!= NULL
&& routes2
!= NULL
) {
4372 /* check if they are different */
4373 if (routes1
->count
!= routes2
->count
) {
4374 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
4375 routes1
->count
, routes2
->count
);
4377 else if (bcmp(routes1
, routes2
,
4378 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
4379 fprintf(stderr
, "routes1 and routes2 are different\n");
4382 printf("routes1 and routes2 are the same\n");
4386 if (routes1
!= NULL
) {
4389 if (routes2
!= NULL
) {
4395 typedef struct compare_context
{
4396 IPv4RouteListRef old
;
4397 IPv4RouteListRef
new;
4398 } compare_context_t
;
4401 compare_callback(IPv4RouteListApplyCommand cmd
, IPv4RouteRef route
, void * arg
)
4403 compare_context_t
* context
= (compare_context_t
*)arg
;
4406 case kIPv4RouteListAddRouteCommand
:
4407 printf("Add new[%d] = ", route
- context
->new->list
);
4408 IPv4RoutePrint(route
);
4411 case kIPv4RouteListRemoveRouteCommand
:
4412 printf("Remove old[%d] = ", route
- context
->old
->list
);
4413 IPv4RoutePrint(route
);
4423 compare_tests(struct ipv4_service_contents
* * old_test
,
4424 struct ipv4_service_contents
* * new_test
)
4426 IPv4RouteListRef new_routes
;
4427 IPv4RouteListRef old_routes
;
4428 compare_context_t context
;
4430 old_routes
= make_IPv4RouteList(old_test
);
4431 new_routes
= make_IPv4RouteList(new_test
);
4433 if (old_routes
== NULL
) {
4434 printf("No Old Routes\n");
4437 printf("Old Routes = ");
4438 IPv4RouteListPrint(old_routes
);
4440 if (new_routes
== NULL
) {
4441 printf("No New Routes\n");
4444 printf("New Routes = ");
4445 IPv4RouteListPrint(new_routes
);
4447 context
.old
= old_routes
;
4448 context
.new = new_routes
;
4449 IPv4RouteListApply(old_routes
, new_routes
, compare_callback
, &context
);
4450 if (old_routes
!= NULL
) {
4453 if (new_routes
!= NULL
) {
4461 main(int argc
, char **argv
)
4464 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
4466 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
4468 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
4471 if (run_test("test1", test1
) == FALSE
) {
4472 fprintf(stderr
, "test1 failed\n");
4475 if (run_test("test2", test2
) == FALSE
) {
4476 fprintf(stderr
, "test2 failed\n");
4479 if (run_test("test3", test4
) == FALSE
) {
4480 fprintf(stderr
, "test3 failed\n");
4483 if (run_test("test4", test4
) == FALSE
) {
4484 fprintf(stderr
, "test4 failed\n");
4487 if (run_test("test5", test5
) == FALSE
) {
4488 fprintf(stderr
, "test5 failed\n");
4492 printf("\nCompare 1 to 2:\n");
4493 compare_tests(test1
, test2
);
4495 printf("\nCompare 2 to 1:\n");
4496 compare_tests(test2
, test1
);
4498 printf("\nCompare 1 to 1:\n");
4499 compare_tests(test1
, test1
);
4501 printf("\nCompare 1 to 3:\n");
4502 compare_tests(test1
, test3
);
4504 printf("\nCompare 3 to 1:\n");
4505 compare_tests(test3
, test1
);
4507 printf("\nCompare 2 to 3:\n");
4508 compare_tests(test2
, test3
);
4510 printf("\nCompare 3 to 2:\n");
4511 compare_tests(test3
, test2
);
4513 printf("\nCompare 3 to 4:\n");
4514 compare_tests(test3
, test4
);
4516 printf("\nCompare 5 to 4:\n");
4517 compare_tests(test5
, test4
);
4519 printf("\nCompare 6 to 7:\n");
4520 compare_tests(test6
, test7
);
4522 printf("\nCompare 7 to 6:\n");
4523 compare_tests(test7
, test6
);
4525 printf("\nCompare 8 to 9:\n");
4526 compare_tests(test8
, test9
);
4528 printf("\nCompare 8 to 10:\n");
4529 compare_tests(test8
, test10
);
4531 printf("\nCompare 8 to 11:\n");
4532 compare_tests(test8
, test11
);
4534 printf("\nCompare 12 to 13:\n");
4535 compare_tests(test12
, test13
);
4537 printf("\nCompare 13 to 14:\n");
4538 compare_tests(test13
, test14
);
4540 printf("\nChecking for leaks\n");
4542 sprintf(cmd
, "leaks %d 2>&1", getpid());
4550 #endif /* TEST_IPV4_ROUTELIST */