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
) {
2957 CFMutableDictionaryRef dict
= NULL
;
2958 CFStringRef if_name
= NULL
;
2959 char ifn
[IFNAMSIZ
] = { '\0' };
2960 char * ifn_p
= NULL
;
2961 boolean_t is_direct
= FALSE
;
2962 CFStringRef val_router
= NULL
;
2964 dict
= CFDictionaryCreateMutable(NULL
, 0,
2965 &kCFTypeDictionaryKeyCallBacks
,
2966 &kCFTypeDictionaryValueCallBacks
);
2967 val_router
= CFDictionaryGetValue(ipv6_dict
, kSCPropNetIPv6Router
);
2968 if (val_router
!= NULL
) {
2971 /* no router if router is one of our IP addresses */
2972 addrs
= CFDictionaryGetValue(ipv6_dict
,
2973 kSCPropNetIPv6Addresses
);
2974 is_direct
= router_is_our_ipv6_address(val_router
, addrs
);
2975 CFDictionarySetValue(dict
, kSCPropNetIPv6Router
,
2978 if_name
= CFDictionaryGetValue(ipv6_dict
, kSCPropInterfaceName
);
2980 CFDictionarySetValue(dict
,
2981 kSCDynamicStorePropNetPrimaryInterface
,
2983 if (CFStringGetCString(if_name
, ifn
, sizeof(ifn
),
2984 kCFStringEncodingASCII
)) {
2988 CFDictionarySetValue(dict
, kSCDynamicStorePropNetPrimaryService
,
2990 keyChangeListSetValue(keys
, S_state_global_ipv6
, dict
);
2993 if (val_router
!= NULL
) { /* route add default ... */
2994 struct in6_addr router
;
2996 (void)cfstring_to_ip6(val_router
, &router
);
2997 set_ipv6_router(&router
, ifn_p
, is_direct
);
3001 keyChangeListRemoveValue(keys
, S_state_global_ipv6
);
3002 set_ipv6_router(NULL
, NULL
, FALSE
);
3008 update_dns(CFDictionaryRef service_info
,
3009 CFStringRef primary
,
3010 keyChangeListRef keys
)
3012 CFDictionaryRef dict
= NULL
;
3014 if (primary
!= NULL
) {
3015 CFDictionaryRef service_dict
;
3017 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3018 if (service_dict
!= NULL
) {
3019 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
3023 #if !TARGET_OS_IPHONE
3025 #endif /* !TARGET_OS_IPHONE */
3026 keyChangeListRemoveValue(keys
, S_state_global_dns
);
3029 #if !TARGET_OS_IPHONE
3030 set_dns(CFDictionaryGetValue(dict
, kSCPropNetDNSSearchDomains
),
3031 CFDictionaryGetValue(dict
, kSCPropNetDNSDomainName
),
3032 CFDictionaryGetValue(dict
, kSCPropNetDNSServerAddresses
),
3033 CFDictionaryGetValue(dict
, kSCPropNetDNSSortList
));
3034 #endif /* !TARGET_OS_IPHONE */
3035 keyChangeListSetValue(keys
, S_state_global_dns
, dict
);
3041 update_dnsinfo(CFDictionaryRef service_info
,
3042 CFStringRef primary
,
3043 keyChangeListRef keys
,
3044 CFArrayRef service_order
)
3046 CFDictionaryRef dict
= NULL
;
3047 CFArrayRef multicastResolvers
;
3048 CFArrayRef privateResolvers
;
3050 multicastResolvers
= CFDictionaryGetValue(service_info
, S_multicast_resolvers
);
3051 privateResolvers
= CFDictionaryGetValue(service_info
, S_private_resolvers
);
3053 if (primary
!= NULL
) {
3054 CFDictionaryRef service_dict
;
3056 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3057 if (service_dict
!= NULL
) {
3058 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetDNS
);
3062 dns_configuration_set(dict
,
3063 S_service_state_dict
,
3067 keyChangeListNotifyKey(keys
, S_state_global_dns
);
3072 update_proxies(CFDictionaryRef service_info
,
3073 CFStringRef primary
,
3074 keyChangeListRef keys
)
3076 CFDictionaryRef dict
= NULL
;
3078 if (primary
!= NULL
) {
3079 CFDictionaryRef service_dict
;
3081 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3082 if (service_dict
!= NULL
) {
3083 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetProxies
);
3085 dict
= my_CFDictionaryGetDictionary(service_info
,
3086 S_setup_global_proxies
);
3091 keyChangeListRemoveValue(keys
, S_state_global_proxies
);
3094 keyChangeListSetValue(keys
, S_state_global_proxies
, dict
);
3099 #if !TARGET_OS_IPHONE
3101 update_smb(CFDictionaryRef service_info
,
3102 CFStringRef primary
,
3103 keyChangeListRef keys
)
3105 CFDictionaryRef dict
= NULL
;
3107 if (primary
!= NULL
) {
3108 CFDictionaryRef service_dict
;
3110 service_dict
= CFDictionaryGetValue(S_service_state_dict
, primary
);
3111 if (service_dict
!= NULL
) {
3112 dict
= CFDictionaryGetValue(service_dict
, kSCEntNetSMB
);
3114 dict
= my_CFDictionaryGetDictionary(service_info
,
3115 S_setup_global_smb
);
3120 keyChangeListRemoveValue(keys
, S_state_global_smb
);
3123 keyChangeListSetValue(keys
, S_state_global_smb
, dict
);
3128 #endif /* !TARGET_OS_IPHONE */
3131 get_service_rank(CFArrayRef order
, int n_order
, CFStringRef serviceID
)
3134 Rank rank
= kRankLast
;
3136 if (serviceID
!= NULL
&& order
!= NULL
&& n_order
> 0) {
3137 for (i
= 0; i
< n_order
; i
++) {
3138 CFStringRef s
= isA_CFString(CFArrayGetValueAtIndex(order
, i
));
3143 if (CFEqual(serviceID
, s
)) {
3153 ** Service election:
3156 * Function: rank_dict_get_service_rank
3158 * Retrieve the service rank in the given dictionary.
3161 rank_dict_get_service_rank(CFDictionaryRef rank_dict
, CFStringRef serviceID
)
3164 Rank rank_val
= kRankLast
;
3166 rank
= CFDictionaryGetValue(rank_dict
, serviceID
);
3168 CFNumberGetValue(rank
, kCFNumberSInt32Type
, &rank_val
);
3174 * Function: rank_dict_set_service_rank
3176 * Save the results of ranking the service so we can look it up later without
3177 * repeating all of the ranking code.
3180 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict
,
3181 CFStringRef serviceID
, Rank rank_val
)
3185 rank
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, (const void *)&rank_val
);
3187 CFDictionarySetValue(rank_dict
, serviceID
, rank
);
3193 typedef struct election_info
{
3197 CFStringRef serviceID
;
3198 CFDictionaryRef service_dict
;
3200 boolean_t choose_last
;
3203 typedef boolean_t
election_func_t(void * context
, election_info_t
* info
);
3206 * Function: elect_ipv4
3208 * This function builds the list of IPv4 routes that should be active.
3209 * As elect_new_primary() invokes us with each service, we build up the
3210 * result in the passed in context, a pointer to an IPv4RouteListRef.
3213 elect_ipv4(void * context
, election_info_t
* info
)
3215 IPv4RouteListRef
* routes_p
= (IPv4RouteListRef
*)context
;
3216 IPv4RouteListRef service_routes
;
3218 service_routes
= service_dict_get_ipv4_routelist(info
->service_dict
);
3219 if (service_routes
== NULL
) {
3222 if ((service_routes
->list
->flags
& kRouteChooseFirstFlag
) != 0) {
3223 info
->service_rank
= kRankFirst
;
3225 else if (S_ppp_override_primary
3226 && (strncmp(PPP_PREFIX
, service_routes
->list
->ifname
,
3227 sizeof(PPP_PREFIX
) - 1) == 0)) {
3228 /* PPP override: make ppp* look the best */
3229 /* Hack: should use interface type, not interface name */
3230 info
->service_rank
= kRankFirst
;
3233 info
->service_rank
= get_service_rank(info
->order
, info
->n_order
,
3235 if ((service_routes
->list
->flags
& kRouteChooseLastFlag
) != 0) {
3236 info
->choose_last
= TRUE
;
3239 if (routes_p
!= NULL
) {
3240 *routes_p
= IPv4RouteListAddRouteList(*routes_p
,
3241 info
->n_services
* 3,
3243 info
->service_rank
);
3245 if ((service_routes
->list
->flags
& kRouteChooseNeverFlag
) != 0) {
3246 /* never elect as primary */
3249 rank_dict_set_service_rank(S_ipv4_service_rank_dict
,
3250 info
->serviceID
, info
->service_rank
);
3255 elect_ipv6(void * context
, election_info_t
* info
)
3257 CFStringRef if_name
;
3258 CFStringRef primaryRank
= NULL
;
3259 CFDictionaryRef proto_dict
;
3261 CFDictionaryRef service_options
;
3263 proto_dict
= CFDictionaryGetValue(info
->service_dict
, kSCEntNetIPv6
);
3264 if (proto_dict
== NULL
) {
3267 service_options
= service_dict_get(info
->serviceID
, kSCEntNetService
);
3268 if (service_options
!= NULL
) {
3269 primaryRank
= CFDictionaryGetValue(service_options
, kSCPropNetServicePrimaryRank
);
3270 if ((primaryRank
!= NULL
)
3271 && CFEqual(primaryRank
, kSCValNetServicePrimaryRankNever
)) {
3275 router
= CFDictionaryGetValue(proto_dict
,
3276 kSCPropNetIPv6Router
);
3277 if (router
== NULL
) {
3278 info
->choose_last
= TRUE
;
3279 info
->service_rank
= kRankLast
;
3281 else if ((primaryRank
!= NULL
)
3282 && CFEqual(primaryRank
, kSCValNetServicePrimaryRankFirst
)) {
3283 info
->service_rank
= kRankFirst
;
3285 else if (get_override_primary(proto_dict
)) {
3286 info
->service_rank
= kRankFirst
;
3288 else if (S_ppp_override_primary
3289 && CFDictionaryGetValueIfPresent(proto_dict
,
3290 kSCPropInterfaceName
,
3291 (const void **)&if_name
)
3292 && CFStringHasPrefix(if_name
, CFSTR(PPP_PREFIX
))) {
3293 /* PPP override: make ppp* look the best */
3294 /* Hack: should use interface type, not interface name */
3295 info
->service_rank
= kRankFirst
;
3298 info
->service_rank
= get_service_rank(info
->order
, info
->n_order
,
3301 rank_dict_set_service_rank(S_ipv6_service_rank_dict
,
3302 info
->serviceID
, info
->service_rank
);
3307 * Function: elect_new_primary
3309 * Walk the list of services, passing each service dictionary to "elect_func".
3310 * "elect_func" returns rank information about the service that let us
3311 * determine the new primary.
3314 elect_new_primary(election_func_t
* elect_func
, void * context
,
3315 CFArrayRef order
, int n_order
)
3319 election_info_t info
;
3321 #define N_KEYS_VALUES_STATIC 10
3322 void * keys_values_buf
[N_KEYS_VALUES_STATIC
* 2];
3323 CFStringRef new_primary
= NULL
;
3324 Rank new_primary_rank
= kRankLast
;
3325 boolean_t new_primary_choose_last
= FALSE
;
3328 count
= CFDictionaryGetCount(S_service_state_dict
);
3329 if (count
<= N_KEYS_VALUES_STATIC
) {
3330 keys
= keys_values_buf
;
3333 keys
= (void * *)malloc(sizeof(*keys
) * count
* 2);
3335 values
= keys
+ count
;
3336 CFDictionaryGetKeysAndValues(S_service_state_dict
,
3337 (const void * *)keys
,
3338 (const void * *)values
);
3340 info
.n_services
= count
;
3342 info
.n_order
= n_order
;
3343 for (i
= 0; i
< count
; i
++) {
3344 boolean_t found_new_primary
= FALSE
;
3346 info
.serviceID
= (CFStringRef
)keys
[i
];
3347 info
.service_dict
= (CFDictionaryRef
)values
[i
];
3348 info
.service_rank
= kRankLast
;
3349 info
.choose_last
= FALSE
;
3351 if ((*elect_func
)(context
, &info
) == FALSE
) {
3354 if (new_primary
== NULL
) {
3355 found_new_primary
= TRUE
;
3357 else if (info
.choose_last
== new_primary_choose_last
) {
3358 found_new_primary
= (info
.service_rank
< new_primary_rank
);
3360 else if (new_primary_choose_last
) {
3361 found_new_primary
= TRUE
;
3363 if (found_new_primary
) {
3364 new_primary
= info
.serviceID
;
3365 new_primary_rank
= info
.service_rank
;
3366 new_primary_choose_last
= info
.choose_last
;
3369 if (new_primary
!= NULL
) {
3370 CFRetain(new_primary
);
3372 if (keys
!= keys_values_buf
) {
3375 return (new_primary
);
3379 service_changed(CFDictionaryRef services_info
, CFStringRef serviceID
)
3381 uint32_t changed
= 0;
3384 /* update service options first (e.g. rank) */
3385 if (get_rank_changes(serviceID
,
3386 get_service_state_entity(services_info
, serviceID
,
3388 get_service_setup_entity(services_info
, serviceID
,
3391 changed
|= (1 << kEntityTypeServiceOptions
);
3393 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
3394 for (i
= 0; i
< ENTITY_TYPES_COUNT
; i
++) {
3395 GetEntityChangesFuncRef func
= entityChangeFunc
[i
];
3396 if ((*func
)(serviceID
,
3397 get_service_state_entity(services_info
, serviceID
,
3398 *entityTypeNames
[i
]),
3399 get_service_setup_entity(services_info
, serviceID
,
3400 *entityTypeNames
[i
]),
3402 changed
|= (1 << i
);
3409 service_order_get(CFDictionaryRef services_info
)
3411 CFArrayRef order
= NULL
;
3412 CFDictionaryRef ipv4_dict
;
3414 ipv4_dict
= my_CFDictionaryGetDictionary(services_info
,
3415 S_setup_global_ipv4
);
3416 if (ipv4_dict
!= NULL
) {
3417 CFNumberRef ppp_override
;
3420 order
= CFDictionaryGetValue(ipv4_dict
, kSCPropNetServiceOrder
);
3421 order
= isA_CFArray(order
);
3423 /* get ppp override primary */
3424 ppp_override
= CFDictionaryGetValue(ipv4_dict
,
3425 kSCPropNetPPPOverridePrimary
);
3426 ppp_override
= isA_CFNumber(ppp_override
);
3427 if (ppp_override
!= NULL
) {
3428 CFNumberGetValue(ppp_override
, kCFNumberIntType
, &ppp_val
);
3430 S_ppp_override_primary
= (ppp_val
!= 0) ? TRUE
: FALSE
;
3433 S_ppp_override_primary
= FALSE
;
3439 set_new_primary(CFStringRef
* primary_p
, CFStringRef new_primary
,
3440 const char * entity
)
3442 boolean_t changed
= FALSE
;
3443 CFStringRef primary
= *primary_p
;
3445 if (new_primary
!= NULL
) {
3446 if (primary
!= NULL
&& CFEqual(new_primary
, primary
)) {
3447 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3448 SCLog(TRUE
, LOG_NOTICE
,
3449 CFSTR("IPMonitor: %@ is still primary %s"),
3450 new_primary
, entity
);
3454 my_CFRelease(primary_p
);
3455 *primary_p
= CFRetain(new_primary
);
3456 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3457 SCLog(TRUE
, LOG_NOTICE
,
3458 CFSTR("IPMonitor: %@ is the new primary %s"),
3459 new_primary
, entity
);
3464 else if (primary
!= NULL
) {
3465 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3466 SCLog(TRUE
, LOG_NOTICE
,
3467 CFSTR("IPMonitor: %@ is no longer primary %s"),
3470 my_CFRelease(primary_p
);
3477 rank_service_entity(CFDictionaryRef rank_dict
, CFStringRef serviceID
,
3480 if (service_dict_get(serviceID
, entity
) == NULL
) {
3483 return (rank_dict_get_service_rank(rank_dict
, serviceID
));
3487 IPMonitorNotify(SCDynamicStoreRef session
, CFArrayRef changed_keys
,
3491 boolean_t dnsinfo_changed
= FALSE
;
3492 boolean_t global_ipv4_changed
= FALSE
;
3493 boolean_t global_ipv6_changed
= FALSE
;
3497 int n_service_order
= 0;
3498 CFArrayRef service_order
;
3499 CFMutableArrayRef service_changes
= NULL
;
3500 CFDictionaryRef services_info
= NULL
;
3502 count
= CFArrayGetCount(changed_keys
);
3507 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3508 SCLog(TRUE
, LOG_NOTICE
,
3509 CFSTR("IPMonitor: changes %@ (%d)"), changed_keys
, count
);
3512 keyChangeListInit(&keys
);
3513 service_changes
= CFArrayCreateMutable(NULL
, 0,
3514 &kCFTypeArrayCallBacks
);
3515 for (i
= 0; i
< count
; i
++) {
3516 CFStringRef change
= CFArrayGetValueAtIndex(changed_keys
, i
);
3517 if (CFEqual(change
, S_setup_global_ipv4
)) {
3518 global_ipv4_changed
= TRUE
;
3519 global_ipv6_changed
= TRUE
;
3521 else if (CFEqual(change
, S_setup_global_proxies
)) {
3522 if (S_primary_proxies
!= NULL
) {
3523 my_CFArrayAppendUniqueValue(service_changes
, S_primary_proxies
);
3526 #if !TARGET_OS_IPHONE
3527 else if (CFEqual(change
, S_setup_global_smb
)) {
3528 if (S_primary_smb
!= NULL
) {
3529 my_CFArrayAppendUniqueValue(service_changes
, S_primary_smb
);
3532 #endif /* !TARGET_OS_IPHONE */
3533 else if (CFEqual(change
, S_multicast_resolvers
)) {
3534 dnsinfo_changed
= TRUE
;
3536 else if (CFEqual(change
, S_private_resolvers
)) {
3537 dnsinfo_changed
= TRUE
;
3539 #if !TARGET_OS_IPHONE
3540 else if (CFEqual(change
, CFSTR(_PATH_RESOLVER_DIR
))) {
3541 dnsinfo_changed
= TRUE
;
3543 #endif /* !TARGET_OS_IPHONE */
3544 else if (CFStringHasPrefix(change
, S_state_service_prefix
)) {
3545 CFStringRef serviceID
= parse_component(change
,
3546 S_state_service_prefix
);
3548 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
3549 CFRelease(serviceID
);
3552 else if (CFStringHasPrefix(change
, S_setup_service_prefix
)) {
3553 CFStringRef serviceID
= parse_component(change
,
3554 S_setup_service_prefix
);
3556 my_CFArrayAppendUniqueValue(service_changes
, serviceID
);
3557 CFRelease(serviceID
);
3562 /* grab a snapshot of everything we need */
3563 services_info
= services_info_copy(session
, service_changes
);
3564 service_order
= service_order_get(services_info
);
3565 if (service_order
!= NULL
) {
3566 n_service_order
= CFArrayGetCount(service_order
);
3567 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3568 SCLog(TRUE
, LOG_NOTICE
,
3569 CFSTR("IPMonitor: service_order %@ "), service_order
);
3572 n
= CFArrayGetCount(service_changes
);
3573 for (i
= 0; i
< n
; i
++) {
3575 CFStringRef serviceID
;
3576 Boolean wasSupplemental
;
3578 serviceID
= CFArrayGetValueAtIndex(service_changes
, i
);
3579 wasSupplemental
= dns_has_supplemental(serviceID
);
3580 changes
= service_changed(services_info
, serviceID
);
3581 if ((changes
& (1 << kEntityTypeServiceOptions
)) != 0) {
3582 /* if __Service__ (e.g. PrimaryRank) changed */
3583 global_ipv4_changed
= TRUE
;
3585 else if (S_primary_ipv4
!= NULL
&& CFEqual(S_primary_ipv4
, serviceID
)) {
3586 // if we are looking at the primary [IPv4] service
3587 if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
3588 // and something changed for THIS service
3589 global_ipv4_changed
= TRUE
;
3592 else if ((changes
& (1 << kEntityTypeIPv4
)) != 0) {
3593 global_ipv4_changed
= TRUE
;
3595 if ((changes
& (1 << kEntityTypeIPv6
)) != 0) {
3596 // if we are looking at the primary [IPv6] service
3597 if (S_primary_ipv6
!= NULL
&& CFEqual(S_primary_ipv6
, serviceID
)) {
3598 update_ipv6(services_info
, serviceID
, &keys
);
3600 // and something changed for THIS service
3601 global_ipv6_changed
= TRUE
;
3603 if ((changes
& (1 << kEntityTypeDNS
)) != 0) {
3604 if (S_primary_dns
!= NULL
&& CFEqual(S_primary_dns
, serviceID
)) {
3605 update_dns(services_info
, serviceID
, &keys
);
3606 dnsinfo_changed
= TRUE
;
3608 else if (wasSupplemental
|| dns_has_supplemental(serviceID
)) {
3609 dnsinfo_changed
= TRUE
;
3612 if ((changes
& (1 << kEntityTypeProxies
)) != 0) {
3613 if (S_primary_proxies
!= NULL
&& CFEqual(S_primary_proxies
, serviceID
)) {
3614 update_proxies(services_info
, serviceID
, &keys
);
3617 #if !TARGET_OS_IPHONE
3618 if ((changes
& (1 << kEntityTypeSMB
)) != 0) {
3619 if (S_primary_smb
!= NULL
&& CFEqual(S_primary_smb
, serviceID
)) {
3620 update_smb(services_info
, serviceID
, &keys
);
3623 #endif /* !TARGET_OS_IPHONE */
3626 if (global_ipv4_changed
) {
3627 IPv4RouteListRef new_routelist
= NULL
;
3628 CFStringRef new_primary
;
3630 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3631 SCLog(TRUE
, LOG_NOTICE
,
3632 CFSTR("IPMonitor: IPv4 service election"));
3634 new_primary
= elect_new_primary(&elect_ipv4
, &new_routelist
,
3635 service_order
, n_service_order
);
3636 (void)set_new_primary(&S_primary_ipv4
, new_primary
, "IPv4");
3637 update_ipv4(S_primary_ipv4
, new_routelist
, &keys
);
3638 my_CFRelease(&new_primary
);
3640 if (global_ipv6_changed
) {
3641 CFStringRef new_primary
;
3643 if ((S_IPMonitor_debug
& kDebugFlag1
) != 0) {
3644 SCLog(TRUE
, LOG_NOTICE
,
3645 CFSTR("IPMonitor: IPv6 service election"));
3647 new_primary
= elect_new_primary(&elect_ipv6
, NULL
,
3648 service_order
, n_service_order
);
3649 if (set_new_primary(&S_primary_ipv6
, new_primary
, "IPv6")) {
3650 update_ipv6(services_info
, S_primary_ipv6
, &keys
);
3652 my_CFRelease(&new_primary
);
3654 if (global_ipv4_changed
|| global_ipv6_changed
) {
3655 CFStringRef new_primary_dns
= NULL
;
3656 CFStringRef new_primary_proxies
= NULL
;
3657 #if !TARGET_OS_IPHONE
3658 CFStringRef new_primary_smb
= NULL
;
3659 #endif /* !TARGET_OS_IPHONE */
3661 if (S_primary_ipv4
!= NULL
&& S_primary_ipv6
!= NULL
) {
3662 /* decide between IPv4 and IPv6 */
3663 if (rank_service_entity(S_ipv4_service_rank_dict
,
3664 S_primary_ipv4
, kSCEntNetDNS
)
3665 <= rank_service_entity(S_ipv6_service_rank_dict
,
3666 S_primary_ipv6
, kSCEntNetDNS
)) {
3667 new_primary_dns
= S_primary_ipv4
;
3670 new_primary_dns
= S_primary_ipv6
;
3672 if (rank_service_entity(S_ipv4_service_rank_dict
,
3673 S_primary_ipv4
, kSCEntNetProxies
)
3674 <= rank_service_entity(S_ipv6_service_rank_dict
,
3675 S_primary_ipv6
, kSCEntNetProxies
)) {
3676 new_primary_proxies
= S_primary_ipv4
;
3679 new_primary_proxies
= S_primary_ipv6
;
3681 #if !TARGET_OS_IPHONE
3682 if (rank_service_entity(S_ipv4_service_rank_dict
,
3683 S_primary_ipv4
, kSCEntNetSMB
)
3684 <= rank_service_entity(S_ipv6_service_rank_dict
,
3685 S_primary_ipv6
, kSCEntNetSMB
)) {
3686 new_primary_smb
= S_primary_ipv4
;
3689 new_primary_smb
= S_primary_ipv6
;
3691 #endif /* !TARGET_OS_IPHONE */
3694 else if (S_primary_ipv6
!= NULL
) {
3695 new_primary_dns
= S_primary_ipv6
;
3696 new_primary_proxies
= S_primary_ipv6
;
3697 #if !TARGET_OS_IPHONE
3698 new_primary_smb
= S_primary_ipv6
;
3699 #endif /* !TARGET_OS_IPHONE */
3701 else if (S_primary_ipv4
!= NULL
) {
3702 new_primary_dns
= S_primary_ipv4
;
3703 new_primary_proxies
= S_primary_ipv4
;
3704 #if !TARGET_OS_IPHONE
3705 new_primary_smb
= S_primary_ipv4
;
3706 #endif /* !TARGET_OS_IPHONE */
3709 if (set_new_primary(&S_primary_dns
, new_primary_dns
, "DNS")) {
3710 update_dns(services_info
, S_primary_dns
, &keys
);
3711 dnsinfo_changed
= TRUE
;
3713 if (set_new_primary(&S_primary_proxies
, new_primary_proxies
, "Proxies")) {
3714 update_proxies(services_info
, S_primary_proxies
, &keys
);
3716 #if !TARGET_OS_IPHONE
3717 if (set_new_primary(&S_primary_smb
, new_primary_smb
, "SMB")) {
3718 update_smb(services_info
, S_primary_smb
, &keys
);
3720 #endif /* !TARGET_OS_IPHONE */
3722 if (dnsinfo_changed
) {
3723 update_dnsinfo(services_info
, S_primary_dns
, &keys
, service_order
);
3725 my_CFRelease(&service_changes
);
3726 my_CFRelease(&services_info
);
3727 keyChangeListApplyToStore(&keys
, session
);
3728 keyChangeListFree(&keys
);
3735 CFMutableArrayRef keys
= NULL
;
3736 CFMutableArrayRef patterns
= NULL
;
3737 CFRunLoopSourceRef rls
= NULL
;
3739 if (S_is_network_boot() != 0) {
3744 if (S_is_scoped_routing_enabled() != 0) {
3745 S_scopedroute
= TRUE
;
3747 #endif /* RTF_IFSCOPE */
3749 S_session
= SCDynamicStoreCreate(NULL
, CFSTR("IPMonitor"),
3750 IPMonitorNotify
, NULL
);
3751 if (S_session
== NULL
) {
3752 SCLog(TRUE
, LOG_ERR
,
3753 CFSTR("IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s"),
3754 SCErrorString(SCError()));
3758 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3759 kSCDynamicStoreDomainState
,
3762 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3763 kSCDynamicStoreDomainState
,
3766 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3767 kSCDynamicStoreDomainState
,
3769 S_state_global_proxies
3770 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3771 kSCDynamicStoreDomainState
,
3773 #if !TARGET_OS_IPHONE
3775 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3776 kSCDynamicStoreDomainState
,
3778 #endif /* !TARGET_OS_IPHONE */
3780 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3781 kSCDynamicStoreDomainSetup
,
3783 S_setup_global_proxies
3784 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3785 kSCDynamicStoreDomainSetup
,
3787 #if !TARGET_OS_IPHONE
3789 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3790 kSCDynamicStoreDomainSetup
,
3792 #endif /* !TARGET_OS_IPHONE */
3793 S_state_service_prefix
3794 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@/"),
3795 kSCDynamicStoreDomainState
,
3798 S_setup_service_prefix
3799 = SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@/"),
3800 kSCDynamicStoreDomainSetup
,
3803 S_service_state_dict
3804 = CFDictionaryCreateMutable(NULL
, 0,
3805 &kCFTypeDictionaryKeyCallBacks
,
3806 &kCFTypeDictionaryValueCallBacks
);
3808 S_ipv4_service_rank_dict
3809 = CFDictionaryCreateMutable(NULL
, 0,
3810 &kCFTypeDictionaryKeyCallBacks
,
3811 &kCFTypeDictionaryValueCallBacks
);
3813 S_ipv6_service_rank_dict
3814 = CFDictionaryCreateMutable(NULL
, 0,
3815 &kCFTypeDictionaryKeyCallBacks
,
3816 &kCFTypeDictionaryValueCallBacks
);
3818 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3819 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3821 /* register for State: and Setup: per-service notifications */
3822 add_service_keys(kSCCompAnyRegex
, keys
, patterns
);
3824 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
3825 CFArrayAppendValue(keys
, S_setup_global_ipv4
);
3827 /* add notifier for multicast DNS configuration (Bonjour/.local) */
3828 S_multicast_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
3829 kSCDynamicStoreDomainState
,
3831 CFSTR(kDNSServiceCompMulticastDNS
));
3832 CFArrayAppendValue(keys
, S_multicast_resolvers
);
3834 /* add notifier for private DNS configuration (Back to My Mac) */
3835 S_private_resolvers
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@/%@/%@"),
3836 kSCDynamicStoreDomainState
,
3838 CFSTR(kDNSServiceCompPrivateDNS
));
3839 CFArrayAppendValue(keys
, S_private_resolvers
);
3841 if (!SCDynamicStoreSetNotificationKeys(S_session
, keys
, patterns
)) {
3842 SCLog(TRUE
, LOG_ERR
,
3843 CFSTR("IPMonitor ip_plugin_init "
3844 "SCDynamicStoreSetNotificationKeys failed: %s"),
3845 SCErrorString(SCError()));
3849 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, S_session
, 0);
3851 SCLog(TRUE
, LOG_ERR
,
3852 CFSTR("IPMonitor ip_plugin_init "
3853 "SCDynamicStoreCreateRunLoopSource failed: %s"),
3854 SCErrorString(SCError()));
3858 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
3861 /* initialize dns configuration */
3862 dns_configuration_set(NULL
, NULL
, NULL
, NULL
, NULL
);
3863 #if !TARGET_OS_IPHONE
3865 #endif /* !TARGET_OS_IPHONE */
3866 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_dns
);
3868 #if !TARGET_OS_IPHONE
3869 /* initialize SMB configuration */
3870 (void)SCDynamicStoreRemoveValue(S_session
, S_state_global_smb
);
3871 #endif /* !TARGET_OS_IPHONE */
3874 my_CFRelease(&keys
);
3875 my_CFRelease(&patterns
);
3883 /* initialize multicast route */
3884 update_ipv4(NULL
, NULL
, NULL
);
3889 S_get_plist_boolean(CFDictionaryRef plist
, CFStringRef key
,
3893 boolean_t ret
= def
;
3895 b
= isA_CFBoolean(CFDictionaryGetValue(plist
, key
));
3897 ret
= CFBooleanGetValue(b
);
3904 load_IPMonitor(CFBundleRef bundle
, Boolean bundleVerbose
)
3906 CFDictionaryRef info_dict
;
3908 info_dict
= CFBundleGetInfoDictionary(bundle
);
3909 if (info_dict
!= NULL
) {
3911 = S_get_plist_boolean(info_dict
,
3912 CFSTR("AppendStateArrayToSetupArray"),
3915 if (bundleVerbose
) {
3916 S_IPMonitor_debug
= kDebugFlagDefault
;
3919 dns_configuration_init(bundle
);
3922 #if !TARGET_OS_IPHONE
3923 if (S_session
!= NULL
) {
3924 dns_configuration_monitor(S_session
, IPMonitorNotify
);
3926 #endif /* !TARGET_OS_IPHONE */
3928 load_hostname((S_IPMonitor_debug
& kDebugFlag1
) != 0);
3929 #if !TARGET_OS_IPHONE
3930 load_smb_configuration((S_IPMonitor_debug
& kDebugFlag1
) != 0);
3931 #endif /* !TARGET_OS_IPHONE */
3937 #ifdef TEST_IPMONITOR
3938 #include "dns-configuration.c"
3939 #include "set-hostname.c"
3942 main(int argc
, char **argv
)
3946 S_IPMonitor_debug
= kDebugFlag1
;
3948 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
3951 load_IPMonitor(CFBundleGetMainBundle(), FALSE
);
3958 #endif /* TEST_IPMONITOR */
3960 #ifdef TEST_IPV4_ROUTELIST
3961 #include "dns-configuration.c"
3962 #include "set-hostname.c"
3964 struct ipv4_service_contents
{
3968 const char * router
;
3969 const char * ifname
;
3971 const CFStringRef
*primaryRank
;
3975 * addr mask dest router ifname pri primaryRank
3977 struct ipv4_service_contents en0_10
= {
3978 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, NULL
3981 struct ipv4_service_contents en0_15
= {
3982 "10.0.0.19", "255.255.255.0", NULL
, "10.0.0.1", "en0", 15, NULL
3985 struct ipv4_service_contents en0_30
= {
3986 "10.0.0.11", "255.255.255.0", NULL
, "10.0.0.1", "en0", 30, NULL
3989 struct ipv4_service_contents en0_40
= {
3990 "10.0.0.12", "255.255.255.0", NULL
, "10.0.0.1", "en0", 40, NULL
3993 struct ipv4_service_contents en0_50
= {
3994 "10.0.0.13", "255.255.255.0", NULL
, "10.0.0.1", "en0", 50, NULL
3997 struct ipv4_service_contents en0_110
= {
3998 "192.168.2.10", "255.255.255.0", NULL
, "192.168.2.1", "en0", 110, NULL
4001 struct ipv4_service_contents en0_1
= {
4002 "17.202.40.191", "255.255.252.0", NULL
, "17.202.20.1", "en0", 1, NULL
4005 struct ipv4_service_contents en1_20
= {
4006 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, NULL
4009 struct ipv4_service_contents en1_2
= {
4010 "17.202.42.24", "255.255.252.0", NULL
, "17.202.20.1", "en1", 2, NULL
4013 struct ipv4_service_contents en1_125
= {
4014 "192.168.2.20", "255.255.255.0", NULL
, "192.168.2.1", "en1", 125, NULL
4017 struct ipv4_service_contents fw0_25
= {
4018 "192.168.2.30", "255.255.255.0", NULL
, "192.168.2.1", "fw0", 25, NULL
4021 struct ipv4_service_contents fw0_21
= {
4022 "192.168.3.30", "255.255.255.0", NULL
, "192.168.3.1", "fw0", 21, NULL
4025 struct ipv4_service_contents ppp0_0_1
= {
4026 "17.219.156.22", NULL
, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
4029 struct ipv4_service_contents en0_test6
= {
4030 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 2, NULL
4032 struct ipv4_service_contents en1_test6
= {
4033 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 3, NULL
4035 struct ipv4_service_contents en2_test6
= {
4036 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
4039 struct ipv4_service_contents en0_test7
= {
4040 "17.202.42.113", "255.255.252.0", NULL
, "17.202.40.1", "en0", 3, NULL
4042 struct ipv4_service_contents en1_test7
= {
4043 "17.202.42.111", "255.255.252.0", NULL
, "17.202.40.1", "en1", 2, NULL
4045 struct ipv4_service_contents en2_test7
= {
4046 "17.255.98.164", "255.255.240.0", NULL
, "17.255.96.1", "en2", 1, NULL
4048 struct ipv4_service_contents fw0_test6_and_7
= {
4049 "169.254.11.33", "255.255.0.0", NULL
, NULL
, "fw0", UINT_MAX
, NULL
4052 struct ipv4_service_contents en0_10_last
= {
4053 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
4056 struct ipv4_service_contents en0_10_never
= {
4057 "10.0.0.10", "255.255.255.0", NULL
, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
4060 struct ipv4_service_contents en1_20_first
= {
4061 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
4064 struct ipv4_service_contents en1_20_never
= {
4065 "10.0.0.20", "255.255.255.0", NULL
, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
4068 struct ipv4_service_contents
* test1
[] = {
4079 struct ipv4_service_contents
* test2
[] = {
4089 struct ipv4_service_contents
* test3
[] = {
4103 struct ipv4_service_contents
* test4
[] = {
4112 struct ipv4_service_contents
* test5
[] = {
4122 struct ipv4_service_contents
* test6
[] = {
4130 struct ipv4_service_contents
* test7
[] = {
4138 struct ipv4_service_contents
* test8
[] = {
4144 struct ipv4_service_contents
* test9
[] = {
4151 struct ipv4_service_contents
* test10
[] = {
4158 struct ipv4_service_contents
* test11
[] = {
4165 struct ipv4_service_contents
* test12
[] = {
4171 struct ipv4_service_contents
* test13
[] = {
4177 struct ipv4_service_contents
* test14
[] = {
4183 dict_add_string(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
4186 CFStringRef prop_val
;
4191 prop_val
= CFStringCreateWithCString(NULL
,
4193 kCFStringEncodingASCII
);
4194 CFDictionarySetValue(dict
, prop_name
, prop_val
);
4195 CFRelease(prop_val
);
4200 dict_add_string_as_array(CFMutableDictionaryRef dict
, CFStringRef prop_name
,
4204 CFStringRef prop_val
;
4209 prop_val
= CFStringCreateWithCString(NULL
,
4211 kCFStringEncodingASCII
);
4212 array
= CFArrayCreate(NULL
,
4213 (const void **)&prop_val
, 1,
4214 &kCFTypeArrayCallBacks
);
4215 CFRelease(prop_val
);
4216 CFDictionarySetValue(dict
, prop_name
, array
);
4221 static CFDictionaryRef
4222 make_IPv4_dict(struct ipv4_service_contents
* t
)
4224 CFMutableDictionaryRef dict
;
4226 dict
= CFDictionaryCreateMutable(NULL
, 0,
4227 &kCFTypeDictionaryKeyCallBacks
,
4228 &kCFTypeDictionaryValueCallBacks
);
4229 dict_add_string_as_array(dict
, kSCPropNetIPv4Addresses
, t
->addr
);
4230 dict_add_string_as_array(dict
, kSCPropNetIPv4SubnetMasks
, t
->mask
);
4231 dict_add_string_as_array(dict
, kSCPropNetIPv4DestAddresses
, t
->dest
);
4232 dict_add_string(dict
, kSCPropNetIPv4Router
, t
->router
);
4233 dict_add_string(dict
, kSCPropInterfaceName
, t
->ifname
);
4237 static IPv4RouteListRef
4238 make_IPv4RouteList(struct ipv4_service_contents
* * this_test
)
4241 IPv4RouteListRef routes
;
4242 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
4243 IPv4RouteListRef ret
= NULL
;
4244 struct ipv4_service_contents
* * scan_test
;
4246 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
4247 CFDictionaryRef dict
;
4249 dict
= make_IPv4_dict(*scan_test
);
4251 fprintf(stderr
, "make_IPv4_dict failed\n");
4254 routes
= (IPv4RouteListRef
)routes_buf
;
4255 routes
->size
= R_STATIC
;
4257 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
4258 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
4260 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
4263 ret
= IPv4RouteListAddRouteList(ret
, 1, r
, (*scan_test
)->rank
);
4273 * Function: run_test
4275 * Runs through the given set of routes first in the forward direction,
4276 * then again backwards. We should end up with exactly the same set of
4277 * routes at the end.
4280 run_test(const char * name
, struct ipv4_service_contents
* * this_test
)
4283 boolean_t ret
= FALSE
;
4285 IPv4RouteListRef routes
;
4286 char routes_buf
[IPv4RouteListComputeSize(R_STATIC
)];
4287 IPv4RouteListRef routes1
= NULL
, routes2
= NULL
;
4288 struct ipv4_service_contents
* * scan_test
;
4290 printf("\nStarting test %s\n", name
);
4291 for (scan_test
= this_test
; *scan_test
!= NULL
; scan_test
++) {
4292 CFDictionaryRef dict
;
4294 dict
= make_IPv4_dict(*scan_test
);
4296 fprintf(stderr
, "make_IPv4_dict failed\n");
4299 routes
= (IPv4RouteListRef
)routes_buf
;
4300 routes
->size
= R_STATIC
;
4302 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
4303 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
4305 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
4308 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4309 descr
= IPv4RouteListCopyDescription(r
);
4310 SCLog(TRUE
, LOG_NOTICE
, CFSTR("test: Adding %@"), descr
);
4314 routes1
= IPv4RouteListAddRouteList(routes1
, 1, r
, (*scan_test
)->rank
);
4320 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4321 if (routes1
!= NULL
) {
4322 descr
= IPv4RouteListCopyDescription(routes1
);
4323 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Routes are %@"), descr
);
4327 for (scan_test
--; scan_test
>= this_test
; scan_test
--) {
4328 CFDictionaryRef dict
;
4330 dict
= make_IPv4_dict(*scan_test
);
4332 fprintf(stderr
, "make_IPv4_dict failed\n");
4335 routes
= (IPv4RouteListRef
)routes_buf
;
4336 routes
->size
= R_STATIC
;
4338 r
= IPv4RouteListCreateWithDictionary(routes
, dict
,
4339 (*scan_test
)->primaryRank
? *(*scan_test
)->primaryRank
: NULL
);
4341 fprintf(stderr
, "IPv4RouteListCreateWithDictionary failed\n");
4344 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4345 descr
= IPv4RouteListCopyDescription(r
);
4346 SCLog(TRUE
, LOG_NOTICE
, CFSTR("test: Adding %@"), descr
);
4349 routes2
= IPv4RouteListAddRouteList(routes2
, 1, r
, (*scan_test
)->rank
);
4355 if ((S_IPMonitor_debug
& kDebugFlag4
) != 0) {
4356 if (routes2
!= NULL
) {
4357 descr
= IPv4RouteListCopyDescription(routes2
);
4358 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Routes are %@"), descr
);
4362 if ((routes1
!= NULL
&& routes2
== NULL
)
4363 || (routes1
== NULL
&& routes2
!= NULL
)) {
4364 fprintf(stderr
, "routes1 is %sNULL but routes2 is %sNULL\n",
4365 (routes1
!= NULL
) ? "not " : "",
4366 (routes2
!= NULL
) ? "not " : "");
4368 else if (routes1
!= NULL
&& routes2
!= NULL
) {
4369 /* check if they are different */
4370 if (routes1
->count
!= routes2
->count
) {
4371 fprintf(stderr
, "routes1 count %d != routes 2 count %d\n",
4372 routes1
->count
, routes2
->count
);
4374 else if (bcmp(routes1
, routes2
,
4375 IPv4RouteListComputeSize(routes1
->count
)) != 0) {
4376 fprintf(stderr
, "routes1 and routes2 are different\n");
4379 printf("routes1 and routes2 are the same\n");
4383 if (routes1
!= NULL
) {
4386 if (routes2
!= NULL
) {
4392 typedef struct compare_context
{
4393 IPv4RouteListRef old
;
4394 IPv4RouteListRef
new;
4395 } compare_context_t
;
4398 compare_callback(IPv4RouteListApplyCommand cmd
, IPv4RouteRef route
, void * arg
)
4400 compare_context_t
* context
= (compare_context_t
*)arg
;
4403 case kIPv4RouteListAddRouteCommand
:
4404 printf("Add new[%d] = ", route
- context
->new->list
);
4405 IPv4RoutePrint(route
);
4408 case kIPv4RouteListRemoveRouteCommand
:
4409 printf("Remove old[%d] = ", route
- context
->old
->list
);
4410 IPv4RoutePrint(route
);
4420 compare_tests(struct ipv4_service_contents
* * old_test
,
4421 struct ipv4_service_contents
* * new_test
)
4423 IPv4RouteListRef new_routes
;
4424 IPv4RouteListRef old_routes
;
4425 compare_context_t context
;
4427 old_routes
= make_IPv4RouteList(old_test
);
4428 new_routes
= make_IPv4RouteList(new_test
);
4430 if (old_routes
== NULL
) {
4431 printf("No Old Routes\n");
4434 printf("Old Routes = ");
4435 IPv4RouteListPrint(old_routes
);
4437 if (new_routes
== NULL
) {
4438 printf("No New Routes\n");
4441 printf("New Routes = ");
4442 IPv4RouteListPrint(new_routes
);
4444 context
.old
= old_routes
;
4445 context
.new = new_routes
;
4446 IPv4RouteListApply(old_routes
, new_routes
, compare_callback
, &context
);
4447 if (old_routes
!= NULL
) {
4450 if (new_routes
!= NULL
) {
4458 main(int argc
, char **argv
)
4461 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
4463 S_IPMonitor_debug
= kDebugFlag1
| kDebugFlag2
| kDebugFlag4
;
4465 S_IPMonitor_debug
= strtoul(argv
[1], NULL
, 0);
4468 if (run_test("test1", test1
) == FALSE
) {
4469 fprintf(stderr
, "test1 failed\n");
4472 if (run_test("test2", test2
) == FALSE
) {
4473 fprintf(stderr
, "test2 failed\n");
4476 if (run_test("test3", test4
) == FALSE
) {
4477 fprintf(stderr
, "test3 failed\n");
4480 if (run_test("test4", test4
) == FALSE
) {
4481 fprintf(stderr
, "test4 failed\n");
4484 if (run_test("test5", test5
) == FALSE
) {
4485 fprintf(stderr
, "test5 failed\n");
4489 printf("\nCompare 1 to 2:\n");
4490 compare_tests(test1
, test2
);
4492 printf("\nCompare 2 to 1:\n");
4493 compare_tests(test2
, test1
);
4495 printf("\nCompare 1 to 1:\n");
4496 compare_tests(test1
, test1
);
4498 printf("\nCompare 1 to 3:\n");
4499 compare_tests(test1
, test3
);
4501 printf("\nCompare 3 to 1:\n");
4502 compare_tests(test3
, test1
);
4504 printf("\nCompare 2 to 3:\n");
4505 compare_tests(test2
, test3
);
4507 printf("\nCompare 3 to 2:\n");
4508 compare_tests(test3
, test2
);
4510 printf("\nCompare 3 to 4:\n");
4511 compare_tests(test3
, test4
);
4513 printf("\nCompare 5 to 4:\n");
4514 compare_tests(test5
, test4
);
4516 printf("\nCompare 6 to 7:\n");
4517 compare_tests(test6
, test7
);
4519 printf("\nCompare 7 to 6:\n");
4520 compare_tests(test7
, test6
);
4522 printf("\nCompare 8 to 9:\n");
4523 compare_tests(test8
, test9
);
4525 printf("\nCompare 8 to 10:\n");
4526 compare_tests(test8
, test10
);
4528 printf("\nCompare 8 to 11:\n");
4529 compare_tests(test8
, test11
);
4531 printf("\nCompare 12 to 13:\n");
4532 compare_tests(test12
, test13
);
4534 printf("\nCompare 13 to 14:\n");
4535 compare_tests(test13
, test14
);
4537 printf("\nChecking for leaks\n");
4539 sprintf(cmd
, "leaks %d 2>&1", getpid());
4547 #endif /* TEST_IPV4_ROUTELIST */