]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/ip_plugin.c
configd-596.15.tar.gz
[apple/configd.git] / Plugins / IPMonitor / ip_plugin.c
1 /*
2 * Copyright (c) 2000-2013 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * ip_plugin.c
26 * - decides which interface will be made the "primary" interface,
27 * that is, the one with the default route assigned
28 */
29
30 /*
31 * Modification History
32 *
33 * July 19, 2000 Dieter Siegmund (dieter@apple.com)
34 * - initial revision
35 *
36 * November 15, 2000 Dieter Siegmund (dieter@apple.com)
37 * - changed to use new configuration model
38 *
39 * March 19, 2001 Dieter Siegmund (dieter@apple.com)
40 * - use service state instead of interface state
41 *
42 * July 16, 2001 Allan Nathanson (ajn@apple.com)
43 * - update to public SystemConfiguration.framework APIs
44 *
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
49 *
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
53 *
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
57 *
58 * July 29, 2003 Dieter Siegmund (dieter@apple.com)
59 * - support installing a default route to a router that's not on our subnet
60 *
61 * March 22, 2004 Allan Nathanson (ajn@apple.com)
62 * - create expanded DNS configuration
63 *
64 * June 20, 2006 Allan Nathanson (ajn@apple.com)
65 * - add SMB configuration
66 *
67 * December 5, 2007 Dieter Siegmund (dieter@apple.com)
68 * - added support for multiple scoped routes
69 */
70
71 #include <stdlib.h>
72 #include <unistd.h>
73 #include <string.h>
74 #include <stdio.h>
75 #include <sys/fcntl.h>
76 #include <sys/ioctl.h>
77 #include <sys/types.h>
78 #include <sys/socket.h>
79 #include <net/route.h>
80 #include <net/if.h>
81 #include <net/if_dl.h>
82 #include <netinet/in.h>
83 #include <netinet/icmp6.h>
84 #include <netinet6/in6_var.h>
85 #include <netinet6/nd6.h>
86 #include <arpa/inet.h>
87 #include <sys/sysctl.h>
88 #include <limits.h>
89 #include <notify.h>
90 #include <mach/mach_time.h>
91 #include <dispatch/dispatch.h>
92 #include <CommonCrypto/CommonDigest.h>
93
94 #include <SystemConfiguration/SystemConfiguration.h>
95 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
96 #include <SystemConfiguration/SCValidation.h>
97 #include <SystemConfiguration/scprefs_observer.h>
98 #include <SystemConfiguration/SCPrivate.h> /* for SCLog() */
99 #include "SCNetworkReachabilityInternal.h"
100 #include "SCNetworkSignaturePrivate.h"
101 #include <dnsinfo.h>
102 #include "dnsinfo_server.h"
103
104 #if defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS)
105 #include <ppp/PPPControllerPriv.h>
106 #endif // !defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS)
107
108 #include <dns_sd.h>
109 #ifndef kDNSServiceCompMulticastDNS
110 #define kDNSServiceCompMulticastDNS "MulticastDNS"
111 #endif
112 #ifndef kDNSServiceCompPrivateDNS
113 #define kDNSServiceCompPrivateDNS "PrivateDNS"
114 #endif
115 #include <network_information.h>
116 #include "network_information_priv.h"
117 #include "network_information_server.h"
118 #include <ppp/ppp_msg.h>
119
120 enum {
121 kProtocolFlagsNone = 0x0,
122 kProtocolFlagsIPv4 = 0x1,
123 kProtocolFlagsIPv6 = 0x2
124 };
125 typedef uint8_t ProtocolFlags;
126
127 enum {
128 kDebugFlag1 = 0x00000001,
129 kDebugFlag2 = 0x00000002,
130 kDebugFlag4 = 0x00000004,
131 kDebugFlag8 = 0x00000008,
132 kDebugFlagDefault = kDebugFlag1,
133 kDebugFlagAll = 0xffffffff
134 };
135
136 #ifdef TEST_IPV4_ROUTELIST
137 #define ROUTELIST_DEBUG(a, f) { if ((S_IPMonitor_debug & (f)) != 0) printf a ;}
138 #else
139 #define ROUTELIST_DEBUG(a, f)
140 #endif
141
142 #if !TARGET_IPHONE_SIMULATOR
143 #include "set-hostname.h"
144 #endif /* !TARGET_IPHONE_SIMULATOR */
145
146 #include "dns-configuration.h"
147 #include "proxy-configuration.h"
148
149 #if !TARGET_OS_IPHONE
150 #include "smb-configuration.h"
151 #endif /* !TARGET_OS_IPHONE */
152
153 /*
154 * Property: kIPIsCoupled
155 * Purpose:
156 * Used to indicate that the IPv4 and IPv6 services are coupled.
157 * Neither the IPv4 part nor the IPv6 part of a coupled service
158 * may become primary if IPv4 or IPv6 is primary for another interface.
159 *
160 * For example, if the service over en3 is "coupled" and has IPv6,
161 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible
162 * to become primary for IPv6.
163 */
164 #define kIPIsCoupled CFSTR("IPIsCoupled")
165
166 #define PPP_PREFIX "ppp"
167
168 #define IP_FORMAT "%d.%d.%d.%d"
169 #define IP_CH(ip) ((u_char *)(ip))
170 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
171
172 #include "ip_plugin.h"
173 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
174 static SCLoggerRef S_IPMonitor_logger;
175 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
176
177 static boolean_t S_bundle_logging_verbose;
178
179 /*
180 * IPv4 Route management
181 */
182
183 typedef uint32_t RouteFlags;
184
185 enum {
186 kRouteIsDirectToInterfaceFlag = 0x00000001,
187 kRouteIsNotSubnetLocalFlag = 0x00000002,
188 kRouteIsScopedFlag = 0x00000004,
189 kRouteIsNULLFlag = 0x00000008
190 };
191
192 typedef struct {
193 struct in_addr dest;
194 struct in_addr mask;
195 struct in_addr gateway;
196 char ifname[IFNAMSIZ];
197 unsigned int ifindex;
198 struct in_addr ifa;
199 Rank rank;
200 RouteFlags flags;
201 } IPv4Route, *IPv4RouteRef;
202
203 typedef struct {
204 int count;
205 int size;
206 boolean_t exclude_from_nwi;
207 IPv4Route list[1]; /* variable length */
208 } IPv4RouteList, *IPv4RouteListRef;
209
210 enum {
211 kIPv4RouteListAddRouteCommand,
212 kIPv4RouteListRemoveRouteCommand
213 };
214
215 /*
216 * Election Information
217 * - information about the current best services
218 */
219 typedef struct Candidate {
220 CFStringRef serviceID;
221 CFStringRef if_name;
222 union {
223 struct in_addr v4;
224 struct in6_addr v6;
225 } addr;
226 Rank rank;
227 boolean_t ip_is_coupled;
228 SCNetworkReachabilityFlags reachability_flags;
229 union {
230 struct sockaddr_in vpn_server_addr4;
231 struct sockaddr_in6 vpn_server_addr6;
232 } vpn_server_addr;
233 CFStringRef signature;
234 } Candidate, * CandidateRef;
235
236 typedef struct ElectionResults {
237 int count;
238 int size;
239 Candidate candidates[1];
240 } ElectionResults, * ElectionResultsRef;
241
242 static __inline__ unsigned int
243 ElectionResultsComputeSize(unsigned int n)
244 {
245 return (offsetof(ElectionResults, candidates[n]));
246 }
247
248 /*
249 * Type: Rank
250 * Purpose:
251 * A 32-bit value to encode the relative rank of a service.
252 *
253 * The top 8 bits are used to hold the rank assertion (first, last
254 * never, default).
255 *
256 * The bottom 24 bits are used to store the service index (i.e. the
257 * position within the service order array).
258 */
259 #define RANK_ASSERTION_MAKE(r) ((Rank)(r) << 24)
260 #define kRankAssertionFirst RANK_ASSERTION_MAKE(0)
261 #define kRankAssertionDefault RANK_ASSERTION_MAKE(1)
262 #define kRankAssertionLast RANK_ASSERTION_MAKE(2)
263 #define kRankAssertionNever RANK_ASSERTION_MAKE(3)
264 #define kRankAssertionMask RANK_ASSERTION_MAKE(0xff)
265 #define RANK_ASSERTION_MASK(r) ((Rank)(r) & kRankAssertionMask)
266
267 #define RANK_INDEX_MAKE(r) ((Rank)(r))
268 #define kRankIndexMask RANK_INDEX_MAKE(0xffffff)
269 #define RANK_INDEX_MASK(r) ((Rank)(r) & kRankIndexMask)
270
271 static __inline__ Rank
272 RankMake(uint32_t service_index, Rank primary_rank)
273 {
274 return (RANK_INDEX_MASK(service_index) | RANK_ASSERTION_MASK(primary_rank));
275 }
276
277 static __inline__ Rank
278 PrimaryRankGetRankAssertion(CFStringRef primaryRank)
279 {
280 if (CFEqual(primaryRank, kSCValNetServicePrimaryRankNever)) {
281 return kRankAssertionNever;
282 } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankFirst)) {
283 return kRankAssertionFirst;
284 } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankLast)) {
285 return kRankAssertionLast;
286 }
287 return kRankAssertionDefault;
288 }
289
290 typedef uint32_t IPv4RouteListApplyCommand;
291
292 typedef void IPv4RouteListApplyCallBackFunc(IPv4RouteListApplyCommand cmd,
293 IPv4RouteRef route, void * arg);
294 typedef IPv4RouteListApplyCallBackFunc * IPv4RouteListApplyCallBackFuncPtr;
295
296 /* SCDynamicStore session */
297 static SCDynamicStoreRef S_session = NULL;
298
299 /* debug output flags */
300 static uint32_t S_IPMonitor_debug = 0;
301 static Boolean S_IPMonitor_verbose = FALSE;
302
303 /* are we netbooted? If so, don't touch the default route */
304 static boolean_t S_netboot = FALSE;
305
306 /* is scoped routing enabled? */
307 #ifdef RTF_IFSCOPE
308 static boolean_t S_scopedroute = FALSE;
309 static boolean_t S_scopedroute_v6 = FALSE;
310 #endif /* RTF_IFSCOPE */
311
312 /* dictionary to hold per-service state: key is the serviceID */
313 static CFMutableDictionaryRef S_service_state_dict = NULL;
314 static CFMutableDictionaryRef S_ipv4_service_rank_dict = NULL;
315 static CFMutableDictionaryRef S_ipv6_service_rank_dict = NULL;
316
317 /* dictionary to hold per-interface rank information */
318 static CFMutableDictionaryRef S_if_rank_dict = NULL;
319
320 /* if set, a PPP interface overrides the primary */
321 static boolean_t S_ppp_override_primary = FALSE;
322
323 /* the current primary serviceID's */
324 static CFStringRef S_primary_ipv4 = NULL;
325 static CFStringRef S_primary_ipv6 = NULL;
326 static CFStringRef S_primary_dns = NULL;
327 static CFStringRef S_primary_proxies = NULL;
328
329 /* the current election results */
330 static ElectionResultsRef S_ipv4_results;
331 static ElectionResultsRef S_ipv6_results;
332
333 static CFStringRef S_state_global_ipv4 = NULL;
334 static CFStringRef S_state_global_ipv6 = NULL;
335 static CFStringRef S_state_global_dns = NULL;
336 static CFStringRef S_state_global_proxies = NULL;
337 static CFStringRef S_state_service_prefix = NULL;
338 static CFStringRef S_setup_global_ipv4 = NULL;
339 static CFStringRef S_setup_service_prefix = NULL;
340
341 static CFStringRef S_multicast_resolvers = NULL;
342 static CFStringRef S_private_resolvers = NULL;
343
344 #if !TARGET_IPHONE_SIMULATOR
345 static IPv4RouteListRef S_ipv4_routelist = NULL;
346 #endif /* !TARGET_IPHONE_SIMULATOR */
347
348 static const struct in_addr S_ip_zeros = { 0 };
349 static const struct in6_addr S_ip6_zeros = IN6ADDR_ANY_INIT;
350
351 static boolean_t S_append_state = FALSE;
352
353 static CFDictionaryRef S_dns_dict = NULL;
354
355 static Boolean S_dnsinfo_synced = TRUE;
356
357 static nwi_state_t S_nwi_state = NULL;
358 static Boolean S_nwi_synced = TRUE;
359
360 static CFDictionaryRef S_proxies_dict = NULL;
361
362 // Note: access should be gated with __network_change_queue()
363 static uint32_t S_network_change_needed = 0;
364 #define NETWORK_CHANGE_NET 1<<0
365 #define NETWORK_CHANGE_DNS 1<<1
366 #define NETWORK_CHANGE_PROXY 1<<2
367 #if !TARGET_OS_IPHONE
368 #define NETWORK_CHANGE_SMB 1<<3
369 #endif /* !TARGET_OS_IPHONE */
370 static struct timeval S_network_change_start;
371 static Boolean S_network_change_timeout = FALSE;
372 static dispatch_source_t S_network_change_timer = NULL;
373
374 #if !TARGET_OS_IPHONE
375 static CFStringRef S_primary_smb = NULL;
376 static CFStringRef S_state_global_smb = NULL;
377 static CFDictionaryRef S_smb_dict = NULL;
378 #endif /* !TARGET_OS_IPHONE */
379
380 #if !TARGET_OS_IPHONE
381 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
382 #endif /* !TARGET_OS_IPHONE */
383
384 #ifndef KERN_NETBOOT
385 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
386 #endif //KERN_NETBOOT
387
388 /**
389 ** entityType*, GetEntityChanges*
390 ** - definitions for the entity types we handle
391 **/
392 enum {
393 kEntityTypeIPv4 = 0,
394 kEntityTypeIPv6,
395 kEntityTypeDNS,
396 kEntityTypeProxies,
397 #if !TARGET_OS_IPHONE
398 kEntityTypeSMB,
399 #endif /* !TARGET_OS_IPHONE */
400 ENTITY_TYPES_COUNT,
401 kEntityTypeVPNStatus,
402 kEntityTypeServiceOptions = 31
403 };
404 typedef uint32_t EntityType;
405
406 static const CFStringRef *entityTypeNames[ENTITY_TYPES_COUNT] = {
407 &kSCEntNetIPv4, /* 0 */
408 &kSCEntNetIPv6, /* 1 */
409 &kSCEntNetDNS, /* 2 */
410 &kSCEntNetProxies, /* 3 */
411 #if !TARGET_OS_IPHONE
412 &kSCEntNetSMB, /* 4 */
413 #endif /* !TARGET_OS_IPHONE */
414 };
415
416 static Boolean
417 S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value);
418
419 static __inline__ char
420 ipvx_char(int af)
421 {
422 return ((af == AF_INET) ? '4' : '6');
423 }
424
425 static __inline__ char
426 ipvx_other_char(int af)
427 {
428 return ((af == AF_INET) ? '6' : '4');
429 }
430
431 static IPv4RouteListRef
432 ipv4_dict_get_routelist(CFDictionaryRef ipv4_dict)
433 {
434 CFDataRef routes;
435 IPv4RouteListRef routes_list = NULL;
436
437 if (isA_CFDictionary(ipv4_dict) == NULL) {
438 return (NULL);
439 }
440
441 routes = CFDictionaryGetValue(ipv4_dict, kIPv4DictRoutes);
442
443 if (routes != NULL) {
444 routes_list = (IPv4RouteListRef)(void*)CFDataGetBytePtr(routes);
445 }
446 return (routes_list);
447 }
448
449 static CFStringRef
450 ipv4_dict_get_ifname(CFDictionaryRef ipv4_dict)
451 {
452 CFDictionaryRef ipv4_service_dict = NULL;
453
454 if (isA_CFDictionary(ipv4_dict) == NULL) {
455 return (NULL);
456 }
457
458 ipv4_service_dict = CFDictionaryGetValue(ipv4_dict,
459 kIPv4DictService);
460
461 if (isA_CFDictionary(ipv4_service_dict) == NULL) {
462 return NULL;
463 }
464
465 return CFDictionaryGetValue(ipv4_service_dict, kSCPropInterfaceName);
466 }
467
468 typedef boolean_t GetEntityChangesFunc(CFStringRef serviceID,
469 CFDictionaryRef state_dict,
470 CFDictionaryRef setup_dict,
471 CFDictionaryRef info);
472 typedef GetEntityChangesFunc * GetEntityChangesFuncRef;
473
474 static GetEntityChangesFunc get_ipv4_changes;
475 static GetEntityChangesFunc get_ipv6_changes;
476 static GetEntityChangesFunc get_dns_changes;
477 static GetEntityChangesFunc get_proxies_changes;
478 #if !TARGET_OS_IPHONE
479 static GetEntityChangesFunc get_smb_changes;
480 #endif /* !TARGET_OS_IPHONE */
481
482 static void
483 my_CFRelease(void * t);
484
485 static void
486 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new);
487
488 static void
489 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key);
490
491 static const GetEntityChangesFuncRef entityChangeFunc[ENTITY_TYPES_COUNT] = {
492 get_ipv4_changes, /* 0 */
493 get_ipv6_changes, /* 1 */
494 get_dns_changes, /* 2 */
495 get_proxies_changes,/* 3 */
496 #if !TARGET_OS_IPHONE
497 get_smb_changes, /* 4 */
498 #endif /* !TARGET_OS_IPHONE */
499 };
500
501 /**
502 ** keyChangeList
503 ** - mechanism to do an atomic update of the SCDynamicStore
504 ** when the content needs to be changed across multiple functions
505 **/
506 typedef struct {
507 CFMutableArrayRef notify;
508 CFMutableArrayRef remove;
509 CFMutableDictionaryRef set;
510 } keyChangeList, * keyChangeListRef;
511
512 static CFStringRef
513 my_CFStringCopyComponent(CFStringRef path, CFStringRef separator,
514 CFIndex component_index)
515 {
516 CFArrayRef arr;
517 CFStringRef component = NULL;
518
519 arr = CFStringCreateArrayBySeparatingStrings(NULL, path, separator);
520 if (arr == NULL) {
521 goto done;
522 }
523 if (CFArrayGetCount(arr) <= component_index) {
524 goto done;
525 }
526 component = CFRetain(CFArrayGetValueAtIndex(arr, component_index));
527
528 done:
529 my_CFRelease(&arr);
530 return (component);
531 }
532
533 static void
534 keyChangeListInit(keyChangeListRef keys)
535 {
536 keys->notify = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
537 keys->remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
538 keys->set = CFDictionaryCreateMutable(NULL, 0,
539 &kCFTypeDictionaryKeyCallBacks,
540 &kCFTypeDictionaryValueCallBacks);
541 return;
542 }
543
544 static void
545 keyChangeListFree(keyChangeListRef keys)
546 {
547 my_CFRelease(&keys->notify);
548 my_CFRelease(&keys->remove);
549 my_CFRelease(&keys->set);
550 return;
551 }
552
553 static Boolean
554 keyChangeListActive(keyChangeListRef keys)
555 {
556 return ((CFDictionaryGetCount(keys->set) > 0) ||
557 (CFArrayGetCount(keys->remove) > 0) ||
558 (CFArrayGetCount(keys->notify) > 0));
559 }
560
561 static void
562 keyChangeListNotifyKey(keyChangeListRef keys, CFStringRef key)
563 {
564 my_CFArrayAppendUniqueValue(keys->notify, key);
565 return;
566 }
567
568 static void
569 keyChangeListRemoveValue(keyChangeListRef keys, CFStringRef key)
570 {
571 my_CFArrayAppendUniqueValue(keys->remove, key);
572 CFDictionaryRemoveValue(keys->set, key);
573 return;
574 }
575
576 static void
577 keyChangeListSetValue(keyChangeListRef keys, CFStringRef key, CFTypeRef value)
578 {
579 my_CFArrayRemoveValue(keys->remove, key);
580 CFDictionarySetValue(keys->set, key, value);
581 return;
582 }
583
584 static void
585 keyChangeListApplyToStore(keyChangeListRef keys, SCDynamicStoreRef session)
586 {
587 CFArrayRef notify = keys->notify;
588 CFArrayRef remove = keys->remove;
589 CFDictionaryRef set = keys->set;
590
591 if (CFArrayGetCount(notify) == 0) {
592 notify = NULL;
593 }
594 if (CFArrayGetCount(remove) == 0) {
595 remove = NULL;
596 }
597 if (CFDictionaryGetCount(set) == 0) {
598 set = NULL;
599 }
600 if (set == NULL && remove == NULL && notify == NULL) {
601 return;
602 }
603 if (S_IPMonitor_debug & kDebugFlag1) {
604 if (set != NULL) {
605 my_log(LOG_DEBUG, "IPMonitor: Setting:\n%@", set);
606 }
607 if (remove != NULL) {
608 my_log(LOG_DEBUG, "IPMonitor: Removing:\n%@", remove);
609 }
610 if (notify != NULL) {
611 my_log(LOG_DEBUG, "IPMonitor: Notifying:\n%@", notify);
612 }
613 }
614 (void)SCDynamicStoreSetMultiple(session, set, remove, notify);
615
616 return;
617 }
618
619 static void
620 S_nwi_ifstate_dump(nwi_ifstate_t ifstate, int i)
621 {
622 const char * addr_str;
623 void * address;
624 char ntopbuf[INET6_ADDRSTRLEN];
625 char vpn_ntopbuf[INET6_ADDRSTRLEN];
626 const struct sockaddr * vpn_addr;
627
628 address = nwi_ifstate_get_address(ifstate);
629 addr_str = inet_ntop(ifstate->af, address, ntopbuf, sizeof(ntopbuf));
630 vpn_addr = nwi_ifstate_get_vpn_server(ifstate);
631 if (vpn_addr != NULL) {
632 _SC_sockaddr_to_string(nwi_ifstate_get_vpn_server(ifstate),
633 vpn_ntopbuf,
634 sizeof(vpn_ntopbuf));
635 }
636 my_log(LOG_DEBUG,
637 " [%d]: %s%s%s%s rank 0x%x iaddr: %s%s%s reachability_flags %u",
638 i, ifstate->ifname,
639 ifstate->diff_str != NULL ? ifstate->diff_str : "",
640 (ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0
641 ? " dns" : "",
642 (ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0
643 ? " never" : "",
644 ifstate->rank,
645 addr_str,
646 (vpn_addr != NULL) ? " vpn_server_addr: " : "",
647 (vpn_addr != NULL) ? vpn_ntopbuf : "",
648 ifstate->reach_flags);
649 return;
650 }
651
652 static void
653 S_nwi_state_dump(nwi_state_t state)
654 {
655 int i;
656 nwi_ifstate_t scan;
657
658 if (state == NULL) {
659 my_log(LOG_DEBUG, "nwi_state = <none>");
660 return;
661 }
662 my_log(LOG_DEBUG,
663 "nwi_state = { "
664 "gen = %llu size = %u #ipv4 = %u #ipv6 = %u "
665 "reach_flags_v4 = %u reach_flags_v6 %u }",
666 state->generation_count,
667 state->size,
668 state->ipv4_count,
669 state->ipv6_count,
670 nwi_state_get_reachability_flags(state, AF_INET),
671 nwi_state_get_reachability_flags(state, AF_INET6));
672 if (state->ipv4_count) {
673 my_log(LOG_DEBUG, "IPv4:");
674 for (i = 0, scan = state->nwi_ifstates;
675 i < state->ipv4_count; i++, scan++) {
676 S_nwi_ifstate_dump(scan, i);
677 }
678 }
679 if (state->ipv6_count) {
680 my_log(LOG_DEBUG, "IPv6:");
681 for (i = 0, scan = state->nwi_ifstates + state->ipv6_start;
682 i < state->ipv6_count; i++, scan++) {
683 S_nwi_ifstate_dump(scan, i);
684 }
685 }
686 return;
687 }
688
689 static boolean_t
690 S_is_network_boot()
691 {
692 int mib[2];
693 size_t len;
694 int netboot = 0;
695
696 mib[0] = CTL_KERN;
697 mib[1] = KERN_NETBOOT;
698 len = sizeof(netboot);
699 sysctl(mib, 2, &netboot, &len, NULL, 0);
700 return (netboot);
701 }
702
703 #if !TARGET_IPHONE_SIMULATOR
704 static __inline__ int
705 inet6_dgram_socket()
706 {
707 return (socket(AF_INET6, SOCK_DGRAM, 0));
708 }
709
710 #ifdef SIOCDRADD_IN6
711 static int
712 siocdradd_in6(int s, int if_index, const struct in6_addr * addr, u_char flags)
713 {
714 struct in6_defrouter dr;
715 struct sockaddr_in6 * sin6;
716
717 bzero(&dr, sizeof(dr));
718 sin6 = &dr.rtaddr;
719 sin6->sin6_len = sizeof(struct sockaddr_in6);
720 sin6->sin6_family = AF_INET6;
721 sin6->sin6_addr = *addr;
722 dr.flags = flags;
723 dr.if_index = if_index;
724 return (ioctl(s, SIOCDRADD_IN6, &dr));
725 }
726
727 static int
728 siocdrdel_in6(int s, int if_index, const struct in6_addr * addr)
729 {
730 struct in6_defrouter dr;
731 struct sockaddr_in6 * sin6;
732
733 bzero(&dr, sizeof(dr));
734 sin6 = &dr.rtaddr;
735 sin6->sin6_len = sizeof(struct sockaddr_in6);
736 sin6->sin6_family = AF_INET6;
737 sin6->sin6_addr = *addr;
738 dr.if_index = if_index;
739 return (ioctl(s, SIOCDRDEL_IN6, &dr));
740 }
741 #endif /* SIOCDRADD_IN6 */
742 #endif /* !TARGET_IPHONE_SIMULATOR */
743
744 #ifdef RTF_IFSCOPE
745 static boolean_t
746 S_is_scoped_routing_enabled()
747 {
748 int scopedroute = 0;
749 size_t len = sizeof(scopedroute);
750
751 if ((sysctlbyname("net.inet.ip.scopedroute",
752 &scopedroute, &len,
753 NULL, 0) == -1)
754 && (errno != ENOENT)) {
755 my_log(LOG_ERR, "sysctlbyname() failed: %s", strerror(errno));
756 }
757 return (scopedroute);
758 }
759
760 static boolean_t
761 S_is_scoped_v6_routing_enabled()
762 {
763 int scopedroute_v6 = 0;
764 size_t len = sizeof(scopedroute_v6);
765
766 if ((sysctlbyname("net.inet6.ip6.scopedroute",
767 &scopedroute_v6, &len,
768 NULL, 0) == -1)
769 && (errno != ENOENT)) {
770 my_log(LOG_ERR, "sysctlbyname() failed: %s", strerror(errno));
771 }
772 return (scopedroute_v6);
773 }
774 #endif /* RTF_IFSCOPE */
775
776 static void
777 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new)
778 {
779 CFIndex n = CFArrayGetCount(arr);
780
781 if (CFArrayContainsValue(arr, CFRangeMake(0, n), new)) {
782 return;
783 }
784 CFArrayAppendValue(arr, new);
785 return;
786 }
787
788 static void
789 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key)
790 {
791 CFIndex i;
792
793 i = CFArrayGetFirstIndexOfValue(arr,
794 CFRangeMake(0, CFArrayGetCount(arr)),
795 key);
796 if (i != kCFNotFound) {
797 CFArrayRemoveValueAtIndex(arr, i);
798 }
799 return;
800 }
801
802 static void
803 my_CFRelease(void * t)
804 {
805 void * * obj = (void * *)t;
806
807 if (obj && *obj) {
808 CFRelease(*obj);
809 *obj = NULL;
810 }
811 return;
812 }
813
814 static CFDictionaryRef
815 my_CFDictionaryGetDictionary(CFDictionaryRef dict, CFStringRef key)
816 {
817 if (isA_CFDictionary(dict) == NULL) {
818 return (NULL);
819 }
820 return (isA_CFDictionary(CFDictionaryGetValue(dict, key)));
821 }
822
823 static CFArrayRef
824 my_CFDictionaryGetArray(CFDictionaryRef dict, CFStringRef key)
825 {
826 if (isA_CFDictionary(dict) == NULL) {
827 return (NULL);
828 }
829 return (isA_CFArray(CFDictionaryGetValue(dict, key)));
830 }
831
832 static boolean_t
833 cfstring_to_ipvx(int family, CFStringRef str, void * addr, int addr_size)
834 {
835 char buf[128];
836
837 if (isA_CFString(str) == NULL) {
838 goto done;
839 }
840
841 switch (family) {
842 case AF_INET:
843 if (addr_size < sizeof(struct in_addr)) {
844 goto done;
845 }
846 break;
847 case AF_INET6:
848 if (addr_size < sizeof(struct in6_addr)) {
849 goto done;
850 }
851 break;
852 default:
853 goto done;
854 }
855 (void)_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII);
856 if (inet_pton(family, buf, addr) == 1) {
857 return (TRUE);
858 }
859 done:
860 bzero(addr, addr_size);
861 return (FALSE);
862 }
863
864 __private_extern__
865 boolean_t
866 cfstring_to_ip(CFStringRef str, struct in_addr * ip_p)
867 {
868 return (cfstring_to_ipvx(AF_INET, str, ip_p, sizeof(*ip_p)));
869 }
870
871 __private_extern__
872 boolean_t
873 cfstring_to_ip6(CFStringRef str, struct in6_addr * ip6_p)
874 {
875 return (cfstring_to_ipvx(AF_INET6, str, ip6_p, sizeof(*ip6_p)));
876 }
877
878 static CF_RETURNS_RETAINED CFStringRef
879 setup_service_key(CFStringRef serviceID, CFStringRef entity)
880 {
881 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
882 kSCDynamicStoreDomainSetup,
883 serviceID,
884 entity));
885 }
886
887 static CF_RETURNS_RETAINED CFStringRef
888 state_service_key(CFStringRef serviceID, CFStringRef entity)
889 {
890 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
891 kSCDynamicStoreDomainState,
892 serviceID,
893 entity));
894 }
895
896 static CFDictionaryRef
897 get_service_setup_entity(CFDictionaryRef services_info, CFStringRef serviceID,
898 CFStringRef entity)
899 {
900 CFStringRef setup_key;
901 CFDictionaryRef setup_dict;
902
903 setup_key = setup_service_key(serviceID, entity);
904 setup_dict = my_CFDictionaryGetDictionary(services_info, setup_key);
905 my_CFRelease(&setup_key);
906 return (setup_dict);
907 }
908
909 static CFDictionaryRef
910 get_service_state_entity(CFDictionaryRef services_info, CFStringRef serviceID,
911 CFStringRef entity)
912 {
913 CFStringRef state_key;
914 CFDictionaryRef state_dict;
915
916 state_key = state_service_key(serviceID, entity);
917 state_dict = my_CFDictionaryGetDictionary(services_info, state_key);
918 my_CFRelease(&state_key);
919 return (state_dict);
920 }
921
922 static boolean_t
923 dict_get_first_ip(CFDictionaryRef dict, CFStringRef prop, struct in_addr * ip_p)
924 {
925 CFArrayRef ip_list;
926
927 ip_list = CFDictionaryGetValue(dict, prop);
928 if (isA_CFArray(ip_list) != NULL
929 && CFArrayGetCount(ip_list) > 0
930 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list, 0), ip_p)) {
931 return (TRUE);
932 }
933 return (FALSE);
934 }
935
936 static boolean_t
937 get_override_primary(CFDictionaryRef dict)
938 {
939 CFTypeRef override;
940
941 override = CFDictionaryGetValue(dict, kSCPropNetOverridePrimary);
942 if (isA_CFNumber(override) != NULL) {
943 int val = 0;
944
945 CFNumberGetValue((CFNumberRef)override, kCFNumberIntType, &val);
946 if (val != 0) {
947 return (TRUE);
948 }
949 }
950 else if (isA_CFBoolean(override) != NULL) {
951 if (CFBooleanGetValue(override)) {
952 return (TRUE);
953 }
954 }
955 return (FALSE);
956 }
957
958 /**
959 ** IPv4Route*
960 **/
961
962 static __inline__ struct in_addr
963 subnet_addr(struct in_addr addr, struct in_addr mask)
964 {
965 struct in_addr net;
966
967 net.s_addr = addr.s_addr & mask.s_addr;
968 return (net);
969 }
970
971 static __inline__ int
972 in_addr_cmp(struct in_addr a, struct in_addr b)
973 {
974 return (uint32_cmp(ntohl(a.s_addr), ntohl(b.s_addr)));
975 }
976
977 static __inline__ int
978 RouteFlagsCompare(RouteFlags a, RouteFlags b)
979 {
980 return (uint32_cmp(a, b));
981 }
982
983 static void
984 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r, CFMutableStringRef str)
985 {
986 Rank rank_assertion = RANK_ASSERTION_MASK(r->rank);
987
988 CFStringAppendFormat(str, NULL,
989 CFSTR("Dest " IP_FORMAT
990 " Mask " IP_FORMAT
991 " Gate " IP_FORMAT
992 " Ifp %s Ifa " IP_FORMAT),
993 IP_LIST(&r->dest),
994 IP_LIST(&r->mask),
995 IP_LIST(&r->gateway),
996 (r->ifname[0] != '\0') ? r->ifname : "<none>",
997 IP_LIST(&r->ifa));
998 if ((r->flags & kRouteIsNULLFlag) != 0) {
999 CFStringAppend(str, CFSTR(" [null]"));
1000 }
1001 else {
1002 if ((r->flags & kRouteIsNotSubnetLocalFlag) != 0) {
1003 CFStringAppend(str, CFSTR(" [non-local]"));
1004 }
1005 else if ((r->flags & kRouteIsDirectToInterfaceFlag) != 0) {
1006 CFStringAppend(str, CFSTR(" [direct]"));
1007 }
1008 switch (rank_assertion) {
1009 case kRankAssertionFirst:
1010 CFStringAppend(str, CFSTR(" [first]"));
1011 break;
1012 case kRankAssertionLast:
1013 CFStringAppend(str, CFSTR(" [last]"));
1014 break;
1015 case kRankAssertionNever:
1016 CFStringAppend(str, CFSTR(" [never]"));
1017 break;
1018 default:
1019 break;
1020 }
1021 if ((r->flags & kRouteIsScopedFlag) != 0) {
1022 CFStringAppend(str, CFSTR(" [SCOPED]"));
1023 }
1024 }
1025 return;
1026 }
1027
1028 static CFStringRef
1029 IPv4RouteCopyDescription(IPv4RouteRef r)
1030 {
1031 CFMutableStringRef str;
1032
1033 str = CFStringCreateMutable(NULL, 0);
1034 IPv4RouteCopyDescriptionWithString(r, str);
1035 return (str);
1036 }
1037
1038 static __inline__ void
1039 IPv4RoutePrint(IPv4RouteRef route)
1040 {
1041 CFStringRef str = IPv4RouteCopyDescription(route);
1042
1043 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
1044 CFRelease(str);
1045 return;
1046 }
1047
1048 static __inline__ void
1049 IPv4RouteLog(int level, IPv4RouteRef route)
1050 {
1051 CFStringRef str = IPv4RouteCopyDescription(route);
1052
1053 my_log(level, "%@", str);
1054 CFRelease(str);
1055 return;
1056 }
1057
1058 static int
1059 IPv4RouteCompare(IPv4RouteRef a, Rank a_rank,
1060 IPv4RouteRef b, Rank b_rank, boolean_t * same_dest)
1061 {
1062 int cmp;
1063
1064 *same_dest = FALSE;
1065 cmp = in_addr_cmp(a->dest, b->dest);
1066 if (cmp == 0) {
1067 cmp = in_addr_cmp(a->mask, b->mask);
1068 if (cmp == 0) {
1069 int name_cmp = strcmp(a->ifname, b->ifname);
1070
1071 if (name_cmp == 0) {
1072 cmp = 0;
1073 }
1074 else {
1075 *same_dest = TRUE;
1076 cmp = RankCompare(a_rank, b_rank);
1077 if (cmp == 0) {
1078 cmp = name_cmp;
1079 }
1080 }
1081 }
1082 }
1083 if ((S_IPMonitor_debug & kDebugFlag8) != 0) {
1084 CFStringRef a_str;
1085 CFStringRef b_str;
1086 char ch;
1087
1088 if (cmp < 0) {
1089 ch = '<';
1090 }
1091 else if (cmp == 0) {
1092 ch = '=';
1093 }
1094 else {
1095 ch = '>';
1096 }
1097 a_str = IPv4RouteCopyDescription(a);
1098 b_str = IPv4RouteCopyDescription(b);
1099 my_log(LOG_DEBUG, "%@ rank 0x%x %c %@ rank 0x%x",
1100 a_str, a_rank, ch, b_str, b_rank);
1101 CFRelease(a_str);
1102 CFRelease(b_str);
1103 }
1104 return (cmp);
1105 }
1106
1107 static CFMutableStringRef
1108 IPv4RouteListCopyDescription(IPv4RouteListRef routes)
1109 {
1110 int i;
1111 IPv4RouteRef r;
1112 CFMutableStringRef str;
1113
1114 str = CFStringCreateMutable(NULL, 0);
1115 CFStringAppendFormat(str, NULL, CFSTR("<IPv4RouteList[%d]> = {"),
1116 routes->count);
1117 for (i = 0, r = routes->list; i < routes->count; i++, r++) {
1118 CFStringAppendFormat(str, NULL, CFSTR("\n%2d. "), i);
1119 IPv4RouteCopyDescriptionWithString(r, str);
1120 }
1121 CFStringAppend(str, CFSTR("\n}"));
1122 return (str);
1123 }
1124
1125 static __inline__ void
1126 IPv4RouteListPrint(IPv4RouteListRef routes)
1127 {
1128 CFStringRef str = IPv4RouteListCopyDescription(routes);
1129
1130 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
1131 CFRelease(str);
1132 return;
1133 }
1134
1135 static __inline__ void
1136 IPv4RouteListLog(int level, IPv4RouteListRef routes)
1137 {
1138 CFStringRef str = IPv4RouteListCopyDescription(routes);
1139
1140 my_log(level, "%@", str);
1141 CFRelease(str);
1142 return;
1143 }
1144
1145 static __inline__ unsigned int
1146 IPv4RouteListComputeSize(unsigned int n)
1147 {
1148 return (offsetof(IPv4RouteList, list[n]));
1149 }
1150
1151 #if !TARGET_IPHONE_SIMULATOR
1152 static IPv4RouteRef
1153 IPv4RouteListFindRoute(IPv4RouteListRef routes, IPv4RouteRef route)
1154 {
1155 int i;
1156 IPv4RouteRef scan_result = NULL;
1157 IPv4RouteRef scan;
1158
1159 for (i = 0, scan = routes->list; i < routes->count; i++, scan++) {
1160 if ((scan->dest.s_addr == route->dest.s_addr)
1161 && (scan->mask.s_addr == route->mask.s_addr)
1162 && (strcmp(scan->ifname, route->ifname) == 0)
1163 && (scan->ifa.s_addr == route->ifa.s_addr)
1164 && (scan->gateway.s_addr == route->gateway.s_addr)
1165 && (scan->flags == route->flags)) {
1166 scan_result = scan;
1167 break;
1168 }
1169 }
1170 return (scan_result);
1171 }
1172
1173 static void
1174 IPv4RouteListApply(IPv4RouteListRef old_routes, IPv4RouteListRef new_routes,
1175 IPv4RouteListApplyCallBackFuncPtr func, void * arg)
1176 {
1177 int i;
1178 IPv4RouteRef scan;
1179
1180 if (old_routes == new_routes && old_routes == NULL) {
1181 /* both old and new are NULL, so there's nothing to do */
1182 return;
1183 }
1184 if (old_routes != NULL) {
1185 for (i = 0, scan = old_routes->list;
1186 i < old_routes->count;
1187 i++, scan++) {
1188 IPv4RouteRef new_route = NULL;
1189
1190 if (new_routes != NULL) {
1191 new_route = IPv4RouteListFindRoute(new_routes, scan);
1192 }
1193 if (new_route == NULL) {
1194 if (func != NULL) {
1195 (*func)(kIPv4RouteListRemoveRouteCommand, scan, arg);
1196 }
1197 }
1198 }
1199 }
1200 if (new_routes != NULL) {
1201 for (i = 0, scan = new_routes->list;
1202 i < new_routes->count;
1203 i++, scan++) {
1204 IPv4RouteRef old_route = NULL;
1205
1206 if (old_routes != NULL) {
1207 old_route = IPv4RouteListFindRoute(old_routes, scan);
1208 }
1209 if (old_route == NULL) {
1210 if (func != NULL) {
1211 (*func)(kIPv4RouteListAddRouteCommand, scan, arg);
1212 }
1213 }
1214 }
1215 }
1216 return;
1217 }
1218 #endif /* !TARGET_IPHONE_SIMULATOR */
1219
1220 /*
1221 * Function: IPv4RouteListAddRoute
1222 *
1223 * Purpose:
1224 * Add the given IPv4Route to the list of routes, eliminating lower-ranked
1225 * duplicates on the same interface, and marking any lower ranked duplicates
1226 * on other interfaces with kRouteIsScopedFlag.
1227 *
1228 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1229 *
1230 * Returns:
1231 * Route list updated with the given route, possibly a different pointer,
1232 * due to using realloc'd memory.
1233 */
1234
1235 enum {
1236 kScopeNone = 0,
1237 kScopeThis = 1,
1238 kScopeNext = 2
1239 };
1240
1241 static IPv4RouteListRef
1242 IPv4RouteListAddRoute(IPv4RouteListRef routes, int init_size,
1243 IPv4RouteRef this_route, Rank this_rank)
1244 {
1245 int i;
1246 IPv4RouteRef first_scan = NULL;
1247 int scope_which = kScopeNone;
1248 IPv4RouteRef scan;
1249 int where = -1;
1250
1251 if (routes == NULL) {
1252 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(init_size));
1253 bzero(routes, sizeof(*routes));
1254 routes->size = init_size;
1255 routes->count = 0;
1256 }
1257 for (i = 0, scan = routes->list; i < routes->count;
1258 i++, scan++) {
1259 int cmp;
1260 boolean_t same_dest;
1261
1262 cmp = IPv4RouteCompare(this_route, this_rank, scan, scan->rank, &same_dest);
1263
1264 if (same_dest == TRUE && first_scan == NULL) {
1265 first_scan = scan;
1266 }
1267
1268 if (cmp < 0) {
1269 if (where == -1) {
1270 if (same_dest == TRUE
1271 && (first_scan->flags & kRouteIsScopedFlag) == 0) {
1272 if ((scan->flags & kRouteIsScopedFlag) != 0) {
1273 ROUTELIST_DEBUG(("Hit 1: set scope on self\n"),
1274 kDebugFlag8);
1275 scope_which = kScopeThis;
1276 }
1277 else {
1278 ROUTELIST_DEBUG(("Hit 2: set scope on next\n"),
1279 kDebugFlag8);
1280 scope_which = kScopeNext;
1281 }
1282 }
1283 /* remember our insertion point, but keep going to find a dup */
1284 where = i;
1285 }
1286 }
1287 else if (cmp == 0) {
1288 /* exact match */
1289 if (where != -1) {
1290 /* this route is a duplicate */
1291 ROUTELIST_DEBUG(("Hit 3: removing [%d]\n", i), kDebugFlag8);
1292 routes->count--;
1293 if (i == routes->count) {
1294 /* last slot, decrementing gets rid of it */
1295 }
1296 else {
1297 bcopy(routes->list + i + 1,
1298 routes->list + i,
1299 sizeof(routes->list[0]) * (routes->count - i));
1300 }
1301 break;
1302 }
1303 /* resolve conflict using rank */
1304 if (this_rank < scan->rank) {
1305 boolean_t is_scoped = FALSE;
1306
1307 if (scan->flags & kRouteIsScopedFlag) {
1308 is_scoped = TRUE;
1309 }
1310 ROUTELIST_DEBUG(("Hit 4:replacing [%d] rank 0x%x < 0x%x\n",
1311 i,
1312 this_rank,
1313 scan->rank), kDebugFlag8);
1314 *scan = *this_route;
1315 scan->rank = this_rank;
1316 if (is_scoped) {
1317 /* preserve whether route was scoped */
1318 ROUTELIST_DEBUG(("Hit 5: preserved scope\n"), kDebugFlag8);
1319 scan->flags |= kRouteIsScopedFlag;
1320 }
1321 }
1322 /* we're done */
1323 goto done;
1324 }
1325 else {
1326 if (same_dest == TRUE) {
1327 if (scope_which == kScopeNone) {
1328 ROUTELIST_DEBUG(("Hit 10: set scope on self\n"),
1329 kDebugFlag8);
1330 scope_which = kScopeThis;
1331 }
1332 }
1333 #ifdef TEST_IPV4_ROUTELIST
1334 else if (where != -1) {
1335 /* not possible because we maintain a sorted list */
1336 ROUTELIST_DEBUG(("Hit 11: moved past routes - can't happen\n"),
1337 kDebugFlag8);
1338 break;
1339 }
1340 #endif /* TEST_IPV4_ROUTELIST */
1341 }
1342 }
1343 if (routes->size == routes->count) {
1344 int how_many;
1345 IPv4RouteListRef new_routes;
1346 int old_size;
1347
1348 /* double the size */
1349 old_size = routes->size;
1350 how_many = old_size * 2;
1351 new_routes = (IPv4RouteListRef)
1352 realloc(routes, IPv4RouteListComputeSize(how_many));
1353 if (new_routes == NULL) {
1354 /* no memory */
1355 goto done;
1356 }
1357 ROUTELIST_DEBUG(("increasing size from %d to %d\n", old_size,
1358 how_many), kDebugFlag8);
1359 new_routes->size = how_many;
1360 routes = new_routes;
1361 }
1362 if (where == -1) {
1363 /* add it to the end */
1364 where = routes->count;
1365 }
1366 else {
1367 /* insert it at [where] */
1368 bcopy(routes->list + where,
1369 routes->list + where + 1,
1370 sizeof(routes->list[0]) * (routes->count - where));
1371 }
1372 /* copy the route */
1373 routes->list[where] = *this_route;
1374 routes->list[where].rank = this_rank;
1375 if (RANK_ASSERTION_MASK(this_rank) == kRankAssertionNever) {
1376 routes->list[where].flags |= kRouteIsScopedFlag;
1377 }
1378
1379 /* set the scope */
1380 switch (scope_which) {
1381 case kScopeThis:
1382 routes->list[where].flags |= kRouteIsScopedFlag;
1383 break;
1384 case kScopeNext:
1385 routes->list[where + 1].flags |= kRouteIsScopedFlag;
1386 break;
1387 default:
1388 case kScopeNone:
1389 break;
1390 }
1391 routes->count++;
1392 done:
1393 return (routes);
1394 }
1395
1396 /*
1397 * Function: IPv4RouteListAddRouteList
1398 *
1399 * Purpose:
1400 * Invoke IPv4RouteListAddRoute for each route in the given list.
1401 *
1402 * Returns:
1403 * See IPv4RouteListAddRoute for more information.
1404 */
1405 static IPv4RouteListRef
1406 IPv4RouteListAddRouteList(IPv4RouteListRef routes, int init_size,
1407 IPv4RouteListRef service_routes, Rank rank)
1408 {
1409 int i;
1410 IPv4RouteRef scan;
1411
1412 for (i = 0, scan = service_routes->list;
1413 i < service_routes->count; i++, scan++) {
1414 routes = IPv4RouteListAddRoute(routes, init_size, scan, rank);
1415 }
1416 return (routes);
1417 }
1418
1419 static boolean_t
1420 plist_get_cstring(CFDictionaryRef dict, CFStringRef prop_name,
1421 char * buf, int buf_size)
1422 {
1423 CFStringRef val;
1424
1425 val = CFDictionaryGetValue(dict, prop_name);
1426 if (isA_CFString(val) == NULL) {
1427 return (FALSE);
1428 }
1429 if (CFStringGetCString(val, buf, buf_size, kCFStringEncodingASCII)
1430 == FALSE) {
1431 return (FALSE);
1432 }
1433 return (TRUE);
1434 }
1435
1436 /*
1437 * Function: IPv4RouteListCreateWithDictionary
1438 *
1439 * Purpose:
1440 * Given the service ipv4 entity dictionary, generate the list of routes.
1441 * Currently, this includes just the default route and subnet route,
1442 * if the service has a subnet mask.
1443 *
1444 * Returns:
1445 * If the passed in route_list is NULL or too small, this routine
1446 * allocates malloc'd memory to hold the routes.
1447 */
1448 static IPv4RouteListRef
1449 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes,
1450 CFDictionaryRef dict,
1451 CFStringRef primaryRank)
1452 {
1453 struct in_addr addr = { 0 };
1454 boolean_t exclude_from_nwi = FALSE;
1455 RouteFlags flags = 0;
1456 unsigned int ifindex;
1457 char ifn[IFNAMSIZ];
1458 struct in_addr mask = { 0 };
1459 int n = 0;
1460 boolean_t add_default = FALSE;
1461 boolean_t add_subnet = FALSE;
1462 IPv4RouteRef r;
1463 struct in_addr subnet = { 0 };
1464 struct in_addr router = { 0 };
1465 Rank rank = kRankAssertionDefault;
1466
1467 if (dict == NULL) {
1468 return (NULL);
1469 }
1470 if (plist_get_cstring(dict, kSCPropInterfaceName, ifn, sizeof(ifn))
1471 == FALSE) {
1472 return (NULL);
1473 }
1474 #ifdef TEST_IPV4_ROUTELIST
1475 ifindex = 0;
1476 #else /* TEST_IPV4_ROUTELIST */
1477 ifindex = if_nametoindex(ifn);
1478 if (ifindex == 0) {
1479 /* interface doesn't exist */
1480 return (NULL);
1481 }
1482 #endif /* TEST_IPV4_ROUTELIST */
1483 if (cfstring_to_ip(CFDictionaryGetValue(dict, kSCPropNetIPv4Router),
1484 &router) == 0) {
1485 (void)dict_get_first_ip(dict, kSCPropNetIPv4DestAddresses, &router);
1486 }
1487 if (dict_get_first_ip(dict, kSCPropNetIPv4Addresses, &addr)
1488 && dict_get_first_ip(dict, kSCPropNetIPv4SubnetMasks, &mask)) {
1489 /* subnet route */
1490 subnet = subnet_addr(addr, mask);
1491 /* ignore link-local subnets, let IPConfiguration handle them for now */
1492 if (ntohl(subnet.s_addr) != IN_LINKLOCALNETNUM) {
1493 add_subnet = TRUE;
1494 n++;
1495 } else if (router.s_addr == 0) {
1496 exclude_from_nwi = TRUE;
1497 }
1498 }
1499 if (addr.s_addr == 0) {
1500 /* thanks for playing */
1501 return (NULL);
1502 }
1503 if (router.s_addr == 0) {
1504 /*
1505 * If no router is configured, demote the rank. If there's already
1506 * a rank assertion that indicates RankNever, use that, otherwise
1507 * use RankLast.
1508 */
1509 flags |= kRouteIsDirectToInterfaceFlag;
1510 if (primaryRank != NULL
1511 && PrimaryRankGetRankAssertion(primaryRank) == kRankAssertionNever) {
1512 rank = kRankAssertionNever;
1513 }
1514 else {
1515 rank = kRankAssertionLast;
1516 }
1517 }
1518 else {
1519 /*
1520 * If the router address is our address and the subnet mask is
1521 * not 255.255.255.255, assume all routes are local to the interface.
1522 */
1523 if (addr.s_addr == router.s_addr
1524 && mask.s_addr != INADDR_BROADCAST) {
1525 flags |= kRouteIsDirectToInterfaceFlag;
1526 }
1527 if (primaryRank != NULL) {
1528 rank = PrimaryRankGetRankAssertion(primaryRank);
1529 } else if (get_override_primary(dict)) {
1530 rank = kRankAssertionFirst;
1531 }
1532 }
1533
1534 if (S_dict_get_boolean(dict, kIsNULL, FALSE)) {
1535 exclude_from_nwi = TRUE;
1536 flags |= kRouteIsNULLFlag;
1537 }
1538
1539 if (rank == kRankAssertionNever) {
1540 flags |= kRouteIsScopedFlag;
1541 }
1542
1543 if (add_subnet && (flags & kRouteIsDirectToInterfaceFlag) == 0
1544 && subnet.s_addr != subnet_addr(router, mask).s_addr) {
1545 flags |= kRouteIsNotSubnetLocalFlag;
1546 }
1547
1548 if (strncmp(ifn, "lo0", sizeof(ifn)) != 0) {
1549 add_default = TRUE;
1550 n++;
1551 }
1552
1553 if (routes == NULL || routes->size < n) {
1554 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(n));
1555 routes->size = n;
1556 }
1557 bzero(routes, IPv4RouteListComputeSize(n));
1558 routes->count = n;
1559 routes->exclude_from_nwi = exclude_from_nwi;
1560
1561 /* start at the beginning */
1562 r = routes->list;
1563
1564 if (add_default) {
1565 /* add the default route */
1566 r->ifindex = ifindex;
1567 strlcpy(r->ifname, ifn, sizeof(r->ifname));
1568 r->ifa = addr;
1569 r->flags = flags;
1570 if ((flags & kRouteIsDirectToInterfaceFlag) == 0) {
1571 r->gateway = router;
1572 }
1573 else {
1574 r->gateway = addr;
1575 }
1576 r->rank = rank;
1577 r++;
1578 }
1579
1580 /* add the subnet route */
1581 if (add_subnet) {
1582 if ((flags & kRouteIsNULLFlag) != 0) {
1583 r->flags |= kRouteIsNULLFlag;
1584 }
1585 r->ifindex = ifindex;
1586 r->gateway = addr;
1587 r->dest = subnet;
1588 r->mask = mask;
1589 strlcpy(r->ifname, ifn, sizeof(r->ifname));
1590 r->ifa = addr;
1591 r->rank = rank;
1592 }
1593 return (routes);
1594 }
1595
1596 /*
1597 * Function: parse_component
1598 * Purpose:
1599 * Given a string 'key' and a string prefix 'prefix',
1600 * return the next component in the slash '/' separated
1601 * key.
1602 *
1603 * Examples:
1604 * 1. key = "a/b/c" prefix = "a/"
1605 * returns "b"
1606 * 2. key = "a/b/c" prefix = "a/b/"
1607 * returns "c"
1608 */
1609 static CF_RETURNS_RETAINED CFStringRef
1610 parse_component(CFStringRef key, CFStringRef prefix)
1611 {
1612 CFMutableStringRef comp;
1613 CFRange range;
1614
1615 if (CFStringHasPrefix(key, prefix) == FALSE) {
1616 return (NULL);
1617 }
1618 comp = CFStringCreateMutableCopy(NULL, 0, key);
1619 if (comp == NULL) {
1620 return (NULL);
1621 }
1622 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
1623 range = CFStringFind(comp, CFSTR("/"), 0);
1624 if (range.location == kCFNotFound) {
1625 return (comp);
1626 }
1627 range.length = CFStringGetLength(comp) - range.location;
1628 CFStringDelete(comp, range);
1629 return (comp);
1630 }
1631
1632 static CFMutableDictionaryRef
1633 service_dict_copy(CFStringRef serviceID)
1634 {
1635 CFDictionaryRef d = NULL;
1636 CFMutableDictionaryRef service_dict;
1637
1638 /* create a modifyable dictionary, a copy or a new one */
1639 d = CFDictionaryGetValue(S_service_state_dict, serviceID);
1640 if (d == NULL) {
1641 service_dict
1642 = CFDictionaryCreateMutable(NULL, 0,
1643 &kCFTypeDictionaryKeyCallBacks,
1644 &kCFTypeDictionaryValueCallBacks);
1645 }
1646 else {
1647 service_dict = CFDictionaryCreateMutableCopy(NULL, 0, d);
1648 }
1649 return (service_dict);
1650 }
1651
1652 static void
1653 log_service_entity(int level, CFStringRef serviceID, CFStringRef entity,
1654 CFStringRef operation, CFTypeRef val)
1655 {
1656
1657 CFDataRef route_list;
1658 CFMutableStringRef this_val = NULL;
1659
1660 if (CFEqual(entity, kSCEntNetIPv4) && isA_CFDictionary(val) != NULL) {
1661 CFDictionaryRef service_dict = NULL;
1662
1663 route_list = CFDictionaryGetValue(val, kIPv4DictRoutes);
1664 if (route_list != NULL) {
1665 /* ALIGN: CF should align to at least 8-byte boundaries */
1666 this_val = IPv4RouteListCopyDescription((IPv4RouteListRef)
1667 (void *)CFDataGetBytePtr(route_list));
1668 }
1669
1670 service_dict = CFDictionaryGetValue(val, kIPv4DictService);
1671
1672 if (service_dict != NULL && isA_CFDictionary(service_dict) != NULL) {
1673 if (this_val == NULL) {
1674 this_val = CFStringCreateMutable(NULL, 0);
1675 }
1676 CFStringAppendFormat(this_val, NULL, CFSTR("\n <IPv4Dictionary>: %@"), service_dict);
1677 }
1678 val = this_val;
1679 }
1680 if (val == NULL) {
1681 val = CFSTR("<none>");
1682 }
1683 my_log(level, "IPMonitor: serviceID %@ %@ %@ value = %@",
1684 serviceID, operation, entity, val);
1685 my_CFRelease(&this_val);
1686 return;
1687 }
1688
1689 static boolean_t
1690 service_dict_set(CFStringRef serviceID, CFStringRef entity,
1691 CFTypeRef new_val)
1692 {
1693 boolean_t changed = FALSE;
1694 CFTypeRef old_val;
1695 CFMutableDictionaryRef service_dict;
1696
1697 service_dict = service_dict_copy(serviceID);
1698 old_val = CFDictionaryGetValue(service_dict, entity);
1699 if (new_val == NULL) {
1700 if (old_val != NULL) {
1701 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1702 log_service_entity(LOG_DEBUG, serviceID, entity,
1703 CFSTR("Removed:"), old_val);
1704 }
1705 CFDictionaryRemoveValue(service_dict, entity);
1706 changed = TRUE;
1707 }
1708 }
1709 else {
1710 if (old_val == NULL || CFEqual(new_val, old_val) == FALSE) {
1711 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1712 log_service_entity(LOG_DEBUG, serviceID, entity,
1713 CFSTR("Changed: old"), old_val);
1714 log_service_entity(LOG_DEBUG, serviceID, entity,
1715 CFSTR("Changed: new"), new_val);
1716 }
1717 CFDictionarySetValue(service_dict, entity, new_val);
1718 changed = TRUE;
1719 }
1720 }
1721 if (CFDictionaryGetCount(service_dict) == 0) {
1722 CFDictionaryRemoveValue(S_service_state_dict, serviceID);
1723 }
1724 else {
1725 CFDictionarySetValue(S_service_state_dict, serviceID, service_dict);
1726 }
1727 my_CFRelease(&service_dict);
1728 return (changed);
1729 }
1730
1731 static CFDictionaryRef
1732 service_dict_get(CFStringRef serviceID, CFStringRef entity)
1733 {
1734 CFDictionaryRef service_dict;
1735
1736 service_dict = CFDictionaryGetValue(S_service_state_dict, serviceID);
1737 if (service_dict == NULL) {
1738 return (NULL);
1739 }
1740 return (CFDictionaryGetValue(service_dict, entity));
1741 }
1742
1743 #ifndef kSCPropNetHostname
1744 #define kSCPropNetHostname CFSTR("Hostname")
1745 #endif
1746
1747 __private_extern__
1748 CFStringRef
1749 copy_dhcp_hostname(CFStringRef serviceID)
1750 {
1751 CFDictionaryRef dict = NULL;
1752 CFStringRef hostname = NULL;
1753 CFDictionaryRef service_dict = NULL;
1754
1755 dict = service_dict_get(serviceID, kSCEntNetIPv4);
1756
1757 if (dict == NULL || isA_CFDictionary(dict) == NULL) {
1758 return (NULL);
1759 }
1760
1761 service_dict =
1762 CFDictionaryGetValue(dict, kIPv4DictService);
1763
1764 if (service_dict == NULL
1765 || isA_CFDictionary(service_dict) == NULL) {
1766 return (NULL);
1767 }
1768
1769 hostname =
1770 CFDictionaryGetValue(service_dict, kSCPropNetHostname);
1771
1772 if (hostname != NULL) {
1773 CFRetain(hostname);
1774 }
1775
1776 return (hostname);
1777 }
1778
1779 #if !TARGET_IPHONE_SIMULATOR
1780 static void
1781 ipv6_service_update_router(CFStringRef serviceID, CFDictionaryRef new_val)
1782 {
1783 #ifdef SIOCDRADD_IN6
1784 int if_index;
1785 char ifn[IFNAMSIZ];
1786 CFStringRef new_router = NULL;
1787 char ntopbuf[INET6_ADDRSTRLEN];
1788 CFDictionaryRef old_val = NULL;
1789 CFStringRef old_router = NULL;
1790 struct in6_addr router_ip;
1791 int s = -1;
1792
1793 ifn[0] = '\0';
1794 old_val = service_dict_get(serviceID, kSCEntNetIPv6);
1795 if (old_val != NULL) {
1796 plist_get_cstring(old_val, kSCPropInterfaceName, ifn, sizeof(ifn));
1797 old_router = CFDictionaryGetValue(old_val, kSCPropNetIPv6Router);
1798 }
1799 if (ifn[0] == '\0') {
1800 if (new_val == NULL
1801 || plist_get_cstring(new_val, kSCPropInterfaceName,
1802 ifn, sizeof(ifn)) == FALSE) {
1803 /* no InterfaceName property, ignore it */
1804 goto done;
1805 }
1806 }
1807 if_index = if_nametoindex(ifn);
1808 if (if_index == 0) {
1809 goto done;
1810 }
1811 s = inet6_dgram_socket();
1812 if (s < 0) {
1813 my_log(LOG_ERR,
1814 "IPMonitor: ipv6_service_update_router: socket failed, %s",
1815 strerror(errno));
1816 goto done;
1817 }
1818 if (new_val != NULL) {
1819 new_router = CFDictionaryGetValue(new_val, kSCPropNetIPv6Router);
1820 }
1821 if (S_dict_get_boolean(old_val, kIsNULL, FALSE) == FALSE
1822 && old_router != NULL
1823 && (new_router == NULL || CFEqual(old_router, new_router) == FALSE)) {
1824 /* remove the old Router */
1825 if (cfstring_to_ip6(old_router, &router_ip)) {
1826 if (IN6_IS_ADDR_LINKLOCAL(&router_ip) ||
1827 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip)) {
1828 /* scope it */
1829 router_ip.__u6_addr.__u6_addr16[1] = htons(if_index);
1830 }
1831 if (siocdrdel_in6(s, if_index, &router_ip) < 0) {
1832 if (errno != EINVAL) {
1833 my_log(LOG_ERR,
1834 "IPMonitor: siocdrdel_in6(%s, %s) failed, %s",
1835 ifn,
1836 inet_ntop(AF_INET6, &router_ip,
1837 ntopbuf, sizeof(ntopbuf)),
1838 strerror(errno));
1839 }
1840 }
1841 else if (S_IPMonitor_debug & kDebugFlag1) {
1842 my_log(LOG_DEBUG,
1843 "IPMonitor: %s removed default route %s",
1844 ifn,
1845 inet_ntop(AF_INET6, &router_ip,
1846 ntopbuf, sizeof(ntopbuf)));
1847 }
1848 }
1849 }
1850 /* add the new Router */
1851 if (S_dict_get_boolean(new_val, kIsNULL, FALSE) == FALSE
1852 && cfstring_to_ip6(new_router, &router_ip)) {
1853 if (IN6_IS_ADDR_LINKLOCAL(&router_ip) ||
1854 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip)) {
1855 /* scope it */
1856 router_ip.__u6_addr.__u6_addr16[1] = htons(if_index);
1857 }
1858 if (siocdradd_in6(s, if_index, &router_ip, 0) < 0) {
1859 if (errno != EINVAL) {
1860 my_log(LOG_ERR,
1861 "IPMonitor: siocdradd_in6(%s, %s) failed, %s",
1862 ifn,
1863 inet_ntop(AF_INET6, &router_ip,
1864 ntopbuf, sizeof(ntopbuf)),
1865 strerror(errno));
1866 }
1867 }
1868 else if (S_IPMonitor_debug & kDebugFlag1) {
1869 my_log(LOG_DEBUG,
1870 "IPMonitor: %s added default route %s",
1871 ifn,
1872 inet_ntop(AF_INET6, &router_ip,
1873 ntopbuf, sizeof(ntopbuf)));
1874 }
1875 }
1876 close(s);
1877
1878 done:
1879 #endif /* SIOCDRADD_IN6 */
1880 return;
1881 }
1882 #endif /* !TARGET_IPHONE_SIMULATOR */
1883
1884 #define ALLOW_EMPTY_STRING 0x1
1885
1886 static CF_RETURNS_RETAINED CFTypeRef
1887 sanitize_prop(CFTypeRef val, uint32_t flags)
1888 {
1889 if (val != NULL) {
1890 if (isA_CFString(val)) {
1891 CFMutableStringRef str;
1892
1893 str = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)val);
1894 CFStringTrimWhitespace(str);
1895 if (!(flags & ALLOW_EMPTY_STRING) && (CFStringGetLength(str) == 0)) {
1896 CFRelease(str);
1897 str = NULL;
1898 }
1899 val = str;
1900 } else {
1901 CFRetain(val);
1902 }
1903 }
1904
1905 return val;
1906 }
1907
1908 static void
1909 merge_array_prop(CFMutableDictionaryRef dict,
1910 CFStringRef key,
1911 CFDictionaryRef state_dict,
1912 CFDictionaryRef setup_dict,
1913 uint32_t flags,
1914 Boolean append)
1915 {
1916 CFMutableArrayRef merge_prop;
1917 CFArrayRef setup_prop = NULL;
1918 CFArrayRef state_prop = NULL;
1919
1920 if (setup_dict != NULL) {
1921 setup_prop = isA_CFArray(CFDictionaryGetValue(setup_dict, key));
1922 }
1923 if (state_dict != NULL) {
1924 state_prop = isA_CFArray(CFDictionaryGetValue(state_dict, key));
1925 }
1926
1927 if ((setup_prop == NULL) && (state_prop == NULL)) {
1928 return;
1929 }
1930
1931 merge_prop = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1932 if (setup_prop != NULL) {
1933 CFIndex i;
1934 CFIndex n;
1935
1936 n = CFArrayGetCount(setup_prop);
1937 for (i = 0; i < n; i++) {
1938 CFTypeRef val;
1939
1940 val = CFArrayGetValueAtIndex(setup_prop, i);
1941 val = sanitize_prop(val, flags);
1942 if (val != NULL) {
1943 CFArrayAppendValue(merge_prop, val);
1944 CFRelease(val);
1945 }
1946 }
1947 }
1948 if (state_prop != NULL
1949 && (setup_prop == NULL || S_append_state)) {
1950 CFIndex i;
1951 CFIndex n;
1952 CFRange setup_range = CFRangeMake(0, CFArrayGetCount(merge_prop));
1953
1954 n = CFArrayGetCount(state_prop);
1955 for (i = 0; i < n; i++) {
1956 CFTypeRef val;
1957
1958 val = CFArrayGetValueAtIndex(state_prop, i);
1959 val = sanitize_prop(val, flags);
1960 if (val != NULL) {
1961 if (append || !CFArrayContainsValue(merge_prop, setup_range, val)) {
1962 CFArrayAppendValue(merge_prop, val);
1963 }
1964 CFRelease(val);
1965 }
1966 }
1967 }
1968 if (CFArrayGetCount(merge_prop) > 0) {
1969 CFDictionarySetValue(dict, key, merge_prop);
1970 }
1971 CFRelease(merge_prop);
1972 return;
1973 }
1974
1975 static void
1976 pick_prop(CFMutableDictionaryRef dict,
1977 CFStringRef key,
1978 CFDictionaryRef state_dict,
1979 CFDictionaryRef setup_dict,
1980 uint32_t flags)
1981 {
1982 CFTypeRef val = NULL;
1983
1984 if (setup_dict != NULL) {
1985 val = CFDictionaryGetValue(setup_dict, key);
1986 val = sanitize_prop(val, flags);
1987 }
1988 if (val == NULL && state_dict != NULL) {
1989 val = CFDictionaryGetValue(state_dict, key);
1990 val = sanitize_prop(val, flags);
1991 }
1992 if (val != NULL) {
1993 CFDictionarySetValue(dict, key, val);
1994 CFRelease(val);
1995 }
1996
1997 return;
1998 }
1999
2000 /**
2001 ** GetEntityChangesFunc functions
2002 **/
2003 static boolean_t
2004 get_ipv4_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2005 CFDictionaryRef setup_dict, CFDictionaryRef info)
2006 {
2007 CFDictionaryRef aggregated_dict = NULL;
2008 boolean_t changed = FALSE;
2009 CFMutableDictionaryRef dict = NULL;
2010 CFStringRef primaryRank = NULL;
2011 IPv4RouteListRef r;
2012 #define R_STATIC 3
2013 IPv4RouteListRef routes;
2014 /* ALIGN: force align */
2015 uint32_t routes_buf[roundup(IPv4RouteListComputeSize(R_STATIC), sizeof(uint32_t))];
2016 CFDataRef routes_data = NULL;
2017 CFDictionaryRef service_options;
2018
2019 if (state_dict == NULL) {
2020 goto done;
2021 }
2022 service_options = service_dict_get(serviceID, kSCEntNetService);
2023 if (service_options != NULL) {
2024 primaryRank = CFDictionaryGetValue(service_options, kSCPropNetServicePrimaryRank);
2025 }
2026 dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2027 if (setup_dict != NULL) {
2028 CFStringRef router;
2029 struct in_addr router_ip;
2030
2031 router = CFDictionaryGetValue(setup_dict,
2032 kSCPropNetIPv4Router);
2033 if (router != NULL
2034 && cfstring_to_ip(router, &router_ip)) {
2035 CFDictionarySetValue(dict,
2036 kSCPropNetIPv4Router,
2037 router);
2038 }
2039 }
2040 routes = (IPv4RouteListRef)(void *)routes_buf;
2041 routes->size = R_STATIC;
2042 routes->count = 0;
2043 r = IPv4RouteListCreateWithDictionary(routes, dict, primaryRank);
2044 if (r != NULL) {
2045 routes_data = CFDataCreate(NULL,
2046 (const void *)r,
2047 IPv4RouteListComputeSize(r->count));
2048 if (r != routes) {
2049 free(r);
2050 }
2051 }
2052 else {
2053 my_log(LOG_NOTICE,
2054 "IPMonitor: %@ invalid IPv4 dictionary = %@",
2055 serviceID,
2056 dict);
2057 }
2058 done:
2059 if (routes_data != NULL) {
2060 CFStringRef keys[2];
2061 CFTypeRef values[2];
2062
2063 keys[0] = kIPv4DictService;
2064 values[0] = dict;
2065 keys[1] = kIPv4DictRoutes;
2066 values[1] = routes_data;
2067
2068 aggregated_dict = CFDictionaryCreate(NULL,
2069 (const void**)keys,
2070 values,
2071 sizeof(keys)/sizeof(keys[0]),
2072 &kCFTypeDictionaryKeyCallBacks,
2073 &kCFTypeDictionaryValueCallBacks);
2074
2075 }
2076 changed = service_dict_set(serviceID, kSCEntNetIPv4, aggregated_dict);
2077 if (routes_data == NULL) {
2078 /* clean up the rank too */
2079 CFDictionaryRemoveValue(S_ipv4_service_rank_dict, serviceID);
2080 }
2081 my_CFRelease(&dict);
2082 my_CFRelease(&aggregated_dict);
2083 my_CFRelease(&routes_data);
2084 return (changed);
2085 }
2086
2087 static boolean_t
2088 get_ipv6_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2089 CFDictionaryRef setup_dict, CFDictionaryRef info)
2090 {
2091 struct in6_addr addr;
2092 CFArrayRef addrs;
2093 boolean_t changed = FALSE;
2094 CFMutableDictionaryRef dict = NULL;
2095 CFDictionaryRef new_dict = NULL;
2096 CFStringRef router = NULL;
2097 struct in6_addr router_ip;
2098 boolean_t valid_ip = FALSE;
2099
2100 if (state_dict == NULL) {
2101 goto done;
2102 }
2103 addrs = isA_CFArray(CFDictionaryGetValue(state_dict,
2104 kSCPropNetIPv6Addresses));
2105 if (addrs != NULL && CFArrayGetCount(addrs) > 0) {
2106 valid_ip = cfstring_to_ip6(CFArrayGetValueAtIndex(addrs, 0), &addr);
2107 }
2108 if (valid_ip == FALSE) {
2109 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2110 my_log(LOG_DEBUG,
2111 "IPMonitor: %@ has no valid IPv6 address, ignoring",
2112 serviceID);
2113 }
2114 goto done;
2115 }
2116 dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2117 if (setup_dict != NULL) {
2118 router = CFDictionaryGetValue(setup_dict,
2119 kSCPropNetIPv6Router);
2120 if (router != NULL && cfstring_to_ip6(router, &router_ip)) {
2121 CFDictionarySetValue(dict,
2122 kSCPropNetIPv6Router,
2123 router);
2124 }
2125 }
2126 else {
2127 router = CFDictionaryGetValue(dict,
2128 kSCPropNetIPv6Router);
2129 if (router != NULL
2130 && cfstring_to_ip6(router, &router_ip) == FALSE) {
2131 CFDictionaryRemoveValue(dict, kSCPropNetIPv6Router);
2132 }
2133 }
2134 new_dict = dict;
2135
2136 done:
2137
2138 #if !TARGET_IPHONE_SIMULATOR
2139 ipv6_service_update_router(serviceID, new_dict);
2140 #endif /* !TARGET_IPHONE_SIMULATOR */
2141
2142 changed = service_dict_set(serviceID, kSCEntNetIPv6, new_dict);
2143 if (new_dict == NULL) {
2144 /* clean up the rank too */
2145 CFDictionaryRemoveValue(S_ipv6_service_rank_dict, serviceID);
2146 }
2147 my_CFRelease(&new_dict);
2148 return (changed);
2149 }
2150
2151 static void
2152 accumulate_dns_servers(CFArrayRef in_servers, ProtocolFlags active_protos,
2153 CFMutableArrayRef out_servers, CFStringRef interface)
2154 {
2155 int count;
2156 int i;
2157
2158 count = CFArrayGetCount(in_servers);
2159 for (i = 0; i < count; i++) {
2160 CFStringRef addr;
2161 struct in6_addr ipv6_addr;
2162 struct in_addr ip_addr;
2163
2164 addr = CFArrayGetValueAtIndex(in_servers, i);
2165 assert(addr != NULL);
2166
2167 if (cfstring_to_ip(addr, &ip_addr)) {
2168 /* IPv4 address */
2169 if ((active_protos & kProtocolFlagsIPv4) == 0
2170 && ntohl(ip_addr.s_addr) != INADDR_LOOPBACK) {
2171 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2172 my_log(LOG_DEBUG,
2173 "IPMonitor: no IPv4 connectivity, "
2174 "ignoring DNS server address ", IP_FORMAT,
2175 IP_LIST(&ip_addr));
2176 }
2177 continue;
2178 }
2179
2180 CFRetain(addr);
2181 }
2182 else if (cfstring_to_ip6(addr, &ipv6_addr)) {
2183 /* IPv6 address */
2184 if ((active_protos & kProtocolFlagsIPv6) == 0
2185 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr)) {
2186 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2187 char ntopbuf[INET6_ADDRSTRLEN];
2188
2189 my_log(LOG_DEBUG,
2190 "IPMonitor: no IPv6 connectivity, "
2191 "ignoring DNS server address %s",
2192 inet_ntop(AF_INET6, &ipv6_addr,
2193 ntopbuf, sizeof(ntopbuf)));
2194 }
2195 continue;
2196 }
2197
2198 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr) ||
2199 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr))
2200 && (interface != NULL)
2201 && (CFStringFind(addr, CFSTR("%"), 0).location == kCFNotFound)) {
2202 // append interface name to IPv6 link local address
2203 addr = CFStringCreateWithFormat(NULL, NULL,
2204 CFSTR("%@%%%@"),
2205 addr,
2206 interface);
2207 } else {
2208 CFRetain(addr);
2209 }
2210 }
2211 else {
2212 /* bad IP address */
2213 my_log(LOG_NOTICE,
2214 "IPMonitor: ignoring bad DNS server address '%@'",
2215 addr);
2216 continue;
2217 }
2218
2219 /* DNS server is valid and one we want */
2220 CFArrayAppendValue(out_servers, addr);
2221 CFRelease(addr);
2222 }
2223 return;
2224 }
2225
2226 static void
2227 merge_dns_servers(CFMutableDictionaryRef new_dict,
2228 CFArrayRef state_servers,
2229 CFArrayRef setup_servers,
2230 Boolean have_setup,
2231 ProtocolFlags active_protos,
2232 CFStringRef interface)
2233 {
2234 CFMutableArrayRef dns_servers;
2235 Boolean have_dns_setup = FALSE;
2236
2237 if (state_servers == NULL && setup_servers == NULL) {
2238 /* no DNS servers */
2239 return;
2240 }
2241 dns_servers = CFArrayCreateMutable(NULL, 0,
2242 &kCFTypeArrayCallBacks);
2243 if (setup_servers != NULL) {
2244 accumulate_dns_servers(setup_servers, active_protos,
2245 dns_servers, interface);
2246 if (CFArrayGetCount(dns_servers) > 0) {
2247 have_dns_setup = TRUE;
2248 }
2249 }
2250 if ((CFArrayGetCount(dns_servers) == 0 || S_append_state)
2251 && state_servers != NULL) {
2252 accumulate_dns_servers(state_servers, active_protos,
2253 dns_servers, NULL);
2254 }
2255
2256 /*
2257 * Here, we determine whether or not we want all queries for this DNS
2258 * configuration to be bound to the associated network interface.
2259 *
2260 * For dynamically derived network configurations (i.e. from State:)
2261 * this would be the preferred option using the argument "Hey, the
2262 * server told us to use these servers on this network so let's not
2263 * argue".
2264 *
2265 * But, when a DNS configuration has been provided by the user/admin
2266 * via the Network pref pane (i.e. from Setup:) we opt to not force
2267 * binding of the outbound queries. The simplest example why we take
2268 * this stance is with a multi-homing configuration. Consider a system
2269 * with one network service associated with "en0" and a second service
2270 * associated with "en1". The "en0" service has been set higher in
2271 * the network service order so it would be primary but the user/admin
2272 * wants the DNS queries to go to a server only accessible via "en1".
2273 * Without this exception we would take the DNS server addresses from
2274 * the Network pref pane (for "en0") and have the queries bound to
2275 * "en0" where they'd never reach their intended destination (via
2276 * "en1"). So, our exception to the rule is that we will not bind
2277 * user/admin configurations to any specific network interface.
2278 *
2279 * We also add an exception to the "follow the dynamically derived
2280 * network configuration" path for on-the-fly (no Setup: content)
2281 * network services.
2282 */
2283 if (CFArrayGetCount(dns_servers) != 0) {
2284 CFDictionarySetValue(new_dict,
2285 kSCPropNetDNSServerAddresses, dns_servers);
2286 if (have_setup && !have_dns_setup) {
2287 CFDictionarySetValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue);
2288 }
2289 }
2290
2291 my_CFRelease(&dns_servers);
2292 return;
2293 }
2294
2295
2296 static boolean_t
2297 get_dns_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2298 CFDictionaryRef setup_dict, CFDictionaryRef info)
2299 {
2300 ProtocolFlags active_protos = kProtocolFlagsNone;
2301 boolean_t changed = FALSE;
2302 CFStringRef domain;
2303 Boolean have_setup = FALSE;
2304 CFStringRef interface = NULL;
2305 CFDictionaryRef ipv4;
2306 CFDictionaryRef ipv6;
2307 int i;
2308 const struct {
2309 CFStringRef key;
2310 uint32_t flags;
2311 Boolean append;
2312 } merge_list[] = {
2313 { kSCPropNetDNSSearchDomains, 0, FALSE },
2314 { kSCPropNetDNSSortList, 0, FALSE },
2315 { kSCPropNetDNSSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE },
2316 { kSCPropNetDNSSupplementalMatchOrders, 0, TRUE },
2317 };
2318 CFMutableDictionaryRef new_dict = NULL;
2319 const CFStringRef pick_list[] = {
2320 kSCPropNetDNSDomainName,
2321 kSCPropNetDNSOptions,
2322 kSCPropNetDNSSearchOrder,
2323 kSCPropNetDNSServerPort,
2324 kSCPropNetDNSServerTimeout,
2325 kSCPropNetDNSServiceIdentifier,
2326 kSCPropNetDNSSupplementalMatchDomainsNoSearch,
2327 };
2328 IPv4RouteListRef routes = NULL;
2329
2330 if ((state_dict == NULL) && (setup_dict == NULL)) {
2331 /* there is no DNS content */
2332 goto done;
2333 }
2334
2335 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
2336 routes = ipv4_dict_get_routelist(ipv4);
2337
2338 if (routes != NULL) {
2339 if (get_service_setup_entity(info, serviceID, kSCEntNetIPv4) != NULL) {
2340 have_setup = TRUE;
2341 }
2342
2343 active_protos |= kProtocolFlagsIPv4;
2344
2345 interface = ipv4_dict_get_ifname(ipv4);
2346 }
2347
2348 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
2349 if (ipv6 != NULL) {
2350 if (!have_setup &&
2351 get_service_setup_entity(info, serviceID, kSCEntNetIPv6) != NULL) {
2352 have_setup = TRUE;
2353 }
2354
2355 active_protos |= kProtocolFlagsIPv6;
2356
2357 if (interface == NULL) {
2358 interface = CFDictionaryGetValue(ipv6,
2359 kSCPropInterfaceName);
2360 }
2361 }
2362
2363
2364 if (active_protos == kProtocolFlagsNone) {
2365 /* there is no IPv4 nor IPv6 */
2366 if (state_dict == NULL) {
2367 /* ... and no DNS content that we care about */
2368 goto done;
2369 }
2370 setup_dict = NULL;
2371 }
2372
2373 /* merge DNS configuration */
2374 new_dict = CFDictionaryCreateMutable(NULL, 0,
2375 &kCFTypeDictionaryKeyCallBacks,
2376 &kCFTypeDictionaryValueCallBacks);
2377
2378 if (active_protos == kProtocolFlagsNone) {
2379 merge_dns_servers(new_dict,
2380 my_CFDictionaryGetArray(state_dict,
2381 kSCPropNetDNSServerAddresses),
2382 NULL,
2383 FALSE,
2384 kProtocolFlagsIPv4 | kProtocolFlagsIPv6,
2385 NULL);
2386 }
2387 else {
2388 merge_dns_servers(new_dict,
2389 my_CFDictionaryGetArray(state_dict,
2390 kSCPropNetDNSServerAddresses),
2391 my_CFDictionaryGetArray(setup_dict,
2392 kSCPropNetDNSServerAddresses),
2393 have_setup,
2394 active_protos,
2395 interface);
2396 }
2397
2398 for (i = 0; i < sizeof(merge_list)/sizeof(merge_list[0]); i++) {
2399 merge_array_prop(new_dict,
2400 merge_list[i].key,
2401 state_dict,
2402 setup_dict,
2403 merge_list[i].flags,
2404 merge_list[i].append);
2405 }
2406
2407 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2408 pick_prop(new_dict,
2409 pick_list[i],
2410 state_dict,
2411 setup_dict,
2412 0);
2413 }
2414
2415 if (active_protos == kProtocolFlagsNone) {
2416 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
2417 if (CFDictionaryContainsKey(new_dict,
2418 kSCPropNetDNSSupplementalMatchDomains)) {
2419 /* only keep State: supplemental */
2420 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSDomainName);
2421 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchDomains);
2422 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchOrder);
2423 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSortList);
2424
2425 if ((interface == NULL) && (setup_dict == NULL) && (state_dict != NULL)) {
2426 /*
2427 * for supplemental-only configurations, add any scoped (or
2428 * wild-card "*") interface
2429 */
2430 interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName);
2431 }
2432 } else if (CFDictionaryContainsKey(new_dict, kSCPropNetDNSServiceIdentifier) &&
2433 (interface == NULL) &&
2434 (state_dict != NULL)) {
2435 interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName);
2436 } else {
2437 goto done;
2438 }
2439 }
2440
2441 if (CFDictionaryGetCount(new_dict) == 0) {
2442 my_CFRelease(&new_dict);
2443 goto done;
2444 }
2445
2446 if (interface != NULL) {
2447 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
2448 }
2449
2450 if (S_append_state) {
2451 /*
2452 * ensure any specified domain name (e.g. the domain returned by
2453 * a DHCP server) is in the search list.
2454 */
2455 domain = CFDictionaryGetValue(new_dict, kSCPropNetDNSDomainName);
2456 if (isA_CFString(domain)) {
2457 CFArrayRef search;
2458
2459 search = CFDictionaryGetValue(new_dict, kSCPropNetDNSSearchDomains);
2460 if (isA_CFArray(search) &&
2461 !CFArrayContainsValue(search, CFRangeMake(0, CFArrayGetCount(search)), domain)) {
2462 CFMutableArrayRef new_search;
2463
2464 new_search = CFArrayCreateMutableCopy(NULL, 0, search);
2465 CFArrayAppendValue(new_search, domain);
2466 CFDictionarySetValue(new_dict, kSCPropNetDNSSearchDomains, new_search);
2467 my_CFRelease(&new_search);
2468 }
2469 }
2470 }
2471
2472 done:
2473 changed = service_dict_set(serviceID, kSCEntNetDNS, new_dict);
2474 my_CFRelease(&new_dict);
2475 return (changed);
2476 }
2477
2478 static void
2479 merge_dict(const void *key, const void *value, void *context)
2480 {
2481 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context;
2482
2483 CFDictionarySetValue(dict, key, value);
2484 return;
2485 }
2486
2487 #define PROXY_AUTO_DISCOVERY_URL 252
2488
2489 static CF_RETURNS_RETAINED CFStringRef
2490 wpadURL_dhcp(CFDictionaryRef dhcp_options)
2491 {
2492 CFStringRef urlString = NULL;
2493
2494 if (isA_CFDictionary(dhcp_options)) {
2495 CFDataRef data;
2496
2497 data = DHCPInfoGetOptionData(dhcp_options, PROXY_AUTO_DISCOVERY_URL);
2498 if (data != NULL) {
2499 CFURLRef url;
2500 const UInt8 *urlBytes;
2501 CFIndex urlLen;
2502
2503 urlBytes = CFDataGetBytePtr(data);
2504 urlLen = CFDataGetLength(data);
2505 while ((urlLen > 0) && (urlBytes[urlLen - 1] == 0)) {
2506 // remove trailing NUL
2507 urlLen--;
2508 }
2509
2510 if (urlLen <= 0) {
2511 return NULL;
2512 }
2513
2514 url = CFURLCreateWithBytes(NULL, urlBytes, urlLen, kCFStringEncodingUTF8, NULL);
2515 if (url != NULL) {
2516 urlString = CFURLGetString(url);
2517 if (urlString != NULL) {
2518 CFRetain(urlString);
2519 }
2520 CFRelease(url);
2521 }
2522 }
2523 }
2524
2525 return urlString;
2526 }
2527
2528 static CF_RETURNS_RETAINED CFStringRef
2529 wpadURL_dns(void)
2530 {
2531 CFURLRef url;
2532 CFStringRef urlString = NULL;
2533
2534 url = CFURLCreateWithString(NULL, CFSTR("http://wpad/wpad.dat"), NULL);
2535 if (url != NULL) {
2536 urlString = CFURLGetString(url);
2537 if (urlString != NULL) {
2538 CFRetain(urlString);
2539 }
2540 CFRelease(url);
2541 }
2542
2543 return urlString;
2544 }
2545
2546 static boolean_t
2547 get_proxies_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2548 CFDictionaryRef setup_dict, CFDictionaryRef info)
2549 {
2550 ProtocolFlags active_protos = kProtocolFlagsNone;
2551 boolean_t changed = FALSE;
2552 CFStringRef interface = NULL;
2553 CFDictionaryRef ipv4;
2554 CFDictionaryRef ipv6;
2555 CFMutableDictionaryRef new_dict = NULL;
2556 const struct {
2557 CFStringRef key;
2558 uint32_t flags;
2559 Boolean append;
2560 } merge_list[] = {
2561 { kSCPropNetProxiesSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE },
2562 { kSCPropNetProxiesSupplementalMatchOrders, 0, TRUE },
2563 };
2564 const struct {
2565 CFStringRef key1; /* an "enable" key */
2566 CFStringRef key2;
2567 CFStringRef key3;
2568 } pick_list[] = {
2569 { kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort },
2570 { kSCPropNetProxiesGopherEnable, kSCPropNetProxiesGopherProxy, kSCPropNetProxiesGopherPort },
2571 { kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort },
2572 { kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort },
2573 { kSCPropNetProxiesRTSPEnable, kSCPropNetProxiesRTSPProxy, kSCPropNetProxiesRTSPPort },
2574 { kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort },
2575 { kSCPropNetProxiesProxyAutoConfigEnable,
2576 kSCPropNetProxiesProxyAutoConfigURLString,
2577 kSCPropNetProxiesProxyAutoConfigJavaScript, },
2578 { kSCPropNetProxiesProxyAutoDiscoveryEnable,
2579 NULL,
2580 NULL, }
2581 };
2582 IPv4RouteListRef routes = NULL;
2583
2584 if ((state_dict == NULL) && (setup_dict == NULL)) {
2585 /* there is no proxy content */
2586 goto done;
2587 }
2588
2589 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
2590 routes = ipv4_dict_get_routelist(ipv4);
2591
2592 if (routes != NULL) {
2593 active_protos |= kProtocolFlagsIPv4;
2594
2595 interface = ipv4_dict_get_ifname(ipv4);
2596 }
2597
2598 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
2599 if (ipv6 != NULL) {
2600 active_protos |= kProtocolFlagsIPv6;
2601
2602 if (interface == NULL) {
2603 interface = CFDictionaryGetValue(ipv6,
2604 kSCPropInterfaceName);
2605 }
2606 }
2607
2608 if (active_protos == kProtocolFlagsNone) {
2609 /* there is no IPv4 nor IPv6 */
2610 if (state_dict == NULL) {
2611 /* ... and no proxy content that we care about */
2612 goto done;
2613 }
2614 setup_dict = NULL;
2615 }
2616
2617 if ((setup_dict != NULL) && (state_dict != NULL)) {
2618 CFIndex i;
2619 CFMutableDictionaryRef setup_copy;
2620
2621 /*
2622 * Merge the per-service "Setup:" and "State:" proxy information with
2623 * the "Setup:" information always taking precedence. Additionally,
2624 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
2625 * Port) is defined than all of the values for that group will be
2626 * used. That is, we don't allow mixing some of the values from
2627 * the "Setup:" keys and others from the "State:" keys.
2628 */
2629 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2630
2631 for (i = 0; i < sizeof(merge_list)/sizeof(merge_list[0]); i++) {
2632 merge_array_prop(new_dict,
2633 merge_list[i].key,
2634 state_dict,
2635 setup_dict,
2636 merge_list[i].flags,
2637 merge_list[i].append);
2638 }
2639
2640 setup_copy = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
2641 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2642 if (CFDictionaryContainsKey(setup_copy, pick_list[i].key1)) {
2643 /*
2644 * if a "Setup:" enabled key has been provided than we want to
2645 * ignore all of the "State:" keys
2646 */
2647 CFDictionaryRemoveValue(new_dict, pick_list[i].key1);
2648 if (pick_list[i].key2 != NULL) {
2649 CFDictionaryRemoveValue(new_dict, pick_list[i].key2);
2650 }
2651 if (pick_list[i].key3 != NULL) {
2652 CFDictionaryRemoveValue(new_dict, pick_list[i].key3);
2653 }
2654 } else if (CFDictionaryContainsKey(state_dict, pick_list[i].key1) ||
2655 ((pick_list[i].key2 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key2)) ||
2656 ((pick_list[i].key3 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key3))) {
2657 /*
2658 * if a "Setup:" enabled key has not been provided and we have
2659 * some" "State:" keys than we remove all of of "Setup:" keys
2660 */
2661 CFDictionaryRemoveValue(setup_copy, pick_list[i].key1);
2662 if (pick_list[i].key2 != NULL) {
2663 CFDictionaryRemoveValue(setup_copy, pick_list[i].key2);
2664 }
2665 if (pick_list[i].key3 != NULL) {
2666 CFDictionaryRemoveValue(setup_copy, pick_list[i].key3);
2667 }
2668 }
2669 }
2670
2671 /* merge the "Setup:" keys */
2672 CFDictionaryApplyFunction(setup_copy, merge_dict, new_dict);
2673 CFRelease(setup_copy);
2674 }
2675 else if (setup_dict != NULL) {
2676 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
2677 }
2678 else if (state_dict != NULL) {
2679 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2680 }
2681
2682 if ((new_dict != NULL) && (CFDictionaryGetCount(new_dict) == 0)) {
2683 CFRelease(new_dict);
2684 new_dict = NULL;
2685 }
2686
2687 if ((new_dict != NULL) && (interface != NULL)) {
2688 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
2689 }
2690
2691 /* process WPAD */
2692 if (new_dict != NULL) {
2693 CFDictionaryRef dhcp_options;
2694 CFNumberRef num;
2695 CFNumberRef wpad = NULL;
2696 int wpadEnabled = 0;
2697 CFStringRef wpadURL = NULL;
2698
2699 if (CFDictionaryGetValueIfPresent(new_dict,
2700 kSCPropNetProxiesProxyAutoDiscoveryEnable,
2701 (const void **)&num) &&
2702 isA_CFNumber(num)) {
2703 /* if we have a WPAD key */
2704 wpad = num;
2705 if (!CFNumberGetValue(num, kCFNumberIntType, &wpadEnabled)) {
2706 /* if we don't like the enabled key/value */
2707 wpadEnabled = 0;
2708 }
2709 }
2710
2711 if (wpadEnabled) {
2712 int pacEnabled = 0;
2713
2714 num = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigEnable);
2715 if (!isA_CFNumber(num) ||
2716 !CFNumberGetValue(num, kCFNumberIntType, &pacEnabled)) {
2717 /* if we don't like the enabled key/value */
2718 pacEnabled = 0;
2719 }
2720
2721 if (pacEnabled) {
2722 CFStringRef pacURL;
2723
2724 pacURL = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigURLString);
2725 if (pacURL != NULL) {
2726 if (!isA_CFString(pacURL)) {
2727 /* if we don't like the PAC URL */
2728 pacEnabled = 0;
2729 }
2730 } else {
2731 CFStringRef pacJS;
2732
2733 pacJS = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigJavaScript);
2734 if (!isA_CFString(pacJS)) {
2735 /* if we don't have (or like) the PAC JavaScript */
2736 pacEnabled = 0;
2737 }
2738 }
2739 }
2740
2741 if (pacEnabled) {
2742 /*
2743 * we already have a PAC URL so disable WPAD.
2744 */
2745 wpadEnabled = 0;
2746 goto setWPAD;
2747 }
2748
2749 /*
2750 * if WPAD is enabled and we don't already have a PAC URL then
2751 * we check for a DHCP provided URL. If not available, we use
2752 * a PAC URL pointing to a well-known file (wpad.dat) on a
2753 * well-known host (wpad.<domain>).
2754 */
2755 dhcp_options = get_service_state_entity(info, serviceID, kSCEntNetDHCP);
2756 wpadURL = wpadURL_dhcp(dhcp_options);
2757 if (wpadURL == NULL) {
2758 wpadURL = wpadURL_dns();
2759 }
2760 if (wpadURL == NULL) {
2761 wpadEnabled = 0; /* if we don't have a WPAD URL */
2762 goto setWPAD;
2763 }
2764
2765 pacEnabled = 1;
2766 num = CFNumberCreate(NULL, kCFNumberIntType, &pacEnabled);
2767 CFDictionarySetValue(new_dict,
2768 kSCPropNetProxiesProxyAutoConfigEnable,
2769 num);
2770 CFRelease(num);
2771 CFDictionarySetValue(new_dict,
2772 kSCPropNetProxiesProxyAutoConfigURLString,
2773 wpadURL);
2774 CFRelease(wpadURL);
2775 }
2776
2777 setWPAD:
2778 if (wpad != NULL) {
2779 num = CFNumberCreate(NULL, kCFNumberIntType, &wpadEnabled);
2780 CFDictionarySetValue(new_dict,
2781 kSCPropNetProxiesProxyAutoDiscoveryEnable,
2782 num);
2783 CFRelease(num);
2784 }
2785 }
2786
2787 done:
2788 changed = service_dict_set(serviceID, kSCEntNetProxies, new_dict);
2789 my_CFRelease(&new_dict);
2790 return (changed);
2791 }
2792
2793 #if !TARGET_OS_IPHONE
2794 static boolean_t
2795 get_smb_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2796 CFDictionaryRef setup_dict, CFDictionaryRef info)
2797 {
2798 boolean_t changed = FALSE;
2799 int i;
2800 CFMutableDictionaryRef new_dict = NULL;
2801 const CFStringRef pick_list[] = {
2802 kSCPropNetSMBNetBIOSName,
2803 kSCPropNetSMBNetBIOSNodeType,
2804 #ifdef ADD_NETBIOS_SCOPE
2805 kSCPropNetSMBNetBIOSScope,
2806 #endif // ADD_NETBIOS_SCOPE
2807 kSCPropNetSMBWorkgroup,
2808 };
2809
2810 if (service_dict_get(serviceID, kSCEntNetIPv4) == NULL) {
2811 /* there is no IPv4 */
2812 goto done;
2813 }
2814
2815 if (state_dict == NULL && setup_dict == NULL) {
2816 /* there is no SMB */
2817 goto done;
2818 }
2819
2820 /* merge SMB configuration */
2821 new_dict = CFDictionaryCreateMutable(NULL, 0,
2822 &kCFTypeDictionaryKeyCallBacks,
2823 &kCFTypeDictionaryValueCallBacks);
2824
2825 merge_array_prop(new_dict,
2826 kSCPropNetSMBWINSAddresses,
2827 state_dict,
2828 setup_dict,
2829 0,
2830 FALSE);
2831 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2832 pick_prop(new_dict,
2833 pick_list[i],
2834 state_dict,
2835 setup_dict,
2836 0);
2837 }
2838
2839 if (CFDictionaryGetCount(new_dict) == 0) {
2840 my_CFRelease(&new_dict);
2841 goto done;
2842 }
2843
2844 done:
2845 changed = service_dict_set(serviceID, kSCEntNetSMB, new_dict);
2846 my_CFRelease(&new_dict);
2847 return (changed);
2848 }
2849 #endif /* !TARGET_OS_IPHONE */
2850
2851 static CFStringRef
2852 services_info_get_interface(CFDictionaryRef services_info,
2853 CFStringRef serviceID)
2854 {
2855 CFStringRef interface = NULL;
2856 CFDictionaryRef ipv4_dict;
2857
2858 ipv4_dict = get_service_state_entity(services_info, serviceID,
2859 kSCEntNetIPv4);
2860 if (isA_CFDictionary(ipv4_dict) != NULL) {
2861 interface = CFDictionaryGetValue(ipv4_dict, kSCPropInterfaceName);
2862 }
2863 else {
2864 CFDictionaryRef ipv6_dict;
2865
2866 ipv6_dict = get_service_state_entity(services_info, serviceID,
2867 kSCEntNetIPv6);
2868 if (isA_CFDictionary(ipv6_dict) != NULL) {
2869 interface = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
2870 }
2871 }
2872 return (interface);
2873 }
2874
2875
2876
2877 static const CFStringRef *statusEntityNames[] = {
2878 &kSCEntNetIPSec,
2879 &kSCEntNetPPP,
2880 &kSCEntNetVPN,
2881 };
2882
2883 static Boolean
2884 get_transient_service_changes(CFStringRef serviceID, CFDictionaryRef services_info)
2885 {
2886 boolean_t changed = FALSE;
2887 int i;
2888
2889 static const struct {
2890 const CFStringRef *entityName;
2891 const CFStringRef *statusKey;
2892 } transientServiceInfo[] = {
2893 { &kSCEntNetIPSec, &kSCPropNetIPSecStatus },
2894 { &kSCEntNetPPP, &kSCPropNetPPPStatus },
2895 { &kSCEntNetVPN, &kSCPropNetVPNStatus },
2896 };
2897
2898 for (i = 0; i < sizeof(transientServiceInfo)/sizeof(transientServiceInfo[0]); i++) {
2899 CFDictionaryRef dict;
2900 CFNumberRef status = NULL;
2901 CFMutableDictionaryRef ts_dict = NULL;
2902
2903 dict = get_service_state_entity(services_info, serviceID, *transientServiceInfo[i].entityName);
2904
2905 if (isA_CFDictionary(dict) != NULL) {
2906 status = CFDictionaryGetValue(dict, *transientServiceInfo[i].statusKey);
2907 }
2908
2909 if (isA_CFNumber(status) != NULL) {
2910 ts_dict = CFDictionaryCreateMutable(NULL,
2911 0,
2912 &kCFTypeDictionaryKeyCallBacks,
2913 &kCFTypeDictionaryValueCallBacks);
2914 CFDictionaryAddValue(ts_dict,
2915 *transientServiceInfo[i].statusKey,
2916 status);
2917 }
2918
2919 if (service_dict_set(serviceID, *transientServiceInfo[i].entityName, ts_dict)) {
2920 changed = TRUE;
2921 }
2922
2923 if (ts_dict != NULL) {
2924 CFRelease(ts_dict);
2925 }
2926 }
2927 return (changed);
2928 }
2929
2930 static boolean_t
2931 get_rank_changes(CFStringRef serviceID, CFDictionaryRef state_options,
2932 CFDictionaryRef setup_options, CFDictionaryRef services_info)
2933 {
2934 boolean_t changed = FALSE;
2935 CFBooleanRef ip_is_coupled = NULL;
2936 CFMutableDictionaryRef new_dict = NULL;
2937 CFStringRef new_rank = NULL;
2938 CFStringRef setup_rank = NULL;
2939 CFStringRef state_rank = NULL;
2940
2941
2942 /*
2943 * Check "PrimaryRank" setting
2944 *
2945 * Note 1: Rank setting in setup/state option overwrites the
2946 * Rank setting in interface
2947 * Within each rank setting, the following precedence is defined:
2948 *
2949 * Note 2: Rank Never > Rank Last > Rank First > Rank None
2950 */
2951 if (isA_CFDictionary(state_options)) {
2952 CFBooleanRef coupled;
2953
2954 state_rank
2955 = CFDictionaryGetValue(state_options, kSCPropNetServicePrimaryRank);
2956 state_rank = isA_CFString(state_rank);
2957 coupled = CFDictionaryGetValue(state_options, kIPIsCoupled);
2958 if (isA_CFBoolean(coupled) != NULL) {
2959 ip_is_coupled = coupled;
2960 }
2961 }
2962 if (isA_CFDictionary(setup_options)) {
2963 CFBooleanRef coupled;
2964
2965 setup_rank
2966 = CFDictionaryGetValue(setup_options, kSCPropNetServicePrimaryRank);
2967 setup_rank = isA_CFString(setup_rank);
2968
2969 coupled = CFDictionaryGetValue(setup_options, kIPIsCoupled);
2970 if (isA_CFBoolean(coupled) != NULL) {
2971 ip_is_coupled = coupled;
2972 }
2973 }
2974
2975 if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankNever)) ||
2976 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankNever))) {
2977 new_rank = kSCValNetServicePrimaryRankNever;
2978 }
2979 else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankLast)) ||
2980 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankLast))) {
2981 new_rank = kSCValNetServicePrimaryRankLast;
2982 }
2983 else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankFirst)) ||
2984 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankFirst))) {
2985 new_rank = kSCValNetServicePrimaryRankFirst;
2986 }
2987
2988 /* This corresponds to Note 1 */
2989 if (setup_rank == NULL && state_rank == NULL) {
2990 /* Fetch the interface associated with the service */
2991 CFStringRef interface;
2992
2993 interface = services_info_get_interface(services_info, serviceID);
2994
2995 /* Get the rank on that interface */
2996 if (interface != NULL) {
2997 new_rank = CFDictionaryGetValue(S_if_rank_dict, interface);
2998 if (S_IPMonitor_debug & kDebugFlag1) {
2999 my_log(LOG_DEBUG,
3000 "serviceID %@ interface %@ rank = %@",
3001 serviceID, interface,
3002 (new_rank != NULL) ? new_rank : CFSTR("<none>"));
3003 }
3004 }
3005 }
3006
3007
3008 if (ip_is_coupled != NULL && CFBooleanGetValue(ip_is_coupled) == FALSE) {
3009 /* don't bother setting a value if it's the default */
3010 ip_is_coupled = NULL;
3011 }
3012 if (new_rank != NULL || ip_is_coupled != NULL) {
3013 new_dict = CFDictionaryCreateMutable(NULL, 0,
3014 &kCFTypeDictionaryKeyCallBacks,
3015 &kCFTypeDictionaryValueCallBacks);
3016 if (new_rank != NULL) {
3017 CFDictionarySetValue(new_dict, kSCPropNetServicePrimaryRank,
3018 new_rank);
3019 }
3020 if (ip_is_coupled != NULL) {
3021 CFDictionarySetValue(new_dict, kIPIsCoupled, kCFBooleanTrue);
3022 }
3023 }
3024 changed = service_dict_set(serviceID, kSCEntNetService, new_dict);
3025 my_CFRelease(&new_dict);
3026 return (changed);
3027 }
3028
3029 static CFStringRef
3030 if_rank_key_copy(CFStringRef ifname)
3031 {
3032 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
3033 kSCDynamicStoreDomainState,
3034 ifname,
3035 kSCEntNetService));
3036 }
3037
3038 static void
3039 if_rank_set(CFStringRef ifname, CFDictionaryRef rank_dict)
3040 {
3041 CFStringRef rank = NULL;
3042
3043 if (isA_CFDictionary(rank_dict) != NULL) {
3044 rank = CFDictionaryGetValue(rank_dict, kSCPropNetServicePrimaryRank);
3045 rank = isA_CFString(rank);
3046 }
3047
3048 /* specific rank is asserted */
3049 if (rank != NULL) {
3050 if (S_IPMonitor_debug & kDebugFlag1) {
3051 my_log(LOG_DEBUG, "Interface %@ asserted rank %@",
3052 ifname, rank);
3053 }
3054 CFDictionarySetValue(S_if_rank_dict, ifname, rank);
3055 } else {
3056 if (S_IPMonitor_debug & kDebugFlag1) {
3057 my_log(LOG_DEBUG, "Interface %@ removed rank",
3058 ifname);
3059 }
3060 CFDictionaryRemoveValue(S_if_rank_dict, ifname);
3061 }
3062 return;
3063 }
3064
3065 static void
3066 if_rank_apply(const void * key, const void * value, void * context)
3067 {
3068 CFStringRef ifname;
3069 CFDictionaryRef rank_dict = (CFDictionaryRef)value;
3070
3071 /* State:/Network/Interface/<ifname>/Service, <ifname> is at index 3 */
3072 ifname = my_CFStringCopyComponent(key, CFSTR("/"), 3);
3073 if (ifname != NULL) {
3074 if_rank_set(ifname, rank_dict);
3075 CFRelease(ifname);
3076 }
3077 return;
3078 }
3079
3080 static void
3081 if_rank_dict_init(void)
3082 {
3083 CFDictionaryRef info;
3084 CFStringRef pattern;
3085 CFArrayRef patterns;
3086
3087 S_if_rank_dict
3088 = CFDictionaryCreateMutable(NULL, 0,
3089 &kCFTypeDictionaryKeyCallBacks,
3090 &kCFTypeDictionaryValueCallBacks);
3091 pattern = if_rank_key_copy(kSCCompAnyRegex);
3092 patterns = CFArrayCreate(NULL,
3093 (const void **)&pattern, 1,
3094 &kCFTypeArrayCallBacks);
3095 CFRelease(pattern);
3096 info = SCDynamicStoreCopyMultiple(S_session, NULL, patterns);
3097 CFRelease(patterns);
3098 if (info != NULL) {
3099 CFDictionaryApplyFunction(info, if_rank_apply, NULL);
3100 CFRelease(info);
3101 }
3102 return;
3103
3104 }
3105
3106 static void
3107 add_service_keys(CFStringRef serviceID, CFMutableArrayRef keys, CFMutableArrayRef patterns)
3108 {
3109 int i;
3110 CFStringRef key;
3111
3112 if (CFEqual(serviceID, kSCCompAnyRegex)) {
3113 keys = patterns;
3114 }
3115
3116 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
3117 key = setup_service_key(serviceID, *entityTypeNames[i]);
3118 CFArrayAppendValue(keys, key);
3119 CFRelease(key);
3120 key = state_service_key(serviceID, *entityTypeNames[i]);
3121 CFArrayAppendValue(keys, key);
3122 CFRelease(key);
3123 }
3124
3125 key = state_service_key(serviceID, kSCEntNetDHCP);
3126 CFArrayAppendValue(patterns, key);
3127 CFRelease(key);
3128
3129 key = setup_service_key(serviceID, NULL);
3130 CFArrayAppendValue(patterns, key);
3131 CFRelease(key);
3132 key = state_service_key(serviceID, NULL);
3133 CFArrayAppendValue(patterns, key);
3134 CFRelease(key);
3135
3136 return;
3137 }
3138
3139 static void
3140 add_status_keys(CFStringRef service_id, CFMutableArrayRef patterns)
3141 {
3142 int i;
3143
3144 for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) {
3145 CFStringRef pattern;
3146
3147 pattern = state_service_key(service_id, *statusEntityNames[i]);
3148 CFArrayAppendValue(patterns, pattern);
3149 CFRelease(pattern);
3150 }
3151
3152 return;
3153 }
3154
3155 static const CFStringRef *reachabilitySetupKeys[] = {
3156 &kSCEntNetPPP,
3157 &kSCEntNetInterface,
3158 &kSCEntNetIPv4,
3159 &kSCEntNetIPv6,
3160 };
3161
3162
3163 static void
3164 add_reachability_keys(CFMutableArrayRef patterns)
3165 {
3166 int i;
3167
3168 for (i = 0; i < sizeof(reachabilitySetupKeys)/(sizeof(reachabilitySetupKeys[0])); i++)
3169 {
3170 CFStringRef pattern;
3171 pattern = setup_service_key(kSCCompAnyRegex, *reachabilitySetupKeys[i]);
3172 CFArrayAppendValue(patterns, pattern);
3173 CFRelease(pattern);
3174 }
3175 }
3176
3177
3178 static void
3179 add_vpn_keys(CFMutableArrayRef patterns)
3180 {
3181 CFStringRef pattern;
3182
3183 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
3184 CFArrayAppendValue(patterns, pattern);
3185 CFRelease(pattern);
3186 }
3187
3188
3189 static CFDictionaryRef
3190 services_info_copy(SCDynamicStoreRef session, CFArrayRef service_list,
3191 CFArrayRef if_rank_list)
3192 {
3193 int count;
3194 CFMutableArrayRef get_keys;
3195 CFMutableArrayRef get_patterns;
3196 int if_count;
3197 CFDictionaryRef info;
3198 int s;
3199
3200 count = CFArrayGetCount(service_list);
3201 get_keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3202 get_patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3203
3204 CFArrayAppendValue(get_keys, S_setup_global_ipv4);
3205 CFArrayAppendValue(get_keys, S_multicast_resolvers);
3206 CFArrayAppendValue(get_keys, S_private_resolvers);
3207
3208 for (s = 0; s < count; s++) {
3209 CFStringRef serviceID = CFArrayGetValueAtIndex(service_list, s);
3210
3211 add_service_keys(serviceID, get_keys, get_patterns);
3212 add_status_keys(serviceID, get_keys);
3213 }
3214
3215 add_reachability_keys(get_patterns);
3216
3217 add_vpn_keys(get_patterns);
3218
3219 if_count = (if_rank_list != NULL)
3220 ? CFArrayGetCount(if_rank_list) : 0;
3221 for (s = 0; s < if_count; s++) {
3222 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_list, s);
3223 CFStringRef key;
3224
3225 key = if_rank_key_copy(ifname);
3226 CFArrayAppendValue(get_keys, key);
3227 CFRelease(key);
3228 }
3229
3230 info = SCDynamicStoreCopyMultiple(session, get_keys, get_patterns);
3231 my_CFRelease(&get_keys);
3232 my_CFRelease(&get_patterns);
3233 return (info);
3234 }
3235
3236 #if !TARGET_IPHONE_SIMULATOR
3237 static int rtm_seq = 0;
3238 #endif /* !TARGET_IPHONE_SIMULATOR */
3239
3240 #if !TARGET_IPHONE_SIMULATOR
3241 static int
3242 route_open_socket(void)
3243 {
3244 int sockfd;
3245
3246 if ((sockfd = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE)) == -1) {
3247 my_log(LOG_NOTICE,
3248 "IPMonitor: route_open_socket: socket failed, %s",
3249 strerror(errno));
3250 }
3251 return (sockfd);
3252 }
3253 #endif /* !TARGET_IPHONE_SIMULATOR */
3254
3255 /*
3256 * Define: ROUTE_MSG_ADDRS_SPACE
3257 * Purpose:
3258 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
3259 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
3260 * someone changes the code and doesn't think to modify this.
3261 */
3262 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
3263 + 2 * sizeof(struct sockaddr_dl) \
3264 + 128)
3265 typedef struct {
3266 struct rt_msghdr hdr;
3267 char addrs[ROUTE_MSG_ADDRS_SPACE];
3268 } route_msg;
3269
3270 #if !TARGET_IPHONE_SIMULATOR
3271 static int
3272 ipv4_route(int sockfd,
3273 int cmd, struct in_addr gateway, struct in_addr netaddr,
3274 struct in_addr netmask, char * ifname, unsigned int ifindex,
3275 struct in_addr ifa, RouteFlags flags)
3276 {
3277 boolean_t default_route = (netaddr.s_addr == 0);
3278 int len;
3279 int ret = 0;
3280 route_msg rtmsg;
3281 union {
3282 struct sockaddr_in * in_p;
3283 struct sockaddr_dl * dl_p;
3284 void * ptr;
3285 } rtaddr;
3286
3287 if (default_route && S_netboot) {
3288 return (0);
3289 }
3290
3291 if (ifname == NULL) {
3292 /* this should not happen, but rather than crash, return an error */
3293 my_log(LOG_NOTICE,
3294 "IPMonitor: ipv4_route ifname is NULL on network address %s",
3295 inet_ntoa(netaddr));
3296 return (EBADF);
3297 }
3298 if ((flags & kRouteIsNULLFlag) != 0) {
3299 my_log(LOG_DEBUG, "IPMonitor: ignoring route %s on %s",
3300 inet_ntoa(netaddr), ifname);
3301 return (0);
3302 }
3303 memset(&rtmsg, 0, sizeof(rtmsg));
3304 rtmsg.hdr.rtm_type = cmd;
3305 rtmsg.hdr.rtm_version = RTM_VERSION;
3306 rtmsg.hdr.rtm_seq = ++rtm_seq;
3307 rtmsg.hdr.rtm_addrs
3308 = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP | RTA_IFA;
3309 if (default_route
3310 && (flags & kRouteIsDirectToInterfaceFlag) == 0) {
3311 rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
3312 }
3313 else {
3314 rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC;
3315 }
3316 if ((flags & kRouteIsScopedFlag) != 0) {
3317 #ifdef RTF_IFSCOPE
3318 if (!S_scopedroute) {
3319 return (0);
3320 }
3321 if (ifindex == 0) {
3322 /* specifically asked for a scoped route, yet no index supplied */
3323 my_log(LOG_NOTICE,
3324 "IPMonitor: ipv4_route index is 0 on %s-scoped route %s",
3325 ifname, inet_ntoa(netaddr));
3326 return (EBADF);
3327 }
3328 rtmsg.hdr.rtm_index = ifindex;
3329 rtmsg.hdr.rtm_flags |= RTF_IFSCOPE;
3330 #else /* RTF_IFSCOPE */
3331 return (0);
3332 #endif /* RTF_IFSCOPE */
3333 }
3334
3335 rtaddr.ptr = rtmsg.addrs;
3336
3337 /* dest */
3338 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3339 rtaddr.in_p->sin_family = AF_INET;
3340 rtaddr.in_p->sin_addr = netaddr;
3341 rtaddr.ptr += sizeof(*rtaddr.in_p);
3342
3343 /* gateway */
3344 if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) {
3345 /* gateway is an IP address */
3346 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3347 rtaddr.in_p->sin_family = AF_INET;
3348 rtaddr.in_p->sin_addr = gateway;
3349 rtaddr.ptr += sizeof(*rtaddr.in_p);
3350 }
3351 else {
3352 /* gateway is the interface itself */
3353 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3354 rtaddr.dl_p->sdl_family = AF_LINK;
3355 rtaddr.dl_p->sdl_nlen = strlen(ifname);
3356 bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen);
3357 rtaddr.ptr += sizeof(*rtaddr.dl_p);
3358 }
3359
3360 /* mask */
3361 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3362 rtaddr.in_p->sin_family = AF_INET;
3363 rtaddr.in_p->sin_addr = netmask;
3364 rtaddr.ptr += sizeof(*rtaddr.in_p);
3365
3366 /* interface name */
3367 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3368 rtaddr.dl_p->sdl_family = AF_LINK;
3369 rtaddr.dl_p->sdl_nlen = strlen(ifname);
3370 bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen);
3371 rtaddr.ptr += sizeof(*rtaddr.dl_p);
3372
3373 /* interface address */
3374 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3375 rtaddr.in_p->sin_family = AF_INET;
3376 rtaddr.in_p->sin_addr = ifa;
3377 rtaddr.ptr += sizeof(*rtaddr.in_p);
3378
3379 len = sizeof(rtmsg.hdr) + (rtaddr.ptr - (void *)rtmsg.addrs);
3380 rtmsg.hdr.rtm_msglen = len;
3381 if (write(sockfd, &rtmsg, len) == -1) {
3382 ret = errno;
3383 }
3384 return (ret);
3385 }
3386 #endif /* !TARGET_IPHONE_SIMULATOR */
3387
3388 #if !TARGET_IPHONE_SIMULATOR
3389 static boolean_t
3390 ipv6_route(int cmd, struct in6_addr gateway, struct in6_addr netaddr,
3391 struct in6_addr netmask, char * ifname, boolean_t is_direct)
3392 {
3393 boolean_t default_route;
3394 int len;
3395 boolean_t ret = TRUE;
3396 struct {
3397 struct rt_msghdr hdr;
3398 struct sockaddr_in6 dst;
3399 struct sockaddr_in6 gway;
3400 struct sockaddr_in6 mask;
3401 struct sockaddr_dl ifp;
3402 } rtmsg;
3403 int sockfd = -1;
3404 struct in6_addr zeroes = IN6ADDR_ANY_INIT;
3405
3406 default_route = (bcmp(&zeroes, &netaddr, sizeof(netaddr)) == 0);
3407
3408 if ((IN6_IS_ADDR_LINKLOCAL(&gateway) ||
3409 IN6_IS_ADDR_MC_LINKLOCAL(&gateway)) &&
3410 (ifname != NULL)) {
3411 unsigned int index = if_nametoindex(ifname);
3412
3413 /* add the scope id to the link local address */
3414 gateway.__u6_addr.__u6_addr16[1] = (uint16_t)htons(index);
3415 }
3416 sockfd = route_open_socket();
3417 if (sockfd == -1) {
3418 return (FALSE);
3419 }
3420 memset(&rtmsg, 0, sizeof(rtmsg));
3421 rtmsg.hdr.rtm_type = cmd;
3422 if (default_route) {
3423 if (is_direct) {
3424 /* if router is directly reachable, don't set the gateway flag */
3425 rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC;
3426 }
3427 else {
3428 rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
3429 }
3430 }
3431 else {
3432 rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC;
3433 }
3434 rtmsg.hdr.rtm_version = RTM_VERSION;
3435 rtmsg.hdr.rtm_seq = ++rtm_seq;
3436 rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
3437 rtmsg.dst.sin6_len = sizeof(rtmsg.dst);
3438 rtmsg.dst.sin6_family = AF_INET6;
3439 rtmsg.dst.sin6_addr = netaddr;
3440 rtmsg.gway.sin6_len = sizeof(rtmsg.gway);
3441 rtmsg.gway.sin6_family = AF_INET6;
3442 rtmsg.gway.sin6_addr = gateway;
3443 rtmsg.mask.sin6_len = sizeof(rtmsg.mask);
3444 rtmsg.mask.sin6_family = AF_INET6;
3445 rtmsg.mask.sin6_addr = netmask;
3446
3447 len = sizeof(rtmsg);
3448 if (ifname) {
3449 rtmsg.ifp.sdl_len = sizeof(rtmsg.ifp);
3450 rtmsg.ifp.sdl_family = AF_LINK;
3451 rtmsg.ifp.sdl_nlen = strlen(ifname);
3452 rtmsg.hdr.rtm_addrs |= RTA_IFP;
3453 bcopy(ifname, rtmsg.ifp.sdl_data, rtmsg.ifp.sdl_nlen);
3454 }
3455 else {
3456 /* no ifp information */
3457 len -= sizeof(rtmsg.ifp);
3458 }
3459 rtmsg.hdr.rtm_msglen = len;
3460 if (write(sockfd, &rtmsg, len) == -1) {
3461 if ((cmd == RTM_ADD) && (errno == EEXIST)) {
3462 /* no sense complaining about a route that already exists */
3463 }
3464 else if ((cmd == RTM_DELETE) && (errno == ESRCH)) {
3465 /* no sense complaining about a route that isn't there */
3466 }
3467 else {
3468 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3469 my_log(LOG_DEBUG,
3470 "IPMonitor ipv6_route: write routing"
3471 " socket failed, %s", strerror(errno));
3472 }
3473 ret = FALSE;
3474 }
3475 }
3476
3477 close(sockfd);
3478 return (ret);
3479 }
3480
3481 static boolean_t
3482 ipv6_default_route_delete(void)
3483 {
3484 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3485 my_log(LOG_DEBUG, "IPMonitor: IPv6 route delete default");
3486 }
3487 return (ipv6_route(RTM_DELETE, S_ip6_zeros, S_ip6_zeros, S_ip6_zeros,
3488 NULL, FALSE));
3489 }
3490
3491 static boolean_t
3492 ipv6_default_route_add(struct in6_addr router, char * ifname,
3493 boolean_t is_direct)
3494 {
3495 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3496 char ntopbuf[INET6_ADDRSTRLEN];
3497
3498 my_log(LOG_DEBUG,
3499 "IPMonitor: IPv6 route add default"
3500 " %s interface %s direct %d",
3501 inet_ntop(AF_INET6, &router, ntopbuf, sizeof(ntopbuf)),
3502 ifname, is_direct);
3503 }
3504 return (ipv6_route(RTM_ADD, router, S_ip6_zeros, S_ip6_zeros,
3505 ifname, is_direct));
3506 }
3507 #endif /* !TARGET_IPHONE_SIMULATOR */
3508
3509
3510 #if !TARGET_IPHONE_SIMULATOR
3511 static int
3512 multicast_route_delete(int sockfd)
3513 {
3514 struct in_addr gateway = { htonl(INADDR_LOOPBACK) };
3515 struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) };
3516 struct in_addr netmask = { htonl(IN_CLASSD_NET) };
3517
3518 return (ipv4_route(sockfd, RTM_DELETE, gateway, netaddr, netmask, "lo0", 0,
3519 gateway, 0));
3520 }
3521
3522 static int
3523 multicast_route_add(int sockfd)
3524 {
3525 struct in_addr gateway = { htonl(INADDR_LOOPBACK) };
3526 struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) };
3527 struct in_addr netmask = { htonl(IN_CLASSD_NET) };
3528
3529 return (ipv4_route(sockfd, RTM_ADD, gateway, netaddr, netmask, "lo0", 0,
3530 gateway, 0));
3531 }
3532 #endif /* !TARGET_IPHONE_SIMULATOR */
3533
3534 #if !TARGET_IPHONE_SIMULATOR
3535 #ifdef RTF_IFSCOPE
3536 static void
3537 set_ipv6_default_interface(char * ifname)
3538 {
3539 struct in6_ndifreq ndifreq;
3540 int sock;
3541
3542 bzero((char *)&ndifreq, sizeof(ndifreq));
3543 if (ifname != NULL) {
3544 strlcpy(ndifreq.ifname, ifname, sizeof(ndifreq.ifname));
3545 ndifreq.ifindex = if_nametoindex(ifname);
3546 } else {
3547 strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname));
3548 ndifreq.ifindex = 0;
3549 }
3550
3551 sock = inet6_dgram_socket();
3552 if (sock == -1) {
3553 my_log(LOG_ERR,
3554 "IPMonitor: set_ipv6_default_interface: socket failed, %s",
3555 strerror(errno));
3556 return;
3557 }
3558 if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) == -1) {
3559 my_log(LOG_ERR,
3560 "IPMonitor: set_ipv6_default_interface: ioctl(SIOCSDEFIFACE_IN6) failed, %s",
3561 strerror(errno));
3562 }
3563 close(sock);
3564 return;
3565 }
3566 #endif /* RTF_IFSCOPE */
3567
3568 static void
3569 set_ipv6_router(struct in6_addr * router, char * ifname, boolean_t is_direct)
3570 {
3571 /* assign the new default route, ensure local multicast route available */
3572 (void)ipv6_default_route_delete();
3573 if (router != NULL) {
3574 (void)ipv6_default_route_add(*router, ifname, is_direct);
3575 }
3576 return;
3577 }
3578 #endif /* !TARGET_IPHONE_SIMULATOR */
3579
3580 #if !TARGET_OS_IPHONE
3581 static __inline__ void
3582 empty_dns()
3583 {
3584 (void)unlink(VAR_RUN_RESOLV_CONF);
3585 }
3586
3587 static void
3588 set_dns(CFArrayRef val_search_domains,
3589 CFStringRef val_domain_name,
3590 CFArrayRef val_servers,
3591 CFArrayRef val_sortlist)
3592 {
3593 FILE * f = fopen(VAR_RUN_RESOLV_CONF "-", "w");
3594
3595 /* publish new resolv.conf */
3596 if (f) {
3597 CFIndex i;
3598 CFIndex n;
3599
3600 SCPrint(TRUE, f, CFSTR("#\n"));
3601 SCPrint(TRUE, f, CFSTR("# Mac OS X Notice\n"));
3602 SCPrint(TRUE, f, CFSTR("#\n"));
3603 SCPrint(TRUE, f, CFSTR("# This file is not used by the host name and address resolution\n"));
3604 SCPrint(TRUE, f, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
3605 SCPrint(TRUE, f, CFSTR("# this Mac OS X system.\n"));
3606 SCPrint(TRUE, f, CFSTR("#\n"));
3607 SCPrint(TRUE, f, CFSTR("# This file is automatically generated.\n"));
3608 SCPrint(TRUE, f, CFSTR("#\n"));
3609
3610 if (isA_CFArray(val_search_domains)) {
3611 SCPrint(TRUE, f, CFSTR("search"));
3612 n = CFArrayGetCount(val_search_domains);
3613 for (i = 0; i < n; i++) {
3614 CFStringRef domain;
3615
3616 domain = CFArrayGetValueAtIndex(val_search_domains, i);
3617 if (isA_CFString(domain)) {
3618 SCPrint(TRUE, f, CFSTR(" %@"), domain);
3619 }
3620 }
3621 SCPrint(TRUE, f, CFSTR("\n"));
3622 }
3623 else if (isA_CFString(val_domain_name)) {
3624 SCPrint(TRUE, f, CFSTR("domain %@\n"), val_domain_name);
3625 }
3626
3627 if (isA_CFArray(val_servers)) {
3628 n = CFArrayGetCount(val_servers);
3629 for (i = 0; i < n; i++) {
3630 CFStringRef nameserver;
3631
3632 nameserver = CFArrayGetValueAtIndex(val_servers, i);
3633 if (isA_CFString(nameserver)) {
3634 SCPrint(TRUE, f, CFSTR("nameserver %@\n"), nameserver);
3635 }
3636 }
3637 }
3638
3639 if (isA_CFArray(val_sortlist)) {
3640 SCPrint(TRUE, f, CFSTR("sortlist"));
3641 n = CFArrayGetCount(val_sortlist);
3642 for (i = 0; i < n; i++) {
3643 CFStringRef address;
3644
3645 address = CFArrayGetValueAtIndex(val_sortlist, i);
3646 if (isA_CFString(address)) {
3647 SCPrint(TRUE, f, CFSTR(" %@"), address);
3648 }
3649 }
3650 SCPrint(TRUE, f, CFSTR("\n"));
3651 }
3652
3653 fclose(f);
3654 rename(VAR_RUN_RESOLV_CONF "-", VAR_RUN_RESOLV_CONF);
3655 }
3656 return;
3657 }
3658 #endif /* !TARGET_OS_IPHONE */
3659
3660 #if !TARGET_IPHONE_SIMULATOR
3661 static boolean_t
3662 router_is_our_ipv6_address(CFStringRef router, CFArrayRef addr_list)
3663 {
3664 CFIndex i;
3665 CFIndex n = CFArrayGetCount(addr_list);
3666 struct in6_addr r;
3667
3668 (void)cfstring_to_ip6(router, &r);
3669 for (i = 0; i < n; i++) {
3670 struct in6_addr ip;
3671
3672 if (cfstring_to_ip6(CFArrayGetValueAtIndex(addr_list, i), &ip)
3673 && bcmp(&r, &ip, sizeof(r)) == 0) {
3674 return (TRUE);
3675 }
3676 }
3677 return (FALSE);
3678 }
3679 #endif /* !TARGET_IPHONE_SIMULATOR */
3680
3681 static IPv4RouteListRef
3682 service_dict_get_ipv4_routelist(CFDictionaryRef service_dict)
3683 {
3684 CFDictionaryRef dict;
3685
3686 dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
3687
3688 return (ipv4_dict_get_routelist(dict));
3689 }
3690
3691 static CFStringRef
3692 service_dict_get_ipv4_ifname(CFDictionaryRef service_dict)
3693 {
3694 CFDictionaryRef dict;
3695
3696 dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
3697 return (ipv4_dict_get_ifname(dict));
3698 }
3699
3700 static boolean_t
3701 service_get_ip_is_coupled(CFStringRef serviceID)
3702 {
3703 CFDictionaryRef dict;
3704 boolean_t ip_is_coupled = FALSE;
3705
3706 dict = service_dict_get(serviceID, kSCEntNetService);
3707 if (dict != NULL) {
3708 if (CFDictionaryContainsKey(dict, kIPIsCoupled)) {
3709 ip_is_coupled = TRUE;
3710 }
3711 }
3712 return (ip_is_coupled);
3713 }
3714
3715 #if !TARGET_IPHONE_SIMULATOR
3716
3717 typedef struct apply_ipv4_route_context {
3718 IPv4RouteListRef old;
3719 IPv4RouteListRef new;
3720 int sockfd;
3721 } apply_ipv4_route_context_t;
3722
3723 /* add/remove a router/32 subnet */
3724 static int
3725 ipv4_route_gateway(int sockfd, int cmd, char * ifn_p,
3726 IPv4RouteRef def_route)
3727 {
3728 struct in_addr mask;
3729
3730 mask.s_addr = htonl(INADDR_BROADCAST);
3731 return (ipv4_route(sockfd, cmd, def_route->ifa,
3732 def_route->gateway, mask, ifn_p, def_route->ifindex,
3733 def_route->ifa, def_route->flags));
3734 }
3735
3736 /*
3737 * Function: apply_ipv4_route
3738 * Purpose:
3739 * Callback function that adds/removes the specified route.
3740 */
3741 static void
3742 apply_ipv4_route(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg)
3743 {
3744 apply_ipv4_route_context_t *context = (apply_ipv4_route_context_t *)arg;
3745 char * ifn_p;
3746 int retval;
3747
3748 ifn_p = route->ifname;
3749 switch (cmd) {
3750 case kIPv4RouteListAddRouteCommand:
3751 if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) {
3752 retval = ipv4_route_gateway(context->sockfd, RTM_ADD,
3753 ifn_p, route);
3754 if (retval == EEXIST) {
3755 /* delete and add again */
3756 (void)ipv4_route_gateway(context->sockfd, RTM_DELETE,
3757 ifn_p, route);
3758 retval = ipv4_route_gateway(context->sockfd, RTM_ADD,
3759 ifn_p, route);
3760 }
3761 if (retval != 0) {
3762 my_log(LOG_NOTICE,
3763 "IPMonitor apply_ipv4_route failed to add"
3764 " %s/32 route, %s",
3765 inet_ntoa(route->gateway), strerror(retval));
3766 }
3767 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3768 my_log(LOG_DEBUG, "Added IPv4 Route %s/32",
3769 inet_ntoa(route->gateway));
3770 }
3771 }
3772 retval = ipv4_route(context->sockfd,
3773 RTM_ADD, route->gateway,
3774 route->dest, route->mask, ifn_p, route->ifindex,
3775 route->ifa, route->flags);
3776 if (retval == EEXIST) {
3777 /* delete and add again */
3778 (void)ipv4_route(context->sockfd,
3779 RTM_DELETE, route->gateway,
3780 route->dest, route->mask, ifn_p, route->ifindex,
3781 route->ifa, route->flags);
3782 retval = ipv4_route(context->sockfd,
3783 RTM_ADD, route->gateway,
3784 route->dest, route->mask,
3785 ifn_p, route->ifindex,
3786 route->ifa, route->flags);
3787 }
3788 if (retval != 0) {
3789 my_log(LOG_NOTICE,
3790 "IPMonitor apply_ipv4_route failed to add"
3791 " route, %s:", strerror(retval));
3792 IPv4RouteLog(LOG_NOTICE, route);
3793 }
3794 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3795 my_log(LOG_DEBUG,
3796 "Added IPv4 route new[%d] = ",
3797 route - context->new->list);
3798 IPv4RouteLog(LOG_DEBUG, route);
3799 }
3800 break;
3801 case kIPv4RouteListRemoveRouteCommand:
3802 retval = ipv4_route(context->sockfd,
3803 RTM_DELETE, route->gateway,
3804 route->dest, route->mask, ifn_p, route->ifindex,
3805 route->ifa, route->flags);
3806 if (retval != 0) {
3807 if (retval != ESRCH) {
3808 my_log(LOG_NOTICE,
3809 "IPMonitor apply_ipv4_route failed to remove"
3810 " route, %s: ", strerror(retval));
3811 IPv4RouteLog(LOG_NOTICE, route);
3812 }
3813 }
3814 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3815 my_log(LOG_DEBUG,
3816 "Removed IPv4 route old[%d] = ",
3817 route - context->old->list);
3818 IPv4RouteLog(LOG_DEBUG, route);
3819 }
3820 if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) {
3821 retval = ipv4_route_gateway(context->sockfd, RTM_DELETE,
3822 ifn_p, route);
3823 if (retval != 0) {
3824 my_log(LOG_NOTICE,
3825 "IPMonitor apply_ipv4_route failed to remove"
3826 " %s/32 route, %s: ",
3827 inet_ntoa(route->gateway), strerror(retval));
3828 }
3829 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3830 my_log(LOG_DEBUG, "Removed IPv4 Route %s/32",
3831 inet_ntoa(route->gateway));
3832 }
3833 }
3834 break;
3835 default:
3836 break;
3837 }
3838 return;
3839 }
3840 #endif /* !TARGET_IPHONE_SIMULATOR */
3841
3842 /*
3843 * Function: update_ipv4
3844 *
3845 * Purpose:
3846 * Update the IPv4 configuration based on the latest information.
3847 * Publish the State:/Network/Global/IPv4 information, and update the
3848 * IPv4 routing table. IPv4RouteListApply() invokes our callback,
3849 * apply_ipv4_route(), to install/remove the routes.
3850 */
3851 static void
3852 update_ipv4(CFStringRef primary,
3853 IPv4RouteListRef new_routelist,
3854 keyChangeListRef keys)
3855 {
3856 #if !TARGET_IPHONE_SIMULATOR
3857 apply_ipv4_route_context_t context;
3858 #endif /* !TARGET_IPHONE_SIMULATOR */
3859
3860 if (keys != NULL) {
3861 if (new_routelist != NULL && primary != NULL) {
3862 char * ifn_p = NULL;
3863 IPv4RouteRef r;
3864 CFMutableDictionaryRef dict = NULL;
3865
3866 dict = CFDictionaryCreateMutable(NULL, 0,
3867 &kCFTypeDictionaryKeyCallBacks,
3868 &kCFTypeDictionaryValueCallBacks);
3869 /* the first entry is the default route */
3870 r = new_routelist->list;
3871 if (r->gateway.s_addr != 0) {
3872 CFStringRef router;
3873
3874 router = CFStringCreateWithCString(NULL,
3875 inet_ntoa(r->gateway),
3876 kCFStringEncodingASCII);
3877 if (router != NULL) {
3878 CFDictionarySetValue(dict, kSCPropNetIPv4Router, router);
3879 CFRelease(router);
3880 }
3881 }
3882 if (r->ifname[0] != '\0') {
3883 ifn_p = r->ifname;
3884 }
3885 if (ifn_p != NULL) {
3886 CFStringRef ifname_cf;
3887
3888 ifname_cf = CFStringCreateWithCString(NULL,
3889 ifn_p,
3890 kCFStringEncodingASCII);
3891 if (ifname_cf != NULL) {
3892 CFDictionarySetValue(dict,
3893 kSCDynamicStorePropNetPrimaryInterface,
3894 ifname_cf);
3895 CFRelease(ifname_cf);
3896 }
3897 }
3898 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
3899 primary);
3900 keyChangeListSetValue(keys, S_state_global_ipv4, dict);
3901 CFRelease(dict);
3902 }
3903 else {
3904 keyChangeListRemoveValue(keys, S_state_global_ipv4);
3905 }
3906 }
3907
3908 #if !TARGET_IPHONE_SIMULATOR
3909 bzero(&context, sizeof(context));
3910 context.sockfd = route_open_socket();
3911 if (context.sockfd != -1) {
3912 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3913 if (S_ipv4_routelist == NULL) {
3914 my_log(LOG_DEBUG, "Old Routes = <none>");
3915 }
3916 else {
3917 my_log(LOG_DEBUG, "Old Routes = ");
3918 IPv4RouteListLog(LOG_DEBUG, S_ipv4_routelist);
3919 }
3920 if (new_routelist == NULL) {
3921 my_log(LOG_DEBUG, "New Routes = <none>");
3922 }
3923 else {
3924 my_log(LOG_DEBUG, "New Routes = ");
3925 IPv4RouteListLog(LOG_DEBUG, new_routelist);
3926 }
3927 }
3928 context.old = S_ipv4_routelist;
3929 context.new = new_routelist;
3930 IPv4RouteListApply(S_ipv4_routelist, new_routelist,
3931 &apply_ipv4_route, (void *)&context);
3932 if (new_routelist != NULL) {
3933 (void)multicast_route_delete(context.sockfd);
3934 }
3935 else {
3936 (void)multicast_route_add(context.sockfd);
3937 }
3938 close(context.sockfd);
3939 }
3940 if (S_ipv4_routelist != NULL) {
3941 free(S_ipv4_routelist);
3942 }
3943 S_ipv4_routelist = new_routelist;
3944 #endif /* !TARGET_IPHONE_SIMULATOR */
3945
3946 return;
3947 }
3948
3949 static void
3950 update_ipv6(CFStringRef primary,
3951 keyChangeListRef keys)
3952 {
3953 CFDictionaryRef ipv6_dict = NULL;
3954
3955 if (primary != NULL) {
3956 ipv6_dict = service_dict_get(primary, kSCEntNetIPv6);
3957 }
3958 if (ipv6_dict != NULL) {
3959 #if !TARGET_IPHONE_SIMULATOR
3960 CFArrayRef addrs;
3961 #endif /* !TARGET_IPHONE_SIMULATOR */
3962 CFMutableDictionaryRef dict = NULL;
3963 CFStringRef if_name = NULL;
3964 #if !TARGET_IPHONE_SIMULATOR
3965 char ifn[IFNAMSIZ] = { '\0' };
3966 char * ifn_p = NULL;
3967 boolean_t is_direct = FALSE;
3968 #endif /* !TARGET_IPHONE_SIMULATOR */
3969 CFStringRef val_router = NULL;
3970
3971 dict = CFDictionaryCreateMutable(NULL, 0,
3972 &kCFTypeDictionaryKeyCallBacks,
3973 &kCFTypeDictionaryValueCallBacks);
3974
3975 #if !TARGET_IPHONE_SIMULATOR
3976 addrs = CFDictionaryGetValue(ipv6_dict,
3977 kSCPropNetIPv6Addresses);
3978 #endif /* !TARGET_IPHONE_SIMULATOR */
3979
3980 val_router = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Router);
3981 if (val_router != NULL) {
3982 #if !TARGET_IPHONE_SIMULATOR
3983 is_direct = router_is_our_ipv6_address(val_router, addrs);
3984 #endif /* !TARGET_IPHONE_SIMULATOR */
3985 /* no router if router is one of our IP addresses */
3986 CFDictionarySetValue(dict, kSCPropNetIPv6Router,
3987 val_router);
3988 }
3989 #if !TARGET_IPHONE_SIMULATOR
3990 else {
3991 val_router = CFArrayGetValueAtIndex(addrs, 0);
3992 is_direct = TRUE;
3993 }
3994 #endif /* !TARGET_IPHONE_SIMULATOR */
3995 if_name = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
3996 if (if_name) {
3997 CFDictionarySetValue(dict,
3998 kSCDynamicStorePropNetPrimaryInterface,
3999 if_name);
4000 #if !TARGET_IPHONE_SIMULATOR
4001 if (CFStringGetCString(if_name, ifn, sizeof(ifn),
4002 kCFStringEncodingASCII)) {
4003 ifn_p = ifn;
4004 }
4005 #endif /* !TARGET_IPHONE_SIMULATOR */
4006 }
4007 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
4008 primary);
4009 keyChangeListSetValue(keys, S_state_global_ipv6, dict);
4010 CFRelease(dict);
4011
4012 #if !TARGET_IPHONE_SIMULATOR
4013 #ifdef RTF_IFSCOPE
4014 if (S_scopedroute_v6) {
4015 set_ipv6_default_interface(ifn_p);
4016 } else
4017 #endif /* RTF_IFSCOPE */
4018 { /* route add default ... */
4019 struct in6_addr router;
4020
4021 (void)cfstring_to_ip6(val_router, &router);
4022 set_ipv6_router(&router, ifn_p, is_direct);
4023 }
4024 #endif /* !TARGET_IPHONE_SIMULATOR */
4025 }
4026 else {
4027 keyChangeListRemoveValue(keys, S_state_global_ipv6);
4028 #if !TARGET_IPHONE_SIMULATOR
4029 #ifdef RTF_IFSCOPE
4030 if (S_scopedroute_v6) {
4031 set_ipv6_default_interface(NULL);
4032 } else
4033 #endif /* RTF_IFSCOPE */
4034 { /* route delete default ... */
4035 set_ipv6_router(NULL, NULL, FALSE);
4036 }
4037 #endif /* !TARGET_IPHONE_SIMULATOR */
4038 }
4039 return;
4040 }
4041
4042 static Boolean
4043 update_dns(CFDictionaryRef services_info,
4044 CFStringRef primary,
4045 keyChangeListRef keys)
4046 {
4047 Boolean changed = FALSE;
4048 CFDictionaryRef dict = NULL;
4049
4050 if (primary != NULL) {
4051 CFDictionaryRef service_dict;
4052
4053 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
4054 if (service_dict != NULL) {
4055 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
4056 }
4057 }
4058
4059 if (!_SC_CFEqual(S_dns_dict, dict)) {
4060 if (dict == NULL) {
4061 #if !TARGET_OS_IPHONE
4062 empty_dns();
4063 #endif /* !TARGET_OS_IPHONE */
4064 keyChangeListRemoveValue(keys, S_state_global_dns);
4065 } else {
4066 CFMutableDictionaryRef new_dict;
4067
4068 #if !TARGET_OS_IPHONE
4069 set_dns(CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains),
4070 CFDictionaryGetValue(dict, kSCPropNetDNSDomainName),
4071 CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses),
4072 CFDictionaryGetValue(dict, kSCPropNetDNSSortList));
4073 #endif /* !TARGET_OS_IPHONE */
4074 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
4075 CFDictionaryRemoveValue(new_dict, kSCPropInterfaceName);
4076 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchDomains);
4077 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchOrders);
4078 CFDictionaryRemoveValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY);
4079 keyChangeListSetValue(keys, S_state_global_dns, new_dict);
4080 CFRelease(new_dict);
4081 }
4082 changed = TRUE;
4083 }
4084
4085 if (dict != NULL) CFRetain(dict);
4086 if (S_dns_dict != NULL) CFRelease(S_dns_dict);
4087 S_dns_dict = dict;
4088
4089 return changed;
4090 }
4091
4092 static Boolean
4093 update_dnsinfo(CFDictionaryRef services_info,
4094 CFStringRef primary,
4095 keyChangeListRef keys,
4096 CFArrayRef service_order)
4097 {
4098 Boolean changed;
4099 CFDictionaryRef dict = NULL;
4100 CFArrayRef multicastResolvers;
4101 CFArrayRef privateResolvers;
4102
4103 multicastResolvers = CFDictionaryGetValue(services_info, S_multicast_resolvers);
4104 privateResolvers = CFDictionaryGetValue(services_info, S_private_resolvers);
4105
4106 if (primary != NULL) {
4107 CFDictionaryRef service_dict;
4108
4109 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
4110 if (service_dict != NULL) {
4111 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
4112 }
4113 }
4114
4115 changed = dns_configuration_set(dict,
4116 S_service_state_dict,
4117 service_order,
4118 multicastResolvers,
4119 privateResolvers);
4120 if (changed) {
4121 keyChangeListNotifyKey(keys, S_state_global_dns);
4122 }
4123 return changed;
4124 }
4125
4126 static Boolean
4127 update_nwi(nwi_state_t state)
4128 {
4129 unsigned char signature[CC_SHA1_DIGEST_LENGTH];
4130 static unsigned char signature_last[CC_SHA1_DIGEST_LENGTH];
4131
4132 _nwi_state_signature(state, signature, sizeof(signature));
4133 if (bcmp(signature, signature_last, sizeof(signature)) == 0) {
4134 return FALSE;
4135 }
4136
4137 // save [new] signature
4138 bcopy(signature, signature_last, sizeof(signature));
4139
4140 // save [new] configuration
4141 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4142 my_log(LOG_DEBUG, "Updating network information");
4143 S_nwi_state_dump(state);
4144 }
4145 if (_nwi_state_store(state) == FALSE) {
4146 my_log(LOG_ERR, "Notifying nwi_state_store failed");
4147 }
4148
4149 return TRUE;
4150 }
4151
4152 static Boolean
4153 update_proxies(CFDictionaryRef services_info,
4154 CFStringRef primary,
4155 keyChangeListRef keys,
4156 CFArrayRef service_order)
4157 {
4158 Boolean changed = FALSE;
4159 CFDictionaryRef dict = NULL;
4160 CFDictionaryRef new_dict;
4161
4162 if (primary != NULL) {
4163 CFDictionaryRef service_dict;
4164
4165 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
4166 if (service_dict != NULL) {
4167 dict = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
4168 }
4169 }
4170
4171 new_dict = proxy_configuration_update(dict,
4172 S_service_state_dict,
4173 service_order,
4174 services_info);
4175 if (!_SC_CFEqual(S_proxies_dict, new_dict)) {
4176 if (new_dict == NULL) {
4177 keyChangeListRemoveValue(keys, S_state_global_proxies);
4178 } else {
4179 keyChangeListSetValue(keys, S_state_global_proxies, new_dict);
4180 }
4181 changed = TRUE;
4182 }
4183
4184 if (S_proxies_dict != NULL) CFRelease(S_proxies_dict);
4185 S_proxies_dict = new_dict;
4186
4187 return changed;
4188 }
4189
4190 #if !TARGET_OS_IPHONE
4191 static Boolean
4192 update_smb(CFDictionaryRef services_info,
4193 CFStringRef primary,
4194 keyChangeListRef keys)
4195 {
4196 Boolean changed = FALSE;
4197 CFDictionaryRef dict = NULL;
4198
4199 if (primary != NULL) {
4200 CFDictionaryRef service_dict;
4201
4202 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
4203 if (service_dict != NULL) {
4204 dict = CFDictionaryGetValue(service_dict, kSCEntNetSMB);
4205 }
4206 }
4207
4208 if (!_SC_CFEqual(S_smb_dict, dict)) {
4209 if (dict == NULL) {
4210 keyChangeListRemoveValue(keys, S_state_global_smb);
4211 } else {
4212 keyChangeListSetValue(keys, S_state_global_smb, dict);
4213 }
4214 changed = TRUE;
4215 }
4216
4217 if (dict != NULL) CFRetain(dict);
4218 if (S_smb_dict != NULL) CFRelease(S_smb_dict);
4219 S_smb_dict = dict;
4220
4221 return changed;
4222 }
4223 #endif /* !TARGET_OS_IPHONE */
4224
4225 static Rank
4226 get_service_rank(CFArrayRef order, int n_order, CFStringRef serviceID)
4227 {
4228 CFIndex i;
4229 Rank rank = kRankIndexMask;
4230
4231 if (serviceID != NULL && order != NULL && n_order > 0) {
4232 for (i = 0; i < n_order; i++) {
4233 CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(order, i));
4234
4235 if (s == NULL) {
4236 continue;
4237 }
4238 if (CFEqual(serviceID, s)) {
4239 rank = i + 1;
4240 break;
4241 }
4242 }
4243 }
4244 return (rank);
4245 }
4246
4247 /**
4248 ** Service election:
4249 **/
4250 /*
4251 * Function: rank_dict_get_service_rank
4252 * Purpose:
4253 * Retrieve the service rank in the given dictionary.
4254 */
4255 static Rank
4256 rank_dict_get_service_rank(CFDictionaryRef rank_dict, CFStringRef serviceID)
4257 {
4258 CFNumberRef rank;
4259 Rank rank_val = RankMake(kRankIndexMask, kRankAssertionDefault);
4260
4261 rank = CFDictionaryGetValue(rank_dict, serviceID);
4262 if (rank != NULL) {
4263 CFNumberGetValue(rank, kCFNumberSInt32Type, &rank_val);
4264 }
4265 return (rank_val);
4266 }
4267
4268 /*
4269 * Function: rank_dict_set_service_rank
4270 * Purpose:
4271 * Save the results of ranking the service so we can look it up later without
4272 * repeating all of the ranking code.
4273 */
4274 static void
4275 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict,
4276 CFStringRef serviceID, Rank rank_val)
4277 {
4278 CFNumberRef rank;
4279
4280 rank = CFNumberCreate(NULL, kCFNumberSInt32Type, (const void *)&rank_val);
4281 if (rank != NULL) {
4282 CFDictionarySetValue(rank_dict, serviceID, rank);
4283 CFRelease(rank);
4284 }
4285 return;
4286 }
4287
4288 static const CFStringRef *transientInterfaceEntityNames[] = {
4289 &kSCEntNetPPP,
4290 };
4291
4292
4293 static void
4294 CollectTransientServices(const void * key,
4295 const void * value,
4296 void * context)
4297 {
4298 int i;
4299 CFStringRef service = key;
4300 CFMutableArrayRef vif_setup_keys = context;
4301
4302 /* This service is either a vpn type service or a comm center service */
4303 if (!CFStringHasPrefix(service, kSCDynamicStoreDomainSetup)) {
4304 return;
4305 }
4306
4307 for (i = 0; i < sizeof(transientInterfaceEntityNames)/sizeof(transientInterfaceEntityNames[0]); i++) {
4308 if (!CFStringHasSuffix(service, *transientInterfaceEntityNames[i])) {
4309 continue;
4310 }
4311
4312 CFArrayAppendValue(vif_setup_keys, service);
4313 }
4314 return;
4315 }
4316
4317
4318 static SCNetworkReachabilityFlags
4319 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info,
4320 CFStringRef service_id,
4321 CFStringRef entity,
4322 CFStringRef vpn_setup_key)
4323 {
4324 CFStringRef key;
4325 CFDictionaryRef dict;
4326 SCNetworkReachabilityFlags flags = 0;
4327
4328
4329 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4330 kSCDynamicStoreDomainSetup,
4331 service_id,
4332 kSCEntNetInterface);
4333 dict = CFDictionaryGetValue(services_info, key);
4334 CFRelease(key);
4335
4336 if (isA_CFDictionary(dict)
4337 && CFDictionaryContainsKey(dict, kSCPropNetInterfaceDeviceName)) {
4338
4339 flags = (kSCNetworkReachabilityFlagsReachable
4340 | kSCNetworkReachabilityFlagsTransientConnection
4341 | kSCNetworkReachabilityFlagsConnectionRequired);
4342
4343 if (CFEqual(entity, kSCEntNetPPP)) {
4344 CFNumberRef num;
4345 CFDictionaryRef p_dict = CFDictionaryGetValue(services_info, vpn_setup_key);
4346
4347 if (!isA_CFDictionary(p_dict)) {
4348 return (flags);
4349 }
4350
4351 // get PPP dial-on-traffic status
4352 num = CFDictionaryGetValue(p_dict, kSCPropNetPPPDialOnDemand);
4353 if (isA_CFNumber(num)) {
4354 int32_t ppp_demand;
4355
4356 if (CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand)) {
4357 if (ppp_demand) {
4358 flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
4359 }
4360 }
4361 }
4362 }
4363 }
4364 return (flags);
4365 }
4366
4367 static Boolean
4368 S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value)
4369 {
4370 Boolean ret = def_value;
4371
4372 if (dict != NULL) {
4373 CFBooleanRef val;
4374
4375 val = CFDictionaryGetValue(dict, key);
4376 if (isA_CFBoolean(val) != NULL) {
4377 ret = CFBooleanGetValue(val);
4378 }
4379 }
4380 return (ret);
4381 }
4382
4383
4384 static void
4385 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info,
4386 SCNetworkReachabilityFlags *reach_flags_v4,
4387 SCNetworkReachabilityFlags *reach_flags_v6)
4388 {
4389 int i;
4390 int count;
4391 CFMutableArrayRef vif_setup_keys;
4392
4393 vif_setup_keys = CFArrayCreateMutable(NULL,
4394 0,
4395 &kCFTypeArrayCallBacks);
4396
4397 CFDictionaryApplyFunction(services_info, CollectTransientServices, vif_setup_keys);
4398
4399 count = CFArrayGetCount(vif_setup_keys);
4400
4401 if (count != 0) {
4402 my_log(LOG_DEBUG, "Collected the following VIF Setup Keys: %@", vif_setup_keys);
4403 }
4404
4405 for (i = 0; i < count; i++) {
4406 CFArrayRef components = NULL;
4407 CFStringRef entity;
4408 CFStringRef service_id;
4409 CFStringRef vif_setup_key;
4410
4411 vif_setup_key = CFArrayGetValueAtIndex(vif_setup_keys, i);
4412
4413 /*
4414 * setup key in the following format:
4415 * Setup:/Network/Service/<Service ID>/<Entity>
4416 */
4417 components = CFStringCreateArrayBySeparatingStrings(NULL, vif_setup_key, CFSTR("/"));
4418
4419 if (CFArrayGetCount(components) != 5) {
4420 my_log(LOG_ERR, "Invalid Setup Key encountered: %@", vif_setup_key);
4421 goto skip;
4422 }
4423
4424 /* service id is the 3rd element */
4425 service_id = CFArrayGetValueAtIndex(components, 3);
4426
4427 /* entity id is the 4th element */
4428 entity = CFArrayGetValueAtIndex(components, 4);
4429
4430 my_log(LOG_DEBUG, "Service %@ is a %@ Entity", service_id, entity);
4431
4432
4433 if (CFEqual(entity, kSCEntNetPPP)) {
4434 SCNetworkReachabilityFlags flags;
4435 CFStringRef key;
4436
4437 flags = GetReachabilityFlagsFromVPN(services_info,
4438 service_id,
4439 entity,
4440 vif_setup_key);
4441
4442 /* Check for the v4 reachability flags */
4443 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4444 kSCDynamicStoreDomainSetup,
4445 service_id,
4446 kSCEntNetIPv4);
4447
4448 if (CFDictionaryContainsKey(services_info, key)) {
4449 *reach_flags_v4 |= flags;
4450 my_log(LOG_DEBUG,"Service %@ setting ipv4 reach flags: %d", service_id, *reach_flags_v4);
4451 }
4452
4453 CFRelease(key);
4454
4455 /* Check for the v6 reachability flags */
4456 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4457 kSCDynamicStoreDomainSetup,
4458 service_id,
4459 kSCEntNetIPv6);
4460
4461 if (CFDictionaryContainsKey(services_info, key)) {
4462 *reach_flags_v6 |= flags;
4463 my_log(LOG_DEBUG,"Service %@ setting ipv6 reach flags: %d", service_id, *reach_flags_v6);
4464 }
4465 CFRelease(key);
4466
4467 if (flags != 0) {
4468 if (components != NULL) {
4469 CFRelease(components);
4470 }
4471 goto done;
4472 }
4473 }
4474 skip:
4475 if (components != NULL) {
4476 CFRelease(components);
4477 }
4478 }
4479 done:
4480 CFRelease(vif_setup_keys);
4481 return;
4482 }
4483
4484 static SCNetworkReachabilityFlags
4485 GetReachFlagsFromStatus(CFStringRef entity, int status)
4486 {
4487 SCNetworkReachabilityFlags flags = 0;
4488
4489 if (CFEqual(entity, kSCEntNetPPP)) {
4490 switch (status) {
4491 case PPP_RUNNING :
4492 /* if we're really UP and RUNNING */
4493 break;
4494 case PPP_ONHOLD :
4495 /* if we're effectively UP and RUNNING */
4496 break;
4497 case PPP_IDLE :
4498 /* if we're not connected at all */
4499 my_log(LOG_INFO, "PPP link idle");
4500 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4501 break;
4502 case PPP_STATERESERVED :
4503 // if we're not connected at all
4504 my_log(LOG_INFO, "PPP link idle, dial-on-traffic to connect");
4505 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4506 break;
4507 default :
4508 /* if we're in the process of [dis]connecting */
4509 my_log(LOG_INFO, "PPP link, connection in progress");
4510 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4511 break;
4512 }
4513 }
4514 #ifdef HAVE_IPSEC_STATUS
4515 else if (CFEqual(entity, kSCEntNetIPSec)) {
4516 switch (status) {
4517 case IPSEC_RUNNING :
4518 /* if we're really UP and RUNNING */
4519 break;
4520 case IPSEC_IDLE :
4521 /* if we're not connected at all */
4522 my_log(LOG_INFO, "IPSec link idle");
4523 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4524 break;
4525 default :
4526 /* if we're in the process of [dis]connecting */
4527 my_log(LOG_INFO, "IPSec link, connection in progress");
4528 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4529 break;
4530 }
4531 }
4532 #endif // HAVE_IPSEC_STATUS
4533 #ifdef HAVE_VPN_STATUS
4534 else if (CFEqual(entity, kSCEntNetVPN)) {
4535 switch (status) {
4536 case VPN_RUNNING :
4537 /* if we're really UP and RUNNING */
4538 break;
4539 case VPN_IDLE :
4540 case VPN_LOADING :
4541 case VPN_LOADED :
4542 case VPN_UNLOADING :
4543 /* if we're not connected at all */
4544 my_log(LOG_INFO, "%s VPN link idle");
4545 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4546 break;
4547 default :
4548 /* if we're in the process of [dis]connecting */
4549 my_log(LOG_INFO, "VPN link, connection in progress");
4550 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4551 break;
4552 }
4553 }
4554 #endif // HAVE_VPN_STATUS
4555 return (flags);
4556 }
4557
4558 static void
4559 VPNAttributesGet(CFStringRef service_id,
4560 CFDictionaryRef services_info,
4561 SCNetworkReachabilityFlags *flags,
4562 CFStringRef *server_address,
4563 int af)
4564 {
4565 int i;
4566 CFDictionaryRef entity_dict;
4567 boolean_t found = FALSE;
4568 CFNumberRef num;
4569 CFDictionaryRef p_state = NULL;
4570 int status = 0;
4571
4572 /* if the IPv[4/6] exist */
4573 entity_dict = service_dict_get(service_id, (af == AF_INET) ? kSCEntNetIPv4 : kSCEntNetIPv6);
4574 if (!isA_CFDictionary(entity_dict)) {
4575 return;
4576 }
4577
4578 if (af == AF_INET) {
4579 entity_dict = CFDictionaryGetValue(entity_dict, kIPv4DictService);
4580 if (!isA_CFDictionary(entity_dict)) {
4581 return;
4582 }
4583 }
4584
4585 for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) {
4586 p_state = service_dict_get(service_id, *statusEntityNames[i]);
4587 /* ensure that this is a VPN Type service */
4588 if (isA_CFDictionary(p_state)) {
4589 found = TRUE;
4590 break;
4591 }
4592 }
4593
4594 /* Did we find a vpn type service? If not, we are done.*/
4595 if (!found) {
4596 return;
4597 }
4598
4599 *flags |= (kSCNetworkReachabilityFlagsReachable| kSCNetworkReachabilityFlagsTransientConnection);
4600
4601 /* Get the Server Address */
4602 if (server_address != NULL) {
4603 *server_address = CFDictionaryGetValue(entity_dict, CFSTR("ServerAddress"));
4604 *server_address = isA_CFString(*server_address);
4605 if (*server_address != NULL) {
4606 CFRetain(*server_address);
4607 }
4608 }
4609
4610 /* get status */
4611 if (!CFDictionaryGetValueIfPresent(p_state,
4612 kSCPropNetVPNStatus,
4613 (const void **)&num) ||
4614 !isA_CFNumber(num) ||
4615 !CFNumberGetValue(num, kCFNumberSInt32Type, &status)) {
4616 return;
4617 }
4618
4619 *flags |= GetReachFlagsFromStatus(*statusEntityNames[i], status);
4620
4621 if (CFEqual(*statusEntityNames[i], kSCEntNetPPP)) {
4622 CFStringRef key;
4623 CFDictionaryRef p_setup;
4624 int ppp_demand;
4625
4626 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4627 kSCDynamicStoreDomainSetup,
4628 service_id,
4629 kSCEntNetPPP);
4630 p_setup = CFDictionaryGetValue(services_info, key);
4631 CFRelease(key);
4632
4633 /* get dial-on-traffic status */
4634 if (isA_CFDictionary(p_setup) &&
4635 CFDictionaryGetValueIfPresent(p_setup,
4636 kSCPropNetPPPDialOnDemand,
4637 (const void **)&num) &&
4638 isA_CFNumber(num) &&
4639 CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand) &&
4640 (ppp_demand != 0)) {
4641 *flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
4642 if (status == PPP_IDLE) {
4643 *flags |= kSCNetworkReachabilityFlagsInterventionRequired;
4644 }
4645 }
4646 }
4647 return;
4648 }
4649
4650
4651 typedef struct ElectionInfo {
4652 int n_services;
4653 CFArrayRef order;
4654 int n_order;
4655 ElectionResultsRef results;
4656 } ElectionInfo, * ElectionInfoRef;
4657
4658 typedef CFDictionaryApplierFunction ElectionFuncRef;
4659
4660 static void
4661 CandidateRelease(CandidateRef candidate)
4662 {
4663 my_CFRelease(&candidate->serviceID);
4664 my_CFRelease(&candidate->if_name);
4665 my_CFRelease(&candidate->signature);
4666 return;
4667 }
4668
4669 static void
4670 CandidateCopy(CandidateRef dest, CandidateRef src)
4671 {
4672 *dest = *src;
4673 if (dest->serviceID) {
4674 CFRetain(dest->serviceID);
4675 }
4676 if (dest->if_name) {
4677 CFRetain(dest->if_name);
4678 }
4679 if(dest->signature) {
4680 CFRetain(dest->signature);
4681 }
4682 return;
4683 }
4684
4685 static ElectionResultsRef
4686 ElectionResultsAlloc(int size)
4687 {
4688 ElectionResultsRef results;
4689
4690 results = (ElectionResultsRef)malloc(ElectionResultsComputeSize(size));
4691 results->count = 0;
4692 results->size = size;
4693 return (results);
4694 }
4695
4696 static void
4697 ElectionResultsRelease(ElectionResultsRef results)
4698 {
4699 int i;
4700 CandidateRef scan;
4701
4702 for (i = 0, scan = results->candidates;
4703 i < results->count;
4704 i++, scan++) {
4705 CandidateRelease(scan);
4706 }
4707 free(results);
4708 return;
4709 }
4710
4711 static void
4712 ElectionResultsLog(int level, ElectionResultsRef results, const char * prefix)
4713 {
4714 int i;
4715 CandidateRef scan;
4716
4717 if (results == NULL) {
4718 my_log(level, "%s: no candidates", prefix);
4719 return;
4720 }
4721 my_log(level, "%s: %d candidates", prefix, results->count);
4722 for (i = 0, scan = results->candidates;
4723 i < results->count;
4724 i++, scan++) {
4725 my_log(level, "%d. %@ Rank=0x%x serviceID=%@", i, scan->if_name,
4726 scan->rank, scan->serviceID);
4727 }
4728 return;
4729 }
4730
4731 /*
4732 * Function: ElectionResultsAddCandidate
4733 * Purpose:
4734 * Add the candidate into the election results. Find the insertion point
4735 * by comparing the rank of the candidate with existing entries.
4736 */
4737 static void
4738 ElectionResultsAddCandidate(ElectionResultsRef results, CandidateRef candidate)
4739 {
4740 int i;
4741 int where;
4742
4743 #define BAD_INDEX (-1)
4744 if (results->count == results->size) {
4745 /* this should not happen */
4746 my_log(LOG_NOTICE, "can't fit another candidate");
4747 return;
4748 }
4749
4750 /* find the insertion point */
4751 where = BAD_INDEX;
4752 for (i = 0; i < results->count; i++) {
4753 CandidateRef this_candidate = results->candidates + i;
4754
4755 if (candidate->rank < this_candidate->rank) {
4756 where = i;
4757 break;
4758 }
4759 }
4760 /* add it to the end */
4761 if (where == BAD_INDEX) {
4762 CandidateCopy(results->candidates + results->count, candidate);
4763 results->count++;
4764 return;
4765 }
4766 /* slide existing entries over */
4767 for (i = results->count; i > where; i--) {
4768 results->candidates[i] = results->candidates[i - 1];
4769 }
4770 /* insert element */
4771 CandidateCopy(results->candidates + where, candidate);
4772 results->count++;
4773 return;
4774 }
4775
4776 /*
4777 * Function: ElectionResultsCopy
4778 * Purpose:
4779 * Visit all of the services and invoke the protocol-specific election
4780 * function. Return the results of the election.
4781 */
4782 static ElectionResultsRef
4783 ElectionResultsCopy(ElectionFuncRef elect_func, CFArrayRef order, int n_order)
4784 {
4785 int count;
4786 ElectionInfo info;
4787
4788 count = CFDictionaryGetCount(S_service_state_dict);
4789 if (count == 0) {
4790 return (NULL);
4791 }
4792 info.results = ElectionResultsAlloc(count);
4793 info.n_services = count;
4794 info.order = order;
4795 info.n_order = n_order;
4796 CFDictionaryApplyFunction(S_service_state_dict, elect_func, (void *)&info);
4797 if (info.results->count == 0) {
4798 ElectionResultsRelease(info.results);
4799 info.results = NULL;
4800 }
4801 return (info.results);
4802 }
4803
4804 /*
4805 * Function: ElectionResultsCandidateNeedsDemotion
4806 * Purpose:
4807 * Check whether the given candidate requires demotion. A candidate
4808 * might need to be demoted if its IPv4 and IPv6 services must be coupled
4809 * but a higher ranked service has IPv4 or IPv6.
4810 */
4811 static Boolean
4812 ElectionResultsCandidateNeedsDemotion(ElectionResultsRef other_results,
4813 CandidateRef candidate)
4814 {
4815 CandidateRef other_candidate;
4816 Boolean ret = FALSE;
4817
4818 if (other_results == NULL
4819 || candidate->ip_is_coupled == FALSE
4820 || RANK_ASSERTION_MASK(candidate->rank) == kRankAssertionNever) {
4821 goto done;
4822 }
4823 other_candidate = other_results->candidates;
4824 if (CFEqual(other_candidate->if_name, candidate->if_name)) {
4825 /* they are over the same interface, no need to demote */
4826 goto done;
4827 }
4828 if (CFStringHasPrefix(other_candidate->if_name, CFSTR("stf"))) {
4829 /* avoid creating a feedback loop */
4830 goto done;
4831 }
4832 if (RANK_ASSERTION_MASK(other_candidate->rank) == kRankAssertionNever) {
4833 /* the other candidate isn't eligible to become primary, ignore */
4834 goto done;
4835 }
4836 if (candidate->rank < other_candidate->rank) {
4837 /* we're higher ranked than the other candidate, ignore */
4838 goto done;
4839 }
4840 ret = TRUE;
4841
4842 done:
4843 return (ret);
4844
4845 }
4846
4847
4848 static void
4849 get_signature_sha1(CFStringRef signature,
4850 unsigned char * sha1)
4851 {
4852 CC_SHA1_CTX ctx;
4853 CFDataRef signature_data;
4854
4855 signature_data = CFStringCreateExternalRepresentation(NULL,
4856 signature,
4857 kCFStringEncodingUTF8,
4858 0);
4859
4860 CC_SHA1_Init(&ctx);
4861 CC_SHA1_Update(&ctx,
4862 signature_data,
4863 CFDataGetLength(signature_data));
4864 CC_SHA1_Final(sha1, &ctx);
4865
4866 CFRelease(signature_data);
4867
4868 return;
4869 }
4870
4871
4872 static void
4873 add_candidate_to_nwi_state(nwi_state_t nwi_state, int af,
4874 CandidateRef candidate, Rank rank)
4875 {
4876 uint64_t flags = 0;
4877 char ifname[IFNAMSIZ];
4878 nwi_ifstate_t ifstate;
4879
4880 if (nwi_state == NULL) {
4881 /* can't happen */
4882 return;
4883 }
4884 if (RANK_ASSERTION_MASK(rank) == kRankAssertionNever) {
4885 flags |= NWI_IFSTATE_FLAGS_NOT_IN_LIST;
4886 }
4887 if (service_dict_get(candidate->serviceID, kSCEntNetDNS) != NULL) {
4888 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
4889 }
4890 CFStringGetCString(candidate->if_name, ifname, sizeof(ifname),
4891 kCFStringEncodingASCII);
4892 if ((S_IPMonitor_debug & kDebugFlag2) != 0) {
4893 my_log(LOG_DEBUG,
4894 "Inserting IPv%c [%s] with flags 0x%x primary_rank 0x%x reach_flags %d",
4895 ipvx_char(af), ifname, rank, candidate->reachability_flags);
4896 }
4897 ifstate = nwi_insert_ifstate(nwi_state, ifname, af, flags, rank,
4898 (void *)&candidate->addr,
4899 (void *)&candidate->vpn_server_addr,
4900 candidate->reachability_flags);
4901 if (ifstate != NULL && candidate->signature) {
4902 uint8_t hash[CC_SHA1_DIGEST_LENGTH];
4903
4904 get_signature_sha1(candidate->signature, hash);
4905 nwi_ifstate_set_signature(ifstate, hash);
4906 }
4907 return;
4908 }
4909
4910
4911 static void
4912 add_reachability_flags_to_candidate(CandidateRef candidate, CFDictionaryRef services_info, int af)
4913 {
4914 SCNetworkReachabilityFlags flags = kSCNetworkReachabilityFlagsReachable;
4915 CFStringRef vpn_server_address = NULL;
4916
4917 VPNAttributesGet(candidate->serviceID,
4918 services_info,
4919 &flags,
4920 &vpn_server_address,
4921 af);
4922
4923 candidate->reachability_flags = flags;
4924
4925 if (vpn_server_address == NULL) {
4926 bzero(&candidate->vpn_server_addr, sizeof(candidate->vpn_server_addr));
4927 } else {
4928 char buf[128];
4929 CFStringGetCString(vpn_server_address, buf, sizeof(buf), kCFStringEncodingASCII);
4930
4931 _SC_string_to_sockaddr(buf,
4932 AF_UNSPEC,
4933 (void *)&candidate->vpn_server_addr,
4934 sizeof(candidate->vpn_server_addr));
4935
4936 CFRelease(vpn_server_address);
4937 }
4938 return;
4939 }
4940 /*
4941 * Function: ElectionResultsCopyPrimary
4942 * Purpose:
4943 * Use the results of the current protocol and the other protocol to
4944 * determine which service should become primary.
4945 *
4946 * At the same time, generate the nwi_state for the protocol.
4947 *
4948 * For IPv4, also generate the IPv4 routing table.
4949 */
4950 static CFStringRef
4951 ElectionResultsCopyPrimary(ElectionResultsRef results,
4952 ElectionResultsRef other_results,
4953 nwi_state_t nwi_state, int af,
4954 IPv4RouteListRef * ret_routes,
4955 CFDictionaryRef services_info)
4956 {
4957 CFStringRef primary = NULL;
4958 Boolean primary_is_null = FALSE;
4959 IPv4RouteListRef routes = NULL;
4960
4961 if (nwi_state != NULL) {
4962 nwi_state_clear(nwi_state, af);
4963 }
4964 if (results != NULL) {
4965 CandidateRef deferred[results->count];
4966 int deferred_count;
4967 int i;
4968 CandidateRef scan;
4969
4970 deferred_count = 0;
4971 for (i = 0, scan = results->candidates;
4972 i < results->count;
4973 i++, scan++) {
4974 Boolean is_primary = FALSE;
4975 Rank rank = scan->rank;
4976 Boolean skip = FALSE;
4977
4978 if (primary == NULL
4979 && RANK_ASSERTION_MASK(rank) != kRankAssertionNever) {
4980 if (ElectionResultsCandidateNeedsDemotion(other_results,
4981 scan)) {
4982 /* demote to RankNever */
4983 my_log(LOG_NOTICE,
4984 "IPv%c over %@ demoted: not primary for IPv%c",
4985 ipvx_char(af), scan->if_name, ipvx_other_char(af));
4986 rank = RankMake(rank, kRankAssertionNever);
4987 deferred[deferred_count++] = scan;
4988 skip = TRUE;
4989 }
4990 else {
4991 primary = CFRetain(scan->serviceID);
4992 is_primary = TRUE;
4993 }
4994 }
4995 if (af == AF_INET) {
4996 /* generate the routing table for IPv4 */
4997 CFDictionaryRef service_dict;
4998 IPv4RouteListRef service_routes;
4999
5000 service_dict
5001 = service_dict_get(scan->serviceID, kSCEntNetIPv4);
5002 service_routes = ipv4_dict_get_routelist(service_dict);
5003 if (service_routes != NULL) {
5004 routes = IPv4RouteListAddRouteList(routes,
5005 results->count * 3,
5006 service_routes,
5007 rank);
5008 if (service_routes->exclude_from_nwi) {
5009 skip = TRUE;
5010 }
5011 }
5012 else {
5013 skip = TRUE;
5014 }
5015 }
5016 else {
5017 /* a NULL service must be excluded from nwi */
5018 CFDictionaryRef ipv6_dict;
5019
5020 ipv6_dict = service_dict_get(scan->serviceID, kSCEntNetIPv6);
5021
5022 if (S_dict_get_boolean(ipv6_dict, kIsNULL, FALSE)) {
5023 skip = TRUE;
5024 }
5025 }
5026 if (skip) {
5027 /* if we're skipping the primary, it's NULL */
5028 if (is_primary) {
5029 primary_is_null = TRUE;
5030 }
5031 }
5032 else {
5033 if (primary_is_null) {
5034 /* everything after the primary must be Never */
5035 rank = RankMake(rank, kRankAssertionNever);
5036 }
5037 add_reachability_flags_to_candidate(scan, services_info, af);
5038 add_candidate_to_nwi_state(nwi_state, af, scan, rank);
5039 }
5040 }
5041 for (i = 0; i < deferred_count; i++) {
5042 CandidateRef candidate = deferred[i];
5043 Rank rank;
5044
5045 /* demote to RankNever */
5046 rank = RankMake(candidate->rank, kRankAssertionNever);
5047 add_reachability_flags_to_candidate(candidate, services_info, af);
5048 add_candidate_to_nwi_state(nwi_state, af, candidate, rank);
5049 }
5050 }
5051 if (nwi_state != NULL) {
5052 nwi_state_set_last(nwi_state, af);
5053 }
5054 if (ret_routes != NULL) {
5055 *ret_routes = routes;
5056 }
5057 else if (routes != NULL) {
5058 free(routes);
5059 }
5060 if (primary_is_null) {
5061 my_CFRelease(&primary);
5062 }
5063 return (primary);
5064 }
5065
5066
5067 static inline
5068 CFStringRef
5069 service_dict_get_signature(CFDictionaryRef service_dict)
5070 {
5071 return (CFDictionaryGetValue(service_dict, kStoreKeyNetworkSignature));
5072 }
5073
5074
5075 /*
5076 * Function: elect_ipv4
5077 * Purpose:
5078 * Evaluate the service and determine what rank the service should have.
5079 * If it's a suitable candidate, add it to the election results.
5080 */
5081 static void
5082 elect_ipv4(const void * key, const void * value, void * context)
5083 {
5084 Candidate candidate;
5085 CFStringRef if_name;
5086 ElectionInfoRef info;
5087 Rank primary_rank;
5088 CFDictionaryRef service_dict = (CFDictionaryRef)value;
5089 IPv4RouteListRef service_routes;
5090 CFDictionaryRef v4_dict;
5091 CFDictionaryRef v4_service_dict;
5092
5093 service_routes = service_dict_get_ipv4_routelist(service_dict);
5094 if (service_routes == NULL) {
5095 /* no service routes, no service */
5096 return;
5097 }
5098 if_name = service_dict_get_ipv4_ifname(service_dict);
5099 if (if_name == NULL) {
5100 /* need an interface name */
5101 return;
5102 }
5103 if (CFEqual(if_name, CFSTR("lo0"))) {
5104 /* don't ever elect loopback */
5105 return;
5106 }
5107 info = (ElectionInfoRef)context;
5108 bzero(&candidate, sizeof(candidate));
5109 candidate.serviceID = (CFStringRef)key;
5110 candidate.rank = get_service_rank(info->order, info->n_order,
5111 candidate.serviceID);
5112 primary_rank = RANK_ASSERTION_MASK(service_routes->list->rank);
5113 if (S_ppp_override_primary
5114 && (strncmp(PPP_PREFIX, service_routes->list->ifname,
5115 sizeof(PPP_PREFIX) - 1) == 0)) {
5116 /* PPP override: make ppp* look the best */
5117 /* Hack: should use interface type, not interface name */
5118 primary_rank = kRankAssertionFirst;
5119 }
5120 candidate.rank = RankMake(candidate.rank, primary_rank);
5121 candidate.ip_is_coupled = service_get_ip_is_coupled(candidate.serviceID);
5122 candidate.if_name = if_name;
5123 candidate.addr.v4 = service_routes->list->ifa;
5124 rank_dict_set_service_rank(S_ipv4_service_rank_dict,
5125 candidate.serviceID, candidate.rank);
5126 v4_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
5127 v4_service_dict = CFDictionaryGetValue(v4_dict, kIPv4DictService);
5128 candidate.signature = service_dict_get_signature(v4_service_dict);
5129 ElectionResultsAddCandidate(info->results, &candidate);
5130 return;
5131 }
5132
5133
5134 /*
5135 * Function: elect_ipv6
5136 * Purpose:
5137 * Evaluate the service and determine what rank the service should have.
5138 * If it's a suitable candidate, add it to the election results.
5139 */
5140 static void
5141 elect_ipv6(const void * key, const void * value, void * context)
5142 {
5143 CFArrayRef addrs;
5144 Candidate candidate;
5145 CFStringRef if_name;
5146 ElectionInfoRef info;
5147 Rank primary_rank = kRankAssertionDefault;
5148 CFDictionaryRef ipv6_dict;
5149 CFStringRef router;
5150 CFDictionaryRef service_dict = (CFDictionaryRef)value;
5151 CFDictionaryRef service_options;
5152
5153
5154 ipv6_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
5155 if (ipv6_dict == NULL) {
5156 /* no IPv6 */
5157 return;
5158 }
5159 if_name = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
5160 if (if_name == NULL) {
5161 /* need an interface name */
5162 return;
5163 }
5164 if (CFEqual(if_name, CFSTR("lo0"))) {
5165 /* don't ever elect loopback */
5166 return;
5167 }
5168 router = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Router);
5169 if (router == NULL) {
5170 /* don't care about services without a router */
5171 return;
5172 }
5173 info = (ElectionInfoRef)context;
5174 bzero(&candidate, sizeof(candidate));
5175 candidate.serviceID = (CFStringRef)key;
5176 candidate.if_name = if_name;
5177 addrs = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Addresses);
5178 (void)cfstring_to_ip6(CFArrayGetValueAtIndex(addrs, 0),
5179 &candidate.addr.v6);
5180 candidate.rank = get_service_rank(info->order, info->n_order,
5181 candidate.serviceID);
5182 service_options
5183 = service_dict_get(candidate.serviceID, kSCEntNetService);
5184 if (service_options != NULL) {
5185 CFStringRef primaryRankStr = NULL;
5186
5187 primaryRankStr = CFDictionaryGetValue(service_options,
5188 kSCPropNetServicePrimaryRank);
5189 if (primaryRankStr != NULL) {
5190 primary_rank = PrimaryRankGetRankAssertion(primaryRankStr);
5191 }
5192 candidate.ip_is_coupled
5193 = CFDictionaryContainsKey(service_options, kIPIsCoupled);
5194 }
5195 if (primary_rank != kRankAssertionNever) {
5196 if (get_override_primary(ipv6_dict)) {
5197 primary_rank = kRankAssertionFirst;
5198 }
5199 else if (S_ppp_override_primary
5200 && CFStringHasPrefix(if_name, CFSTR(PPP_PREFIX))) {
5201 /* PPP override: make ppp* look the best */
5202 /* Hack: should use interface type, not interface name */
5203 primary_rank = kRankAssertionFirst;
5204 }
5205 }
5206 candidate.rank = RankMake(candidate.rank, primary_rank);
5207 rank_dict_set_service_rank(S_ipv6_service_rank_dict,
5208 candidate.serviceID, candidate.rank);
5209 candidate.signature = service_dict_get_signature(ipv6_dict);
5210 ElectionResultsAddCandidate(info->results, &candidate);
5211 return;
5212 }
5213
5214 static uint32_t
5215 service_changed(CFDictionaryRef services_info, CFStringRef serviceID)
5216 {
5217 uint32_t changed = 0;
5218 int i;
5219
5220 /* update service options first (e.g. rank) */
5221 if (get_rank_changes(serviceID,
5222 get_service_state_entity(services_info, serviceID,
5223 NULL),
5224 get_service_setup_entity(services_info, serviceID,
5225 NULL),
5226 services_info)) {
5227 changed |= (1 << kEntityTypeServiceOptions);
5228 }
5229 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
5230 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
5231 GetEntityChangesFuncRef func = entityChangeFunc[i];
5232 if ((*func)(serviceID,
5233 get_service_state_entity(services_info, serviceID,
5234 *entityTypeNames[i]),
5235 get_service_setup_entity(services_info, serviceID,
5236 *entityTypeNames[i]),
5237 services_info)) {
5238 changed |= (1 << i);
5239 }
5240 }
5241
5242 if (get_transient_service_changes(serviceID, services_info)) {
5243 changed |= (1 << kEntityTypeVPNStatus);
5244 }
5245
5246 return (changed);
5247 }
5248
5249 static CFArrayRef
5250 service_order_get(CFDictionaryRef services_info)
5251 {
5252 CFArrayRef order = NULL;
5253 CFDictionaryRef ipv4_dict;
5254
5255 ipv4_dict = my_CFDictionaryGetDictionary(services_info,
5256 S_setup_global_ipv4);
5257 if (ipv4_dict != NULL) {
5258 CFNumberRef ppp_override;
5259 int ppp_val = 0;
5260
5261 order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
5262 order = isA_CFArray(order);
5263
5264 /* get ppp override primary */
5265 ppp_override = CFDictionaryGetValue(ipv4_dict,
5266 kSCPropNetPPPOverridePrimary);
5267 ppp_override = isA_CFNumber(ppp_override);
5268 if (ppp_override != NULL) {
5269 CFNumberGetValue(ppp_override, kCFNumberIntType, &ppp_val);
5270 }
5271 S_ppp_override_primary = (ppp_val != 0) ? TRUE : FALSE;
5272 }
5273 else {
5274 S_ppp_override_primary = FALSE;
5275 }
5276 return (order);
5277 }
5278
5279 static boolean_t
5280 set_new_primary(CFStringRef * primary_p, CFStringRef new_primary,
5281 const char * entity)
5282 {
5283 boolean_t changed = FALSE;
5284 CFStringRef primary = *primary_p;
5285
5286 if (new_primary != NULL) {
5287 if (primary != NULL && CFEqual(new_primary, primary)) {
5288 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5289 my_log(LOG_DEBUG,
5290 "IPMonitor: %@ is still primary %s",
5291 new_primary, entity);
5292 }
5293 }
5294 else {
5295 my_CFRelease(primary_p);
5296 *primary_p = CFRetain(new_primary);
5297 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5298 my_log(LOG_DEBUG,
5299 "IPMonitor: %@ is the new primary %s",
5300 new_primary, entity);
5301 }
5302 changed = TRUE;
5303 }
5304 }
5305 else if (primary != NULL) {
5306 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5307 my_log(LOG_DEBUG,
5308 "IPMonitor: %@ is no longer primary %s",
5309 primary, entity);
5310 }
5311 my_CFRelease(primary_p);
5312 changed = TRUE;
5313 }
5314 return (changed);
5315 }
5316
5317 static Rank
5318 rank_service_entity(CFDictionaryRef rank_dict, CFStringRef serviceID,
5319 CFStringRef entity)
5320 {
5321 if (service_dict_get(serviceID, entity) == NULL) {
5322 return (RankMake(kRankIndexMask, kRankAssertionDefault));
5323 }
5324 return (rank_dict_get_service_rank(rank_dict, serviceID));
5325 }
5326
5327 static void
5328 update_interface_rank(CFDictionaryRef services_info, CFStringRef ifname)
5329 {
5330 CFStringRef if_rank_key;
5331 CFDictionaryRef rank_dict;
5332
5333 if_rank_key = if_rank_key_copy(ifname);
5334 rank_dict = CFDictionaryGetValue(services_info, if_rank_key);
5335 CFRelease(if_rank_key);
5336 if_rank_set(ifname, rank_dict);
5337 return;
5338 }
5339
5340 static void
5341 append_serviceIDs_for_interface(CFMutableArrayRef services_changed,
5342 CFStringRef ifname)
5343 {
5344 int count;
5345 int i;
5346 void * * keys;
5347 #define N_KEYS_VALUES_STATIC 10
5348 void * keys_values_buf[N_KEYS_VALUES_STATIC * 2];
5349 void * * values;
5350
5351 count = CFDictionaryGetCount(S_service_state_dict);
5352 if (count <= N_KEYS_VALUES_STATIC) {
5353 keys = keys_values_buf;
5354 } else {
5355 keys = (void * *)malloc(sizeof(*keys) * count * 2);
5356 }
5357 values = keys + count;
5358 CFDictionaryGetKeysAndValues(S_service_state_dict,
5359 (const void * *)keys,
5360 (const void * *)values);
5361
5362 for (i = 0; i < count; i++) {
5363 CFDictionaryRef ipv4 = NULL;
5364 CFStringRef interface = NULL;
5365 CFStringRef serviceID;
5366 CFDictionaryRef service_dict;
5367
5368 serviceID = (CFStringRef)keys[i];
5369 service_dict = (CFDictionaryRef)values[i];
5370
5371 /* check if this is a ipv4 dictionary */
5372 ipv4 = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
5373 if (ipv4 != NULL) {
5374 interface = ipv4_dict_get_ifname(ipv4);
5375 if (interface != NULL && CFEqual(interface, ifname)) {
5376 if (S_IPMonitor_debug & kDebugFlag1) {
5377 my_log(LOG_DEBUG,
5378 "Found ipv4 service %@ on interface %@.",
5379 serviceID, ifname);
5380 }
5381
5382 my_CFArrayAppendUniqueValue(services_changed, serviceID);
5383 }
5384 } else {
5385 CFDictionaryRef proto_dict;
5386
5387 /* check if this is a ipv6 dictionary */
5388 proto_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
5389 if (proto_dict == NULL) {
5390 continue;
5391 }
5392 interface = CFDictionaryGetValue(proto_dict, kSCPropInterfaceName);
5393 if (interface != NULL && CFEqual(interface, ifname)) {
5394 if (S_IPMonitor_debug & kDebugFlag1) {
5395 my_log(LOG_DEBUG, "Found ipv6 service %@ on interface %@.",
5396 serviceID, ifname);
5397 }
5398
5399 my_CFArrayAppendUniqueValue(services_changed, serviceID);
5400 }
5401 }
5402 }
5403
5404 if (keys != keys_values_buf) {
5405 free(keys);
5406 }
5407 }
5408
5409 static __inline__ const char *
5410 get_changed_str(CFStringRef serviceID, CFStringRef entity, CFDictionaryRef old_dict)
5411 {
5412 CFDictionaryRef new_dict = NULL;
5413
5414 if (serviceID != NULL) {
5415 new_dict = service_dict_get(serviceID, entity);
5416 }
5417
5418 if (old_dict == NULL) {
5419 if (new_dict != NULL) {
5420 return "+";
5421 }
5422 } else {
5423 if (new_dict == NULL) {
5424 return "-";
5425 } else if (!CFEqual(old_dict, new_dict)) {
5426 return "!";
5427 }
5428 }
5429 return "";
5430 }
5431
5432 static CF_RETURNS_RETAINED CFStringRef
5433 generate_log_changes(nwi_state_t changes_state,
5434 boolean_t dns_changed,
5435 boolean_t dnsinfo_changed,
5436 CFDictionaryRef old_primary_dns,
5437 boolean_t proxy_changed,
5438 CFDictionaryRef old_primary_proxy,
5439 boolean_t smb_changed,
5440 CFDictionaryRef old_primary_smb
5441 )
5442 {
5443 int idx;
5444 CFMutableStringRef log_output;
5445 nwi_ifstate_t scan;
5446
5447 log_output = CFStringCreateMutable(NULL, 0);
5448
5449 if (changes_state != NULL) {
5450 for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) {
5451 CFMutableStringRef changes = NULL;
5452 CFMutableStringRef primary_str = NULL;
5453
5454 scan = nwi_state_get_first_ifstate(changes_state, nwi_af_list[idx]);
5455
5456 while (scan != NULL) {
5457 const char * changed_str;
5458
5459 changed_str = nwi_ifstate_get_diff_str(scan);
5460 if (changed_str != NULL) {
5461 void * address;
5462 const char * addr_str;
5463 char ntopbuf[INET6_ADDRSTRLEN];
5464
5465 address = (void *)nwi_ifstate_get_address(scan);
5466 addr_str = inet_ntop(scan->af, address,
5467 ntopbuf, sizeof(ntopbuf));
5468
5469 if (primary_str == NULL) {
5470 primary_str = CFStringCreateMutable(NULL, 0);
5471 CFStringAppendFormat(primary_str, NULL, CFSTR("%s%s:%s"),
5472 nwi_ifstate_get_ifname(scan),
5473 changed_str, addr_str);
5474 } else {
5475 if (changes == NULL) {
5476 changes = CFStringCreateMutable(NULL, 0);
5477 }
5478 CFStringAppendFormat(changes, NULL, CFSTR(", %s"),
5479 nwi_ifstate_get_ifname(scan));
5480 if (strcmp(changed_str, "") != 0) {
5481 CFStringAppendFormat(changes, NULL, CFSTR("%s:%s"),
5482 changed_str, addr_str);
5483 }
5484 }
5485 }
5486 scan = nwi_ifstate_get_next(scan, scan->af);
5487 }
5488
5489 if (primary_str != NULL) {
5490 CFStringAppendFormat(log_output, NULL, CFSTR(" %s(%@"),
5491 nwi_af_list[idx] == AF_INET ? "v4" : "v6",
5492 primary_str);
5493
5494 if (changes != NULL && CFStringGetLength(changes) != 0) {
5495 CFStringAppendFormat(log_output, NULL, CFSTR("%@"),
5496 changes);
5497 }
5498 CFStringAppendFormat(log_output, NULL, CFSTR(")"));
5499
5500 my_CFRelease(&primary_str);
5501 my_CFRelease(&changes);
5502 }
5503 }
5504 }
5505
5506 if (dns_changed || dnsinfo_changed) {
5507 const char *str;
5508
5509 str = get_changed_str(S_primary_dns, kSCEntNetDNS, old_primary_dns);
5510 if ((strcmp(str, "") == 0) && dnsinfo_changed) {
5511 str = "*"; // dnsinfo change w/no change to primary
5512 }
5513 CFStringAppendFormat(log_output, NULL, CFSTR(" DNS%s"), str);
5514 } else if (S_primary_dns != NULL) {
5515 CFStringAppendFormat(log_output, NULL, CFSTR(" DNS"));
5516 }
5517
5518 if (proxy_changed) {
5519 const char *str;
5520
5521 str = get_changed_str(S_primary_proxies, kSCEntNetProxies, old_primary_proxy);
5522 CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy%s"), str);
5523 } else if (S_primary_proxies != NULL) {
5524 CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy"));
5525 }
5526
5527 #if !TARGET_OS_IPHONE
5528 if (smb_changed) {
5529 const char *str;
5530
5531 str = get_changed_str(S_primary_smb, kSCEntNetSMB, old_primary_smb);
5532 CFStringAppendFormat(log_output, NULL, CFSTR(" SMB%s"), str);
5533 } else if (S_primary_smb != NULL) {
5534 CFStringAppendFormat(log_output, NULL, CFSTR(" SMB"));
5535 }
5536 #endif // !TARGET_OS_IPHONE
5537
5538 return log_output;
5539 }
5540
5541 #pragma mark -
5542 #pragma mark Network changed notification
5543
5544 static dispatch_queue_t
5545 __network_change_queue()
5546 {
5547 static dispatch_once_t once;
5548 static dispatch_queue_t q;
5549
5550 dispatch_once(&once, ^{
5551 q = dispatch_queue_create("network change queue", NULL);
5552 });
5553
5554 return q;
5555 }
5556
5557 // Note: must run on __network_change_queue()
5558 static void
5559 post_network_change_when_ready()
5560 {
5561 int status;
5562
5563 if (S_network_change_needed == 0) {
5564 return;
5565 }
5566
5567 if (!S_network_change_timeout &&
5568 (!S_dnsinfo_synced || !S_nwi_synced)) {
5569 // if we [still] need to wait for the DNS configuration
5570 // or network information changes to be ack'd
5571
5572 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5573 my_log(LOG_DEBUG,
5574 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s, %s)",
5575 S_dnsinfo_synced ? "DNS" : "!DNS",
5576 S_nwi_synced ? "nwi" : "!nwi");
5577 }
5578 return;
5579 }
5580
5581 // cancel any running timer
5582 if (S_network_change_timer != NULL) {
5583 dispatch_source_cancel(S_network_change_timer);
5584 dispatch_release(S_network_change_timer);
5585 S_network_change_timer = NULL;
5586 S_network_change_timeout = FALSE;
5587 }
5588
5589 // set (and log?) the post time
5590 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5591 struct timeval elapsed;
5592 struct timeval end;
5593
5594 (void) gettimeofday(&end, NULL);
5595 timersub(&end, &S_network_change_start, &elapsed);
5596
5597 #define QUERY_TIME__FMT "%d.%6.6d"
5598 #define QUERY_TIME__DIV 1
5599
5600 my_log(LOG_DEBUG,
5601 "Post \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s: " QUERY_TIME__FMT ": 0x%x)",
5602 S_network_change_timeout ? "timeout" : "delayed",
5603 elapsed.tv_sec,
5604 elapsed.tv_usec / QUERY_TIME__DIV,
5605 S_network_change_needed);
5606 }
5607
5608 if ((S_network_change_needed & NETWORK_CHANGE_NET) != 0) {
5609 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI);
5610 if (status != NOTIFY_STATUS_OK) {
5611 my_log(LOG_ERR,
5612 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI ") failed: error=%ld", status);
5613 }
5614 }
5615
5616 if ((S_network_change_needed & NETWORK_CHANGE_DNS) != 0) {
5617 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS);
5618 if (status != NOTIFY_STATUS_OK) {
5619 my_log(LOG_ERR,
5620 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS ") failed: error=%ld", status);
5621 }
5622 }
5623
5624 if ((S_network_change_needed & NETWORK_CHANGE_PROXY) != 0) {
5625 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
5626 if (status != NOTIFY_STATUS_OK) {
5627 my_log(LOG_ERR,
5628 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY ") failed: error=%ld", status);
5629 }
5630 }
5631
5632 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE);
5633 if (status != NOTIFY_STATUS_OK) {
5634 my_log(LOG_ERR,
5635 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE ") failed: error=%ld", status);
5636 }
5637
5638 S_network_change_needed = 0;
5639 return;
5640 }
5641
5642 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
5643
5644 // Note: must run on __network_change_queue()
5645 static void
5646 post_network_change(uint32_t change)
5647 {
5648 if (S_network_change_needed == 0) {
5649 // set the start time
5650 (void) gettimeofday(&S_network_change_start, NULL);
5651 }
5652
5653 // indicate that we need to post a change for ...
5654 S_network_change_needed |= change;
5655
5656 // cancel any running timer
5657 if (S_network_change_timer != NULL) {
5658 dispatch_source_cancel(S_network_change_timer);
5659 dispatch_release(S_network_change_timer);
5660 S_network_change_timer = NULL;
5661 S_network_change_timeout = FALSE;
5662 }
5663
5664 // if needed, start new timer
5665 if (!S_dnsinfo_synced || !S_nwi_synced) {
5666 S_network_change_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
5667 0,
5668 0,
5669 __network_change_queue());
5670 dispatch_source_set_event_handler(S_network_change_timer, ^{
5671 S_network_change_timeout = TRUE;
5672 post_network_change_when_ready();
5673 });
5674 dispatch_source_set_timer(S_network_change_timer,
5675 dispatch_time(DISPATCH_TIME_NOW,
5676 TRAILING_EDGE_TIMEOUT_NSEC), // start
5677 0, // interval
5678 10 * NSEC_PER_MSEC); // leeway
5679 dispatch_resume(S_network_change_timer);
5680 }
5681
5682 post_network_change_when_ready();
5683
5684 return;
5685 }
5686
5687 #pragma mark -
5688 #pragma mark Process network (SCDynamicStore) changes
5689
5690 static void
5691 IPMonitorNotify(SCDynamicStoreRef session, CFArrayRef changed_keys,
5692 void * not_used)
5693 {
5694 CFIndex count;
5695 uint32_t changes = 0;
5696 nwi_state_t changes_state = NULL;
5697 boolean_t dns_changed = FALSE;
5698 boolean_t dnsinfo_changed = FALSE;
5699 boolean_t global_ipv4_changed = FALSE;
5700 boolean_t global_ipv6_changed = FALSE;
5701 int i;
5702 CFMutableArrayRef if_rank_changes = NULL;
5703 keyChangeList keys;
5704 CFIndex n;
5705 CFStringRef network_change_msg = NULL;
5706 int n_services;
5707 int n_service_order = 0;
5708 nwi_state_t old_nwi_state = NULL;
5709 CFDictionaryRef old_primary_dns = NULL;
5710 CFDictionaryRef old_primary_proxy = NULL;
5711 #if !TARGET_OS_IPHONE
5712 CFDictionaryRef old_primary_smb = NULL;
5713 #endif // !TARGET_OS_IPHONE
5714 boolean_t proxies_changed = FALSE;
5715 boolean_t reachability_changed = FALSE;
5716 CFArrayRef service_order;
5717 CFMutableArrayRef service_changes = NULL;
5718 CFDictionaryRef services_info = NULL;
5719 #if !TARGET_OS_IPHONE
5720 boolean_t smb_changed = FALSE;
5721 #endif // !TARGET_OS_IPHONE
5722
5723 count = CFArrayGetCount(changed_keys);
5724 if (count == 0) {
5725 return;
5726 }
5727
5728 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5729 my_log(LOG_DEBUG,
5730 "IPMonitor: changes %@ (%d)", changed_keys, count);
5731 }
5732
5733 if (S_primary_dns != NULL) {
5734 old_primary_dns = service_dict_get(S_primary_dns, kSCEntNetDNS);
5735 if (old_primary_dns != NULL) {
5736 old_primary_dns = CFDictionaryCreateCopy(NULL, old_primary_dns);
5737 }
5738 }
5739
5740 if (S_primary_proxies != NULL) {
5741 old_primary_proxy = service_dict_get(S_primary_proxies, kSCEntNetProxies);
5742 if (old_primary_proxy != NULL) {
5743 old_primary_proxy = CFDictionaryCreateCopy(NULL, old_primary_proxy);
5744 }
5745 }
5746
5747 #if !TARGET_OS_IPHONE
5748 if (S_primary_smb != NULL) {
5749 old_primary_smb = service_dict_get(S_primary_smb, kSCEntNetSMB);
5750 if (old_primary_smb != NULL) {
5751 old_primary_smb = CFDictionaryCreateCopy(NULL, old_primary_smb);
5752 }
5753 }
5754 #endif // !TARGET_OS_IPHONE
5755
5756 keyChangeListInit(&keys);
5757 service_changes = CFArrayCreateMutable(NULL, 0,
5758 &kCFTypeArrayCallBacks);
5759
5760 for (i = 0; i < count; i++) {
5761 CFStringRef change = CFArrayGetValueAtIndex(changed_keys, i);
5762 if (CFEqual(change, S_setup_global_ipv4)) {
5763 global_ipv4_changed = TRUE;
5764 global_ipv6_changed = TRUE;
5765 }
5766 else if (CFEqual(change, S_multicast_resolvers)) {
5767 dnsinfo_changed = TRUE;
5768 }
5769 else if (CFEqual(change, S_private_resolvers)) {
5770 dnsinfo_changed = TRUE;
5771 }
5772 #if !TARGET_OS_IPHONE
5773 else if (CFEqual(change, CFSTR(_PATH_RESOLVER_DIR))) {
5774 dnsinfo_changed = TRUE;
5775 }
5776 #endif /* !TARGET_OS_IPHONE */
5777 else if (CFStringHasPrefix(change, S_state_service_prefix)) {
5778 int i;
5779 CFStringRef serviceID;
5780
5781 for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) {
5782 if (CFStringHasSuffix(change, *statusEntityNames[i])) {
5783 dnsinfo_changed = TRUE;
5784 break;
5785 }
5786 }
5787
5788 serviceID = parse_component(change, S_state_service_prefix);
5789 if (serviceID) {
5790 my_CFArrayAppendUniqueValue(service_changes, serviceID);
5791 CFRelease(serviceID);
5792 }
5793 }
5794 else if (CFStringHasPrefix(change, S_setup_service_prefix)) {
5795 int j;
5796
5797 CFStringRef serviceID = parse_component(change,
5798 S_setup_service_prefix);
5799 if (serviceID) {
5800 my_CFArrayAppendUniqueValue(service_changes, serviceID);
5801 CFRelease(serviceID);
5802 }
5803
5804 for (j = 0; j < sizeof(transientInterfaceEntityNames)/sizeof(transientInterfaceEntityNames[0]); j++) {
5805 if (CFStringHasSuffix(change, *transientInterfaceEntityNames[j])) {
5806 reachability_changed = TRUE;
5807 break;
5808 }
5809 }
5810
5811 if (CFStringHasSuffix(change, kSCEntNetInterface)) {
5812 reachability_changed = TRUE;
5813 }
5814
5815
5816 }
5817 else if (CFStringHasSuffix(change, kSCEntNetService)) {
5818 CFStringRef ifname = my_CFStringCopyComponent(change, CFSTR("/"), 3);
5819
5820 if (ifname != NULL) {
5821 if (if_rank_changes == NULL) {
5822 if_rank_changes = CFArrayCreateMutable(NULL, 0,
5823 &kCFTypeArrayCallBacks);
5824 }
5825 my_CFArrayAppendUniqueValue(if_rank_changes, ifname);
5826 CFRelease(ifname);
5827 }
5828 }
5829 }
5830
5831 /* determine which serviceIDs are impacted by the interface rank changes */
5832 if (if_rank_changes != NULL) {
5833 n = CFArrayGetCount(if_rank_changes);
5834 for (i = 0; i < n; i++) {
5835 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i);
5836
5837 if (S_IPMonitor_debug & kDebugFlag1) {
5838 my_log(LOG_DEBUG, "Interface rank changed %@",
5839 ifname);
5840 }
5841 append_serviceIDs_for_interface(service_changes, ifname);
5842 }
5843 }
5844
5845 /* grab a snapshot of everything we need */
5846 services_info = services_info_copy(session, service_changes,
5847 if_rank_changes);
5848 service_order = service_order_get(services_info);
5849 if (service_order != NULL) {
5850 n_service_order = CFArrayGetCount(service_order);
5851 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5852 my_log(LOG_DEBUG,
5853 "IPMonitor: service_order %@ ", service_order);
5854 }
5855 }
5856
5857 if (if_rank_changes != NULL) {
5858 for (i = 0; i < n; i++) {
5859 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i);
5860 update_interface_rank(services_info, ifname);
5861 }
5862 }
5863
5864 n = CFArrayGetCount(service_changes);
5865 for (i = 0; i < n; i++) {
5866 uint32_t changes;
5867 CFStringRef serviceID;
5868
5869 serviceID = CFArrayGetValueAtIndex(service_changes, i);
5870 changes = service_changed(services_info, serviceID);
5871 if ((changes & (1 << kEntityTypeServiceOptions)) != 0) {
5872 /* if __Service__ (e.g. PrimaryRank) changed */
5873 global_ipv4_changed = TRUE;
5874 global_ipv6_changed = TRUE;
5875 }
5876 else {
5877 if ((changes & (1 << kEntityTypeIPv4)) != 0) {
5878 global_ipv4_changed = TRUE;
5879 dnsinfo_changed = TRUE;
5880 proxies_changed = TRUE;
5881 }
5882 if ((changes & (1 << kEntityTypeIPv6)) != 0) {
5883 global_ipv6_changed = TRUE;
5884 dnsinfo_changed = TRUE;
5885 proxies_changed = TRUE;
5886 }
5887 }
5888 if ((changes & (1 << kEntityTypeDNS)) != 0) {
5889 if (S_primary_dns != NULL && CFEqual(S_primary_dns, serviceID)) {
5890 dns_changed = TRUE;
5891 }
5892 dnsinfo_changed = TRUE;
5893 }
5894 if ((changes & (1 << kEntityTypeProxies)) != 0) {
5895 proxies_changed = TRUE;
5896 }
5897 #if !TARGET_OS_IPHONE
5898 if ((changes & (1 << kEntityTypeSMB)) != 0) {
5899 if (S_primary_smb != NULL && CFEqual(S_primary_smb, serviceID)) {
5900 smb_changed = TRUE;
5901 }
5902 }
5903 #endif
5904 }
5905
5906 if ((changes & (1 <<kEntityTypeVPNStatus)) != 0) {
5907 global_ipv4_changed = TRUE;
5908 global_ipv6_changed = TRUE;
5909 }
5910
5911 /* ensure S_nwi_state can hold as many services as we have currently */
5912 n_services = CFDictionaryGetCount(S_service_state_dict);
5913 old_nwi_state = nwi_state_copy_priv(S_nwi_state);
5914 S_nwi_state = nwi_state_new(S_nwi_state, n_services);
5915
5916 if (global_ipv4_changed) {
5917 if (S_ipv4_results != NULL) {
5918 ElectionResultsRelease(S_ipv4_results);
5919 }
5920 S_ipv4_results
5921 = ElectionResultsCopy(elect_ipv4, service_order, n_service_order);
5922 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5923 ElectionResultsLog(LOG_DEBUG, S_ipv4_results, "IPv4");
5924 }
5925 }
5926 if (global_ipv6_changed) {
5927 if (S_ipv6_results != NULL) {
5928 ElectionResultsRelease(S_ipv6_results);
5929 }
5930 S_ipv6_results
5931 = ElectionResultsCopy(elect_ipv6, service_order, n_service_order);
5932 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5933 ElectionResultsLog(LOG_DEBUG, S_ipv6_results, "IPv6");
5934 }
5935 }
5936 if (global_ipv4_changed || global_ipv6_changed || dnsinfo_changed) {
5937 CFStringRef new_primary;
5938 IPv4RouteListRef new_routelist = NULL;
5939
5940 /* IPv4 */
5941 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5942 my_log(LOG_DEBUG,
5943 "IPMonitor: electing IPv4 primary");
5944 }
5945 new_primary = ElectionResultsCopyPrimary(S_ipv4_results,
5946 S_ipv6_results,
5947 S_nwi_state, AF_INET,
5948 &new_routelist,
5949 services_info);
5950 (void)set_new_primary(&S_primary_ipv4, new_primary, "IPv4");
5951 update_ipv4(S_primary_ipv4, new_routelist, &keys);
5952 my_CFRelease(&new_primary);
5953
5954 /* IPv6 */
5955 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5956 my_log(LOG_DEBUG,
5957 "IPMonitor: electing IPv6 primary");
5958 }
5959 new_primary = ElectionResultsCopyPrimary(S_ipv6_results,
5960 S_ipv4_results,
5961 S_nwi_state, AF_INET6,
5962 NULL,
5963 services_info);
5964 (void)set_new_primary(&S_primary_ipv6, new_primary, "IPv6");
5965 update_ipv6(S_primary_ipv6, &keys);
5966 my_CFRelease(&new_primary);
5967 }
5968
5969 if (global_ipv4_changed || global_ipv6_changed) {
5970 CFStringRef new_primary_dns = NULL;
5971 CFStringRef new_primary_proxies = NULL;
5972 #if !TARGET_OS_IPHONE
5973 CFStringRef new_primary_smb = NULL;
5974 #endif /* !TARGET_OS_IPHONE */
5975
5976 if (S_primary_ipv4 != NULL && S_primary_ipv6 != NULL) {
5977 /* decide between IPv4 and IPv6 */
5978 if (rank_service_entity(S_ipv4_service_rank_dict,
5979 S_primary_ipv4, kSCEntNetDNS)
5980 <= rank_service_entity(S_ipv6_service_rank_dict,
5981 S_primary_ipv6, kSCEntNetDNS)) {
5982 new_primary_dns = S_primary_ipv4;
5983 }
5984 else {
5985 new_primary_dns = S_primary_ipv6;
5986 }
5987 if (rank_service_entity(S_ipv4_service_rank_dict,
5988 S_primary_ipv4, kSCEntNetProxies)
5989 <= rank_service_entity(S_ipv6_service_rank_dict,
5990 S_primary_ipv6, kSCEntNetProxies)) {
5991 new_primary_proxies = S_primary_ipv4;
5992 }
5993 else {
5994 new_primary_proxies = S_primary_ipv6;
5995 }
5996 #if !TARGET_OS_IPHONE
5997 if (rank_service_entity(S_ipv4_service_rank_dict,
5998 S_primary_ipv4, kSCEntNetSMB)
5999 <= rank_service_entity(S_ipv6_service_rank_dict,
6000 S_primary_ipv6, kSCEntNetSMB)) {
6001 new_primary_smb = S_primary_ipv4;
6002 }
6003 else {
6004 new_primary_smb = S_primary_ipv6;
6005 }
6006 #endif /* !TARGET_OS_IPHONE */
6007
6008 }
6009 else if (S_primary_ipv6 != NULL) {
6010 new_primary_dns = S_primary_ipv6;
6011 new_primary_proxies = S_primary_ipv6;
6012 #if !TARGET_OS_IPHONE
6013 new_primary_smb = S_primary_ipv6;
6014 #endif /* !TARGET_OS_IPHONE */
6015 }
6016 else if (S_primary_ipv4 != NULL) {
6017 new_primary_dns = S_primary_ipv4;
6018 new_primary_proxies = S_primary_ipv4;
6019 #if !TARGET_OS_IPHONE
6020 new_primary_smb = S_primary_ipv4;
6021 #endif /* !TARGET_OS_IPHONE */
6022 }
6023
6024 if (set_new_primary(&S_primary_dns, new_primary_dns, "DNS")) {
6025 dns_changed = TRUE;
6026 dnsinfo_changed = TRUE;
6027 }
6028 if (set_new_primary(&S_primary_proxies, new_primary_proxies, "Proxies")) {
6029 proxies_changed = TRUE;
6030 }
6031 #if !TARGET_OS_IPHONE
6032 if (set_new_primary(&S_primary_smb, new_primary_smb, "SMB")) {
6033 smb_changed = TRUE;
6034 }
6035 #endif /* !TARGET_OS_IPHONE */
6036 }
6037
6038 if (!proxies_changed && dnsinfo_changed &&
6039 ((G_supplemental_proxies_follow_dns != NULL) && CFBooleanGetValue(G_supplemental_proxies_follow_dns))) {
6040 proxies_changed = TRUE;
6041 }
6042
6043 changes_state = nwi_state_diff(old_nwi_state, S_nwi_state);
6044
6045 if (global_ipv4_changed || global_ipv6_changed || dnsinfo_changed || reachability_changed) {
6046 if (S_nwi_state != NULL) {
6047 S_nwi_state->generation_count = mach_absolute_time();
6048 if (global_ipv4_changed || global_ipv6_changed || reachability_changed) {
6049 SCNetworkReachabilityFlags reach_flags_v4 = 0;
6050 SCNetworkReachabilityFlags reach_flags_v6 = 0;
6051
6052 GetReachabilityFlagsFromTransientServices(services_info,
6053 &reach_flags_v4,
6054 &reach_flags_v6);
6055
6056 _nwi_state_set_reachability_flags(S_nwi_state, reach_flags_v4, reach_flags_v6);
6057 }
6058
6059 /* Update the per-interface generation count */
6060 _nwi_state_update_interface_generations(old_nwi_state, S_nwi_state, changes_state);
6061 }
6062
6063 if (update_nwi(S_nwi_state)) {
6064 changes |= NETWORK_CHANGE_NET;
6065
6066 /*
6067 * the DNS configuration includes per-resolver configuration
6068 * reachability flags that are based on the nwi state. Let's
6069 * make sure that we check for changes
6070 */
6071 dnsinfo_changed = TRUE;
6072 }
6073 }
6074 if (dns_changed) {
6075 if (update_dns(services_info, S_primary_dns, &keys)) {
6076 changes |= NETWORK_CHANGE_DNS;
6077 dnsinfo_changed = TRUE;
6078 } else {
6079 dns_changed = FALSE;
6080 }
6081 }
6082 if (dnsinfo_changed) {
6083 if (update_dnsinfo(services_info, S_primary_dns, &keys, service_order)) {
6084 changes |= NETWORK_CHANGE_DNS;
6085 } else {
6086 dnsinfo_changed = FALSE;
6087 }
6088 }
6089 if (proxies_changed) {
6090 // if proxy change OR supplemental Proxies follow supplemental DNS
6091 if (update_proxies(services_info, S_primary_proxies, &keys, service_order)) {
6092 changes |= NETWORK_CHANGE_PROXY;
6093 } else {
6094 proxies_changed = FALSE;
6095 }
6096 }
6097 #if !TARGET_OS_IPHONE
6098 if (smb_changed) {
6099 if (update_smb(services_info, S_primary_smb, &keys)) {
6100 changes |= NETWORK_CHANGE_SMB;
6101 } else {
6102 smb_changed = FALSE;
6103 }
6104 }
6105 #endif /* !TARGET_OS_IPHONE */
6106 my_CFRelease(&service_changes);
6107 my_CFRelease(&services_info);
6108 my_CFRelease(&if_rank_changes);
6109
6110 if (changes != 0) {
6111 network_change_msg =
6112 generate_log_changes(changes_state,
6113 dns_changed,
6114 dnsinfo_changed,
6115 old_primary_dns,
6116 proxies_changed,
6117 old_primary_proxy,
6118 #if !TARGET_OS_IPHONE
6119 smb_changed,
6120 old_primary_smb
6121 #else // !TARGET_OS_IPHONE
6122 FALSE, // smb_changed
6123 NULL // old_primary_smb
6124 #endif // !TARGET_OS_IPHONE
6125 );
6126 }
6127
6128 keyChangeListApplyToStore(&keys, session);
6129 my_CFRelease(&old_primary_dns);
6130 my_CFRelease(&old_primary_proxy);
6131 #if !TARGET_OS_IPHONE
6132 my_CFRelease(&old_primary_smb);
6133 #endif // !TARGET_OS_IPHONE
6134
6135 if (changes != 0) {
6136 dispatch_async(__network_change_queue(), ^{
6137 post_network_change(changes);
6138 });
6139 }
6140
6141 if ((network_change_msg != NULL) && (CFStringGetLength(network_change_msg) != 0)) {
6142 my_log(LOG_NOTICE, "network changed:%@", network_change_msg);
6143 } else if (keyChangeListActive(&keys)) {
6144 my_log(LOG_NOTICE, "network changed.");
6145 } else {
6146 my_log(LOG_DEBUG, "network event w/no changes");
6147 }
6148
6149 my_CFRelease(&network_change_msg);
6150
6151 if (changes_state != NULL) {
6152 nwi_state_release(changes_state);
6153 }
6154 if (old_nwi_state != NULL) {
6155 nwi_state_release(old_nwi_state);
6156 }
6157 keyChangeListFree(&keys);
6158 return;
6159 }
6160
6161 static void
6162 watch_proxies()
6163 {
6164 #if !TARGET_OS_IPHONE
6165 const _scprefs_observer_type type = scprefs_observer_type_mcx;
6166 #else
6167 const _scprefs_observer_type type = scprefs_observer_type_global;
6168 #endif
6169 static dispatch_queue_t proxy_cb_queue;
6170
6171 proxy_cb_queue = dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL);
6172 _scprefs_observer_watch(type,
6173 "com.apple.SystemConfiguration.plist",
6174 proxy_cb_queue,
6175 ^{
6176 SCDynamicStoreNotifyValue(NULL, S_state_global_proxies);
6177 notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
6178 my_log(LOG_DEBUG, "IPMonitor: Notifying:\n%@",
6179 S_state_global_proxies);
6180 });
6181 return;
6182 }
6183
6184 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6185 #include "IPMonitorControlPrefs.h"
6186
6187 __private_extern__ SCLoggerRef
6188 my_log_get_logger()
6189 {
6190 return (S_IPMonitor_logger);
6191 }
6192
6193 static void
6194 prefs_changed(__unused SCPreferencesRef prefs)
6195 {
6196 if (S_bundle_logging_verbose || IPMonitorControlPrefsIsVerbose()) {
6197 S_IPMonitor_debug = kDebugFlagDefault;
6198 S_IPMonitor_verbose = TRUE;
6199 SCLoggerSetFlags(S_IPMonitor_logger, kSCLoggerFlagsFile | kSCLoggerFlagsDefault);
6200 my_log(LOG_DEBUG, "IPMonitor: Setting logging verbose mode on.");
6201 } else {
6202 my_log(LOG_DEBUG, "IPMonitor: Setting logging verbose mode off.");
6203 S_IPMonitor_debug = 0;
6204 S_IPMonitor_verbose = FALSE;
6205 SCLoggerSetFlags(S_IPMonitor_logger, kSCLoggerFlagsDefault);
6206 }
6207 return;
6208 }
6209
6210 #define LOGGER_ID CFSTR("com.apple.networking.IPMonitor")
6211 static void
6212 my_log_init()
6213 {
6214 if (S_IPMonitor_logger != NULL) {
6215 return;
6216 }
6217 S_IPMonitor_logger = SCLoggerCreate(LOGGER_ID);
6218 return;
6219
6220 }
6221
6222 #else // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6223
6224 static void
6225 my_log_init()
6226 {
6227 return;
6228 }
6229
6230 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6231
6232
6233 static void
6234 ip_plugin_init()
6235 {
6236 CFMutableArrayRef keys = NULL;
6237 CFStringRef pattern;
6238 CFMutableArrayRef patterns = NULL;
6239 CFRunLoopSourceRef rls = NULL;
6240
6241 if (S_is_network_boot() != 0) {
6242 S_netboot = TRUE;
6243 }
6244
6245 #ifdef RTF_IFSCOPE
6246 if (S_is_scoped_routing_enabled() != 0) {
6247 S_scopedroute = TRUE;
6248 }
6249
6250 if (S_is_scoped_v6_routing_enabled() != 0) {
6251 S_scopedroute_v6 = TRUE;
6252 }
6253 #endif /* RTF_IFSCOPE */
6254
6255 S_session = SCDynamicStoreCreate(NULL, CFSTR("IPMonitor"),
6256 IPMonitorNotify, NULL);
6257 if (S_session == NULL) {
6258 my_log(LOG_ERR,
6259 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
6260 SCErrorString(SCError()));
6261 return;
6262 }
6263 S_state_global_ipv4
6264 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6265 kSCDynamicStoreDomainState,
6266 kSCEntNetIPv4);
6267 S_state_global_ipv6
6268 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6269 kSCDynamicStoreDomainState,
6270 kSCEntNetIPv6);
6271 S_state_global_dns
6272 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6273 kSCDynamicStoreDomainState,
6274 kSCEntNetDNS);
6275 S_state_global_proxies
6276 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6277 kSCDynamicStoreDomainState,
6278 kSCEntNetProxies);
6279 #if !TARGET_OS_IPHONE
6280 S_state_global_smb
6281 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6282 kSCDynamicStoreDomainState,
6283 kSCEntNetSMB);
6284 #endif /* !TARGET_OS_IPHONE */
6285 S_setup_global_ipv4
6286 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6287 kSCDynamicStoreDomainSetup,
6288 kSCEntNetIPv4);
6289 S_state_service_prefix
6290 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6291 kSCDynamicStoreDomainState,
6292 CFSTR(""),
6293 NULL);
6294 S_setup_service_prefix
6295 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6296 kSCDynamicStoreDomainSetup,
6297 CFSTR(""),
6298 NULL);
6299 S_service_state_dict
6300 = CFDictionaryCreateMutable(NULL, 0,
6301 &kCFTypeDictionaryKeyCallBacks,
6302 &kCFTypeDictionaryValueCallBacks);
6303
6304 S_ipv4_service_rank_dict
6305 = CFDictionaryCreateMutable(NULL, 0,
6306 &kCFTypeDictionaryKeyCallBacks,
6307 &kCFTypeDictionaryValueCallBacks);
6308
6309 S_ipv6_service_rank_dict
6310 = CFDictionaryCreateMutable(NULL, 0,
6311 &kCFTypeDictionaryKeyCallBacks,
6312 &kCFTypeDictionaryValueCallBacks);
6313
6314 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
6315 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
6316
6317 /* register for State: and Setup: per-service notifications */
6318 add_service_keys(kSCCompAnyRegex, keys, patterns);
6319
6320 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetPPP);
6321 CFArrayAppendValue(patterns, pattern);
6322 CFRelease(pattern);
6323
6324 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
6325 CFArrayAppendValue(patterns, pattern);
6326 CFRelease(pattern);
6327
6328 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetInterface);
6329 CFArrayAppendValue(patterns, pattern);
6330 CFRelease(pattern);
6331
6332 /* register for State: per-service PPP/VPN/IPSec status notifications */
6333 add_status_keys(kSCCompAnyRegex, patterns);
6334
6335 /* register for interface rank notifications */
6336 pattern = if_rank_key_copy(kSCCompAnyRegex);
6337 CFArrayAppendValue(patterns, pattern);
6338 CFRelease(pattern);
6339
6340 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
6341 CFArrayAppendValue(keys, S_setup_global_ipv4);
6342
6343 /* add notifier for multicast DNS configuration (Bonjour/.local) */
6344 S_multicast_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
6345 kSCDynamicStoreDomainState,
6346 kSCCompNetwork,
6347 CFSTR(kDNSServiceCompMulticastDNS));
6348 CFArrayAppendValue(keys, S_multicast_resolvers);
6349
6350 /* add notifier for private DNS configuration (Back to My Mac) */
6351 S_private_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
6352 kSCDynamicStoreDomainState,
6353 kSCCompNetwork,
6354 CFSTR(kDNSServiceCompPrivateDNS));
6355 CFArrayAppendValue(keys, S_private_resolvers);
6356
6357 if (!SCDynamicStoreSetNotificationKeys(S_session, keys, patterns)) {
6358 my_log(LOG_ERR,
6359 "IPMonitor ip_plugin_init "
6360 "SCDynamicStoreSetNotificationKeys failed: %s",
6361 SCErrorString(SCError()));
6362 goto done;
6363 }
6364
6365 rls = SCDynamicStoreCreateRunLoopSource(NULL, S_session, 0);
6366 if (rls == NULL) {
6367 my_log(LOG_ERR,
6368 "IPMonitor ip_plugin_init "
6369 "SCDynamicStoreCreateRunLoopSource failed: %s",
6370 SCErrorString(SCError()));
6371 goto done;
6372 }
6373
6374 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
6375 CFRelease(rls);
6376
6377 /* initialize dns configuration */
6378 (void)dns_configuration_set(NULL, NULL, NULL, NULL, NULL);
6379 #if !TARGET_OS_IPHONE
6380 empty_dns();
6381 #endif /* !TARGET_OS_IPHONE */
6382 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_dns);
6383
6384 #if !TARGET_OS_IPHONE
6385 /* initialize SMB configuration */
6386 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_smb);
6387 #endif /* !TARGET_OS_IPHONE */
6388
6389 if_rank_dict_init();
6390 watch_proxies();
6391
6392 done:
6393 my_CFRelease(&keys);
6394 my_CFRelease(&patterns);
6395 return;
6396 }
6397
6398 __private_extern__
6399 void
6400 prime_IPMonitor()
6401 {
6402 /* initialize multicast route */
6403 update_ipv4(NULL, NULL, NULL);
6404 return;
6405 }
6406
6407 static boolean_t
6408 S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
6409 boolean_t def)
6410 {
6411 CFBooleanRef b;
6412 boolean_t ret = def;
6413
6414 b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
6415 if (b != NULL) {
6416 ret = CFBooleanGetValue(b);
6417 }
6418 return (ret);
6419 }
6420
6421 __private_extern__
6422 void
6423 load_IPMonitor(CFBundleRef bundle, Boolean bundleVerbose)
6424 {
6425 CFDictionaryRef info_dict;
6426
6427 info_dict = CFBundleGetInfoDictionary(bundle);
6428
6429 if (info_dict != NULL) {
6430 S_append_state
6431 = S_get_plist_boolean(info_dict,
6432 CFSTR("AppendStateArrayToSetupArray"),
6433 FALSE);
6434 }
6435 if (bundleVerbose) {
6436 S_IPMonitor_debug = kDebugFlagDefault;
6437 S_bundle_logging_verbose = bundleVerbose;
6438 S_IPMonitor_verbose = TRUE;
6439 }
6440
6441 my_log_init();
6442
6443 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6444 /* register to receive changes to verbose and read the initial setting */
6445 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed);
6446 prefs_changed(NULL);
6447
6448 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6449
6450 load_DNSConfiguration(bundle, // bundle
6451 S_IPMonitor_logger, // SCLogger
6452 &S_IPMonitor_verbose, // bundleVerbose
6453 ^(Boolean inSync) { // syncHandler
6454 dispatch_async(__network_change_queue(), ^{
6455 S_dnsinfo_synced = inSync;
6456
6457 if (inSync &&
6458 ((S_network_change_needed & NETWORK_CHANGE_DNS) == 0)) {
6459 // all of the mDNSResponder ack's should result
6460 // in a [new] network change being posted
6461 post_network_change(NETWORK_CHANGE_DNS);
6462 } else {
6463 post_network_change_when_ready();
6464 }
6465 });
6466 });
6467
6468 load_NetworkInformation(bundle, // bundle
6469 S_IPMonitor_logger, // SCLogger
6470 &S_IPMonitor_verbose, // bundleVerbose
6471 ^(Boolean inSync) { // syncHandler
6472 dispatch_async(__network_change_queue(), ^{
6473 S_nwi_synced = inSync;
6474 post_network_change_when_ready();
6475 });
6476 });
6477
6478 dns_configuration_init(bundle);
6479
6480 proxy_configuration_init(bundle);
6481
6482 ip_plugin_init();
6483
6484 #if !TARGET_OS_IPHONE
6485 if (S_session != NULL) {
6486 dns_configuration_monitor(S_session, IPMonitorNotify);
6487 }
6488 #endif /* !TARGET_OS_IPHONE */
6489
6490 #if !TARGET_IPHONE_SIMULATOR
6491 load_hostname((S_IPMonitor_debug & kDebugFlag1) != 0);
6492 #endif /* !TARGET_IPHONE_SIMULATOR */
6493
6494 #if !TARGET_OS_IPHONE
6495 load_smb_configuration((S_IPMonitor_debug & kDebugFlag1) != 0);
6496 #endif /* !TARGET_OS_IPHONE */
6497
6498 return;
6499 }
6500
6501
6502 #pragma mark -
6503 #pragma mark Standalone test code
6504
6505
6506 #ifdef TEST_IPMONITOR
6507
6508 #include "dns-configuration.c"
6509
6510 #if !TARGET_IPHONE_SIMULATOR
6511 #include "set-hostname.c"
6512 #endif /* !TARGET_IPHONE_SIMULATOR */
6513
6514 int
6515 main(int argc, char **argv)
6516 {
6517 _sc_log = FALSE;
6518
6519 S_IPMonitor_debug = kDebugFlag1;
6520 if (argc > 1) {
6521 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
6522 }
6523
6524 load_IPMonitor(CFBundleGetMainBundle(), FALSE);
6525 prime_IPMonitor();
6526 S_IPMonitor_debug = kDebugFlag1;
6527 CFRunLoopRun();
6528 /* not reached */
6529 exit(0);
6530 return 0;
6531 }
6532 #endif /* TEST_IPMONITOR */
6533
6534 #ifdef TEST_IPV4_ROUTELIST
6535
6536 #include "dns-configuration.c"
6537
6538 #if !TARGET_IPHONE_SIMULATOR
6539 #include "set-hostname.c"
6540 #endif /* !TARGET_IPHONE_SIMULATOR */
6541
6542 struct ipv4_service_contents {
6543 const char * addr;
6544 const char * mask;
6545 const char * dest;
6546 const char * router;
6547 const char * ifname;
6548 Rank rank;
6549 const CFStringRef *primaryRank;
6550 };
6551
6552 /*
6553 * addr mask dest router ifname pri primaryRank
6554 */
6555 struct ipv4_service_contents en0_10 = {
6556 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, NULL
6557 };
6558
6559 struct ipv4_service_contents en0_15 = {
6560 "10.0.0.19", "255.255.255.0", NULL, "10.0.0.1", "en0", 15, NULL
6561 };
6562
6563 struct ipv4_service_contents en0_30 = {
6564 "10.0.0.11", "255.255.255.0", NULL, "10.0.0.1", "en0", 30, NULL
6565 };
6566
6567 struct ipv4_service_contents en0_40 = {
6568 "10.0.0.12", "255.255.255.0", NULL, "10.0.0.1", "en0", 40, NULL
6569 };
6570
6571 struct ipv4_service_contents en0_50 = {
6572 "10.0.0.13", "255.255.255.0", NULL, "10.0.0.1", "en0", 50, NULL
6573 };
6574
6575 struct ipv4_service_contents en0_110 = {
6576 "192.168.2.10", "255.255.255.0", NULL, "192.168.2.1", "en0", 110, NULL
6577 };
6578
6579 struct ipv4_service_contents en0_1 = {
6580 "17.202.40.191", "255.255.252.0", NULL, "17.202.20.1", "en0", 1, NULL
6581 };
6582
6583 struct ipv4_service_contents en1_20 = {
6584 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, NULL
6585 };
6586
6587 struct ipv4_service_contents en1_2 = {
6588 "17.202.42.24", "255.255.252.0", NULL, "17.202.20.1", "en1", 2, NULL
6589 };
6590
6591 struct ipv4_service_contents en1_125 = {
6592 "192.168.2.20", "255.255.255.0", NULL, "192.168.2.1", "en1", 125, NULL
6593 };
6594
6595 struct ipv4_service_contents fw0_25 = {
6596 "192.168.2.30", "255.255.255.0", NULL, "192.168.2.1", "fw0", 25, NULL
6597 };
6598
6599 struct ipv4_service_contents fw0_21 = {
6600 "192.168.3.30", "255.255.255.0", NULL, "192.168.3.1", "fw0", 21, NULL
6601 };
6602
6603 struct ipv4_service_contents ppp0_0_1 = {
6604 "17.219.156.22", NULL, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
6605 };
6606
6607 struct ipv4_service_contents en0_test6 = {
6608 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 2, NULL
6609 };
6610 struct ipv4_service_contents en1_test6 = {
6611 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 3, NULL
6612 };
6613 struct ipv4_service_contents en2_test6 = {
6614 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL
6615 };
6616
6617 struct ipv4_service_contents en0_test7 = {
6618 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 3, NULL
6619 };
6620 struct ipv4_service_contents en1_test7 = {
6621 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 2, NULL
6622 };
6623 struct ipv4_service_contents en2_test7 = {
6624 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL
6625 };
6626 struct ipv4_service_contents fw0_test6_and_7 = {
6627 "169.254.11.33", "255.255.0.0", NULL, NULL, "fw0", 0x0ffffff, NULL
6628 };
6629
6630 struct ipv4_service_contents en0_10_last = {
6631 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
6632 };
6633
6634 struct ipv4_service_contents en0_10_never = {
6635 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
6636 };
6637
6638 struct ipv4_service_contents en1_20_first = {
6639 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
6640 };
6641
6642 struct ipv4_service_contents en1_20_never = {
6643 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
6644 };
6645
6646 struct ipv4_service_contents en0_linklocal = {
6647 "169.254.22.44", "255.255.0.0", NULL, NULL, "en0", 0xfffff, NULL
6648 };
6649
6650 struct ipv4_service_contents * test1[] = {
6651 &en0_40,
6652 &en0_15,
6653 &fw0_25,
6654 &en0_30,
6655 &en1_20,
6656 &en0_50,
6657 &en0_10,
6658 NULL
6659 };
6660
6661 struct ipv4_service_contents * test2[] = {
6662 &en0_40,
6663 &fw0_25,
6664 &en0_30,
6665 &en1_20,
6666 &en0_50,
6667 &en0_10,
6668 NULL
6669 };
6670
6671 struct ipv4_service_contents * test3[] = {
6672 &en0_40,
6673 &en1_20,
6674 &en0_50,
6675 &en0_10,
6676 &en0_110,
6677 &en1_125,
6678 &fw0_25,
6679 &fw0_21,
6680 &en0_40,
6681 &en0_30,
6682 NULL
6683 };
6684
6685 struct ipv4_service_contents * test4[] = {
6686 &en0_1,
6687 &en0_40,
6688 &en0_30,
6689 &en1_20,
6690 &en1_2,
6691 NULL
6692 };
6693
6694 struct ipv4_service_contents * test5[] = {
6695 &ppp0_0_1,
6696 &en0_1,
6697 &en0_40,
6698 &en0_30,
6699 &en1_20,
6700 &en1_2,
6701 NULL
6702 };
6703
6704 struct ipv4_service_contents * test6[] = {
6705 &en0_test6,
6706 &en1_test6,
6707 &en2_test6,
6708 &fw0_test6_and_7,
6709 NULL
6710 };
6711
6712 struct ipv4_service_contents * test7[] = {
6713 &en0_test7,
6714 &en1_test7,
6715 &en2_test7,
6716 &fw0_test6_and_7,
6717 NULL
6718 };
6719
6720 struct ipv4_service_contents * test8[] = {
6721 &en0_10,
6722 &en1_20,
6723 NULL
6724 };
6725
6726 struct ipv4_service_contents * test9[] = {
6727 &en0_10,
6728 &en1_20_first,
6729 &fw0_25,
6730 NULL
6731 };
6732
6733 struct ipv4_service_contents * test10[] = {
6734 &en0_10_last,
6735 &en1_20,
6736 &fw0_25,
6737 NULL
6738 };
6739
6740 struct ipv4_service_contents * test11[] = {
6741 &en0_10_never,
6742 &en1_20,
6743 &fw0_25,
6744 NULL
6745 };
6746
6747 struct ipv4_service_contents * test12[] = {
6748 &en0_10,
6749 &en1_20,
6750 NULL
6751 };
6752
6753 struct ipv4_service_contents * test13[] = {
6754 &en0_10,
6755 &en1_20_never,
6756 NULL
6757 };
6758
6759 struct ipv4_service_contents * test14[] = {
6760 &en1_20_never,
6761 NULL
6762 };
6763
6764 struct ipv4_service_contents * test15[] = {
6765 &en0_linklocal,
6766 NULL
6767 };
6768
6769 static void
6770 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
6771 const char * str)
6772 {
6773 CFStringRef prop_val;
6774
6775 if (str == NULL) {
6776 return;
6777 }
6778 prop_val = CFStringCreateWithCString(NULL,
6779 str,
6780 kCFStringEncodingASCII);
6781 CFDictionarySetValue(dict, prop_name, prop_val);
6782 CFRelease(prop_val);
6783 return;
6784 }
6785
6786 static void
6787 dict_add_string_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
6788 const char * str)
6789 {
6790 CFArrayRef array;
6791 CFStringRef prop_val;
6792
6793 if (str == NULL) {
6794 return;
6795 }
6796 prop_val = CFStringCreateWithCString(NULL,
6797 str,
6798 kCFStringEncodingASCII);
6799 array = CFArrayCreate(NULL,
6800 (const void **)&prop_val, 1,
6801 &kCFTypeArrayCallBacks);
6802 CFRelease(prop_val);
6803 CFDictionarySetValue(dict, prop_name, array);
6804 CFRelease(array);
6805 return;
6806 }
6807
6808 static CFDictionaryRef
6809 make_IPv4_dict(struct ipv4_service_contents * t)
6810 {
6811 CFMutableDictionaryRef dict;
6812
6813 dict = CFDictionaryCreateMutable(NULL, 0,
6814 &kCFTypeDictionaryKeyCallBacks,
6815 &kCFTypeDictionaryValueCallBacks);
6816 dict_add_string_as_array(dict, kSCPropNetIPv4Addresses, t->addr);
6817 dict_add_string_as_array(dict, kSCPropNetIPv4SubnetMasks, t->mask);
6818 dict_add_string_as_array(dict, kSCPropNetIPv4DestAddresses, t->dest);
6819 dict_add_string(dict, kSCPropNetIPv4Router, t->router);
6820 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
6821 return (dict);
6822 }
6823
6824 static IPv4RouteListRef
6825 make_IPv4RouteList(struct ipv4_service_contents * * this_test)
6826 {
6827 IPv4RouteListRef r;
6828 IPv4RouteListRef routes;
6829 char routes_buf[IPv4RouteListComputeSize(R_STATIC)];
6830 IPv4RouteListRef ret = NULL;
6831 struct ipv4_service_contents * * scan_test;
6832
6833 for (scan_test = this_test; *scan_test != NULL; scan_test++) {
6834 CFDictionaryRef dict;
6835
6836 dict = make_IPv4_dict(*scan_test);
6837 if (dict == NULL) {
6838 fprintf(stderr, "make_IPv4_dict failed\n");
6839 exit(1);
6840 }
6841 routes = (IPv4RouteListRef)routes_buf;
6842 routes->size = R_STATIC;
6843 routes->count = 0;
6844 r = IPv4RouteListCreateWithDictionary(routes, dict,
6845 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
6846 if (r == NULL) {
6847 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
6848 exit(1);
6849 }
6850
6851 (*scan_test)->rank = RankMake((*scan_test)->rank, kRankAssertionDefault);
6852
6853 if ((*scan_test)->primaryRank != NULL) {
6854 (*scan_test)->rank = RankMake((*scan_test)->rank,
6855 PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank));
6856 }
6857
6858 if ((*scan_test)->router == NULL) {
6859 (*scan_test)->rank = RankMake((*scan_test)->rank,
6860 PrimaryRankGetRankAssertion(kSCValNetServicePrimaryRankLast));
6861 }
6862
6863 ret = IPv4RouteListAddRouteList(ret, 1, r, (*scan_test)->rank);
6864 if (r != routes) {
6865 free(r);
6866 }
6867 CFRelease(dict);
6868 }
6869 return (ret);
6870 }
6871
6872 /*
6873 * Function: run_test
6874 * Purpose:
6875 * Runs through the given set of routes first in the forward direction,
6876 * then again backwards. We should end up with exactly the same set of
6877 * routes at the end.
6878 */
6879 static boolean_t
6880 run_test(const char * name, struct ipv4_service_contents * * this_test)
6881 {
6882 CFStringRef descr;
6883 boolean_t ret = FALSE;
6884 IPv4RouteListRef r;
6885 IPv4RouteListRef routes;
6886 char routes_buf[IPv4RouteListComputeSize(R_STATIC)];
6887 IPv4RouteListRef routes1 = NULL, routes2 = NULL;
6888 struct ipv4_service_contents * * scan_test;
6889
6890 printf("\nStarting test %s\n", name);
6891 for (scan_test = this_test; *scan_test != NULL; scan_test++) {
6892 CFDictionaryRef dict;
6893
6894 dict = make_IPv4_dict(*scan_test);
6895 if (dict == NULL) {
6896 fprintf(stderr, "make_IPv4_dict failed\n");
6897 exit(1);
6898 }
6899 routes = (IPv4RouteListRef)routes_buf;
6900 routes->size = R_STATIC;
6901 routes->count = 0;
6902 r = IPv4RouteListCreateWithDictionary(routes, dict,
6903 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
6904 if (r == NULL) {
6905 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
6906 exit(1);
6907 }
6908 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
6909 descr = IPv4RouteListCopyDescription(r);
6910 SCPrint(TRUE, stdout, CFSTR("test: Adding %@\n"), descr);
6911 CFRelease(descr);
6912 }
6913
6914 (*scan_test)->rank = RankMake((*scan_test)->rank, kRankAssertionDefault);
6915
6916 if ((*scan_test)->primaryRank != NULL) {
6917 (*scan_test)->rank = RankMake((*scan_test)->rank,
6918 PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank));
6919 }
6920
6921 routes1 = IPv4RouteListAddRouteList(routes1, 1, r, (*scan_test)->rank);
6922 if (r != routes) {
6923 free(r);
6924 }
6925 CFRelease(dict);
6926 }
6927 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
6928 if (routes1 != NULL) {
6929 descr = IPv4RouteListCopyDescription(routes1);
6930 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
6931 CFRelease(descr);
6932 }
6933 }
6934 for (scan_test--; scan_test >= this_test; scan_test--) {
6935 CFDictionaryRef dict;
6936
6937 dict = make_IPv4_dict(*scan_test);
6938 if (dict == NULL) {
6939 fprintf(stderr, "make_IPv4_dict failed\n");
6940 exit(1);
6941 }
6942 routes = (IPv4RouteListRef)routes_buf;
6943 routes->size = R_STATIC;
6944 routes->count = 0;
6945 r = IPv4RouteListCreateWithDictionary(routes, dict,
6946 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
6947 if (r == NULL) {
6948 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
6949 exit(1);
6950 }
6951 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
6952 descr = IPv4RouteListCopyDescription(r);
6953 SCPrint(TRUE, stdout, CFSTR("test: Adding %@\n"), descr);
6954 CFRelease(descr);
6955 }
6956 if ((*scan_test)->primaryRank != NULL) {
6957 (*scan_test)->rank = RankMake((*scan_test)->rank,
6958 PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank));
6959 }
6960
6961 routes2 = IPv4RouteListAddRouteList(routes2, 1, r, (*scan_test)->rank);
6962 if (r != routes) {
6963 free(r);
6964 }
6965 CFRelease(dict);
6966 }
6967 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
6968 if (routes2 != NULL) {
6969 descr = IPv4RouteListCopyDescription(routes2);
6970 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
6971 CFRelease(descr);
6972 }
6973 }
6974 if ((routes1 != NULL && routes2 == NULL)
6975 || (routes1 == NULL && routes2 != NULL)) {
6976 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
6977 (routes1 != NULL) ? "not " : "",
6978 (routes2 != NULL) ? "not " : "");
6979 }
6980 else if (routes1 != NULL && routes2 != NULL) {
6981 /* check if they are different */
6982 if (routes1->count != routes2->count) {
6983 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
6984 routes1->count, routes2->count);
6985 }
6986 else if (bcmp(routes1, routes2,
6987 IPv4RouteListComputeSize(routes1->count)) != 0) {
6988 fprintf(stderr, "routes1 and routes2 are different\n");
6989 }
6990 else {
6991 printf("routes1 and routes2 are the same\n");
6992 ret = TRUE;
6993 }
6994 }
6995 if (routes1 != NULL) {
6996 free(routes1);
6997 }
6998 if (routes2 != NULL) {
6999 free(routes2);
7000 }
7001 return (ret);
7002 }
7003
7004 typedef struct compare_context {
7005 IPv4RouteListRef old;
7006 IPv4RouteListRef new;
7007 } compare_context_t;
7008
7009 static void
7010 compare_callback(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg)
7011 {
7012 compare_context_t * context = (compare_context_t *)arg;
7013
7014 switch (cmd) {
7015 case kIPv4RouteListAddRouteCommand:
7016 printf("Add new[%ld] = ", route - context->new->list);
7017 IPv4RoutePrint(route);
7018 break;
7019 case kIPv4RouteListRemoveRouteCommand:
7020 printf("Remove old[%ld] = ", route - context->old->list);
7021 IPv4RoutePrint(route);
7022 break;
7023 default:
7024 break;
7025 }
7026 return;
7027 }
7028
7029 static void
7030 compare_tests(struct ipv4_service_contents * * old_test,
7031 struct ipv4_service_contents * * new_test)
7032 {
7033 IPv4RouteListRef new_routes;
7034 IPv4RouteListRef old_routes;
7035 compare_context_t context;
7036
7037 old_routes = make_IPv4RouteList(old_test);
7038 new_routes = make_IPv4RouteList(new_test);
7039
7040 if (old_routes == NULL) {
7041 printf("No Old Routes\n");
7042 }
7043 else {
7044 printf("Old Routes = ");
7045 IPv4RouteListPrint(old_routes);
7046 }
7047 if (new_routes == NULL) {
7048 printf("No New Routes\n");
7049 }
7050 else {
7051 printf("New Routes = ");
7052 IPv4RouteListPrint(new_routes);
7053 }
7054 context.old = old_routes;
7055 context.new = new_routes;
7056 IPv4RouteListApply(old_routes, new_routes, compare_callback, &context);
7057 if (old_routes != NULL) {
7058 free(old_routes);
7059 }
7060 if (new_routes != NULL) {
7061 free(new_routes);
7062 }
7063
7064 return;
7065 }
7066
7067 int
7068 main(int argc, char **argv)
7069 {
7070 _sc_log = FALSE;
7071 _sc_verbose = (argc > 1) ? TRUE : FALSE;
7072
7073 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
7074 if (argc > 1) {
7075 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
7076 }
7077
7078 if (run_test("test1", test1) == FALSE) {
7079 fprintf(stderr, "test1 failed\n");
7080 exit(1);
7081 }
7082 if (run_test("test2", test2) == FALSE) {
7083 fprintf(stderr, "test2 failed\n");
7084 exit(1);
7085 }
7086 if (run_test("test3", test4) == FALSE) {
7087 fprintf(stderr, "test3 failed\n");
7088 exit(1);
7089 }
7090 if (run_test("test4", test4) == FALSE) {
7091 fprintf(stderr, "test4 failed\n");
7092 exit(1);
7093 }
7094 if (run_test("test5", test5) == FALSE) {
7095 fprintf(stderr, "test5 failed\n");
7096 exit(1);
7097 }
7098 if (run_test("test15", test15) == FALSE) {
7099 fprintf(stderr, "test15 failed\n");
7100 exit(1);
7101 }
7102
7103
7104 printf("\nCompare 1 to 2:\n");
7105 compare_tests(test1, test2);
7106
7107 printf("\nCompare 2 to 1:\n");
7108 compare_tests(test2, test1);
7109
7110 printf("\nCompare 1 to 1:\n");
7111 compare_tests(test1, test1);
7112
7113 printf("\nCompare 1 to 3:\n");
7114 compare_tests(test1, test3);
7115
7116 printf("\nCompare 3 to 1:\n");
7117 compare_tests(test3, test1);
7118
7119 printf("\nCompare 2 to 3:\n");
7120 compare_tests(test2, test3);
7121
7122 printf("\nCompare 3 to 2:\n");
7123 compare_tests(test3, test2);
7124
7125 printf("\nCompare 3 to 4:\n");
7126 compare_tests(test3, test4);
7127
7128 printf("\nCompare 5 to 4:\n");
7129 compare_tests(test5, test4);
7130
7131 printf("\nCompare 6 to 7:\n");
7132 compare_tests(test6, test7);
7133
7134 printf("\nCompare 7 to 6:\n");
7135 compare_tests(test7, test6);
7136
7137 printf("\nCompare 8 to 9:\n");
7138 compare_tests(test8, test9);
7139
7140 printf("\nCompare 8 to 10:\n");
7141 compare_tests(test8, test10);
7142
7143 printf("\nCompare 8 to 11:\n");
7144 compare_tests(test8, test11);
7145
7146 printf("\nCompare 12 to 13:\n");
7147 compare_tests(test12, test13);
7148
7149 printf("\nCompare 13 to 14:\n");
7150 compare_tests(test13, test14);
7151
7152 printf("\nChecking for leaks\n");
7153 char cmd[128];
7154 sprintf(cmd, "leaks %d 2>&1", getpid());
7155 fflush(stdout);
7156 (void)system(cmd);
7157
7158 exit(0);
7159 return (0);
7160 }
7161
7162 #endif /* TEST_IPV4_ROUTELIST */
7163