]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/ip_plugin.c
configd-453.16.tar.gz
[apple/configd.git] / Plugins / IPMonitor / ip_plugin.c
1 /*
2 * Copyright (c) 2000-2012 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
92 #include <SystemConfiguration/SystemConfiguration.h>
93 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
94 #include <SystemConfiguration/SCValidation.h>
95 #include <SystemConfiguration/SCPrivate.h> /* for SCLog() */
96 #include <dnsinfo.h>
97 #if !TARGET_OS_IPHONE
98 #include <dnsinfo_create.h>
99 #endif /* !TARGET_OS_IPHONE */
100
101 #include <dns_sd.h>
102 #ifndef kDNSServiceCompMulticastDNS
103 #define kDNSServiceCompMulticastDNS "MulticastDNS"
104 #endif
105 #ifndef kDNSServiceCompPrivateDNS
106 #define kDNSServiceCompPrivateDNS "PrivateDNS"
107 #endif
108 #include <network_information.h>
109 #include "network_information_priv.h"
110
111 enum {
112 kProtocolFlagsNone = 0x0,
113 kProtocolFlagsIPv4 = 0x1,
114 kProtocolFlagsIPv6 = 0x2
115 };
116 typedef uint8_t ProtocolFlags;
117
118 enum {
119 kDebugFlag1 = 0x00000001,
120 kDebugFlag2 = 0x00000002,
121 kDebugFlag4 = 0x00000004,
122 kDebugFlag8 = 0x00000008,
123 kDebugFlagDefault = kDebugFlag1,
124 kDebugFlagAll = 0xffffffff
125 };
126
127 #ifdef TEST_IPV4_ROUTELIST
128 #define ROUTELIST_DEBUG(a, f) { if ((S_IPMonitor_debug & (f)) != 0) printf a ;}
129 #else
130 #define ROUTELIST_DEBUG(a, f)
131 #endif
132
133 #include "set-hostname.h"
134 #include "dns-configuration.h"
135 #include "proxy-configuration.h"
136 #if !TARGET_OS_IPHONE
137 #include "smb-configuration.h"
138 #endif /* !TARGET_OS_IPHONE */
139
140 #define PPP_PREFIX "ppp"
141
142 #define IP_FORMAT "%d.%d.%d.%d"
143 #define IP_CH(ip) ((u_char *)(ip))
144 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
145
146 /*
147 * IPv4 Route management
148 */
149
150 typedef uint32_t RouteFlags;
151
152 enum {
153 kRouteIsDirectToInterfaceFlag = 0x00000001,
154 kRouteIsNotSubnetLocalFlag = 0x00000002,
155 kRouteIsScopedFlag = 0x00000004,
156 };
157
158 typedef struct {
159 struct in_addr dest;
160 struct in_addr mask;
161 struct in_addr gateway;
162 char ifname[IFNAMSIZ];
163 unsigned int ifindex;
164 struct in_addr ifa;
165 Rank rank;
166 RouteFlags flags;
167 } IPv4Route, *IPv4RouteRef;
168
169 typedef struct {
170 int count;
171 int size;
172 IPv4Route list[1]; /* variable length */
173 } IPv4RouteList, *IPv4RouteListRef;
174
175 enum {
176 kIPv4RouteListAddRouteCommand,
177 kIPv4RouteListRemoveRouteCommand
178 };
179
180 typedef struct {
181 char ifname[IFNAMSIZ];
182 uint32_t flags;
183 Rank rank;
184 struct in6_addr iaddr6;
185 } IPv6RankedE, *IPv6RankedERef;
186
187 typedef struct {
188 int count;
189 int size;
190 IPv6RankedE elem[1];
191 } IPv6RankedList, *IPv6RankedListRef;
192
193 /*
194 * Type: Rank
195 * Purpose:
196 * A 32-bit value to encode the relative rank of a service.
197 *
198 * The top 8 bits are used to hold the rank assertion (first, last, never, default),
199 * the bottom 24 bits are used to store the service index i.e. the position within
200 * the service order array.
201 */
202 #define RANK_ASSERTION_MAKE(r) ((Rank)(r) << 24)
203 #define kRankAssertionFirst RANK_ASSERTION_MAKE(0)
204 #define kRankAssertionDefault RANK_ASSERTION_MAKE(1)
205 #define kRankAssertionLast RANK_ASSERTION_MAKE(2)
206 #define kRankAssertionNever RANK_ASSERTION_MAKE(3)
207 #define kRankAssertionMask RANK_ASSERTION_MAKE(0xff)
208 #define RANK_ASSERTION_MASK(r) ((Rank)(r) & kRankAssertionMask)
209
210 #define RANK_INDEX_MAKE(r) ((Rank)(r))
211 #define kRankIndexMask RANK_INDEX_MAKE(0xffffff)
212 #define RANK_INDEX_MASK(r) ((Rank)(r) & kRankIndexMask)
213
214 static __inline__ Rank
215 RankMake(uint32_t service_index, Rank primary_rank)
216 {
217 return (RANK_INDEX_MASK(service_index) | RANK_ASSERTION_MASK(primary_rank));
218 }
219
220 static __inline__ Rank
221 PrimaryRankGetRankAssertion(CFStringRef primaryRank)
222 {
223 if (CFEqual(primaryRank, kSCValNetServicePrimaryRankNever)) {
224 return kRankAssertionNever;
225 } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankFirst)) {
226 return kRankAssertionFirst;
227 } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankLast)) {
228 return kRankAssertionLast;
229 }
230 return kRankAssertionDefault;
231 }
232
233 typedef uint32_t IPv4RouteListApplyCommand;
234
235 typedef void IPv4RouteListApplyCallBackFunc(IPv4RouteListApplyCommand cmd,
236 IPv4RouteRef route, void * arg);
237 typedef IPv4RouteListApplyCallBackFunc * IPv4RouteListApplyCallBackFuncPtr;
238
239 /* SCDynamicStore session */
240 static SCDynamicStoreRef S_session = NULL;
241
242 /* debug output flags */
243 static uint32_t S_IPMonitor_debug = 0;
244
245 /* are we netbooted? If so, don't touch the default route */
246 static boolean_t S_netboot = FALSE;
247
248 /* is scoped routing enabled? */
249 #ifdef RTF_IFSCOPE
250 static boolean_t S_scopedroute = FALSE;
251 static boolean_t S_scopedroute_v6 = FALSE;
252 #endif /* RTF_IFSCOPE */
253
254 /* dictionary to hold per-service state: key is the serviceID */
255 static CFMutableDictionaryRef S_service_state_dict = NULL;
256 static CFMutableDictionaryRef S_ipv4_service_rank_dict = NULL;
257 static CFMutableDictionaryRef S_ipv6_service_rank_dict = NULL;
258
259 /* dictionary to hold per-interface rank information */
260 static CFMutableDictionaryRef S_if_rank_dict = NULL;
261
262 /* if set, a PPP interface overrides the primary */
263 static boolean_t S_ppp_override_primary = FALSE;
264
265 /* the current primary serviceID's */
266 static CFStringRef S_primary_ipv4 = NULL;
267 static CFStringRef S_primary_ipv6 = NULL;
268 static CFStringRef S_primary_dns = NULL;
269 static CFStringRef S_primary_proxies = NULL;
270
271 static CFStringRef S_state_global_ipv4 = NULL;
272 static CFStringRef S_state_global_ipv6 = NULL;
273 static CFStringRef S_state_global_dns = NULL;
274 static CFStringRef S_state_global_proxies = NULL;
275 static CFStringRef S_state_service_prefix = NULL;
276 static CFStringRef S_setup_global_ipv4 = NULL;
277 static CFStringRef S_setup_service_prefix = NULL;
278
279 static CFStringRef S_multicast_resolvers = NULL;
280 static CFStringRef S_private_resolvers = NULL;
281
282 static IPv4RouteListRef S_ipv4_routelist = NULL;
283
284 static const struct in_addr S_ip_zeros = { 0 };
285 static const struct in6_addr S_ip6_zeros = IN6ADDR_ANY_INIT;
286
287 static boolean_t S_append_state = FALSE;
288
289 static nwi_state_t S_nwi_state = NULL;
290
291 #if !TARGET_OS_IPHONE
292 static CFStringRef S_primary_smb = NULL;
293 static CFStringRef S_state_global_smb = NULL;
294 #endif /* !TARGET_OS_IPHONE */
295
296 #if !TARGET_OS_IPHONE
297 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
298 #endif /* !TARGET_OS_IPHONE */
299
300 #ifndef KERN_NETBOOT
301 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
302 #endif //KERN_NETBOOT
303
304 /**
305 ** entityType*, GetEntityChanges*
306 ** - definitions for the entity types we handle
307 **/
308 enum {
309 kEntityTypeIPv4 = 0,
310 kEntityTypeIPv6,
311 kEntityTypeDNS,
312 kEntityTypeProxies,
313 #if !TARGET_OS_IPHONE
314 kEntityTypeSMB,
315 #endif /* !TARGET_OS_IPHONE */
316 ENTITY_TYPES_COUNT,
317 kEntityTypeServiceOptions = 31
318 };
319 typedef uint32_t EntityType;
320
321 static const CFStringRef *entityTypeNames[ENTITY_TYPES_COUNT] = {
322 &kSCEntNetIPv4, /* 0 */
323 &kSCEntNetIPv6, /* 1 */
324 &kSCEntNetDNS, /* 2 */
325 &kSCEntNetProxies, /* 3 */
326 #if !TARGET_OS_IPHONE
327 &kSCEntNetSMB, /* 4 */
328 #endif /* !TARGET_OS_IPHONE */
329 };
330
331 #ifndef kSCEntNetIPv4RouteList
332 #define kSCEntNetIPv4RouteList CFSTR("IPv4RouteList")
333 #endif
334
335 #ifndef kSCEntNetIPv4ServiceDict
336 #define kSCEntNetIPv4ServiceDict CFSTR("IPv4ServiceDict")
337 #endif
338
339 static IPv4RouteListRef
340 ipv4_dict_get_routelist(CFDictionaryRef ipv4_dict)
341 {
342 CFDataRef routes;
343 IPv4RouteListRef routes_list = NULL;
344
345 if (isA_CFDictionary(ipv4_dict) == NULL) {
346 return (NULL);
347 }
348
349 routes = CFDictionaryGetValue(ipv4_dict, kSCEntNetIPv4RouteList);
350
351 if (routes != NULL) {
352 routes_list = (IPv4RouteListRef)(void*)CFDataGetBytePtr(routes);
353 }
354 return (routes_list);
355 }
356
357 static CFStringRef
358 ipv4_dict_get_ifname(CFDictionaryRef ipv4_dict)
359 {
360 CFDictionaryRef ipv4_service_dict = NULL;
361
362 if (isA_CFDictionary(ipv4_dict) == NULL) {
363 return (NULL);
364 }
365
366 ipv4_service_dict = CFDictionaryGetValue(ipv4_dict,
367 kSCEntNetIPv4ServiceDict);
368
369 if (isA_CFDictionary(ipv4_service_dict) == NULL) {
370 return NULL;
371 }
372
373 return CFDictionaryGetValue(ipv4_service_dict, kSCPropInterfaceName);
374 }
375
376 typedef boolean_t GetEntityChangesFunc(CFStringRef serviceID,
377 CFDictionaryRef state_dict,
378 CFDictionaryRef setup_dict,
379 CFDictionaryRef info);
380 typedef GetEntityChangesFunc * GetEntityChangesFuncRef;
381
382 static GetEntityChangesFunc get_ipv4_changes;
383 static GetEntityChangesFunc get_ipv6_changes;
384 static GetEntityChangesFunc get_dns_changes;
385 static GetEntityChangesFunc get_proxies_changes;
386 #if !TARGET_OS_IPHONE
387 static GetEntityChangesFunc get_smb_changes;
388 #endif /* !TARGET_OS_IPHONE */
389
390 static void
391 my_CFRelease(void * t);
392
393 static void
394 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new);
395
396 static void
397 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key);
398
399 static const GetEntityChangesFuncRef entityChangeFunc[ENTITY_TYPES_COUNT] = {
400 get_ipv4_changes, /* 0 */
401 get_ipv6_changes, /* 1 */
402 get_dns_changes, /* 2 */
403 get_proxies_changes,/* 3 */
404 #if !TARGET_OS_IPHONE
405 get_smb_changes, /* 4 */
406 #endif /* !TARGET_OS_IPHONE */
407 };
408
409 /**
410 ** keyChangeList
411 ** - mechanism to do an atomic update of the SCDynamicStore
412 ** when the content needs to be changed across multiple functions
413 **/
414 typedef struct {
415 CFMutableArrayRef notify;
416 CFMutableArrayRef remove;
417 CFMutableDictionaryRef set;
418 } keyChangeList, * keyChangeListRef;
419
420 static CFStringRef
421 my_CFStringCopyComponent(CFStringRef path, CFStringRef separator,
422 CFIndex component_index)
423 {
424 CFArrayRef arr;
425 CFStringRef component = NULL;
426
427 arr = CFStringCreateArrayBySeparatingStrings(NULL, path, separator);
428 if (arr == NULL) {
429 goto done;
430 }
431 if (CFArrayGetCount(arr) <= component_index) {
432 goto done;
433 }
434 component = CFRetain(CFArrayGetValueAtIndex(arr, component_index));
435
436 done:
437 my_CFRelease(&arr);
438 return (component);
439 }
440
441 static void
442 keyChangeListInit(keyChangeListRef keys)
443 {
444 keys->notify = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
445 keys->remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
446 keys->set = CFDictionaryCreateMutable(NULL, 0,
447 &kCFTypeDictionaryKeyCallBacks,
448 &kCFTypeDictionaryValueCallBacks);
449 return;
450 }
451
452 static void
453 keyChangeListFree(keyChangeListRef keys)
454 {
455 my_CFRelease(&keys->notify);
456 my_CFRelease(&keys->remove);
457 my_CFRelease(&keys->set);
458 return;
459 }
460
461 static void
462 keyChangeListNotifyKey(keyChangeListRef keys, CFStringRef key)
463 {
464 my_CFArrayAppendUniqueValue(keys->notify, key);
465 return;
466 }
467
468 static void
469 keyChangeListRemoveValue(keyChangeListRef keys, CFStringRef key)
470 {
471 my_CFArrayAppendUniqueValue(keys->remove, key);
472 CFDictionaryRemoveValue(keys->set, key);
473 return;
474 }
475
476 static void
477 keyChangeListSetValue(keyChangeListRef keys, CFStringRef key, CFTypeRef value)
478 {
479 my_CFArrayRemoveValue(keys->remove, key);
480 CFDictionarySetValue(keys->set, key, value);
481 return;
482 }
483
484 static void
485 keyChangeListApplyToStore(keyChangeListRef keys, SCDynamicStoreRef session,
486 CFStringRef network_change_msg)
487 {
488 CFArrayRef notify = keys->notify;
489 CFArrayRef remove = keys->remove;
490 CFDictionaryRef set = keys->set;
491 int status;
492
493 if (CFArrayGetCount(notify) == 0) {
494 notify = NULL;
495 }
496 if (CFArrayGetCount(remove) == 0) {
497 remove = NULL;
498 }
499 if (CFDictionaryGetCount(set) == 0) {
500 set = NULL;
501 }
502 if (set == NULL && remove == NULL && notify == NULL) {
503 return;
504 }
505 if (S_IPMonitor_debug & kDebugFlag1) {
506 if (set != NULL) {
507 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: Setting:\n%@"),
508 set);
509 }
510 if (remove != NULL) {
511 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: Removing:\n%@"),
512 remove);
513 }
514 if (notify != NULL) {
515 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: Notifying:\n%@"),
516 notify);
517 }
518 }
519 (void)SCDynamicStoreSetMultiple(session, set, remove, notify);
520
521 status = notify_post("com.apple.system.config.network_change");
522 if (status == NOTIFY_STATUS_OK) {
523 if (CFStringGetLength(network_change_msg) != 0) {
524 SCLog(TRUE, LOG_NOTICE, CFSTR("network changed:%@"), network_change_msg);
525 } else {
526 SCLog(TRUE, LOG_NOTICE, CFSTR("network changed."));
527 }
528 } else {
529 SCLog(TRUE, LOG_NOTICE,
530 CFSTR("IPMonitor: notify_post() failed: error=%ld"), status);
531 }
532
533 return;
534 }
535
536 static boolean_t
537 S_is_network_boot()
538 {
539 int mib[2];
540 size_t len;
541 int netboot = 0;
542
543 mib[0] = CTL_KERN;
544 mib[1] = KERN_NETBOOT;
545 len = sizeof(netboot);
546 sysctl(mib, 2, &netboot, &len, NULL, 0);
547 return (netboot);
548 }
549
550 static __inline__ int
551 inet6_dgram_socket()
552 {
553 return (socket(AF_INET6, SOCK_DGRAM, 0));
554 }
555
556 #ifdef SIOCDRADD_IN6
557 static int
558 siocdradd_in6(int s, int if_index, const struct in6_addr * addr, u_char flags)
559 {
560 struct in6_defrouter dr;
561 struct sockaddr_in6 * sin6;
562
563 bzero(&dr, sizeof(dr));
564 sin6 = &dr.rtaddr;
565 sin6->sin6_len = sizeof(struct sockaddr_in6);
566 sin6->sin6_family = AF_INET6;
567 sin6->sin6_addr = *addr;
568 dr.flags = flags;
569 dr.if_index = if_index;
570 return (ioctl(s, SIOCDRADD_IN6, &dr));
571 }
572
573 static int
574 siocdrdel_in6(int s, int if_index, const struct in6_addr * addr)
575 {
576 struct in6_defrouter dr;
577 struct sockaddr_in6 * sin6;
578
579 bzero(&dr, sizeof(dr));
580 sin6 = &dr.rtaddr;
581 sin6->sin6_len = sizeof(struct sockaddr_in6);
582 sin6->sin6_family = AF_INET6;
583 sin6->sin6_addr = *addr;
584 dr.if_index = if_index;
585 return (ioctl(s, SIOCDRDEL_IN6, &dr));
586 }
587 #endif /* SIOCDRADD_IN6 */
588
589 #ifdef RTF_IFSCOPE
590 static boolean_t
591 S_is_scoped_routing_enabled()
592 {
593 int scopedroute = 0;
594 size_t len = sizeof(scopedroute);
595
596 if ((sysctlbyname("net.inet.ip.scopedroute",
597 &scopedroute, &len,
598 NULL, 0) == -1)
599 && (errno != ENOENT)) {
600 SCLog(TRUE, LOG_ERR, CFSTR("sysctlbyname() failed: %s"), strerror(errno));
601 }
602 return (scopedroute);
603 }
604
605 static boolean_t
606 S_is_scoped_v6_routing_enabled()
607 {
608 int scopedroute_v6 = 0;
609 size_t len = sizeof(scopedroute_v6);
610
611 if ((sysctlbyname("net.inet6.ip6.scopedroute",
612 &scopedroute_v6, &len,
613 NULL, 0) == -1)
614 && (errno != ENOENT)) {
615 SCLog(TRUE, LOG_ERR, CFSTR("sysctlbyname() failed: %s"), strerror(errno));
616 }
617 return (scopedroute_v6);
618 }
619 #endif /* RTF_IFSCOPE */
620
621 static void
622 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new)
623 {
624 CFIndex n = CFArrayGetCount(arr);
625
626 if (CFArrayContainsValue(arr, CFRangeMake(0, n), new)) {
627 return;
628 }
629 CFArrayAppendValue(arr, new);
630 return;
631 }
632
633 static void
634 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key)
635 {
636 CFIndex i;
637
638 i = CFArrayGetFirstIndexOfValue(arr,
639 CFRangeMake(0, CFArrayGetCount(arr)),
640 key);
641 if (i != kCFNotFound) {
642 CFArrayRemoveValueAtIndex(arr, i);
643 }
644 return;
645 }
646
647 static void
648 my_CFRelease(void * t)
649 {
650 void * * obj = (void * *)t;
651
652 if (obj && *obj) {
653 CFRelease(*obj);
654 *obj = NULL;
655 }
656 return;
657 }
658
659 static CFDictionaryRef
660 my_CFDictionaryGetDictionary(CFDictionaryRef dict, CFStringRef key)
661 {
662 if (isA_CFDictionary(dict) == NULL) {
663 return (NULL);
664 }
665 return (isA_CFDictionary(CFDictionaryGetValue(dict, key)));
666 }
667
668 static CFArrayRef
669 my_CFDictionaryGetArray(CFDictionaryRef dict, CFStringRef key)
670 {
671 if (isA_CFDictionary(dict) == NULL) {
672 return (NULL);
673 }
674 return (isA_CFArray(CFDictionaryGetValue(dict, key)));
675 }
676
677 static boolean_t
678 cfstring_to_ipvx(int family, CFStringRef str, void * addr, int addr_size)
679 {
680 char buf[128];
681
682 if (isA_CFString(str) == NULL) {
683 goto done;
684 }
685
686 switch (family) {
687 case AF_INET:
688 if (addr_size < sizeof(struct in_addr)) {
689 goto done;
690 }
691 break;
692 case AF_INET6:
693 if (addr_size < sizeof(struct in6_addr)) {
694 goto done;
695 }
696 break;
697 default:
698 goto done;
699 }
700 (void)_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII);
701 if (inet_pton(family, buf, addr) == 1) {
702 return (TRUE);
703 }
704 done:
705 bzero(addr, addr_size);
706 return (FALSE);
707 }
708
709 static boolean_t
710 cfstring_to_ip(CFStringRef str, struct in_addr * ip_p)
711 {
712 return (cfstring_to_ipvx(AF_INET, str, ip_p, sizeof(*ip_p)));
713 }
714
715 static boolean_t
716 cfstring_to_ip6(CFStringRef str, struct in6_addr * ip6_p)
717 {
718 return (cfstring_to_ipvx(AF_INET6, str, ip6_p, sizeof(*ip6_p)));
719 }
720
721 static CF_RETURNS_RETAINED CFStringRef
722 setup_service_key(CFStringRef serviceID, CFStringRef entity)
723 {
724 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
725 kSCDynamicStoreDomainSetup,
726 serviceID,
727 entity));
728 }
729
730 static CF_RETURNS_RETAINED CFStringRef
731 state_service_key(CFStringRef serviceID, CFStringRef entity)
732 {
733 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
734 kSCDynamicStoreDomainState,
735 serviceID,
736 entity));
737 }
738
739 static CFDictionaryRef
740 get_service_setup_entity(CFDictionaryRef services_info, CFStringRef serviceID,
741 CFStringRef entity)
742 {
743 CFStringRef setup_key;
744 CFDictionaryRef setup_dict;
745
746 setup_key = setup_service_key(serviceID, entity);
747 setup_dict = my_CFDictionaryGetDictionary(services_info, setup_key);
748 my_CFRelease(&setup_key);
749 return (setup_dict);
750 }
751
752 static CFDictionaryRef
753 get_service_state_entity(CFDictionaryRef services_info, CFStringRef serviceID,
754 CFStringRef entity)
755 {
756 CFStringRef state_key;
757 CFDictionaryRef state_dict;
758
759 state_key = state_service_key(serviceID, entity);
760 state_dict = my_CFDictionaryGetDictionary(services_info, state_key);
761 my_CFRelease(&state_key);
762 return (state_dict);
763 }
764
765 static boolean_t
766 dict_get_first_ip(CFDictionaryRef dict, CFStringRef prop, struct in_addr * ip_p)
767 {
768 CFArrayRef ip_list;
769
770 ip_list = CFDictionaryGetValue(dict, prop);
771 if (isA_CFArray(ip_list) != NULL
772 && CFArrayGetCount(ip_list) > 0
773 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list, 0), ip_p)) {
774 return (TRUE);
775 }
776 return (FALSE);
777 }
778
779 static boolean_t
780 get_override_primary(CFDictionaryRef dict)
781 {
782 CFTypeRef override;
783
784 override = CFDictionaryGetValue(dict, kSCPropNetOverridePrimary);
785 if (isA_CFNumber(override) != NULL) {
786 int val = 0;
787
788 CFNumberGetValue((CFNumberRef)override, kCFNumberIntType, &val);
789 if (val != 0) {
790 return (TRUE);
791 }
792 }
793 else if (isA_CFBoolean(override) != NULL) {
794 if (CFBooleanGetValue(override)) {
795 return (TRUE);
796 }
797 }
798 return (FALSE);
799 }
800
801 /**
802 ** IPv4Route*
803 **/
804
805 static __inline__ struct in_addr
806 subnet_addr(struct in_addr addr, struct in_addr mask)
807 {
808 struct in_addr net;
809
810 net.s_addr = addr.s_addr & mask.s_addr;
811 return (net);
812 }
813
814 static __inline__ int
815 in_addr_cmp(struct in_addr a, struct in_addr b)
816 {
817 return (uint32_cmp(ntohl(a.s_addr), ntohl(b.s_addr)));
818 }
819
820 static __inline__ int
821 RouteFlagsCompare(RouteFlags a, RouteFlags b)
822 {
823 return (uint32_cmp(a, b));
824 }
825
826 static void
827 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r, CFMutableStringRef str)
828 {
829 Rank rank_assertion = RANK_ASSERTION_MASK(r->rank);
830
831 CFStringAppendFormat(str, NULL,
832 CFSTR("Dest " IP_FORMAT
833 " Mask " IP_FORMAT
834 " Gate " IP_FORMAT
835 " Ifp %s Ifa " IP_FORMAT),
836 IP_LIST(&r->dest),
837 IP_LIST(&r->mask),
838 IP_LIST(&r->gateway),
839 (r->ifname[0] != '\0') ? r->ifname : "<none>",
840 IP_LIST(&r->ifa));
841 if ((r->flags & kRouteIsNotSubnetLocalFlag) != 0) {
842 CFStringAppend(str, CFSTR(" [non-local]"));
843 }
844 else if ((r->flags & kRouteIsDirectToInterfaceFlag) != 0) {
845 CFStringAppend(str, CFSTR(" [direct]"));
846 }
847
848 switch(rank_assertion) {
849 case kRankAssertionFirst:
850 CFStringAppend(str, CFSTR(" [first]"));
851 break;
852 case kRankAssertionLast:
853 CFStringAppend(str, CFSTR(" [last]"));
854 break;
855 case kRankAssertionNever:
856 CFStringAppend(str, CFSTR(" [never]"));
857 break;
858 default:
859 break;
860 }
861
862 if ((r->flags & kRouteIsScopedFlag) != 0) {
863 CFStringAppend(str, CFSTR(" [SCOPED]"));
864 }
865 return;
866 }
867
868 static CFStringRef
869 IPv4RouteCopyDescription(IPv4RouteRef r)
870 {
871 CFMutableStringRef str;
872
873 str = CFStringCreateMutable(NULL, 0);
874 IPv4RouteCopyDescriptionWithString(r, str);
875 return (str);
876 }
877
878 static __inline__ void
879 IPv4RoutePrint(IPv4RouteRef route)
880 {
881 CFStringRef str = IPv4RouteCopyDescription(route);
882
883 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
884 CFRelease(str);
885 return;
886 }
887
888 static __inline__ void
889 IPv4RouteLog(IPv4RouteRef route)
890 {
891 CFStringRef str = IPv4RouteCopyDescription(route);
892
893 SCLog(TRUE, LOG_NOTICE, CFSTR("%@"), str);
894 CFRelease(str);
895 return;
896 }
897
898 static int
899 IPv4RouteCompare(IPv4RouteRef a, Rank a_rank,
900 IPv4RouteRef b, Rank b_rank, boolean_t * same_dest)
901 {
902 int cmp;
903
904 *same_dest = FALSE;
905 cmp = in_addr_cmp(a->dest, b->dest);
906 if (cmp == 0) {
907 cmp = in_addr_cmp(a->mask, b->mask);
908 if (cmp == 0) {
909 int name_cmp = strcmp(a->ifname, b->ifname);
910
911 if (name_cmp == 0) {
912 cmp = 0;
913 }
914 else {
915 *same_dest = TRUE;
916 cmp = RankCompare(a_rank, b_rank);
917 if (cmp == 0) {
918 cmp = name_cmp;
919 }
920 }
921 }
922 }
923 if ((S_IPMonitor_debug & kDebugFlag8) != 0) {
924 CFStringRef a_str;
925 CFStringRef b_str;
926 char ch;
927
928 if (cmp < 0) {
929 ch = '<';
930 }
931 else if (cmp == 0) {
932 ch = '=';
933 }
934 else {
935 ch = '>';
936 }
937 a_str = IPv4RouteCopyDescription(a);
938 b_str = IPv4RouteCopyDescription(b);
939 SCLog(TRUE, LOG_NOTICE, CFSTR("%@ rank %u %c %@ rank %u"),
940 a_str, a_rank, ch, b_str, b_rank);
941 CFRelease(a_str);
942 CFRelease(b_str);
943 }
944 return (cmp);
945 }
946
947 static CFMutableStringRef
948 IPv4RouteListCopyDescription(IPv4RouteListRef routes)
949 {
950 int i;
951 IPv4RouteRef r;
952 CFMutableStringRef str;
953
954 str = CFStringCreateMutable(NULL, 0);
955 CFStringAppendFormat(str, NULL, CFSTR("<IPv4RouteList[%d]> = {"),
956 routes->count);
957 for (i = 0, r = routes->list; i < routes->count; i++, r++) {
958 CFStringAppendFormat(str, NULL, CFSTR("\n%2d. "), i);
959 IPv4RouteCopyDescriptionWithString(r, str);
960 }
961 CFStringAppend(str, CFSTR("\n}"));
962 return (str);
963 }
964
965 static __inline__ void
966 IPv4RouteListPrint(IPv4RouteListRef routes)
967 {
968 CFStringRef str = IPv4RouteListCopyDescription(routes);
969
970 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
971 CFRelease(str);
972 return;
973 }
974
975 static __inline__ void
976 IPv4RouteListLog(IPv4RouteListRef routes)
977 {
978 CFStringRef str = IPv4RouteListCopyDescription(routes);
979
980 SCLog(TRUE, LOG_NOTICE, CFSTR("%@"), str);
981 CFRelease(str);
982 return;
983 }
984
985 static __inline__ unsigned int
986 IPv4RouteListComputeSize(unsigned int n)
987 {
988 return (offsetof(IPv4RouteList, list[n]));
989 }
990
991 static IPv4RouteRef
992 IPv4RouteListFindRoute(IPv4RouteListRef routes, IPv4RouteRef route)
993 {
994 int i;
995 IPv4RouteRef scan_result = NULL;
996 IPv4RouteRef scan;
997
998 for (i = 0, scan = routes->list; i < routes->count; i++, scan++) {
999 if ((scan->dest.s_addr == route->dest.s_addr)
1000 && (scan->mask.s_addr == route->mask.s_addr)
1001 && (strcmp(scan->ifname, route->ifname) == 0)
1002 && (scan->ifa.s_addr == route->ifa.s_addr)
1003 && (scan->gateway.s_addr == route->gateway.s_addr)
1004 && (scan->flags == route->flags)) {
1005 scan_result = scan;
1006 break;
1007 }
1008 }
1009 return (scan_result);
1010 }
1011
1012 static void
1013 IPv4RouteListApply(IPv4RouteListRef old_routes, IPv4RouteListRef new_routes,
1014 IPv4RouteListApplyCallBackFuncPtr func, void * arg)
1015 {
1016 int i;
1017 IPv4RouteRef scan;
1018
1019 if (old_routes == new_routes && old_routes == NULL) {
1020 /* both old and new are NULL, so there's nothing to do */
1021 return;
1022 }
1023 if (old_routes != NULL) {
1024 for (i = 0, scan = old_routes->list;
1025 i < old_routes->count;
1026 i++, scan++) {
1027 IPv4RouteRef new_route = NULL;
1028
1029 if (new_routes != NULL) {
1030 new_route = IPv4RouteListFindRoute(new_routes, scan);
1031 }
1032 if (new_route == NULL) {
1033 if (func != NULL) {
1034 (*func)(kIPv4RouteListRemoveRouteCommand, scan, arg);
1035 }
1036 }
1037 }
1038 }
1039 if (new_routes != NULL) {
1040 for (i = 0, scan = new_routes->list;
1041 i < new_routes->count;
1042 i++, scan++) {
1043 IPv4RouteRef old_route = NULL;
1044
1045 if (old_routes != NULL) {
1046 old_route = IPv4RouteListFindRoute(old_routes, scan);
1047 }
1048 if (old_route == NULL) {
1049 if (func != NULL) {
1050 (*func)(kIPv4RouteListAddRouteCommand, scan, arg);
1051 }
1052 }
1053 }
1054 }
1055 return;
1056 }
1057
1058 /*
1059 * Function: IPv4RouteListAddRoute
1060 *
1061 * Purpose:
1062 * Add the given IPv4Route to the list of routes, eliminating lower-ranked
1063 * duplicates on the same interface, and marking any lower ranked duplicates
1064 * on other interfaces with kRouteIsScopedFlag.
1065 *
1066 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1067 *
1068 * Returns:
1069 * Route list updated with the given route, possibly a different pointer,
1070 * due to using realloc'd memory.
1071 */
1072
1073 enum {
1074 kScopeNone = 0,
1075 kScopeThis = 1,
1076 kScopeNext = 2
1077 };
1078
1079 static IPv4RouteListRef
1080 IPv4RouteListAddRoute(IPv4RouteListRef routes, int init_size,
1081 IPv4RouteRef this_route, Rank this_rank)
1082 {
1083 int i;
1084 IPv4RouteRef first_scan = NULL;
1085 int scope_which = kScopeNone;
1086 IPv4RouteRef scan;
1087 int where = -1;
1088
1089 if (routes == NULL) {
1090 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(init_size));
1091 routes->size = init_size;
1092 routes->count = 0;
1093 }
1094 for (i = 0, scan = routes->list; i < routes->count;
1095 i++, scan++) {
1096 int cmp;
1097 boolean_t same_dest;
1098
1099 cmp = IPv4RouteCompare(this_route, this_rank, scan, scan->rank, &same_dest);
1100
1101 if (same_dest == TRUE && first_scan == NULL) {
1102 first_scan = scan;
1103 }
1104
1105 if (cmp < 0) {
1106 if (where == -1) {
1107 if (same_dest == TRUE
1108 && (first_scan->flags & kRouteIsScopedFlag) == 0) {
1109 if ((scan->flags & kRouteIsScopedFlag) != 0) {
1110 ROUTELIST_DEBUG(("Hit 1: set scope on self\n"),
1111 kDebugFlag8);
1112 scope_which = kScopeThis;
1113 }
1114 else {
1115 ROUTELIST_DEBUG(("Hit 2: set scope on next\n"),
1116 kDebugFlag8);
1117 scope_which = kScopeNext;
1118 }
1119 }
1120 /* remember our insertion point, but keep going to find a dup */
1121 where = i;
1122 }
1123 }
1124 else if (cmp == 0) {
1125 /* exact match */
1126 if (where != -1) {
1127 /* this route is a duplicate */
1128 ROUTELIST_DEBUG(("Hit 3: removing [%d]\n", i), kDebugFlag8);
1129 routes->count--;
1130 if (i == routes->count) {
1131 /* last slot, decrementing gets rid of it */
1132 }
1133 else {
1134 bcopy(routes->list + i + 1,
1135 routes->list + i,
1136 sizeof(routes->list[0]) * (routes->count - i));
1137 }
1138 break;
1139 }
1140 /* resolve conflict using rank */
1141 if (this_rank < scan->rank) {
1142 boolean_t is_scoped = FALSE;
1143
1144 if (scan->flags & kRouteIsScopedFlag) {
1145 is_scoped = TRUE;
1146 }
1147 ROUTELIST_DEBUG(("Hit 4:replacing [%d] rank %u < %u\n", i,
1148 this_rank,
1149 scan->rank), kDebugFlag8);
1150 *scan = *this_route;
1151 scan->rank = this_rank;
1152 if (is_scoped) {
1153 /* preserve whether route was scoped */
1154 ROUTELIST_DEBUG(("Hit 5: preserved scope\n"), kDebugFlag8);
1155 scan->flags |= kRouteIsScopedFlag;
1156 }
1157 }
1158 /* we're done */
1159 goto done;
1160 }
1161 else {
1162 if (same_dest == TRUE) {
1163 if (scope_which == kScopeNone) {
1164 ROUTELIST_DEBUG(("Hit 10: set scope on self\n"),
1165 kDebugFlag8);
1166 scope_which = kScopeThis;
1167 }
1168 }
1169 #ifdef TEST_IPV4_ROUTELIST
1170 else if (where != -1) {
1171 /* not possible because we maintain a sorted list */
1172 ROUTELIST_DEBUG(("Hit 11: moved past routes - can't happen\n"),
1173 kDebugFlag8);
1174 break;
1175 }
1176 #endif /* TEST_IPV4_ROUTELIST */
1177 }
1178 }
1179 if (routes->size == routes->count) {
1180 int how_many;
1181 IPv4RouteListRef new_routes;
1182 int old_size;
1183
1184 /* double the size */
1185 old_size = routes->size;
1186 how_many = old_size * 2;
1187 new_routes = (IPv4RouteListRef)
1188 realloc(routes, IPv4RouteListComputeSize(how_many));
1189 if (new_routes == NULL) {
1190 /* no memory */
1191 goto done;
1192 }
1193 ROUTELIST_DEBUG(("increasing size from %d to %d\n", old_size,
1194 how_many), kDebugFlag8);
1195 new_routes->size = how_many;
1196 routes = new_routes;
1197 }
1198 if (where == -1) {
1199 /* add it to the end */
1200 where = routes->count;
1201 }
1202 else {
1203 /* insert it at [where] */
1204 bcopy(routes->list + where,
1205 routes->list + where + 1,
1206 sizeof(routes->list[0]) * (routes->count - where));
1207 }
1208 /* copy the route */
1209 routes->list[where] = *this_route;
1210 routes->list[where].rank = this_rank;
1211
1212 /* set the scope */
1213 switch (scope_which) {
1214 case kScopeThis:
1215 routes->list[where].flags |= kRouteIsScopedFlag;
1216 break;
1217 case kScopeNext:
1218 routes->list[where + 1].flags |= kRouteIsScopedFlag;
1219 break;
1220 default:
1221 case kScopeNone:
1222 break;
1223 }
1224 routes->count++;
1225 done:
1226 return (routes);
1227 }
1228
1229 /*
1230 * Function: IPv4RouteListAddRouteList
1231 *
1232 * Purpose:
1233 * Invoke IPv4RouteListAddRoute for each route in the given list.
1234 *
1235 * Returns:
1236 * See IPv4RouteListAddRoute for more information.
1237 */
1238 static IPv4RouteListRef
1239 IPv4RouteListAddRouteList(IPv4RouteListRef routes, int init_size,
1240 IPv4RouteListRef service_routes, Rank rank)
1241 {
1242 int i;
1243 IPv4RouteRef scan;
1244
1245 for (i = 0, scan = service_routes->list;
1246 i < service_routes->count; i++, scan++) {
1247 routes = IPv4RouteListAddRoute(routes, init_size, scan, rank);
1248 }
1249 return (routes);
1250 }
1251
1252 static boolean_t
1253 plist_get_cstring(CFDictionaryRef dict, CFStringRef prop_name,
1254 char * buf, int buf_size)
1255 {
1256 CFStringRef val;
1257
1258 val = CFDictionaryGetValue(dict, prop_name);
1259 if (isA_CFString(val) == NULL) {
1260 return (FALSE);
1261 }
1262 if (CFStringGetCString(val, buf, buf_size, kCFStringEncodingASCII)
1263 == FALSE) {
1264 return (FALSE);
1265 }
1266 return (TRUE);
1267 }
1268
1269 /*
1270 * Function: IPv4RouteListCreateWithDictionary
1271 *
1272 * Purpose:
1273 * Given the service ipv4 entity dictionary, generate the list of routes.
1274 * Currently, this includes just the default route and subnet route,
1275 * if the service has a subnet mask.
1276 *
1277 * Returns:
1278 * If the passed in route_list is NULL or too small, this routine
1279 * allocates malloc'd memory to hold the routes.
1280 */
1281 static IPv4RouteListRef
1282 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes,
1283 CFDictionaryRef dict,
1284 CFStringRef primaryRank)
1285 {
1286 struct in_addr addr = { 0 };
1287 RouteFlags flags = 0;
1288 unsigned int ifindex;
1289 char ifn[IFNAMSIZ];
1290 struct in_addr mask = { 0 };
1291 int n = 0;
1292 boolean_t add_default = FALSE;
1293 boolean_t add_subnet = FALSE;
1294 IPv4RouteRef r;
1295 struct in_addr subnet = { 0 };
1296 struct in_addr router = { 0 };
1297 Rank rank = kRankAssertionDefault;
1298
1299 if (dict == NULL) {
1300 return (NULL);
1301 }
1302 if (plist_get_cstring(dict, kSCPropInterfaceName, ifn, sizeof(ifn))
1303 == FALSE) {
1304 return (NULL);
1305 }
1306 #ifdef TEST_IPV4_ROUTELIST
1307 ifindex = 0;
1308 #else /* TEST_IPV4_ROUTELIST */
1309 ifindex = if_nametoindex(ifn);
1310 if (ifindex == 0) {
1311 /* interface doesn't exist */
1312 return (NULL);
1313 }
1314 #endif /* TEST_IPV4_ROUTELIST */
1315 if (cfstring_to_ip(CFDictionaryGetValue(dict, kSCPropNetIPv4Router),
1316 &router) == 0) {
1317 (void)dict_get_first_ip(dict, kSCPropNetIPv4DestAddresses, &router);
1318 }
1319 if (dict_get_first_ip(dict, kSCPropNetIPv4Addresses, &addr)
1320 && dict_get_first_ip(dict, kSCPropNetIPv4SubnetMasks, &mask)) {
1321 /* subnet route */
1322 subnet = subnet_addr(addr, mask);
1323 /* ignore link-local subnets, let IPConfiguration handle them for now */
1324 if (ntohl(subnet.s_addr) != IN_LINKLOCALNETNUM) {
1325 add_subnet = TRUE;
1326 n++;
1327 }
1328 }
1329 if (addr.s_addr == 0) {
1330 /* thanks for playing */
1331 return (NULL);
1332 }
1333 if (router.s_addr == 0) {
1334 flags |= kRouteIsDirectToInterfaceFlag;
1335 rank = kRankAssertionLast;
1336 }
1337 else {
1338 /*
1339 * If the router address is our address and the subnet mask is
1340 * not 255.255.255.255, assume all routes are local to the interface.
1341 */
1342 if (addr.s_addr == router.s_addr
1343 && mask.s_addr != INADDR_BROADCAST) {
1344 flags |= kRouteIsDirectToInterfaceFlag;
1345 }
1346 if (primaryRank != NULL) {
1347 rank = PrimaryRankGetRankAssertion(primaryRank);
1348 } else if (get_override_primary(dict)) {
1349 rank = kRankAssertionFirst;
1350 }
1351 }
1352
1353 if (rank == kRankAssertionNever) {
1354 flags |= kRouteIsScopedFlag;
1355 }
1356
1357 if (add_subnet && (flags & kRouteIsDirectToInterfaceFlag) == 0
1358 && subnet.s_addr != subnet_addr(router, mask).s_addr) {
1359 flags |= kRouteIsNotSubnetLocalFlag;
1360 }
1361
1362 if (strncmp(ifn, "lo0", sizeof(ifn)) != 0) {
1363 add_default = TRUE;
1364 n++;
1365 }
1366
1367 if (routes == NULL || routes->size < n) {
1368 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(n));
1369 routes->size = n;
1370 }
1371 bzero(routes, IPv4RouteListComputeSize(n));
1372 routes->count = n;
1373
1374 /* start at the beginning */
1375 r = routes->list;
1376
1377 if (add_default) {
1378 /* add the default route */
1379 r->ifindex = ifindex;
1380 strlcpy(r->ifname, ifn, sizeof(r->ifname));
1381 r->ifa = addr;
1382 r->flags = flags;
1383 if ((flags & kRouteIsDirectToInterfaceFlag) == 0) {
1384 r->gateway = router;
1385 }
1386 else {
1387 r->gateway = addr;
1388 }
1389 r->rank = rank;
1390 if (r->rank == kRankAssertionNever) {
1391 r->flags |= kRouteIsScopedFlag;
1392 }
1393 r++;
1394 }
1395
1396 /* add the subnet route */
1397 if (add_subnet) {
1398 r->ifindex = ifindex;
1399 r->gateway = addr;
1400 r->dest = subnet;
1401 r->mask = mask;
1402 strlcpy(r->ifname, ifn, sizeof(r->ifname));
1403 r->ifa = addr;
1404 r->rank = rank;
1405 if (r->rank == kRankAssertionNever) {
1406 r->flags |= kRouteIsScopedFlag;
1407 }
1408 }
1409
1410 return (routes);
1411 }
1412
1413 /*
1414 * Function: parse_component
1415 * Purpose:
1416 * Given a string 'key' and a string prefix 'prefix',
1417 * return the next component in the slash '/' separated
1418 * key.
1419 *
1420 * Examples:
1421 * 1. key = "a/b/c" prefix = "a/"
1422 * returns "b"
1423 * 2. key = "a/b/c" prefix = "a/b/"
1424 * returns "c"
1425 */
1426 static CF_RETURNS_RETAINED CFStringRef
1427 parse_component(CFStringRef key, CFStringRef prefix)
1428 {
1429 CFMutableStringRef comp;
1430 CFRange range;
1431
1432 if (CFStringHasPrefix(key, prefix) == FALSE) {
1433 return (NULL);
1434 }
1435 comp = CFStringCreateMutableCopy(NULL, 0, key);
1436 if (comp == NULL) {
1437 return (NULL);
1438 }
1439 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
1440 range = CFStringFind(comp, CFSTR("/"), 0);
1441 if (range.location == kCFNotFound) {
1442 return (comp);
1443 }
1444 range.length = CFStringGetLength(comp) - range.location;
1445 CFStringDelete(comp, range);
1446 return (comp);
1447 }
1448
1449 static CFMutableDictionaryRef
1450 service_dict_copy(CFStringRef serviceID)
1451 {
1452 CFDictionaryRef d = NULL;
1453 CFMutableDictionaryRef service_dict;
1454
1455 /* create a modifyable dictionary, a copy or a new one */
1456 d = CFDictionaryGetValue(S_service_state_dict, serviceID);
1457 if (d == NULL) {
1458 service_dict
1459 = CFDictionaryCreateMutable(NULL, 0,
1460 &kCFTypeDictionaryKeyCallBacks,
1461 &kCFTypeDictionaryValueCallBacks);
1462 }
1463 else {
1464 service_dict = CFDictionaryCreateMutableCopy(NULL, 0, d);
1465 }
1466 return (service_dict);
1467 }
1468
1469 static void
1470 dump_service_entity(CFStringRef serviceID, CFStringRef entity,
1471 CFStringRef operation, CFTypeRef val)
1472 {
1473
1474 CFDataRef route_list;
1475 CFMutableStringRef this_val = NULL;
1476
1477 if (CFEqual(entity, kSCEntNetIPv4) && isA_CFDictionary(val) != NULL) {
1478 CFDictionaryRef service_dict = NULL;
1479
1480 route_list = CFDictionaryGetValue(val, kSCEntNetIPv4RouteList);
1481 if (route_list != NULL) {
1482 /* ALIGN: CF should align to at least 8-byte boundaries */
1483 this_val = IPv4RouteListCopyDescription((IPv4RouteListRef)
1484 (void *)CFDataGetBytePtr(route_list));
1485 }
1486
1487 service_dict = CFDictionaryGetValue(val, kSCEntNetIPv4ServiceDict);
1488
1489 if (service_dict != NULL && isA_CFDictionary(service_dict) != NULL) {
1490 if (this_val == NULL) {
1491 this_val = CFStringCreateMutable(NULL, 0);
1492 }
1493 CFStringAppendFormat(this_val, NULL, CFSTR("\n <IPv4Dictionary>: %@"), service_dict);
1494 }
1495 val = this_val;
1496 }
1497 if (val == NULL) {
1498 val = CFSTR("<none>");
1499 }
1500 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: serviceID %@ %@ %@ value = %@"),
1501 serviceID, operation, entity, val);
1502 my_CFRelease(&this_val);
1503 return;
1504 }
1505
1506 static boolean_t
1507 service_dict_set(CFStringRef serviceID, CFStringRef entity,
1508 CFTypeRef new_val)
1509 {
1510 boolean_t changed = FALSE;
1511 CFTypeRef old_val;
1512 CFMutableDictionaryRef service_dict;
1513
1514 service_dict = service_dict_copy(serviceID);
1515 old_val = CFDictionaryGetValue(service_dict, entity);
1516 if (new_val == NULL) {
1517 if (old_val != NULL) {
1518 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1519 dump_service_entity(serviceID, entity, CFSTR("Removed:"),
1520 old_val);
1521 }
1522 CFDictionaryRemoveValue(service_dict, entity);
1523 changed = TRUE;
1524 }
1525 }
1526 else {
1527 if (old_val == NULL || CFEqual(new_val, old_val) == FALSE) {
1528 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1529 dump_service_entity(serviceID, entity,
1530 CFSTR("Changed: old"), old_val);
1531 dump_service_entity(serviceID, entity,
1532 CFSTR("Changed: new"), new_val);
1533 }
1534 CFDictionarySetValue(service_dict, entity, new_val);
1535 changed = TRUE;
1536 }
1537 }
1538 if (CFDictionaryGetCount(service_dict) == 0) {
1539 CFDictionaryRemoveValue(S_service_state_dict, serviceID);
1540 }
1541 else {
1542 CFDictionarySetValue(S_service_state_dict, serviceID, service_dict);
1543 }
1544 my_CFRelease(&service_dict);
1545 return (changed);
1546 }
1547
1548 static CFDictionaryRef
1549 service_dict_get(CFStringRef serviceID, CFStringRef entity)
1550 {
1551 CFDictionaryRef service_dict;
1552
1553 service_dict = CFDictionaryGetValue(S_service_state_dict, serviceID);
1554 if (service_dict == NULL) {
1555 return (NULL);
1556 }
1557 return (CFDictionaryGetValue(service_dict, entity));
1558 }
1559
1560 #ifndef kSCPropNetHostname
1561 #define kSCPropNetHostname CFSTR("Hostname")
1562 #endif
1563
1564 __private_extern__
1565 CFStringRef
1566 copy_dhcp_hostname(CFStringRef serviceID)
1567 {
1568 CFDictionaryRef dict = NULL;
1569 CFStringRef hostname = NULL;
1570 CFDictionaryRef service_dict = NULL;
1571
1572 dict = service_dict_get(serviceID, kSCEntNetIPv4);
1573
1574 if (dict == NULL || isA_CFDictionary(dict) == NULL) {
1575 return (NULL);
1576 }
1577
1578 service_dict =
1579 CFDictionaryGetValue(dict, kSCEntNetIPv4ServiceDict);
1580
1581 if (service_dict == NULL
1582 || isA_CFDictionary(service_dict) == NULL) {
1583 return (NULL);
1584 }
1585
1586 hostname =
1587 CFDictionaryGetValue(service_dict, kSCPropNetHostname);
1588
1589 if (hostname != NULL) {
1590 CFRetain(hostname);
1591 }
1592
1593 return (hostname);
1594 }
1595
1596 static boolean_t
1597 ipv6_service_dict_set(CFStringRef serviceID, CFDictionaryRef new_val)
1598 {
1599 #ifdef SIOCDRADD_IN6
1600 int if_index;
1601 char ifn[IFNAMSIZ];
1602 CFStringRef new_router = NULL;
1603 char ntopbuf[INET6_ADDRSTRLEN];
1604 CFDictionaryRef old_val = NULL;
1605 CFStringRef old_router = NULL;
1606 struct in6_addr router_ip;
1607 int s = -1;
1608
1609 ifn[0] = '\0';
1610 old_val = service_dict_get(serviceID, kSCEntNetIPv6);
1611 if (old_val != NULL) {
1612 plist_get_cstring(old_val, kSCPropInterfaceName, ifn, sizeof(ifn));
1613 old_router = CFDictionaryGetValue(old_val, kSCPropNetIPv6Router);
1614 }
1615 if (ifn[0] == '\0') {
1616 if (new_val == NULL
1617 || plist_get_cstring(new_val, kSCPropInterfaceName,
1618 ifn, sizeof(ifn)) == FALSE) {
1619 /* no InterfaceName property, ignore it */
1620 goto done;
1621 }
1622 }
1623 if_index = if_nametoindex(ifn);
1624 if (if_index == 0) {
1625 goto done;
1626 }
1627 s = inet6_dgram_socket();
1628 if (s < 0) {
1629 syslog(LOG_ERR,
1630 "IPMonitor: ipv6_service_dict_set: socket failed, %s",
1631 strerror(errno));
1632 goto done;
1633 }
1634 if (new_val != NULL) {
1635 new_router = CFDictionaryGetValue(new_val, kSCPropNetIPv6Router);
1636 }
1637 if (old_router != NULL
1638 && (new_router == NULL || CFEqual(old_router, new_router) == FALSE)) {
1639 /* remove the old Router */
1640 if (cfstring_to_ip6(old_router, &router_ip)) {
1641 if (IN6_IS_ADDR_LINKLOCAL(&router_ip) ||
1642 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip)) {
1643 /* scope it */
1644 router_ip.__u6_addr.__u6_addr16[1] = htons(if_index);
1645 }
1646 if (siocdrdel_in6(s, if_index, &router_ip) < 0) {
1647 if (errno != EINVAL) {
1648 syslog(LOG_ERR,
1649 "IPMonitor: siocdrdel_in6(%s, %s) failed, %s",
1650 ifn,
1651 inet_ntop(AF_INET6, &router_ip,
1652 ntopbuf, sizeof(ntopbuf)),
1653 strerror(errno));
1654 }
1655 }
1656 else if (S_IPMonitor_debug & kDebugFlag1) {
1657 syslog(LOG_NOTICE,
1658 "IPMonitor: %s removed default route %s",
1659 ifn,
1660 inet_ntop(AF_INET6, &router_ip,
1661 ntopbuf, sizeof(ntopbuf)));
1662 }
1663 }
1664 }
1665 /* add the new Router */
1666 if (cfstring_to_ip6(new_router, &router_ip)) {
1667 if (IN6_IS_ADDR_LINKLOCAL(&router_ip) ||
1668 IN6_IS_ADDR_MC_LINKLOCAL(&router_ip)) {
1669 /* scope it */
1670 router_ip.__u6_addr.__u6_addr16[1] = htons(if_index);
1671 }
1672 if (siocdradd_in6(s, if_index, &router_ip, 0) < 0) {
1673 if (errno != EINVAL) {
1674 syslog(LOG_ERR,
1675 "IPMonitor: siocdradd_in6(%s, %s) failed, %s",
1676 ifn,
1677 inet_ntop(AF_INET6, &router_ip,
1678 ntopbuf, sizeof(ntopbuf)),
1679 strerror(errno));
1680 }
1681 }
1682 else if (S_IPMonitor_debug & kDebugFlag1) {
1683 syslog(LOG_NOTICE,
1684 "IPMonitor: %s added default route %s",
1685 ifn,
1686 inet_ntop(AF_INET6, &router_ip,
1687 ntopbuf, sizeof(ntopbuf)));
1688 }
1689 }
1690 close(s);
1691
1692 done:
1693 #endif /* SIOCDRADD_IN6 */
1694 return (service_dict_set(serviceID, kSCEntNetIPv6, new_val));
1695 }
1696
1697 #define ALLOW_EMPTY_STRING 0x1
1698
1699 static CF_RETURNS_RETAINED CFTypeRef
1700 sanitize_prop(CFTypeRef val, uint32_t flags)
1701 {
1702 if (val != NULL) {
1703 if (isA_CFString(val)) {
1704 CFMutableStringRef str;
1705
1706 str = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)val);
1707 CFStringTrimWhitespace(str);
1708 if (!(flags & ALLOW_EMPTY_STRING) && (CFStringGetLength(str) == 0)) {
1709 CFRelease(str);
1710 str = NULL;
1711 }
1712 val = str;
1713 } else {
1714 CFRetain(val);
1715 }
1716 }
1717
1718 return val;
1719 }
1720
1721 static void
1722 merge_array_prop(CFMutableDictionaryRef dict,
1723 CFStringRef key,
1724 CFDictionaryRef state_dict,
1725 CFDictionaryRef setup_dict,
1726 uint32_t flags,
1727 Boolean append)
1728 {
1729 CFMutableArrayRef merge_prop;
1730 CFArrayRef setup_prop = NULL;
1731 CFArrayRef state_prop = NULL;
1732
1733 if (setup_dict != NULL) {
1734 setup_prop = isA_CFArray(CFDictionaryGetValue(setup_dict, key));
1735 }
1736 if (state_dict != NULL) {
1737 state_prop = isA_CFArray(CFDictionaryGetValue(state_dict, key));
1738 }
1739
1740 if ((setup_prop == NULL) && (state_prop == NULL)) {
1741 return;
1742 }
1743
1744 merge_prop = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1745 if (setup_prop != NULL) {
1746 CFIndex i;
1747 CFIndex n;
1748
1749 n = CFArrayGetCount(setup_prop);
1750 for (i = 0; i < n; i++) {
1751 CFTypeRef val;
1752
1753 val = CFArrayGetValueAtIndex(setup_prop, i);
1754 val = sanitize_prop(val, flags);
1755 if (val != NULL) {
1756 CFArrayAppendValue(merge_prop, val);
1757 CFRelease(val);
1758 }
1759 }
1760 }
1761 if (state_prop != NULL
1762 && (setup_prop == NULL || S_append_state)) {
1763 CFIndex i;
1764 CFIndex n;
1765 CFRange setup_range = CFRangeMake(0, CFArrayGetCount(merge_prop));
1766
1767 n = CFArrayGetCount(state_prop);
1768 for (i = 0; i < n; i++) {
1769 CFTypeRef val;
1770
1771 val = CFArrayGetValueAtIndex(state_prop, i);
1772 val = sanitize_prop(val, flags);
1773 if (val != NULL) {
1774 if (append || !CFArrayContainsValue(merge_prop, setup_range, val)) {
1775 CFArrayAppendValue(merge_prop, val);
1776 }
1777 CFRelease(val);
1778 }
1779 }
1780 }
1781 if (CFArrayGetCount(merge_prop) > 0) {
1782 CFDictionarySetValue(dict, key, merge_prop);
1783 }
1784 CFRelease(merge_prop);
1785 return;
1786 }
1787
1788 static void
1789 pick_prop(CFMutableDictionaryRef dict,
1790 CFStringRef key,
1791 CFDictionaryRef state_dict,
1792 CFDictionaryRef setup_dict,
1793 uint32_t flags)
1794 {
1795 CFTypeRef val = NULL;
1796
1797 if (setup_dict != NULL) {
1798 val = CFDictionaryGetValue(setup_dict, key);
1799 val = sanitize_prop(val, flags);
1800 }
1801 if (val == NULL && state_dict != NULL) {
1802 val = CFDictionaryGetValue(state_dict, key);
1803 val = sanitize_prop(val, flags);
1804 }
1805 if (val != NULL) {
1806 CFDictionarySetValue(dict, key, val);
1807 CFRelease(val);
1808 }
1809
1810 return;
1811 }
1812
1813 /**
1814 ** GetEntityChangesFunc functions
1815 **/
1816 static boolean_t
1817 get_ipv4_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
1818 CFDictionaryRef setup_dict, CFDictionaryRef info)
1819 {
1820 CFDictionaryRef aggregated_dict = NULL;
1821 boolean_t changed = FALSE;
1822 CFMutableDictionaryRef dict = NULL;
1823 CFStringRef primaryRank = NULL;
1824 IPv4RouteListRef r;
1825 #define R_STATIC 3
1826 IPv4RouteListRef routes;
1827 /* ALIGN: force align */
1828 uint32_t routes_buf[roundup(IPv4RouteListComputeSize(R_STATIC), sizeof(uint32_t))];
1829 CFDataRef routes_data = NULL;
1830 CFDictionaryRef service_options;
1831
1832 if (state_dict == NULL) {
1833 goto done;
1834 }
1835 service_options = service_dict_get(serviceID, kSCEntNetService);
1836 if (service_options != NULL) {
1837 primaryRank = CFDictionaryGetValue(service_options, kSCPropNetServicePrimaryRank);
1838 }
1839 dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
1840 if (setup_dict != NULL) {
1841 CFStringRef router;
1842 struct in_addr router_ip;
1843
1844 router = CFDictionaryGetValue(setup_dict,
1845 kSCPropNetIPv4Router);
1846 if (router != NULL
1847 && cfstring_to_ip(router, &router_ip)) {
1848 CFDictionarySetValue(dict,
1849 kSCPropNetIPv4Router,
1850 router);
1851 }
1852 }
1853 routes = (IPv4RouteListRef)(void *)routes_buf;
1854 routes->size = R_STATIC;
1855 routes->count = 0;
1856 r = IPv4RouteListCreateWithDictionary(routes, dict, primaryRank);
1857 if (r != NULL) {
1858 routes_data = CFDataCreate(NULL,
1859 (const void *)r,
1860 IPv4RouteListComputeSize(r->count));
1861 if (r != routes) {
1862 free(r);
1863 }
1864 }
1865 else {
1866 SCLog(TRUE, LOG_NOTICE,
1867 CFSTR("IPMonitor: %@ invalid IPv4 dictionary = %@"),
1868 serviceID,
1869 dict);
1870 }
1871 done:
1872 if (routes_data != NULL) {
1873 CFStringRef keys[2];
1874 CFTypeRef values[2];
1875
1876 keys[0] = kSCEntNetIPv4ServiceDict;
1877 values[0] = dict;
1878 keys[1] = kSCEntNetIPv4RouteList;
1879 values[1] = routes_data;
1880
1881 aggregated_dict = CFDictionaryCreate(NULL,
1882 (const void**)keys,
1883 values,
1884 sizeof(keys)/sizeof(keys[0]),
1885 &kCFTypeDictionaryKeyCallBacks,
1886 &kCFTypeDictionaryValueCallBacks);
1887
1888 }
1889 changed = service_dict_set(serviceID, kSCEntNetIPv4, aggregated_dict);
1890 if (routes_data == NULL) {
1891 /* clean up the rank too */
1892 CFDictionaryRemoveValue(S_ipv4_service_rank_dict, serviceID);
1893 }
1894 my_CFRelease(&dict);
1895 my_CFRelease(&aggregated_dict);
1896 my_CFRelease(&routes_data);
1897 return (changed);
1898 }
1899
1900 static boolean_t
1901 get_ipv6_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
1902 CFDictionaryRef setup_dict, CFDictionaryRef info)
1903 {
1904 struct in6_addr addr;
1905 CFArrayRef addrs;
1906 boolean_t changed = FALSE;
1907 CFMutableDictionaryRef dict = NULL;
1908 CFDictionaryRef new_dict = NULL;
1909 CFStringRef router = NULL;
1910 struct in6_addr router_ip;
1911 boolean_t valid_ip = FALSE;
1912
1913 if (state_dict == NULL) {
1914 goto done;
1915 }
1916 addrs = isA_CFArray(CFDictionaryGetValue(state_dict,
1917 kSCPropNetIPv6Addresses));
1918 if (addrs != NULL && CFArrayGetCount(addrs) > 0) {
1919 valid_ip = cfstring_to_ip6(CFArrayGetValueAtIndex(addrs, 0), &addr);
1920 }
1921 if (valid_ip == FALSE) {
1922 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1923 SCLog(TRUE, LOG_NOTICE,
1924 CFSTR("IPMonitor: %@ has no valid IPv6 address, ignoring"),
1925 serviceID);
1926 }
1927 goto done;
1928 }
1929 dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
1930 if (setup_dict != NULL) {
1931 router = CFDictionaryGetValue(setup_dict,
1932 kSCPropNetIPv6Router);
1933 if (router != NULL && cfstring_to_ip6(router, &router_ip)) {
1934 CFDictionarySetValue(dict,
1935 kSCPropNetIPv6Router,
1936 router);
1937 }
1938 }
1939 else {
1940 router = CFDictionaryGetValue(dict,
1941 kSCPropNetIPv6Router);
1942 if (router != NULL
1943 && cfstring_to_ip6(router, &router_ip) == FALSE) {
1944 CFDictionaryRemoveValue(dict, kSCPropNetIPv6Router);
1945 }
1946 }
1947 new_dict = dict;
1948
1949 done:
1950 changed = ipv6_service_dict_set(serviceID, new_dict);
1951 if (new_dict == NULL) {
1952 /* clean up the rank too */
1953 CFDictionaryRemoveValue(S_ipv6_service_rank_dict, serviceID);
1954 }
1955 my_CFRelease(&new_dict);
1956 return (changed);
1957 }
1958
1959 static void
1960 accumulate_dns_servers(CFArrayRef in_servers, ProtocolFlags active_protos,
1961 CFMutableArrayRef out_servers, CFStringRef interface)
1962 {
1963 int count;
1964 int i;
1965
1966 count = CFArrayGetCount(in_servers);
1967 for (i = 0; i < count; i++) {
1968 CFStringRef addr = CFArrayGetValueAtIndex(in_servers, i);
1969 struct in6_addr ipv6_addr;
1970 struct in_addr ip_addr;
1971
1972 if (cfstring_to_ip(addr, &ip_addr)) {
1973 /* IPv4 address */
1974 if ((active_protos & kProtocolFlagsIPv4) == 0
1975 && ntohl(ip_addr.s_addr) != INADDR_LOOPBACK) {
1976 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1977 syslog(LOG_NOTICE,
1978 "IPMonitor: no IPv4 connectivity, "
1979 "ignoring DNS server address " IP_FORMAT,
1980 IP_LIST(&ip_addr));
1981 }
1982 continue;
1983 }
1984
1985 CFRetain(addr);
1986 }
1987 else if (cfstring_to_ip6(addr, &ipv6_addr)) {
1988 /* IPv6 address */
1989 if ((active_protos & kProtocolFlagsIPv6) == 0
1990 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr)) {
1991 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1992 char ntopbuf[INET6_ADDRSTRLEN];
1993
1994 syslog(LOG_NOTICE,
1995 "IPMonitor: no IPv6 connectivity, "
1996 "ignoring DNS server address %s",
1997 inet_ntop(AF_INET6, &ipv6_addr,
1998 ntopbuf, sizeof(ntopbuf)));
1999 }
2000 continue;
2001 }
2002
2003 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr) ||
2004 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr))
2005 && (interface != NULL)
2006 && (CFStringFind(addr, CFSTR("%"), 0).location == kCFNotFound)) {
2007 // append interface name to IPv6 link local address
2008 addr = CFStringCreateWithFormat(NULL, NULL,
2009 CFSTR("%@%%%@"),
2010 addr,
2011 interface);
2012 } else {
2013 CFRetain(addr);
2014 }
2015 }
2016 else {
2017 /* bad IP address */
2018 SCLog(TRUE, LOG_NOTICE,
2019 CFSTR("IPMonitor: ignoring bad DNS server address '%@'"),
2020 addr);
2021 continue;
2022 }
2023
2024 /* DNS server is valid and one we want */
2025 CFArrayAppendValue(out_servers, addr);
2026 CFRelease(addr);
2027 }
2028 return;
2029 }
2030
2031 static void
2032 merge_dns_servers(CFMutableDictionaryRef new_dict,
2033 CFArrayRef state_servers,
2034 CFArrayRef setup_servers,
2035 Boolean have_setup,
2036 ProtocolFlags active_protos,
2037 CFStringRef interface)
2038 {
2039 CFMutableArrayRef dns_servers;
2040 Boolean have_dns_setup = FALSE;
2041
2042 if (state_servers == NULL && setup_servers == NULL) {
2043 /* no DNS servers */
2044 return;
2045 }
2046 dns_servers = CFArrayCreateMutable(NULL, 0,
2047 &kCFTypeArrayCallBacks);
2048 if (setup_servers != NULL) {
2049 accumulate_dns_servers(setup_servers, active_protos,
2050 dns_servers, interface);
2051 if (CFArrayGetCount(dns_servers) > 0) {
2052 have_dns_setup = TRUE;
2053 }
2054 }
2055 if ((CFArrayGetCount(dns_servers) == 0 || S_append_state)
2056 && state_servers != NULL) {
2057 accumulate_dns_servers(state_servers, active_protos,
2058 dns_servers, NULL);
2059 }
2060
2061 /*
2062 * Here, we determine whether or not we want all queries for this DNS
2063 * configuration to be bound to the associated network interface.
2064 *
2065 * For dynamically derived network configurations (i.e. from State:)
2066 * this would be the preferred option using the argument "Hey, the
2067 * server told us to use these servers on this network so let's not
2068 * argue".
2069 *
2070 * But, when a DNS configuration has been provided by the user/admin
2071 * via the Network pref pane (i.e. from Setup:) we opt to not force
2072 * binding of the outbound queries. The simplest example why we take
2073 * this stance is with a multi-homing configuration. Consider a system
2074 * with one network service associated with "en0" and a second service
2075 * associated with "en1". The "en0" service has been set higher in
2076 * the network service order so it would be primary but the user/admin
2077 * wants the DNS queries to go to a server only accessible via "en1".
2078 * Without this exception we would take the DNS server addresses from
2079 * the Network pref pane (for "en0") and have the queries bound to
2080 * "en0" where they'd never reach their intended destination (via
2081 * "en1"). So, our exception to the rule is that we will not bind
2082 * user/admin configurations to any specific network interface.
2083 *
2084 * We also add an exception to the "follow the dynamically derived
2085 * network configuration" path for on-the-fly (no Setup: content)
2086 * network services.
2087 */
2088 if (CFArrayGetCount(dns_servers) != 0) {
2089 CFDictionarySetValue(new_dict,
2090 kSCPropNetDNSServerAddresses, dns_servers);
2091 if (have_setup && !have_dns_setup) {
2092 CFDictionarySetValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue);
2093 }
2094 }
2095
2096 my_CFRelease(&dns_servers);
2097 return;
2098 }
2099
2100
2101 static boolean_t
2102 get_dns_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2103 CFDictionaryRef setup_dict, CFDictionaryRef info)
2104 {
2105 ProtocolFlags active_protos = kProtocolFlagsNone;
2106 boolean_t changed = FALSE;
2107 CFStringRef domain;
2108 Boolean have_setup = FALSE;
2109 CFStringRef interface = NULL;
2110 CFDictionaryRef ipv4;
2111 CFDictionaryRef ipv6;
2112 int i;
2113 const struct {
2114 CFStringRef key;
2115 uint32_t flags;
2116 Boolean append;
2117 } merge_list[] = {
2118 { kSCPropNetDNSSearchDomains, 0, FALSE },
2119 { kSCPropNetDNSSortList, 0, FALSE },
2120 { kSCPropNetDNSSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE },
2121 { kSCPropNetDNSSupplementalMatchOrders, 0, TRUE },
2122 };
2123 CFMutableDictionaryRef new_dict = NULL;
2124 const CFStringRef pick_list[] = {
2125 kSCPropNetDNSDomainName,
2126 kSCPropNetDNSOptions,
2127 kSCPropNetDNSSearchOrder,
2128 kSCPropNetDNSServerPort,
2129 kSCPropNetDNSServerTimeout,
2130 };
2131 IPv4RouteListRef routes = NULL;
2132
2133 if ((state_dict == NULL) && (setup_dict == NULL)) {
2134 /* there is no DNS content */
2135 goto done;
2136 }
2137
2138 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
2139 routes = ipv4_dict_get_routelist(ipv4);
2140
2141 if (routes != NULL) {
2142 if (get_service_setup_entity(info, serviceID, kSCEntNetIPv4) != NULL) {
2143 have_setup = TRUE;
2144 }
2145
2146 active_protos |= kProtocolFlagsIPv4;
2147
2148 interface = ipv4_dict_get_ifname(ipv4);
2149 }
2150
2151 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
2152 if (ipv6 != NULL) {
2153 if (!have_setup &&
2154 get_service_setup_entity(info, serviceID, kSCEntNetIPv6) != NULL) {
2155 have_setup = TRUE;
2156 }
2157
2158 active_protos |= kProtocolFlagsIPv6;
2159
2160 if (interface == NULL) {
2161 interface = CFDictionaryGetValue(ipv6,
2162 kSCPropInterfaceName);
2163 }
2164 }
2165
2166
2167 if (active_protos == kProtocolFlagsNone) {
2168 /* there is no IPv4 nor IPv6 */
2169 if (state_dict == NULL) {
2170 /* ... and no DNS content that we care about */
2171 goto done;
2172 }
2173 setup_dict = NULL;
2174 }
2175
2176 /* merge DNS configuration */
2177 new_dict = CFDictionaryCreateMutable(NULL, 0,
2178 &kCFTypeDictionaryKeyCallBacks,
2179 &kCFTypeDictionaryValueCallBacks);
2180
2181 if (active_protos == kProtocolFlagsNone) {
2182 merge_dns_servers(new_dict,
2183 my_CFDictionaryGetArray(state_dict,
2184 kSCPropNetDNSServerAddresses),
2185 NULL,
2186 FALSE,
2187 kProtocolFlagsIPv4 | kProtocolFlagsIPv6,
2188 NULL);
2189 }
2190 else {
2191 merge_dns_servers(new_dict,
2192 my_CFDictionaryGetArray(state_dict,
2193 kSCPropNetDNSServerAddresses),
2194 my_CFDictionaryGetArray(setup_dict,
2195 kSCPropNetDNSServerAddresses),
2196 have_setup,
2197 active_protos,
2198 interface);
2199 }
2200
2201 for (i = 0; i < sizeof(merge_list)/sizeof(merge_list[0]); i++) {
2202 merge_array_prop(new_dict,
2203 merge_list[i].key,
2204 state_dict,
2205 setup_dict,
2206 merge_list[i].flags,
2207 merge_list[i].append);
2208 }
2209
2210 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2211 pick_prop(new_dict,
2212 pick_list[i],
2213 state_dict,
2214 setup_dict,
2215 0);
2216 }
2217
2218 if (active_protos == kProtocolFlagsNone) {
2219 /* there is no IPv4 nor IPv6, only supplemental DNS */
2220 if (CFDictionaryContainsKey(new_dict,
2221 kSCPropNetDNSSupplementalMatchDomains)) {
2222 /* only keep State: supplemental */
2223 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSDomainName);
2224 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchDomains);
2225 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchOrder);
2226 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSortList);
2227 } else {
2228 goto done;
2229 }
2230 }
2231
2232 if (CFDictionaryGetCount(new_dict) == 0) {
2233 my_CFRelease(&new_dict);
2234 goto done;
2235 }
2236
2237 if (interface != NULL) {
2238 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
2239 }
2240
2241 if (S_append_state) {
2242 /*
2243 * ensure any specified domain name (e.g. the domain returned by
2244 * a DHCP server) is in the search list.
2245 */
2246 domain = CFDictionaryGetValue(new_dict, kSCPropNetDNSDomainName);
2247 if (isA_CFString(domain)) {
2248 CFArrayRef search;
2249
2250 search = CFDictionaryGetValue(new_dict, kSCPropNetDNSSearchDomains);
2251 if (isA_CFArray(search) &&
2252 !CFArrayContainsValue(search, CFRangeMake(0, CFArrayGetCount(search)), domain)) {
2253 CFMutableArrayRef new_search;
2254
2255 new_search = CFArrayCreateMutableCopy(NULL, 0, search);
2256 CFArrayAppendValue(new_search, domain);
2257 CFDictionarySetValue(new_dict, kSCPropNetDNSSearchDomains, new_search);
2258 my_CFRelease(&new_search);
2259 }
2260 }
2261 }
2262
2263 done:
2264 changed = service_dict_set(serviceID, kSCEntNetDNS, new_dict);
2265 my_CFRelease(&new_dict);
2266 return (changed);
2267 }
2268
2269 static void
2270 merge_dict(const void *key, const void *value, void *context)
2271 {
2272 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context;
2273
2274 CFDictionarySetValue(dict, key, value);
2275 return;
2276 }
2277
2278 #define PROXY_AUTO_DISCOVERY_URL 252
2279
2280 static CF_RETURNS_RETAINED CFStringRef
2281 wpadURL_dhcp(CFDictionaryRef dhcp_options)
2282 {
2283 CFStringRef urlString = NULL;
2284
2285 if (isA_CFDictionary(dhcp_options)) {
2286 CFDataRef data;
2287
2288 data = DHCPInfoGetOptionData(dhcp_options, PROXY_AUTO_DISCOVERY_URL);
2289 if (data != NULL) {
2290 CFURLRef url;
2291 const UInt8 *urlBytes;
2292 CFIndex urlLen;
2293
2294 urlBytes = CFDataGetBytePtr(data);
2295 urlLen = CFDataGetLength(data);
2296 while ((urlLen > 0) && (urlBytes[urlLen - 1] == 0)) {
2297 // remove trailing NUL
2298 urlLen--;
2299 }
2300
2301 if (urlLen <= 0) {
2302 return NULL;
2303 }
2304
2305 url = CFURLCreateWithBytes(NULL, urlBytes, urlLen, kCFStringEncodingUTF8, NULL);
2306 if (url != NULL) {
2307 urlString = CFURLGetString(url);
2308 if (urlString != NULL) {
2309 CFRetain(urlString);
2310 }
2311 CFRelease(url);
2312 }
2313 }
2314 }
2315
2316 return urlString;
2317 }
2318
2319 static CF_RETURNS_RETAINED CFStringRef
2320 wpadURL_dns(void)
2321 {
2322 CFURLRef url;
2323 CFStringRef urlString = NULL;
2324
2325 url = CFURLCreateWithString(NULL, CFSTR("http://wpad/wpad.dat"), NULL);
2326 if (url != NULL) {
2327 urlString = CFURLGetString(url);
2328 if (urlString != NULL) {
2329 CFRetain(urlString);
2330 }
2331 CFRelease(url);
2332 }
2333
2334 return urlString;
2335 }
2336
2337 static boolean_t
2338 get_proxies_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2339 CFDictionaryRef setup_dict, CFDictionaryRef info)
2340 {
2341 ProtocolFlags active_protos = kProtocolFlagsNone;
2342 boolean_t changed = FALSE;
2343 CFStringRef interface = NULL;
2344 CFDictionaryRef ipv4;
2345 CFDictionaryRef ipv6;
2346 CFMutableDictionaryRef new_dict = NULL;
2347 const struct {
2348 CFStringRef key;
2349 uint32_t flags;
2350 Boolean append;
2351 } merge_list[] = {
2352 { kSCPropNetProxiesSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE },
2353 { kSCPropNetProxiesSupplementalMatchOrders, 0, TRUE },
2354 };
2355 const struct {
2356 CFStringRef key1; /* an "enable" key */
2357 CFStringRef key2;
2358 CFStringRef key3;
2359 } pick_list[] = {
2360 { kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort },
2361 { kSCPropNetProxiesGopherEnable, kSCPropNetProxiesGopherProxy, kSCPropNetProxiesGopherPort },
2362 { kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort },
2363 { kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort },
2364 { kSCPropNetProxiesRTSPEnable, kSCPropNetProxiesRTSPProxy, kSCPropNetProxiesRTSPPort },
2365 { kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort },
2366 { kSCPropNetProxiesProxyAutoConfigEnable,
2367 kSCPropNetProxiesProxyAutoConfigURLString,
2368 kSCPropNetProxiesProxyAutoConfigJavaScript, },
2369 { kSCPropNetProxiesProxyAutoDiscoveryEnable,
2370 NULL,
2371 NULL, }
2372 };
2373 IPv4RouteListRef routes = NULL;
2374
2375 if ((state_dict == NULL) && (setup_dict == NULL)) {
2376 /* there is no proxy content */
2377 goto done;
2378 }
2379
2380 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
2381 routes = ipv4_dict_get_routelist(ipv4);
2382
2383 if (routes != NULL) {
2384 active_protos |= kProtocolFlagsIPv4;
2385
2386 interface = ipv4_dict_get_ifname(ipv4);
2387 }
2388
2389 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
2390 if (ipv6 != NULL) {
2391 active_protos |= kProtocolFlagsIPv6;
2392
2393 if (interface == NULL) {
2394 interface = CFDictionaryGetValue(ipv6,
2395 kSCPropInterfaceName);
2396 }
2397 }
2398
2399 if (active_protos == kProtocolFlagsNone) {
2400 /* there is no IPv4 nor IPv6 */
2401 if (state_dict == NULL) {
2402 /* ... and no proxy content that we care about */
2403 goto done;
2404 }
2405 setup_dict = NULL;
2406 }
2407
2408 if ((setup_dict != NULL) && (state_dict != NULL)) {
2409 CFIndex i;
2410 CFMutableDictionaryRef setup_copy;
2411
2412 /*
2413 * Merge the per-service "Setup:" and "State:" proxy information with
2414 * the "Setup:" information always taking precedence. Additionally,
2415 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
2416 * Port) is defined than all of the values for that group will be
2417 * used. That is, we don't allow mixing some of the values from
2418 * the "Setup:" keys and others from the "State:" keys.
2419 */
2420 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2421
2422 for (i = 0; i < sizeof(merge_list)/sizeof(merge_list[0]); i++) {
2423 merge_array_prop(new_dict,
2424 merge_list[i].key,
2425 state_dict,
2426 setup_dict,
2427 merge_list[i].flags,
2428 merge_list[i].append);
2429 }
2430
2431 setup_copy = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
2432 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2433 if (CFDictionaryContainsKey(setup_copy, pick_list[i].key1)) {
2434 /*
2435 * if a "Setup:" enabled key has been provided than we want to
2436 * ignore all of the "State:" keys
2437 */
2438 CFDictionaryRemoveValue(new_dict, pick_list[i].key1);
2439 if (pick_list[i].key2 != NULL) {
2440 CFDictionaryRemoveValue(new_dict, pick_list[i].key2);
2441 }
2442 if (pick_list[i].key3 != NULL) {
2443 CFDictionaryRemoveValue(new_dict, pick_list[i].key3);
2444 }
2445 } else if (CFDictionaryContainsKey(state_dict, pick_list[i].key1) ||
2446 ((pick_list[i].key2 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key2)) ||
2447 ((pick_list[i].key3 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key3))) {
2448 /*
2449 * if a "Setup:" enabled key has not been provided and we have
2450 * some" "State:" keys than we remove all of of "Setup:" keys
2451 */
2452 CFDictionaryRemoveValue(setup_copy, pick_list[i].key1);
2453 if (pick_list[i].key2 != NULL) {
2454 CFDictionaryRemoveValue(setup_copy, pick_list[i].key2);
2455 }
2456 if (pick_list[i].key3 != NULL) {
2457 CFDictionaryRemoveValue(setup_copy, pick_list[i].key3);
2458 }
2459 }
2460 }
2461
2462 /* merge the "Setup:" keys */
2463 CFDictionaryApplyFunction(setup_copy, merge_dict, new_dict);
2464 CFRelease(setup_copy);
2465 }
2466 else if (setup_dict != NULL) {
2467 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
2468 }
2469 else if (state_dict != NULL) {
2470 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2471 }
2472
2473 if ((new_dict != NULL) && (CFDictionaryGetCount(new_dict) == 0)) {
2474 CFRelease(new_dict);
2475 new_dict = NULL;
2476 }
2477
2478 if ((new_dict != NULL) && (interface != NULL)) {
2479 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
2480 }
2481
2482 /* process WPAD */
2483 if (new_dict != NULL) {
2484 CFDictionaryRef dhcp_options;
2485 CFNumberRef num;
2486 CFNumberRef wpad = NULL;
2487 int wpadEnabled = 0;
2488 CFStringRef wpadURL = NULL;
2489
2490 if (CFDictionaryGetValueIfPresent(new_dict,
2491 kSCPropNetProxiesProxyAutoDiscoveryEnable,
2492 (const void **)&num) &&
2493 isA_CFNumber(num)) {
2494 /* if we have a WPAD key */
2495 wpad = num;
2496 if (!CFNumberGetValue(num, kCFNumberIntType, &wpadEnabled)) {
2497 /* if we don't like the enabled key/value */
2498 wpadEnabled = 0;
2499 }
2500 }
2501
2502 if (wpadEnabled) {
2503 int pacEnabled = 0;
2504
2505 num = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigEnable);
2506 if (!isA_CFNumber(num) ||
2507 !CFNumberGetValue(num, kCFNumberIntType, &pacEnabled)) {
2508 /* if we don't like the enabled key/value */
2509 pacEnabled = 0;
2510 }
2511
2512 if (pacEnabled) {
2513 CFStringRef pacURL;
2514
2515 pacURL = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigURLString);
2516 if (pacURL != NULL) {
2517 if (!isA_CFString(pacURL)) {
2518 /* if we don't like the PAC URL */
2519 pacEnabled = 0;
2520 }
2521 } else {
2522 CFStringRef pacJS;
2523
2524 pacJS = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigJavaScript);
2525 if (!isA_CFString(pacJS)) {
2526 /* if we don't have (or like) the PAC JavaScript */
2527 pacEnabled = 0;
2528 }
2529 }
2530 }
2531
2532 if (pacEnabled) {
2533 /*
2534 * we already have a PAC URL so disable WPAD.
2535 */
2536 wpadEnabled = 0;
2537 goto setWPAD;
2538 }
2539
2540 /*
2541 * if WPAD is enabled and we don't already have a PAC URL then
2542 * we check for a DHCP provided URL. If not available, we use
2543 * a PAC URL pointing to a well-known file (wpad.dat) on a
2544 * well-known host (wpad.<domain>).
2545 */
2546 dhcp_options = get_service_state_entity(info, serviceID, kSCEntNetDHCP);
2547 wpadURL = wpadURL_dhcp(dhcp_options);
2548 if (wpadURL == NULL) {
2549 wpadURL = wpadURL_dns();
2550 }
2551 if (wpadURL == NULL) {
2552 wpadEnabled = 0; /* if we don't have a WPAD URL */
2553 goto setWPAD;
2554 }
2555
2556 pacEnabled = 1;
2557 num = CFNumberCreate(NULL, kCFNumberIntType, &pacEnabled);
2558 CFDictionarySetValue(new_dict,
2559 kSCPropNetProxiesProxyAutoConfigEnable,
2560 num);
2561 CFRelease(num);
2562 CFDictionarySetValue(new_dict,
2563 kSCPropNetProxiesProxyAutoConfigURLString,
2564 wpadURL);
2565 CFRelease(wpadURL);
2566 }
2567
2568 setWPAD:
2569 if (wpad != NULL) {
2570 num = CFNumberCreate(NULL, kCFNumberIntType, &wpadEnabled);
2571 CFDictionarySetValue(new_dict,
2572 kSCPropNetProxiesProxyAutoDiscoveryEnable,
2573 num);
2574 CFRelease(num);
2575 }
2576 }
2577
2578 done:
2579 changed = service_dict_set(serviceID, kSCEntNetProxies, new_dict);
2580 my_CFRelease(&new_dict);
2581 return (changed);
2582 }
2583
2584 #if !TARGET_OS_IPHONE
2585 static boolean_t
2586 get_smb_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2587 CFDictionaryRef setup_dict, CFDictionaryRef info)
2588 {
2589 boolean_t changed = FALSE;
2590 int i;
2591 CFMutableDictionaryRef new_dict = NULL;
2592 const CFStringRef pick_list[] = {
2593 kSCPropNetSMBNetBIOSName,
2594 kSCPropNetSMBNetBIOSNodeType,
2595 #ifdef ADD_NETBIOS_SCOPE
2596 kSCPropNetSMBNetBIOSScope,
2597 #endif // ADD_NETBIOS_SCOPE
2598 kSCPropNetSMBWorkgroup,
2599 };
2600
2601 if (service_dict_get(serviceID, kSCEntNetIPv4) == NULL) {
2602 /* there is no IPv4 */
2603 goto done;
2604 }
2605
2606 if (state_dict == NULL && setup_dict == NULL) {
2607 /* there is no SMB */
2608 goto done;
2609 }
2610
2611 /* merge SMB configuration */
2612 new_dict = CFDictionaryCreateMutable(NULL, 0,
2613 &kCFTypeDictionaryKeyCallBacks,
2614 &kCFTypeDictionaryValueCallBacks);
2615
2616 merge_array_prop(new_dict,
2617 kSCPropNetSMBWINSAddresses,
2618 state_dict,
2619 setup_dict,
2620 0,
2621 FALSE);
2622 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2623 pick_prop(new_dict,
2624 pick_list[i],
2625 state_dict,
2626 setup_dict,
2627 0);
2628 }
2629
2630 if (CFDictionaryGetCount(new_dict) == 0) {
2631 my_CFRelease(&new_dict);
2632 goto done;
2633 }
2634
2635 done:
2636 changed = service_dict_set(serviceID, kSCEntNetSMB, new_dict);
2637 my_CFRelease(&new_dict);
2638 return (changed);
2639 }
2640 #endif /* !TARGET_OS_IPHONE */
2641
2642 static CFStringRef
2643 services_info_get_interface(CFDictionaryRef services_info,
2644 CFStringRef serviceID)
2645 {
2646 CFStringRef interface = NULL;
2647 CFDictionaryRef ipv4_dict;
2648
2649 ipv4_dict = get_service_state_entity(services_info, serviceID,
2650 kSCEntNetIPv4);
2651 if (isA_CFDictionary(ipv4_dict) != NULL) {
2652 interface = CFDictionaryGetValue(ipv4_dict, kSCPropInterfaceName);
2653 }
2654 else {
2655 CFDictionaryRef ipv6_dict;
2656
2657 ipv6_dict = get_service_state_entity(services_info, serviceID,
2658 kSCEntNetIPv6);
2659 if (isA_CFDictionary(ipv6_dict) != NULL) {
2660 interface = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
2661 }
2662 }
2663 return (interface);
2664 }
2665
2666 static boolean_t
2667 get_rank_changes(CFStringRef serviceID, CFDictionaryRef state_options,
2668 CFDictionaryRef setup_options, CFDictionaryRef services_info)
2669 {
2670 boolean_t changed = FALSE;
2671 CFMutableDictionaryRef new_dict = NULL;
2672 CFStringRef new_rank = NULL;
2673 CFStringRef setup_rank = NULL;
2674 CFStringRef state_rank = NULL;
2675
2676
2677 /*
2678 * Check "PrimaryRank" setting
2679 *
2680 * Note 1: Rank setting in setup/state option overwrites the
2681 * Rank setting in interface
2682 * Within each rank setting, the following precedence is defined:
2683 *
2684 * Note 2: Rank Never > Rank Last > Rank First > Rank None
2685 */
2686 if (isA_CFDictionary(setup_options)) {
2687 setup_rank = CFDictionaryGetValue(setup_options, kSCPropNetServicePrimaryRank);
2688 setup_rank = isA_CFString(setup_rank);
2689 }
2690 if (isA_CFDictionary(state_options)) {
2691 state_rank = CFDictionaryGetValue(state_options, kSCPropNetServicePrimaryRank);
2692 state_rank = isA_CFString(state_rank);
2693 }
2694
2695 if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankNever)) ||
2696 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankNever))) {
2697 new_rank = kSCValNetServicePrimaryRankNever;
2698 }
2699 else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankLast)) ||
2700 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankLast))) {
2701 new_rank = kSCValNetServicePrimaryRankLast;
2702 }
2703 else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankFirst)) ||
2704 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankFirst))) {
2705 new_rank = kSCValNetServicePrimaryRankFirst;
2706 }
2707
2708 /* This corresponds to Note 1 */
2709 if (setup_rank == NULL && state_rank == NULL) {
2710 /* Fetch the interface associated with the service */
2711 CFStringRef interface;
2712
2713 interface = services_info_get_interface(services_info, serviceID);
2714
2715 /* Get the rank on that interface */
2716 if (interface != NULL) {
2717 new_rank = CFDictionaryGetValue(S_if_rank_dict, interface);
2718 if (S_IPMonitor_debug & kDebugFlag1) {
2719 SCLog(TRUE, LOG_NOTICE,
2720 CFSTR("serviceID %@ interface %@ rank = %@"),
2721 serviceID, interface,
2722 (new_rank != NULL) ? new_rank : CFSTR("<none>"));
2723 }
2724 }
2725 }
2726
2727 if (new_rank != NULL) {
2728 new_dict = CFDictionaryCreateMutable(NULL, 0,
2729 &kCFTypeDictionaryKeyCallBacks,
2730 &kCFTypeDictionaryValueCallBacks);
2731 CFDictionarySetValue(new_dict, kSCPropNetServicePrimaryRank, new_rank);
2732 }
2733
2734 changed = service_dict_set(serviceID, kSCEntNetService, new_dict);
2735 my_CFRelease(&new_dict);
2736 return (changed);
2737 }
2738
2739 static CFStringRef
2740 if_rank_key_copy(CFStringRef ifname)
2741 {
2742 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2743 kSCDynamicStoreDomainState,
2744 ifname,
2745 kSCEntNetService));
2746 }
2747
2748 static void
2749 if_rank_set(CFStringRef ifname, CFDictionaryRef rank_dict)
2750 {
2751 CFStringRef rank = NULL;
2752
2753 if (isA_CFDictionary(rank_dict) != NULL) {
2754 rank = CFDictionaryGetValue(rank_dict, kSCPropNetServicePrimaryRank);
2755 rank = isA_CFString(rank);
2756 }
2757
2758 /* specific rank is asserted */
2759 if (rank != NULL) {
2760 if (S_IPMonitor_debug & kDebugFlag1) {
2761 SCLog(TRUE, LOG_NOTICE, CFSTR("Interface %@ asserted rank %@"),
2762 ifname, rank);
2763 }
2764 CFDictionarySetValue(S_if_rank_dict, ifname, rank);
2765 } else {
2766 if (S_IPMonitor_debug & kDebugFlag1) {
2767 SCLog(TRUE, LOG_NOTICE, CFSTR("Interface %@ removed rank."),
2768 ifname);
2769 }
2770 CFDictionaryRemoveValue(S_if_rank_dict, ifname);
2771 }
2772 return;
2773 }
2774
2775 static void
2776 if_rank_apply(const void * key, const void * value, void * context)
2777 {
2778 CFStringRef ifname;
2779 CFDictionaryRef rank_dict = (CFDictionaryRef)value;
2780
2781 /* State:/Network/Interface/<ifname>/Service, <ifname> is at index 3 */
2782 ifname = my_CFStringCopyComponent(key, CFSTR("/"), 3);
2783 if (ifname != NULL) {
2784 if_rank_set(ifname, rank_dict);
2785 CFRelease(ifname);
2786 }
2787 return;
2788 }
2789
2790 static void
2791 if_rank_dict_init(void)
2792 {
2793 CFDictionaryRef info;
2794 CFStringRef pattern;
2795 CFArrayRef patterns;
2796
2797 S_if_rank_dict
2798 = CFDictionaryCreateMutable(NULL, 0,
2799 &kCFTypeDictionaryKeyCallBacks,
2800 &kCFTypeDictionaryValueCallBacks);
2801 pattern = if_rank_key_copy(kSCCompAnyRegex);
2802 patterns = CFArrayCreate(NULL,
2803 (const void **)&pattern, 1,
2804 &kCFTypeArrayCallBacks);
2805 CFRelease(pattern);
2806 info = SCDynamicStoreCopyMultiple(S_session, NULL, patterns);
2807 CFRelease(patterns);
2808 if (info != NULL) {
2809 CFDictionaryApplyFunction(info, if_rank_apply, NULL);
2810 CFRelease(info);
2811 }
2812 return;
2813
2814 }
2815
2816 static void
2817 add_service_keys(CFStringRef serviceID, CFMutableArrayRef keys, CFMutableArrayRef patterns)
2818 {
2819 int i;
2820 CFStringRef key;
2821
2822 if (CFEqual(serviceID, kSCCompAnyRegex)) {
2823 keys = patterns;
2824 }
2825
2826 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
2827 key = setup_service_key(serviceID, *entityTypeNames[i]);
2828 CFArrayAppendValue(keys, key);
2829 CFRelease(key);
2830 key = state_service_key(serviceID, *entityTypeNames[i]);
2831 CFArrayAppendValue(keys, key);
2832 CFRelease(key);
2833 }
2834
2835 key = state_service_key(serviceID, kSCEntNetDHCP);
2836 CFArrayAppendValue(patterns, key);
2837 CFRelease(key);
2838
2839 key = setup_service_key(serviceID, NULL);
2840 CFArrayAppendValue(patterns, key);
2841 CFRelease(key);
2842 key = state_service_key(serviceID, NULL);
2843 CFArrayAppendValue(patterns, key);
2844 CFRelease(key);
2845
2846
2847 return;
2848 }
2849
2850 static CFDictionaryRef
2851 services_info_copy(SCDynamicStoreRef session, CFArrayRef service_list,
2852 CFArrayRef if_rank_list)
2853 {
2854 int count;
2855 CFMutableArrayRef get_keys;
2856 CFMutableArrayRef get_patterns;
2857 int if_count;
2858 CFDictionaryRef info;
2859 int s;
2860
2861 count = CFArrayGetCount(service_list);
2862 get_keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2863 get_patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2864
2865 CFArrayAppendValue(get_keys, S_setup_global_ipv4);
2866 CFArrayAppendValue(get_keys, S_multicast_resolvers);
2867 CFArrayAppendValue(get_keys, S_private_resolvers);
2868
2869 for (s = 0; s < count; s++) {
2870 CFStringRef serviceID = CFArrayGetValueAtIndex(service_list, s);
2871
2872 add_service_keys(serviceID, get_keys, get_patterns);
2873 }
2874
2875 if_count = (if_rank_list != NULL)
2876 ? CFArrayGetCount(if_rank_list) : 0;
2877 for (s = 0; s < if_count; s++) {
2878 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_list, s);
2879 CFStringRef key;
2880
2881 key = if_rank_key_copy(ifname);
2882 CFArrayAppendValue(get_keys, key);
2883 CFRelease(key);
2884 }
2885
2886 info = SCDynamicStoreCopyMultiple(session, get_keys, get_patterns);
2887 my_CFRelease(&get_keys);
2888 my_CFRelease(&get_patterns);
2889 return (info);
2890 }
2891
2892 static int rtm_seq = 0;
2893
2894 static int
2895 route_open_socket(void)
2896 {
2897 int sockfd;
2898
2899 if ((sockfd = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE)) == -1) {
2900 SCLog(TRUE, LOG_NOTICE,
2901 CFSTR("IPMonitor: route_open_socket: socket failed, %s"),
2902 strerror(errno));
2903 }
2904 return (sockfd);
2905 }
2906
2907 /*
2908 * Define: ROUTE_MSG_ADDRS_SPACE
2909 * Purpose:
2910 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2911 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2912 * someone changes the code and doesn't think to modify this.
2913 */
2914 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2915 + 2 * sizeof(struct sockaddr_dl) \
2916 + 128)
2917 typedef struct {
2918 struct rt_msghdr hdr;
2919 char addrs[ROUTE_MSG_ADDRS_SPACE];
2920 } route_msg;
2921
2922 static int
2923 ipv4_route(int sockfd,
2924 int cmd, struct in_addr gateway, struct in_addr netaddr,
2925 struct in_addr netmask, char * ifname, unsigned int ifindex,
2926 struct in_addr ifa, RouteFlags flags)
2927 {
2928 boolean_t default_route = (netaddr.s_addr == 0);
2929 int len;
2930 int ret = 0;
2931 route_msg rtmsg;
2932 union {
2933 struct sockaddr_in * in_p;
2934 struct sockaddr_dl * dl_p;
2935 void * ptr;
2936 } rtaddr;
2937
2938 if (default_route && S_netboot) {
2939 return (0);
2940 }
2941
2942 if (ifname == NULL) {
2943 /* this should not happen, but rather than crash, return an error */
2944 syslog(LOG_NOTICE,
2945 "IPMonitor: ipv4_route ifname is NULL on network address %s",
2946 inet_ntoa(netaddr));
2947 return (EBADF);
2948 }
2949 memset(&rtmsg, 0, sizeof(rtmsg));
2950 rtmsg.hdr.rtm_type = cmd;
2951 rtmsg.hdr.rtm_version = RTM_VERSION;
2952 rtmsg.hdr.rtm_seq = ++rtm_seq;
2953 rtmsg.hdr.rtm_addrs
2954 = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP | RTA_IFA;
2955 if (default_route
2956 && (flags & kRouteIsDirectToInterfaceFlag) == 0) {
2957 rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
2958 }
2959 else {
2960 rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC;
2961 }
2962 if ((flags & kRouteIsScopedFlag) != 0) {
2963 #ifdef RTF_IFSCOPE
2964 if (!S_scopedroute) {
2965 return (0);
2966 }
2967 if (ifindex == 0) {
2968 /* specifically asked for a scoped route, yet no index supplied */
2969 syslog(LOG_NOTICE,
2970 "IPMonitor: ipv4_route index is 0 on %s-scoped route %s",
2971 ifname, inet_ntoa(netaddr));
2972 return (EBADF);
2973 }
2974 rtmsg.hdr.rtm_index = ifindex;
2975 rtmsg.hdr.rtm_flags |= RTF_IFSCOPE;
2976 #else /* RTF_IFSCOPE */
2977 return (0);
2978 #endif /* RTF_IFSCOPE */
2979 }
2980
2981 rtaddr.ptr = rtmsg.addrs;
2982
2983 /* dest */
2984 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2985 rtaddr.in_p->sin_family = AF_INET;
2986 rtaddr.in_p->sin_addr = netaddr;
2987 rtaddr.ptr += sizeof(*rtaddr.in_p);
2988
2989 /* gateway */
2990 if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) {
2991 /* gateway is an IP address */
2992 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2993 rtaddr.in_p->sin_family = AF_INET;
2994 rtaddr.in_p->sin_addr = gateway;
2995 rtaddr.ptr += sizeof(*rtaddr.in_p);
2996 }
2997 else {
2998 /* gateway is the interface itself */
2999 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3000 rtaddr.dl_p->sdl_family = AF_LINK;
3001 rtaddr.dl_p->sdl_nlen = strlen(ifname);
3002 bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen);
3003 rtaddr.ptr += sizeof(*rtaddr.dl_p);
3004 }
3005
3006 /* mask */
3007 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3008 rtaddr.in_p->sin_family = AF_INET;
3009 rtaddr.in_p->sin_addr = netmask;
3010 rtaddr.ptr += sizeof(*rtaddr.in_p);
3011
3012 /* interface name */
3013 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3014 rtaddr.dl_p->sdl_family = AF_LINK;
3015 rtaddr.dl_p->sdl_nlen = strlen(ifname);
3016 bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen);
3017 rtaddr.ptr += sizeof(*rtaddr.dl_p);
3018
3019 /* interface address */
3020 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3021 rtaddr.in_p->sin_family = AF_INET;
3022 rtaddr.in_p->sin_addr = ifa;
3023 rtaddr.ptr += sizeof(*rtaddr.in_p);
3024
3025 len = sizeof(rtmsg.hdr) + (rtaddr.ptr - (void *)rtmsg.addrs);
3026 rtmsg.hdr.rtm_msglen = len;
3027 if (write(sockfd, &rtmsg, len) == -1) {
3028 ret = errno;
3029 }
3030 return (ret);
3031 }
3032
3033 static boolean_t
3034 ipv6_route(int cmd, struct in6_addr gateway, struct in6_addr netaddr,
3035 struct in6_addr netmask, char * ifname, boolean_t is_direct)
3036 {
3037 boolean_t default_route;
3038 int len;
3039 boolean_t ret = TRUE;
3040 struct {
3041 struct rt_msghdr hdr;
3042 struct sockaddr_in6 dst;
3043 struct sockaddr_in6 gway;
3044 struct sockaddr_in6 mask;
3045 struct sockaddr_dl ifp;
3046 } rtmsg;
3047 int sockfd = -1;
3048 struct in6_addr zeroes = IN6ADDR_ANY_INIT;
3049
3050 default_route = (bcmp(&zeroes, &netaddr, sizeof(netaddr)) == 0);
3051
3052 if ((IN6_IS_ADDR_LINKLOCAL(&gateway) ||
3053 IN6_IS_ADDR_MC_LINKLOCAL(&gateway)) &&
3054 (ifname != NULL)) {
3055 unsigned int index = if_nametoindex(ifname);
3056
3057 /* add the scope id to the link local address */
3058 gateway.__u6_addr.__u6_addr16[1] = (uint16_t)htons(index);
3059 }
3060 sockfd = route_open_socket();
3061 if (sockfd == -1) {
3062 return (FALSE);
3063 }
3064 memset(&rtmsg, 0, sizeof(rtmsg));
3065 rtmsg.hdr.rtm_type = cmd;
3066 if (default_route) {
3067 if (is_direct) {
3068 /* if router is directly reachable, don't set the gateway flag */
3069 rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC;
3070 }
3071 else {
3072 rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
3073 }
3074 }
3075 else {
3076 rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC;
3077 }
3078 rtmsg.hdr.rtm_version = RTM_VERSION;
3079 rtmsg.hdr.rtm_seq = ++rtm_seq;
3080 rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
3081 rtmsg.dst.sin6_len = sizeof(rtmsg.dst);
3082 rtmsg.dst.sin6_family = AF_INET6;
3083 rtmsg.dst.sin6_addr = netaddr;
3084 rtmsg.gway.sin6_len = sizeof(rtmsg.gway);
3085 rtmsg.gway.sin6_family = AF_INET6;
3086 rtmsg.gway.sin6_addr = gateway;
3087 rtmsg.mask.sin6_len = sizeof(rtmsg.mask);
3088 rtmsg.mask.sin6_family = AF_INET6;
3089 rtmsg.mask.sin6_addr = netmask;
3090
3091 len = sizeof(rtmsg);
3092 if (ifname) {
3093 rtmsg.ifp.sdl_len = sizeof(rtmsg.ifp);
3094 rtmsg.ifp.sdl_family = AF_LINK;
3095 rtmsg.ifp.sdl_nlen = strlen(ifname);
3096 rtmsg.hdr.rtm_addrs |= RTA_IFP;
3097 bcopy(ifname, rtmsg.ifp.sdl_data, rtmsg.ifp.sdl_nlen);
3098 }
3099 else {
3100 /* no ifp information */
3101 len -= sizeof(rtmsg.ifp);
3102 }
3103 rtmsg.hdr.rtm_msglen = len;
3104 if (write(sockfd, &rtmsg, len) == -1) {
3105 if ((cmd == RTM_ADD) && (errno == EEXIST)) {
3106 /* no sense complaining about a route that already exists */
3107 }
3108 else if ((cmd == RTM_DELETE) && (errno == ESRCH)) {
3109 /* no sense complaining about a route that isn't there */
3110 }
3111 else {
3112 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3113 SCLog(TRUE, LOG_NOTICE,
3114 CFSTR("IPMonitor ipv6_route: write routing"
3115 " socket failed, %s"), strerror(errno));
3116 }
3117 ret = FALSE;
3118 }
3119 }
3120
3121 close(sockfd);
3122 return (ret);
3123 }
3124
3125 static boolean_t
3126 ipv6_default_route_delete(void)
3127 {
3128 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3129 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: IPv6 route delete default"));
3130 }
3131 return (ipv6_route(RTM_DELETE, S_ip6_zeros, S_ip6_zeros, S_ip6_zeros,
3132 NULL, FALSE));
3133 }
3134
3135 static boolean_t
3136 ipv6_default_route_add(struct in6_addr router, char * ifname,
3137 boolean_t is_direct)
3138 {
3139 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3140 char ntopbuf[INET6_ADDRSTRLEN];
3141
3142 SCLog(TRUE, LOG_NOTICE,
3143 CFSTR("IPMonitor: IPv6 route add default"
3144 " %s interface %s direct %d"),
3145 inet_ntop(AF_INET6, &router, ntopbuf, sizeof(ntopbuf)),
3146 ifname, is_direct);
3147 }
3148 return (ipv6_route(RTM_ADD, router, S_ip6_zeros, S_ip6_zeros,
3149 ifname, is_direct));
3150 }
3151
3152
3153 static int
3154 multicast_route_delete(int sockfd)
3155 {
3156 struct in_addr gateway = { htonl(INADDR_LOOPBACK) };
3157 struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) };
3158 struct in_addr netmask = { htonl(IN_CLASSD_NET) };
3159
3160 return (ipv4_route(sockfd, RTM_DELETE, gateway, netaddr, netmask, "lo0", 0,
3161 gateway, 0));
3162 }
3163
3164 static int
3165 multicast_route_add(int sockfd)
3166 {
3167 struct in_addr gateway = { htonl(INADDR_LOOPBACK) };
3168 struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) };
3169 struct in_addr netmask = { htonl(IN_CLASSD_NET) };
3170
3171 return (ipv4_route(sockfd, RTM_ADD, gateway, netaddr, netmask, "lo0", 0,
3172 gateway, 0));
3173 }
3174
3175 #ifdef RTF_IFSCOPE
3176 static void
3177 set_ipv6_default_interface(char * ifname)
3178 {
3179 struct in6_ndifreq ndifreq;
3180 int sock;
3181
3182 bzero((char *)&ndifreq, sizeof(ndifreq));
3183 if (ifname != NULL) {
3184 strlcpy(ndifreq.ifname, ifname, sizeof(ndifreq.ifname));
3185 ndifreq.ifindex = if_nametoindex(ifname);
3186 } else {
3187 strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname));
3188 ndifreq.ifindex = 0;
3189 }
3190
3191 sock = inet6_dgram_socket();
3192 if (sock == -1) {
3193 SCLog(TRUE, LOG_ERR,
3194 CFSTR("IPMonitor: set_ipv6_default_interface: socket failed, %s"),
3195 strerror(errno));
3196 return;
3197 }
3198 if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) == -1) {
3199 SCLog(TRUE, LOG_ERR,
3200 CFSTR("IPMonitor: set_ipv6_default_interface: ioctl(SIOCSDEFIFACE_IN6) failed, %s"),
3201 strerror(errno));
3202 }
3203 close(sock);
3204 return;
3205 }
3206 #endif /* RTF_IFSCOPE */
3207
3208 static void
3209 set_ipv6_router(struct in6_addr * router, char * ifname, boolean_t is_direct)
3210 {
3211 /* assign the new default route, ensure local multicast route available */
3212 (void)ipv6_default_route_delete();
3213 if (router != NULL) {
3214 (void)ipv6_default_route_add(*router, ifname, is_direct);
3215 }
3216 return;
3217 }
3218
3219 #if !TARGET_OS_IPHONE
3220 static __inline__ void
3221 empty_dns()
3222 {
3223 (void)unlink(VAR_RUN_RESOLV_CONF);
3224 }
3225
3226 static void
3227 set_dns(CFArrayRef val_search_domains,
3228 CFStringRef val_domain_name,
3229 CFArrayRef val_servers,
3230 CFArrayRef val_sortlist)
3231 {
3232 FILE * f = fopen(VAR_RUN_RESOLV_CONF "-", "w");
3233
3234 /* publish new resolv.conf */
3235 if (f) {
3236 CFIndex i;
3237 CFIndex n;
3238
3239 SCPrint(TRUE, f, CFSTR("#\n"));
3240 SCPrint(TRUE, f, CFSTR("# Mac OS X Notice\n"));
3241 SCPrint(TRUE, f, CFSTR("#\n"));
3242 SCPrint(TRUE, f, CFSTR("# This file is not used by the host name and address resolution\n"));
3243 SCPrint(TRUE, f, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
3244 SCPrint(TRUE, f, CFSTR("# this Mac OS X system.\n"));
3245 SCPrint(TRUE, f, CFSTR("#\n"));
3246 SCPrint(TRUE, f, CFSTR("# This file is automatically generated.\n"));
3247 SCPrint(TRUE, f, CFSTR("#\n"));
3248
3249 if (isA_CFArray(val_search_domains)) {
3250 SCPrint(TRUE, f, CFSTR("search"));
3251 n = CFArrayGetCount(val_search_domains);
3252 for (i = 0; i < n; i++) {
3253 CFStringRef domain;
3254
3255 domain = CFArrayGetValueAtIndex(val_search_domains, i);
3256 if (isA_CFString(domain)) {
3257 SCPrint(TRUE, f, CFSTR(" %@"), domain);
3258 }
3259 }
3260 SCPrint(TRUE, f, CFSTR("\n"));
3261 }
3262 else if (isA_CFString(val_domain_name)) {
3263 SCPrint(TRUE, f, CFSTR("domain %@\n"), val_domain_name);
3264 }
3265
3266 if (isA_CFArray(val_servers)) {
3267 n = CFArrayGetCount(val_servers);
3268 for (i = 0; i < n; i++) {
3269 CFStringRef nameserver;
3270
3271 nameserver = CFArrayGetValueAtIndex(val_servers, i);
3272 if (isA_CFString(nameserver)) {
3273 SCPrint(TRUE, f, CFSTR("nameserver %@\n"), nameserver);
3274 }
3275 }
3276 }
3277
3278 if (isA_CFArray(val_sortlist)) {
3279 SCPrint(TRUE, f, CFSTR("sortlist"));
3280 n = CFArrayGetCount(val_sortlist);
3281 for (i = 0; i < n; i++) {
3282 CFStringRef address;
3283
3284 address = CFArrayGetValueAtIndex(val_sortlist, i);
3285 if (isA_CFString(address)) {
3286 SCPrint(TRUE, f, CFSTR(" %@"), address);
3287 }
3288 }
3289 SCPrint(TRUE, f, CFSTR("\n"));
3290 }
3291
3292 fclose(f);
3293 rename(VAR_RUN_RESOLV_CONF "-", VAR_RUN_RESOLV_CONF);
3294 }
3295 return;
3296 }
3297 #endif /* !TARGET_OS_IPHONE */
3298
3299 static boolean_t
3300 router_is_our_ipv6_address(CFStringRef router, CFArrayRef addr_list)
3301 {
3302 CFIndex i;
3303 CFIndex n = CFArrayGetCount(addr_list);
3304 struct in6_addr r;
3305
3306 (void)cfstring_to_ip6(router, &r);
3307 for (i = 0; i < n; i++) {
3308 struct in6_addr ip;
3309
3310 if (cfstring_to_ip6(CFArrayGetValueAtIndex(addr_list, i), &ip)
3311 && bcmp(&r, &ip, sizeof(r)) == 0) {
3312 return (TRUE);
3313 }
3314 }
3315 return (FALSE);
3316 }
3317
3318 static IPv4RouteListRef
3319 service_dict_get_ipv4_routelist(CFDictionaryRef service_dict)
3320 {
3321 CFDictionaryRef dict;
3322 IPv4RouteListRef routes = NULL;
3323
3324 dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
3325
3326 routes = ipv4_dict_get_routelist(dict);
3327 return (routes);
3328 }
3329
3330 typedef struct apply_ipv4_route_context {
3331 IPv4RouteListRef old;
3332 IPv4RouteListRef new;
3333 int sockfd;
3334 } apply_ipv4_route_context_t;
3335
3336 /* add/remove a router/32 subnet */
3337 static int
3338 ipv4_route_gateway(int sockfd, int cmd, char * ifn_p,
3339 IPv4RouteRef def_route)
3340 {
3341 struct in_addr mask;
3342
3343 mask.s_addr = htonl(INADDR_BROADCAST);
3344 return (ipv4_route(sockfd, cmd, def_route->ifa,
3345 def_route->gateway, mask, ifn_p, def_route->ifindex,
3346 def_route->ifa, def_route->flags));
3347 }
3348
3349 /*
3350 * Function: apply_ipv4_route
3351 * Purpose:
3352 * Callback function that adds/removes the specified route.
3353 */
3354 static void
3355 apply_ipv4_route(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg)
3356 {
3357 apply_ipv4_route_context_t *context = (apply_ipv4_route_context_t *)arg;
3358 char * ifn_p;
3359 int retval;
3360
3361 ifn_p = route->ifname;
3362 switch (cmd) {
3363 case kIPv4RouteListAddRouteCommand:
3364 if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) {
3365 retval = ipv4_route_gateway(context->sockfd, RTM_ADD,
3366 ifn_p, route);
3367 if (retval == EEXIST) {
3368 /* delete and add again */
3369 (void)ipv4_route_gateway(context->sockfd, RTM_DELETE,
3370 ifn_p, route);
3371 retval = ipv4_route_gateway(context->sockfd, RTM_ADD,
3372 ifn_p, route);
3373 }
3374 if (retval != 0) {
3375 SCLog(TRUE, LOG_NOTICE,
3376 CFSTR("IPMonitor apply_ipv4_route failed to add"
3377 " %s/32 route, %s"),
3378 inet_ntoa(route->gateway), strerror(retval));
3379 }
3380 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3381 SCLog(TRUE, LOG_NOTICE, CFSTR("Added IPv4 Route %s/32"),
3382 inet_ntoa(route->gateway));
3383 }
3384 }
3385 retval = ipv4_route(context->sockfd,
3386 RTM_ADD, route->gateway,
3387 route->dest, route->mask, ifn_p, route->ifindex,
3388 route->ifa, route->flags);
3389 if (retval == EEXIST) {
3390 /* delete and add again */
3391 (void)ipv4_route(context->sockfd,
3392 RTM_DELETE, route->gateway,
3393 route->dest, route->mask, ifn_p, route->ifindex,
3394 route->ifa, route->flags);
3395 retval = ipv4_route(context->sockfd,
3396 RTM_ADD, route->gateway,
3397 route->dest, route->mask,
3398 ifn_p, route->ifindex,
3399 route->ifa, route->flags);
3400 }
3401 if (retval != 0) {
3402 SCLog(TRUE, LOG_NOTICE,
3403 CFSTR("IPMonitor apply_ipv4_route failed to add"
3404 " route, %s:"), strerror(retval));
3405 IPv4RouteLog(route);
3406 }
3407 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3408 SCLog(TRUE, LOG_NOTICE,
3409 CFSTR("Added IPv4 route new[%d] = "),
3410 route - context->new->list);
3411 IPv4RouteLog(route);
3412 }
3413 break;
3414 case kIPv4RouteListRemoveRouteCommand:
3415 retval = ipv4_route(context->sockfd,
3416 RTM_DELETE, route->gateway,
3417 route->dest, route->mask, ifn_p, route->ifindex,
3418 route->ifa, route->flags);
3419 if (retval != 0) {
3420 if (retval != ESRCH) {
3421 SCLog(TRUE, LOG_NOTICE,
3422 CFSTR("IPMonitor apply_ipv4_route failed to remove"
3423 " route, %s: "), strerror(retval));
3424 IPv4RouteLog(route);
3425 }
3426 }
3427 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3428 SCLog(TRUE, LOG_NOTICE,
3429 CFSTR("Removed IPv4 route old[%d] = "),
3430 route - context->old->list);
3431 IPv4RouteLog(route);
3432 }
3433 if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) {
3434 retval = ipv4_route_gateway(context->sockfd, RTM_DELETE,
3435 ifn_p, route);
3436 if (retval != 0) {
3437 SCLog(TRUE, LOG_NOTICE,
3438 CFSTR("IPMonitor apply_ipv4_route failed to remove"
3439 " %s/32 route, %s: "),
3440 inet_ntoa(route->gateway), strerror(retval));
3441 }
3442 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3443 SCLog(TRUE, LOG_NOTICE, CFSTR("Removed IPv4 Route %s/32"),
3444 inet_ntoa(route->gateway));
3445 }
3446 }
3447 break;
3448 default:
3449 break;
3450 }
3451 return;
3452 }
3453
3454 /*
3455 * Function: update_ipv4
3456 *
3457 * Purpose:
3458 * Update the IPv4 configuration based on the latest information.
3459 * Publish the State:/Network/Global/IPv4 information, and update the
3460 * IPv4 routing table. IPv4RouteListApply() invokes our callback,
3461 * apply_ipv4_route(), to install/remove the routes.
3462 */
3463 static void
3464 update_ipv4(CFStringRef primary,
3465 IPv4RouteListRef new_routelist,
3466 keyChangeListRef keys)
3467 {
3468 apply_ipv4_route_context_t context;
3469
3470 if (keys != NULL) {
3471 if (new_routelist != NULL && primary != NULL) {
3472 char * ifn_p = NULL;
3473 IPv4RouteRef r;
3474 CFMutableDictionaryRef dict = NULL;
3475
3476 dict = CFDictionaryCreateMutable(NULL, 0,
3477 &kCFTypeDictionaryKeyCallBacks,
3478 &kCFTypeDictionaryValueCallBacks);
3479 /* the first entry is the default route */
3480 r = new_routelist->list;
3481 if (r->gateway.s_addr != 0) {
3482 CFStringRef router;
3483
3484 router = CFStringCreateWithCString(NULL,
3485 inet_ntoa(r->gateway),
3486 kCFStringEncodingASCII);
3487 if (router != NULL) {
3488 CFDictionarySetValue(dict, kSCPropNetIPv4Router, router);
3489 CFRelease(router);
3490 }
3491 }
3492 if (r->ifname[0] != '\0') {
3493 ifn_p = r->ifname;
3494 }
3495 if (ifn_p != NULL) {
3496 CFStringRef ifname_cf;
3497
3498 ifname_cf = CFStringCreateWithCString(NULL,
3499 ifn_p,
3500 kCFStringEncodingASCII);
3501 if (ifname_cf != NULL) {
3502 CFDictionarySetValue(dict,
3503 kSCDynamicStorePropNetPrimaryInterface,
3504 ifname_cf);
3505 CFRelease(ifname_cf);
3506 }
3507 }
3508 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
3509 primary);
3510 keyChangeListSetValue(keys, S_state_global_ipv4, dict);
3511 CFRelease(dict);
3512 }
3513 else {
3514 keyChangeListRemoveValue(keys, S_state_global_ipv4);
3515 }
3516 }
3517
3518 bzero(&context, sizeof(context));
3519 context.sockfd = route_open_socket();
3520 if (context.sockfd != -1) {
3521 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3522 if (S_ipv4_routelist == NULL) {
3523 SCLog(TRUE, LOG_NOTICE, CFSTR("Old Routes = <none>"));
3524 }
3525 else {
3526 SCLog(TRUE, LOG_NOTICE, CFSTR("Old Routes = "));
3527 IPv4RouteListLog(S_ipv4_routelist);
3528 }
3529 if (new_routelist == NULL) {
3530 SCLog(TRUE, LOG_NOTICE, CFSTR("New Routes = <none>"));
3531 }
3532 else {
3533 SCLog(TRUE, LOG_NOTICE, CFSTR("New Routes = "));
3534 IPv4RouteListLog(new_routelist);
3535 }
3536 }
3537 context.old = S_ipv4_routelist;
3538 context.new = new_routelist;
3539 IPv4RouteListApply(S_ipv4_routelist, new_routelist,
3540 &apply_ipv4_route, (void *)&context);
3541 if (new_routelist != NULL) {
3542 (void)multicast_route_delete(context.sockfd);
3543 }
3544 else {
3545 (void)multicast_route_add(context.sockfd);
3546 }
3547 close(context.sockfd);
3548 }
3549 if (S_ipv4_routelist != NULL) {
3550 free(S_ipv4_routelist);
3551 }
3552 S_ipv4_routelist = new_routelist;
3553 return;
3554 }
3555
3556 static void
3557 update_ipv6(CFDictionaryRef services_info,
3558 CFStringRef primary,
3559 keyChangeListRef keys)
3560 {
3561 CFDictionaryRef ipv6_dict = NULL;
3562
3563 if (primary != NULL) {
3564 CFDictionaryRef service_dict;
3565
3566 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3567 if (service_dict != NULL) {
3568 ipv6_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
3569 }
3570 }
3571 if (ipv6_dict != NULL) {
3572 CFArrayRef addrs;
3573 CFMutableDictionaryRef dict = NULL;
3574 CFStringRef if_name = NULL;
3575 char ifn[IFNAMSIZ] = { '\0' };
3576 char * ifn_p = NULL;
3577 boolean_t is_direct = FALSE;
3578 CFStringRef val_router = NULL;
3579
3580 dict = CFDictionaryCreateMutable(NULL, 0,
3581 &kCFTypeDictionaryKeyCallBacks,
3582 &kCFTypeDictionaryValueCallBacks);
3583 val_router = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Router);
3584 addrs = CFDictionaryGetValue(ipv6_dict,
3585 kSCPropNetIPv6Addresses);
3586 if (val_router != NULL) {
3587 /* no router if router is one of our IP addresses */
3588 is_direct = router_is_our_ipv6_address(val_router, addrs);
3589 CFDictionarySetValue(dict, kSCPropNetIPv6Router,
3590 val_router);
3591 }
3592 else {
3593 val_router = CFArrayGetValueAtIndex(addrs, 0);
3594 is_direct = TRUE;
3595 }
3596 if_name = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
3597 if (if_name) {
3598 CFDictionarySetValue(dict,
3599 kSCDynamicStorePropNetPrimaryInterface,
3600 if_name);
3601 if (CFStringGetCString(if_name, ifn, sizeof(ifn),
3602 kCFStringEncodingASCII)) {
3603 ifn_p = ifn;
3604 }
3605 }
3606 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
3607 primary);
3608 keyChangeListSetValue(keys, S_state_global_ipv6, dict);
3609 CFRelease(dict);
3610
3611 #ifdef RTF_IFSCOPE
3612 if (S_scopedroute_v6) {
3613 set_ipv6_default_interface(ifn_p);
3614 } else
3615 #endif /* RTF_IFSCOPE */
3616 { /* route add default ... */
3617 struct in6_addr router;
3618
3619 (void)cfstring_to_ip6(val_router, &router);
3620 set_ipv6_router(&router, ifn_p, is_direct);
3621 }
3622 }
3623 else {
3624 keyChangeListRemoveValue(keys, S_state_global_ipv6);
3625 #ifdef RTF_IFSCOPE
3626 if (S_scopedroute_v6) {
3627 set_ipv6_default_interface(NULL);
3628 } else
3629 #endif /* RTF_IFSCOPE */
3630 { /* route delete default ... */
3631 set_ipv6_router(NULL, NULL, FALSE);
3632 }
3633 }
3634 return;
3635 }
3636
3637 static void
3638 update_dns(CFDictionaryRef services_info,
3639 CFStringRef primary,
3640 keyChangeListRef keys)
3641 {
3642 CFDictionaryRef dict = NULL;
3643
3644 if (primary != NULL) {
3645 CFDictionaryRef service_dict;
3646
3647 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3648 if (service_dict != NULL) {
3649 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
3650 }
3651 }
3652 if (dict == NULL) {
3653 #if !TARGET_OS_IPHONE
3654 empty_dns();
3655 #endif /* !TARGET_OS_IPHONE */
3656 keyChangeListRemoveValue(keys, S_state_global_dns);
3657 }
3658 else {
3659 CFMutableDictionaryRef new_dict;
3660
3661 #if !TARGET_OS_IPHONE
3662 set_dns(CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains),
3663 CFDictionaryGetValue(dict, kSCPropNetDNSDomainName),
3664 CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses),
3665 CFDictionaryGetValue(dict, kSCPropNetDNSSortList));
3666 #endif /* !TARGET_OS_IPHONE */
3667 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
3668 CFDictionaryRemoveValue(new_dict, kSCPropInterfaceName);
3669 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchDomains);
3670 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchOrders);
3671 CFDictionaryRemoveValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY);
3672 keyChangeListSetValue(keys, S_state_global_dns, new_dict);
3673 CFRelease(new_dict);
3674 }
3675 return;
3676 }
3677
3678 static void
3679 update_dnsinfo(CFDictionaryRef services_info,
3680 CFStringRef primary,
3681 keyChangeListRef keys,
3682 CFArrayRef service_order)
3683 {
3684 Boolean changed;
3685 CFDictionaryRef dict = NULL;
3686 CFArrayRef multicastResolvers;
3687 CFArrayRef privateResolvers;
3688
3689 multicastResolvers = CFDictionaryGetValue(services_info, S_multicast_resolvers);
3690 privateResolvers = CFDictionaryGetValue(services_info, S_private_resolvers);
3691
3692 if (primary != NULL) {
3693 CFDictionaryRef service_dict;
3694
3695 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3696 if (service_dict != NULL) {
3697 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
3698 }
3699 }
3700
3701 changed = dns_configuration_set(dict,
3702 S_service_state_dict,
3703 service_order,
3704 multicastResolvers,
3705 privateResolvers);
3706 if (changed) {
3707 keyChangeListNotifyKey(keys, S_state_global_dns);
3708 }
3709 return;
3710 }
3711
3712 static void
3713 update_proxies(CFDictionaryRef services_info,
3714 CFStringRef primary,
3715 keyChangeListRef keys,
3716 CFArrayRef service_order)
3717 {
3718 CFDictionaryRef dict = NULL;
3719 CFDictionaryRef new_dict;
3720
3721 if (primary != NULL) {
3722 CFDictionaryRef service_dict;
3723
3724 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3725 if (service_dict != NULL) {
3726 dict = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
3727 }
3728 }
3729
3730 new_dict = proxy_configuration_update(dict,
3731 S_service_state_dict,
3732 service_order);
3733 if (new_dict == NULL) {
3734 keyChangeListRemoveValue(keys, S_state_global_proxies);
3735 }
3736 else {
3737 keyChangeListSetValue(keys, S_state_global_proxies, new_dict);
3738 CFRelease(new_dict);
3739 }
3740 return;
3741 }
3742
3743 #if !TARGET_OS_IPHONE
3744 static void
3745 update_smb(CFDictionaryRef services_info,
3746 CFStringRef primary,
3747 keyChangeListRef keys)
3748 {
3749 CFDictionaryRef dict = NULL;
3750
3751 if (primary != NULL) {
3752 CFDictionaryRef service_dict;
3753
3754 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3755 if (service_dict != NULL) {
3756 dict = CFDictionaryGetValue(service_dict, kSCEntNetSMB);
3757 }
3758 }
3759 if (dict == NULL) {
3760 keyChangeListRemoveValue(keys, S_state_global_smb);
3761 }
3762 else {
3763 keyChangeListSetValue(keys, S_state_global_smb, dict);
3764 }
3765
3766 return;
3767 }
3768 #endif /* !TARGET_OS_IPHONE */
3769
3770 static Rank
3771 get_service_rank(CFArrayRef order, int n_order, CFStringRef serviceID)
3772 {
3773 CFIndex i;
3774 Rank rank = kRankIndexMask;
3775
3776 if (serviceID != NULL && order != NULL && n_order > 0) {
3777 for (i = 0; i < n_order; i++) {
3778 CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(order, i));
3779
3780 if (s == NULL) {
3781 continue;
3782 }
3783 if (CFEqual(serviceID, s)) {
3784 rank = i + 1;
3785 break;
3786 }
3787 }
3788 }
3789 return (rank);
3790 }
3791
3792 /**
3793 ** Service election:
3794 **/
3795 /*
3796 * Function: rank_dict_get_service_rank
3797 * Purpose:
3798 * Retrieve the service rank in the given dictionary.
3799 */
3800 static Rank
3801 rank_dict_get_service_rank(CFDictionaryRef rank_dict, CFStringRef serviceID)
3802 {
3803 CFNumberRef rank;
3804 Rank rank_val = RankMake(kRankIndexMask, kRankAssertionDefault);
3805
3806 rank = CFDictionaryGetValue(rank_dict, serviceID);
3807 if (rank != NULL) {
3808 CFNumberGetValue(rank, kCFNumberSInt32Type, &rank_val);
3809 }
3810 return (rank_val);
3811 }
3812
3813 /*
3814 * Function: rank_dict_set_service_rank
3815 * Purpose:
3816 * Save the results of ranking the service so we can look it up later without
3817 * repeating all of the ranking code.
3818 */
3819 static void
3820 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict,
3821 CFStringRef serviceID, Rank rank_val)
3822 {
3823 CFNumberRef rank;
3824
3825 rank = CFNumberCreate(NULL, kCFNumberSInt32Type, (const void *)&rank_val);
3826 if (rank != NULL) {
3827 CFDictionarySetValue(rank_dict, serviceID, rank);
3828 CFRelease(rank);
3829 }
3830 return;
3831 }
3832
3833 typedef struct election_info {
3834 int n_services;
3835 CFArrayRef order;
3836 int n_order;
3837 CFStringRef serviceID;
3838 CFDictionaryRef service_dict;
3839 Rank rank;
3840 } election_info_t;
3841
3842 typedef boolean_t election_func_t(void * context, election_info_t * info);
3843
3844 /*
3845 * Function: elect_ipv4
3846 * Purpose:
3847 * This function builds the list of IPv4 routes that should be active.
3848 * As elect_new_primary() invokes us with each service, we build up the
3849 * result in the passed in context, a pointer to an IPv4RouteListRef.
3850 */
3851 static boolean_t
3852 elect_ipv4(void * context, election_info_t * info)
3853 {
3854 Rank primary_rank;
3855 IPv4RouteListRef * routes_p = (IPv4RouteListRef *)context;
3856 IPv4RouteListRef service_routes;
3857
3858 service_routes = service_dict_get_ipv4_routelist(info->service_dict);
3859
3860 info->rank = get_service_rank(info->order, info->n_order,
3861 info->serviceID);
3862
3863 if (service_routes == NULL) {
3864 return (FALSE);
3865 }
3866
3867 primary_rank = RANK_ASSERTION_MASK(service_routes->list->rank);
3868
3869 if (S_ppp_override_primary
3870 && (strncmp(PPP_PREFIX, service_routes->list->ifname,
3871 sizeof(PPP_PREFIX) - 1) == 0)) {
3872 /* PPP override: make ppp* look the best */
3873 /* Hack: should use interface type, not interface name */
3874 primary_rank = kRankAssertionFirst;
3875 }
3876
3877 info->rank = RankMake(info->rank, primary_rank);
3878
3879 if (routes_p != NULL) {
3880 *routes_p = IPv4RouteListAddRouteList(*routes_p,
3881 info->n_services * 3,
3882 service_routes,
3883 info->rank);
3884 }
3885
3886 if (primary_rank == kRankAssertionNever) {
3887 /* never elect as primary */
3888 return (FALSE);
3889 }
3890 if (strncmp(service_routes->list->ifname, "lo0",
3891 sizeof(service_routes->list->ifname)) == 0) {
3892 /* never elect as primary */
3893 return (FALSE);
3894 }
3895
3896 rank_dict_set_service_rank(S_ipv4_service_rank_dict,
3897 info->serviceID, info->rank);
3898 return (TRUE);
3899 }
3900
3901 static void
3902 IPv6RankedListInsertE(IPv6RankedListRef list, IPv6RankedERef e)
3903 {
3904 int idx = 0;
3905 int elems_to_be_moved = 0;
3906
3907 if (list == NULL) return;
3908
3909 for (idx = 0; idx < list->count; idx++) {
3910 if ((e->rank < list->elem[idx].rank)) {
3911 break;
3912 }
3913 }
3914
3915 if ((S_IPMonitor_debug & kDebugFlag2) != 0) {
3916 SCLog(TRUE, LOG_NOTICE,
3917 CFSTR("IPv6RankedListInsertE: %s flags 0x%x at index %d with rank %u."),
3918 e->ifname, e->flags, idx, e->rank);
3919 }
3920
3921 elems_to_be_moved = list->count - idx;
3922 if (elems_to_be_moved > 0) {
3923 bcopy((void*) &list->elem[idx], (void*) &list->elem[idx+1],
3924 elems_to_be_moved * sizeof(*e));
3925 }
3926
3927 bcopy((void*) e, (void*) &list->elem[idx], sizeof(*e));
3928 list->count++;
3929 return;
3930 }
3931
3932 static boolean_t
3933 elect_ipv6(void * context, election_info_t * info)
3934 {
3935 CFStringRef if_name;
3936 CFStringRef primaryRankStr = NULL;
3937 CFDictionaryRef proto_dict;
3938 CFStringRef router;
3939 CFDictionaryRef service_options;
3940 IPv6RankedListRef list = (IPv6RankedListRef) context;
3941 IPv6RankedE elem;
3942 CFDictionaryRef dns_dict = NULL;
3943 Rank primary_rank = kRankAssertionDefault;
3944
3945 memset((void *)&elem, 0, sizeof(elem));
3946
3947 info->rank = get_service_rank(info->order, info->n_order, info->serviceID);
3948
3949 proto_dict = CFDictionaryGetValue(info->service_dict, kSCEntNetIPv6);
3950 if (proto_dict == NULL) {
3951 return (FALSE);
3952 }
3953 if_name = CFDictionaryGetValue(proto_dict, kSCPropInterfaceName);
3954 if (if_name == NULL) {
3955 /* we need an interface name */
3956 return (FALSE);
3957 }
3958 if (CFEqual(if_name, CFSTR("lo0"))) {
3959 /* don't ever elect loopback */
3960 return (FALSE);
3961 }
3962 CFStringGetCString(if_name, elem.ifname,
3963 sizeof(elem.ifname),
3964 kCFStringEncodingASCII);
3965 router = CFDictionaryGetValue(proto_dict,
3966 kSCPropNetIPv6Router);
3967 if (router == NULL) {
3968 /* can't become primary without a router */
3969 return (FALSE);
3970 }
3971
3972 dns_dict = service_dict_get(info->serviceID, kSCEntNetDNS);
3973 if (dns_dict != NULL) {
3974 elem.flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
3975 }
3976
3977 service_options = service_dict_get(info->serviceID, kSCEntNetService);
3978 if (service_options != NULL) {
3979 primaryRankStr = CFDictionaryGetValue(service_options, kSCPropNetServicePrimaryRank);
3980 if (primaryRankStr != NULL) {
3981 primary_rank = PrimaryRankGetRankAssertion(primaryRankStr);
3982 }
3983 if (primary_rank == kRankAssertionNever) {
3984 elem.flags |= NWI_IFSTATE_FLAGS_NOT_IN_LIST;
3985 elem.rank = RankMake(info->rank, primary_rank);
3986 IPv6RankedListInsertE(list, &elem);
3987 return (FALSE);
3988 }
3989 }
3990
3991 if (get_override_primary(proto_dict)) {
3992 primary_rank = kRankAssertionFirst;
3993 }
3994 else if (S_ppp_override_primary
3995 && CFStringHasPrefix(if_name, CFSTR(PPP_PREFIX))) {
3996 /* PPP override: make ppp* look the best */
3997 /* Hack: should use interface type, not interface name */
3998 primary_rank = kRankAssertionFirst;
3999 }
4000
4001 elem.rank = RankMake(info->rank, primary_rank);
4002 info->rank = RankMake(info->rank, primary_rank);
4003
4004 (void)cfstring_to_ip6(router, &elem.iaddr6);
4005
4006 IPv6RankedListInsertE(list, &elem);
4007 rank_dict_set_service_rank(S_ipv6_service_rank_dict,
4008 info->serviceID, info->rank);
4009 return (TRUE);
4010 }
4011
4012 static void
4013 update_nwi_state_ipv6(nwi_state_t state, IPv6RankedListRef list)
4014 {
4015 int idx;
4016
4017 if (state == NULL) {
4018 return;
4019 }
4020
4021 /* A null list indicates to clear the ifstates */
4022 nwi_state_clear(state, AF_INET6);
4023
4024 if (list == NULL) {
4025 return;
4026 }
4027
4028 for (idx = 0 ; idx < list->count; idx++) {
4029 if ((S_IPMonitor_debug & kDebugFlag2) != 0) {
4030 char ntopbuf[INET6_ADDRSTRLEN];
4031
4032 inet_ntop(AF_INET6, &list->elem[idx].iaddr6, ntopbuf,
4033 sizeof(ntopbuf));
4034
4035 SCLog(TRUE, LOG_NOTICE,
4036 CFSTR("Inserting v6 [%s] into list with flags 0x%x"
4037 " with rank %lu ipv6 addr %s"),
4038 list->elem[idx].ifname, list->elem[idx].flags,
4039 list->elem[idx].rank, ntopbuf);
4040 }
4041
4042 nwi_insert_ifstate(state, list->elem[idx].ifname,
4043 AF_INET6, list->elem[idx].flags,
4044 list->elem[idx].rank,
4045 (void *)&list->elem[idx].iaddr6);
4046 }
4047 nwi_state_set_last(state, AF_INET6);
4048 }
4049
4050 static boolean_t
4051 interface_has_dns(const void * * values, int values_count,
4052 const char * ifname)
4053 {
4054 boolean_t has_dns = FALSE;
4055 int i;
4056 CFStringRef ifname_cf;
4057
4058 ifname_cf = CFStringCreateWithCString(NULL, ifname, kCFStringEncodingASCII);
4059 for (i = 0; i < values_count; i++) {
4060 CFDictionaryRef dns_dict;
4061
4062 dns_dict = CFDictionaryGetValue(values[i], kSCEntNetDNS);
4063 if (dns_dict != NULL) {
4064 CFStringRef this_ifname;
4065
4066 this_ifname = CFDictionaryGetValue(dns_dict, kSCPropInterfaceName);
4067 if (this_ifname != NULL
4068 && CFEqual(this_ifname, ifname_cf)) {
4069 has_dns = TRUE;
4070 break;
4071 }
4072 }
4073 }
4074 CFRelease(ifname_cf);
4075 return (has_dns);
4076 }
4077
4078 static void
4079 update_nwi_state_ipv4(nwi_state_t state, IPv4RouteListRef route)
4080 {
4081 int idx;
4082 IPv4RouteRef r;
4083 const void * * values;
4084 const void * values_buf[10];
4085 int values_count;
4086
4087 if (state == NULL) {
4088 return;
4089 }
4090 nwi_state_clear(state, AF_INET);
4091 if (route == NULL) {
4092 return;
4093 }
4094 values_count = CFDictionaryGetCount(S_service_state_dict);
4095 if (values_count == 0) {
4096 return;
4097 }
4098 if (values_count <= (sizeof(values_buf) / sizeof(values_buf[0]))) {
4099 values = values_buf;
4100 }
4101 else {
4102 values = (const void * *)malloc(sizeof(*values) * values_count);
4103 }
4104 CFDictionaryGetKeysAndValues(S_service_state_dict,
4105 NULL, (const void * *)values);
4106 for (idx = 0, r = route->list; idx < route->count; idx++, r++) {
4107 uint64_t flags = 0ULL;
4108
4109 if (r->dest.s_addr != 0) {
4110 /* we're done, no more default routes */
4111 break;
4112 }
4113 if (RANK_ASSERTION_MASK(r->rank) == kRankAssertionNever) {
4114 flags |= NWI_IFSTATE_FLAGS_NOT_IN_LIST;
4115 }
4116
4117 if (interface_has_dns(values, values_count, r->ifname)) {
4118 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
4119 }
4120 if ((S_IPMonitor_debug & kDebugFlag2) != 0) {
4121 SCLog(TRUE, LOG_NOTICE, CFSTR("Inserting v4 [%s] with flags 0x%x primary_rank %u"),
4122 r->ifname, flags, r->rank);
4123 }
4124 nwi_insert_ifstate(state, r->ifname, AF_INET, flags, r->rank,
4125 (void *)&r->ifa);
4126 }
4127 if (values != values_buf) {
4128 free(values);
4129 }
4130 nwi_state_set_last(state, AF_INET);
4131 return;
4132 }
4133
4134 /*
4135 * Function: elect_new_primary
4136 * Purpose:
4137 * Walk the list of services, passing each service dictionary to "elect_func".
4138 * "elect_func" returns rank information about the service that let us
4139 * determine the new primary.
4140 */
4141 static CFStringRef
4142 elect_new_primary(election_func_t * elect_func, void * context,
4143 CFArrayRef order, int n_order)
4144 {
4145 int count;
4146 int i;
4147 election_info_t info;
4148 void * * keys;
4149 #define N_KEYS_VALUES_STATIC 10
4150 void * keys_values_buf[N_KEYS_VALUES_STATIC * 2];
4151 CFStringRef new_primary = NULL;
4152 Rank new_rank = 0;
4153 void * * values;
4154
4155 count = CFDictionaryGetCount(S_service_state_dict);
4156 if (count <= N_KEYS_VALUES_STATIC) {
4157 keys = keys_values_buf;
4158 }
4159 else {
4160 keys = (void * *)malloc(sizeof(*keys) * count * 2);
4161 }
4162 values = keys + count;
4163 CFDictionaryGetKeysAndValues(S_service_state_dict,
4164 (const void * *)keys,
4165 (const void * *)values);
4166
4167 info.n_services = count;
4168 info.order = order;
4169 info.n_order = n_order;
4170 new_rank = 0;
4171 for (i = 0; i < count; i++) {
4172 boolean_t found_new_primary = FALSE;
4173
4174 info.serviceID = (CFStringRef)keys[i];
4175 info.service_dict = (CFDictionaryRef)values[i];
4176 info.rank = kRankAssertionMask;
4177
4178 if ((*elect_func)(context, &info) == FALSE) {
4179 continue;
4180 }
4181 if (new_primary == NULL
4182 || (info.rank < new_rank)) {
4183 found_new_primary = TRUE;
4184 }
4185
4186 if (found_new_primary) {
4187 new_primary = info.serviceID;
4188 new_rank = info.rank;
4189 }
4190 }
4191 if (new_primary != NULL) {
4192 CFRetain(new_primary);
4193 }
4194 if (keys != keys_values_buf) {
4195 free(keys);
4196 }
4197 return (new_primary);
4198 }
4199
4200 static uint32_t
4201 service_changed(CFDictionaryRef services_info, CFStringRef serviceID)
4202 {
4203 uint32_t changed = 0;
4204 int i;
4205
4206 /* update service options first (e.g. rank) */
4207 if (get_rank_changes(serviceID,
4208 get_service_state_entity(services_info, serviceID,
4209 NULL),
4210 get_service_setup_entity(services_info, serviceID,
4211 NULL),
4212 services_info)) {
4213 changed |= (1 << kEntityTypeServiceOptions);
4214 }
4215 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
4216 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
4217 GetEntityChangesFuncRef func = entityChangeFunc[i];
4218 if ((*func)(serviceID,
4219 get_service_state_entity(services_info, serviceID,
4220 *entityTypeNames[i]),
4221 get_service_setup_entity(services_info, serviceID,
4222 *entityTypeNames[i]),
4223 services_info)) {
4224 changed |= (1 << i);
4225 }
4226 }
4227 return (changed);
4228 }
4229
4230 static CFArrayRef
4231 service_order_get(CFDictionaryRef services_info)
4232 {
4233 CFArrayRef order = NULL;
4234 CFDictionaryRef ipv4_dict;
4235
4236 ipv4_dict = my_CFDictionaryGetDictionary(services_info,
4237 S_setup_global_ipv4);
4238 if (ipv4_dict != NULL) {
4239 CFNumberRef ppp_override;
4240 int ppp_val = 0;
4241
4242 order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
4243 order = isA_CFArray(order);
4244
4245 /* get ppp override primary */
4246 ppp_override = CFDictionaryGetValue(ipv4_dict,
4247 kSCPropNetPPPOverridePrimary);
4248 ppp_override = isA_CFNumber(ppp_override);
4249 if (ppp_override != NULL) {
4250 CFNumberGetValue(ppp_override, kCFNumberIntType, &ppp_val);
4251 }
4252 S_ppp_override_primary = (ppp_val != 0) ? TRUE : FALSE;
4253 }
4254 else {
4255 S_ppp_override_primary = FALSE;
4256 }
4257 return (order);
4258 }
4259
4260 static boolean_t
4261 set_new_primary(CFStringRef * primary_p, CFStringRef new_primary,
4262 const char * entity)
4263 {
4264 boolean_t changed = FALSE;
4265 CFStringRef primary = *primary_p;
4266
4267 if (new_primary != NULL) {
4268 if (primary != NULL && CFEqual(new_primary, primary)) {
4269 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4270 SCLog(TRUE, LOG_NOTICE,
4271 CFSTR("IPMonitor: %@ is still primary %s"),
4272 new_primary, entity);
4273 }
4274 }
4275 else {
4276 my_CFRelease(primary_p);
4277 *primary_p = CFRetain(new_primary);
4278 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4279 SCLog(TRUE, LOG_NOTICE,
4280 CFSTR("IPMonitor: %@ is the new primary %s"),
4281 new_primary, entity);
4282 }
4283 changed = TRUE;
4284 }
4285 }
4286 else if (primary != NULL) {
4287 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4288 SCLog(TRUE, LOG_NOTICE,
4289 CFSTR("IPMonitor: %@ is no longer primary %s"),
4290 primary, entity);
4291 }
4292 my_CFRelease(primary_p);
4293 changed = TRUE;
4294 }
4295 return (changed);
4296 }
4297
4298 static Rank
4299 rank_service_entity(CFDictionaryRef rank_dict, CFStringRef serviceID,
4300 CFStringRef entity)
4301 {
4302 if (service_dict_get(serviceID, entity) == NULL) {
4303 return (RankMake(kRankIndexMask, kRankAssertionDefault));
4304 }
4305 return (rank_dict_get_service_rank(rank_dict, serviceID));
4306 }
4307
4308 static __inline__ unsigned int
4309 IPv6RankedListComputeSize(int n)
4310 {
4311 return (offsetof(IPv6RankedList, elem[n]));
4312
4313 }
4314
4315 static void
4316 update_interface_rank(CFDictionaryRef services_info, CFStringRef ifname)
4317 {
4318 CFStringRef if_rank_key;
4319 CFDictionaryRef rank_dict;
4320
4321 if_rank_key = if_rank_key_copy(ifname);
4322 rank_dict = CFDictionaryGetValue(services_info, if_rank_key);
4323 CFRelease(if_rank_key);
4324 if_rank_set(ifname, rank_dict);
4325 return;
4326 }
4327
4328 static void
4329 append_serviceIDs_for_interface(CFMutableArrayRef services_changed,
4330 CFStringRef ifname)
4331 {
4332 int count;
4333 int i;
4334 void * * keys;
4335 #define N_KEYS_VALUES_STATIC 10
4336 void * keys_values_buf[N_KEYS_VALUES_STATIC * 2];
4337 void * * values;
4338
4339 count = CFDictionaryGetCount(S_service_state_dict);
4340 if (count <= N_KEYS_VALUES_STATIC) {
4341 keys = keys_values_buf;
4342 } else {
4343 keys = (void * *)malloc(sizeof(*keys) * count * 2);
4344 }
4345 values = keys + count;
4346 CFDictionaryGetKeysAndValues(S_service_state_dict,
4347 (const void * *)keys,
4348 (const void * *)values);
4349
4350 for (i = 0; i < count; i++) {
4351 CFDictionaryRef ipv4 = NULL;
4352 CFStringRef interface = NULL;
4353 CFStringRef serviceID;
4354 CFDictionaryRef service_dict;
4355
4356 serviceID = (CFStringRef)keys[i];
4357 service_dict = (CFDictionaryRef)values[i];
4358
4359 /* check if this is a ipv4 dictionary */
4360 ipv4 = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
4361 if (ipv4 != NULL) {
4362 interface = ipv4_dict_get_ifname(ipv4);
4363 if (interface != NULL && CFEqual(interface, ifname)) {
4364 if (S_IPMonitor_debug & kDebugFlag1) {
4365 SCLog(TRUE, LOG_NOTICE, CFSTR("Found ipv4 service %@ on interface %@."),
4366 serviceID, ifname);
4367 }
4368
4369 my_CFArrayAppendUniqueValue(services_changed, serviceID);
4370 }
4371 } else {
4372 CFDictionaryRef proto_dict;
4373
4374 /* check if this is a ipv6 dictionary */
4375 proto_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
4376 if (proto_dict == NULL) {
4377 continue;
4378 }
4379 interface = CFDictionaryGetValue(proto_dict, kSCPropInterfaceName);
4380 if (interface != NULL && CFEqual(interface, ifname)) {
4381 if (S_IPMonitor_debug & kDebugFlag1) {
4382 SCLog(TRUE, LOG_NOTICE, CFSTR("Found ipv6 service %@ on interface %@."),
4383 serviceID, ifname);
4384 }
4385
4386 my_CFArrayAppendUniqueValue(services_changed, serviceID);
4387 }
4388 }
4389 }
4390
4391 if (keys != keys_values_buf) {
4392 free(keys);
4393 }
4394 }
4395
4396 static const CFStringRef *statusEntityNames[] = {
4397 &kSCEntNetIPSec,
4398 &kSCEntNetPPP,
4399 &kSCEntNetVPN,
4400 };
4401
4402 static void
4403 add_status_keys(CFMutableArrayRef patterns)
4404 {
4405 int i;
4406
4407 for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) {
4408 CFStringRef pattern;
4409
4410 pattern = state_service_key(kSCCompAnyRegex, *statusEntityNames[i]);
4411 CFArrayAppendValue(patterns, pattern);
4412 CFRelease(pattern);
4413 }
4414
4415 return;
4416 }
4417
4418 static
4419 inline
4420 const char *
4421 get_changed_str(CFStringRef serviceID, CFStringRef entity, CFDictionaryRef old_dict)
4422 {
4423 CFDictionaryRef new_dict = NULL;
4424
4425 if (serviceID != NULL) {
4426 new_dict = service_dict_get(serviceID, entity);
4427 }
4428
4429 if (old_dict == NULL) {
4430 if (new_dict != NULL) {
4431 return "+";
4432 }
4433 } else {
4434 if (new_dict == NULL) {
4435 return "-";
4436 } else if (!CFEqual(old_dict, new_dict)) {
4437 return "!";
4438 }
4439 }
4440 return "";
4441 }
4442
4443 static CF_RETURNS_RETAINED CFStringRef
4444 generate_log_changes(nwi_state_t changes_state,
4445 boolean_t dns_changed,
4446 boolean_t dnsinfo_changed,
4447 CFDictionaryRef old_primary_dns,
4448 boolean_t proxy_changed,
4449 CFDictionaryRef old_primary_proxy,
4450 boolean_t smb_changed,
4451 CFDictionaryRef old_primary_smb
4452 )
4453 {
4454 int idx;
4455 CFMutableStringRef log_output;
4456 nwi_ifstate_t scan;
4457
4458 log_output = CFStringCreateMutable(NULL, 0);
4459
4460 if (changes_state != NULL) {
4461 for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) {
4462 CFMutableStringRef changes = NULL;
4463 CFMutableStringRef primary_str = NULL;
4464
4465 scan = nwi_state_get_first_ifstate(changes_state, nwi_af_list[idx]);
4466
4467 while (scan != NULL) {
4468 const char * changed_str;
4469
4470 changed_str = nwi_ifstate_get_diff_str(scan);
4471 if (changed_str != NULL) {
4472 void * address;
4473 const char * addr_str;
4474 char ntopbuf[INET6_ADDRSTRLEN];
4475
4476 address = (void *)nwi_ifstate_get_address(scan);
4477 addr_str = inet_ntop(scan->af, address,
4478 ntopbuf, sizeof(ntopbuf));
4479
4480 if (primary_str == NULL) {
4481 primary_str = CFStringCreateMutable(NULL, 0);
4482 CFStringAppendFormat(primary_str, NULL, CFSTR("%s%s:%s"),
4483 nwi_ifstate_get_ifname(scan),
4484 changed_str, addr_str);
4485 } else {
4486 if (changes == NULL) {
4487 changes = CFStringCreateMutable(NULL, 0);
4488 }
4489 CFStringAppendFormat(changes, NULL, CFSTR(", %s"),
4490 nwi_ifstate_get_ifname(scan));
4491 if (strcmp(changed_str, "") != 0) {
4492 CFStringAppendFormat(changes, NULL, CFSTR("%s:%s"),
4493 changed_str, addr_str);
4494 }
4495 }
4496 }
4497 scan = nwi_ifstate_get_next(scan, scan->af);
4498 }
4499
4500 if (primary_str != NULL) {
4501 CFStringAppendFormat(log_output, NULL, CFSTR(" %s(%@"),
4502 nwi_af_list[idx] == AF_INET ? "v4" : "v6",
4503 primary_str);
4504
4505 if (changes != NULL && CFStringGetLength(changes) != 0) {
4506 CFStringAppendFormat(log_output, NULL, CFSTR("%@"),
4507 changes);
4508 }
4509 CFStringAppendFormat(log_output, NULL, CFSTR(")"));
4510
4511 my_CFRelease(&primary_str);
4512 my_CFRelease(&changes);
4513 }
4514 }
4515 }
4516
4517 if (dns_changed || dnsinfo_changed) {
4518 const char *str;
4519
4520 str = get_changed_str(S_primary_dns, kSCEntNetDNS, old_primary_dns);
4521 if ((strcmp(str, "") == 0) && dnsinfo_changed) {
4522 str = "*"; // dnsinfo change w/no change to primary
4523 }
4524 CFStringAppendFormat(log_output, NULL, CFSTR(" DNS%s"), str);
4525 } else if (S_primary_dns != NULL){
4526 CFStringAppendFormat(log_output, NULL, CFSTR(" DNS"));
4527 }
4528
4529 if (proxy_changed) {
4530 const char *str;
4531
4532 str = get_changed_str(S_primary_proxies, kSCEntNetProxies, old_primary_proxy);
4533 CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy%s"), str);
4534 } else if (S_primary_proxies != NULL) {
4535 CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy"));
4536 }
4537
4538 #if !TARGET_OS_IPHONE
4539 if (smb_changed) {
4540 const char *str;
4541
4542 str = get_changed_str(S_primary_smb, kSCEntNetSMB, old_primary_smb);
4543 CFStringAppendFormat(log_output, NULL, CFSTR(" SMB%s"), str);
4544 } else if (S_primary_smb != NULL) {
4545 CFStringAppendFormat(log_output, NULL, CFSTR(" SMB"));
4546 }
4547 #endif // !TARGET_OS_IPHONE
4548
4549 return log_output;
4550 }
4551
4552 static void
4553 IPMonitorNotify(SCDynamicStoreRef session, CFArrayRef changed_keys,
4554 void * not_used)
4555 {
4556 CFIndex count;
4557 nwi_state_t changes_state = NULL;
4558 boolean_t dns_changed = FALSE;
4559 boolean_t dnsinfo_changed = FALSE;
4560 boolean_t global_ipv4_changed = FALSE;
4561 boolean_t global_ipv6_changed = FALSE;
4562 int i;
4563 CFMutableArrayRef if_rank_changes = NULL;
4564 keyChangeList keys;
4565 CFIndex n;
4566 CFStringRef network_change_msg;
4567 int n_services;
4568 int n_service_order = 0;
4569 nwi_state_t old_nwi_state = NULL;
4570 CFDictionaryRef old_primary_dns = NULL;
4571 CFDictionaryRef old_primary_proxy = NULL;
4572 #if !TARGET_OS_IPHONE
4573 CFDictionaryRef old_primary_smb = NULL;
4574 #endif // !TARGET_OS_IPHONE
4575 boolean_t proxies_changed = FALSE;
4576 CFArrayRef service_order;
4577 CFMutableArrayRef service_changes = NULL;
4578 CFDictionaryRef services_info = NULL;
4579 #if !TARGET_OS_IPHONE
4580 boolean_t smb_changed = FALSE;
4581 #endif // !TARGET_OS_IPHONE
4582
4583 count = CFArrayGetCount(changed_keys);
4584 if (count == 0) {
4585 return;
4586 }
4587
4588 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4589 SCLog(TRUE, LOG_NOTICE,
4590 CFSTR("IPMonitor: changes %@ (%d)"), changed_keys, count);
4591 }
4592
4593 if (S_primary_dns != NULL) {
4594 old_primary_dns = service_dict_get(S_primary_dns, kSCEntNetDNS);
4595 if (old_primary_dns != NULL) {
4596 old_primary_dns = CFDictionaryCreateCopy(NULL, old_primary_dns);
4597 }
4598 }
4599
4600 if (S_primary_proxies != NULL) {
4601 old_primary_proxy = service_dict_get(S_primary_proxies, kSCEntNetProxies);
4602 if (old_primary_proxy != NULL) {
4603 old_primary_proxy = CFDictionaryCreateCopy(NULL, old_primary_proxy);
4604 }
4605 }
4606
4607 #if !TARGET_OS_IPHONE
4608 if (S_primary_smb != NULL) {
4609 old_primary_smb = service_dict_get(S_primary_smb, kSCEntNetSMB);
4610 if (old_primary_smb != NULL) {
4611 old_primary_smb = CFDictionaryCreateCopy(NULL, old_primary_smb);
4612 }
4613 }
4614 #endif // !TARGET_OS_IPHONE
4615
4616 keyChangeListInit(&keys);
4617 service_changes = CFArrayCreateMutable(NULL, 0,
4618 &kCFTypeArrayCallBacks);
4619
4620 for (i = 0; i < count; i++) {
4621 CFStringRef change = CFArrayGetValueAtIndex(changed_keys, i);
4622 if (CFEqual(change, S_setup_global_ipv4)) {
4623 global_ipv4_changed = TRUE;
4624 global_ipv6_changed = TRUE;
4625 }
4626 else if (CFEqual(change, S_multicast_resolvers)) {
4627 dnsinfo_changed = TRUE;
4628 }
4629 else if (CFEqual(change, S_private_resolvers)) {
4630 dnsinfo_changed = TRUE;
4631 }
4632 #if !TARGET_OS_IPHONE
4633 else if (CFEqual(change, CFSTR(_PATH_RESOLVER_DIR))) {
4634 dnsinfo_changed = TRUE;
4635 }
4636 #endif /* !TARGET_OS_IPHONE */
4637 else if (CFStringHasPrefix(change, S_state_service_prefix)) {
4638 int i;
4639 boolean_t status_changed = FALSE;
4640
4641 for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) {
4642 if (CFStringHasSuffix(change, *statusEntityNames[i])) {
4643 status_changed = TRUE;
4644 dnsinfo_changed = TRUE;
4645 break;
4646 }
4647 }
4648
4649 if (!status_changed) {
4650 CFStringRef serviceID = parse_component(change,
4651 S_state_service_prefix);
4652 if (serviceID) {
4653 my_CFArrayAppendUniqueValue(service_changes, serviceID);
4654 CFRelease(serviceID);
4655 }
4656 }
4657 }
4658 else if (CFStringHasPrefix(change, S_setup_service_prefix)) {
4659 CFStringRef serviceID = parse_component(change,
4660 S_setup_service_prefix);
4661 if (serviceID) {
4662 my_CFArrayAppendUniqueValue(service_changes, serviceID);
4663 CFRelease(serviceID);
4664 }
4665 }
4666 else if (CFStringHasSuffix(change, kSCEntNetService)) {
4667 CFStringRef ifname = my_CFStringCopyComponent(change, CFSTR("/"), 3);
4668
4669 if (ifname != NULL) {
4670 if (if_rank_changes == NULL) {
4671 if_rank_changes = CFArrayCreateMutable(NULL, 0,
4672 &kCFTypeArrayCallBacks);
4673 }
4674 my_CFArrayAppendUniqueValue(if_rank_changes, ifname);
4675 CFRelease(ifname);
4676 }
4677 }
4678 }
4679
4680 /* determine which serviceIDs are impacted by the interface rank changes */
4681 if (if_rank_changes != NULL) {
4682 n = CFArrayGetCount(if_rank_changes);
4683 for (i = 0; i < n; i++) {
4684 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i);
4685
4686 if (S_IPMonitor_debug & kDebugFlag1) {
4687 SCLog(TRUE, LOG_NOTICE, CFSTR("Interface rank changed %@"),
4688 ifname);
4689 }
4690 append_serviceIDs_for_interface(service_changes, ifname);
4691 }
4692 }
4693
4694 /* grab a snapshot of everything we need */
4695 services_info = services_info_copy(session, service_changes,
4696 if_rank_changes);
4697 service_order = service_order_get(services_info);
4698 if (service_order != NULL) {
4699 n_service_order = CFArrayGetCount(service_order);
4700 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4701 SCLog(TRUE, LOG_NOTICE,
4702 CFSTR("IPMonitor: service_order %@ "), service_order);
4703 }
4704 }
4705
4706 if (if_rank_changes != NULL) {
4707 for (i = 0; i < n; i++) {
4708 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i);
4709 update_interface_rank(services_info, ifname);
4710 }
4711 }
4712
4713 n = CFArrayGetCount(service_changes);
4714 for (i = 0; i < n; i++) {
4715 uint32_t changes;
4716 CFStringRef serviceID;
4717
4718 serviceID = CFArrayGetValueAtIndex(service_changes, i);
4719 changes = service_changed(services_info, serviceID);
4720 if ((changes & (1 << kEntityTypeServiceOptions)) != 0) {
4721 /* if __Service__ (e.g. PrimaryRank) changed */
4722 global_ipv4_changed = TRUE;
4723 global_ipv6_changed = TRUE;
4724 }
4725 else {
4726 if ((changes & (1 << kEntityTypeIPv4)) != 0) {
4727 global_ipv4_changed = TRUE;
4728 dnsinfo_changed = TRUE;
4729 proxies_changed = TRUE;
4730 }
4731 if ((changes & (1 << kEntityTypeIPv6)) != 0) {
4732 global_ipv6_changed = TRUE;
4733 dnsinfo_changed = TRUE;
4734 proxies_changed = TRUE;
4735 }
4736 }
4737 if ((changes & (1 << kEntityTypeDNS)) != 0) {
4738 if (S_primary_dns != NULL && CFEqual(S_primary_dns, serviceID)) {
4739 update_dns(services_info, serviceID, &keys);
4740 }
4741 dnsinfo_changed = TRUE;
4742 }
4743 if ((changes & (1 << kEntityTypeProxies)) != 0) {
4744 proxies_changed = TRUE;
4745 }
4746 #if !TARGET_OS_IPHONE
4747 if ((changes & (1 << kEntityTypeSMB)) != 0) {
4748 if (S_primary_smb != NULL && CFEqual(S_primary_smb, serviceID)) {
4749 update_smb(services_info, serviceID, &keys);
4750 }
4751 }
4752 #endif /* !TARGET_OS_IPHONE */
4753 }
4754
4755 /* ensure S_nwi_state can hold as many services as we have currently */
4756 n_services = CFDictionaryGetCount(S_service_state_dict);
4757 old_nwi_state = nwi_state_copy_priv(S_nwi_state);
4758 S_nwi_state = nwi_state_new(S_nwi_state, n_services);
4759
4760 if (global_ipv4_changed || dnsinfo_changed) {
4761 IPv4RouteListRef new_routelist = NULL;
4762 CFStringRef new_primary;
4763
4764 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4765 SCLog(TRUE, LOG_NOTICE,
4766 CFSTR("IPMonitor: IPv4 service election"));
4767 }
4768 new_primary = elect_new_primary(&elect_ipv4, &new_routelist,
4769 service_order, n_service_order);
4770 update_nwi_state_ipv4(S_nwi_state, new_routelist);
4771
4772 if (global_ipv4_changed) {
4773 (void)set_new_primary(&S_primary_ipv4, new_primary, "IPv4");
4774 update_ipv4(S_primary_ipv4, new_routelist, &keys);
4775 }
4776 else if (new_routelist != NULL) {
4777 free(new_routelist);
4778 new_routelist = NULL;
4779 }
4780 my_CFRelease(&new_primary);
4781 }
4782 if (global_ipv6_changed || dnsinfo_changed) {
4783 IPv6RankedListRef list = NULL;
4784 CFStringRef new_primary;
4785 int size;
4786
4787 size = (n_services != 0)
4788 ? (sizeof(IPv6RankedList)
4789 + IPv6RankedListComputeSize(n_services)) : 0;
4790
4791 if (size != 0) {
4792 list = malloc(size);
4793 }
4794 if (list != NULL) {
4795 list->count = 0;
4796 list->size = size;
4797 }
4798
4799 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4800 SCLog(TRUE, LOG_NOTICE,
4801 CFSTR("IPMonitor: IPv6 service election"));
4802 }
4803 new_primary = elect_new_primary(&elect_ipv6, list,
4804 service_order, n_service_order);
4805 update_nwi_state_ipv6(S_nwi_state, list);
4806
4807 if (global_ipv6_changed) {
4808 (void)set_new_primary(&S_primary_ipv6, new_primary, "IPv6");
4809 update_ipv6(services_info, S_primary_ipv6, &keys);
4810 }
4811
4812 if (list != NULL) {
4813 free(list);
4814 }
4815 my_CFRelease(&new_primary);
4816 }
4817 if (global_ipv4_changed || global_ipv6_changed) {
4818 CFStringRef new_primary_dns = NULL;
4819 CFStringRef new_primary_proxies = NULL;
4820 #if !TARGET_OS_IPHONE
4821 CFStringRef new_primary_smb = NULL;
4822 #endif /* !TARGET_OS_IPHONE */
4823
4824 if (S_primary_ipv4 != NULL && S_primary_ipv6 != NULL) {
4825 /* decide between IPv4 and IPv6 */
4826 if (rank_service_entity(S_ipv4_service_rank_dict,
4827 S_primary_ipv4, kSCEntNetDNS)
4828 <= rank_service_entity(S_ipv6_service_rank_dict,
4829 S_primary_ipv6, kSCEntNetDNS)) {
4830 new_primary_dns = S_primary_ipv4;
4831 }
4832 else {
4833 new_primary_dns = S_primary_ipv6;
4834 }
4835 if (rank_service_entity(S_ipv4_service_rank_dict,
4836 S_primary_ipv4, kSCEntNetProxies)
4837 <= rank_service_entity(S_ipv6_service_rank_dict,
4838 S_primary_ipv6, kSCEntNetProxies)) {
4839 new_primary_proxies = S_primary_ipv4;
4840 }
4841 else {
4842 new_primary_proxies = S_primary_ipv6;
4843 }
4844 #if !TARGET_OS_IPHONE
4845 if (rank_service_entity(S_ipv4_service_rank_dict,
4846 S_primary_ipv4, kSCEntNetSMB)
4847 <= rank_service_entity(S_ipv6_service_rank_dict,
4848 S_primary_ipv6, kSCEntNetSMB)) {
4849 new_primary_smb = S_primary_ipv4;
4850 }
4851 else {
4852 new_primary_smb = S_primary_ipv6;
4853 }
4854 #endif /* !TARGET_OS_IPHONE */
4855
4856 }
4857 else if (S_primary_ipv6 != NULL) {
4858 new_primary_dns = S_primary_ipv6;
4859 new_primary_proxies = S_primary_ipv6;
4860 #if !TARGET_OS_IPHONE
4861 new_primary_smb = S_primary_ipv6;
4862 #endif /* !TARGET_OS_IPHONE */
4863 }
4864 else if (S_primary_ipv4 != NULL) {
4865 new_primary_dns = S_primary_ipv4;
4866 new_primary_proxies = S_primary_ipv4;
4867 #if !TARGET_OS_IPHONE
4868 new_primary_smb = S_primary_ipv4;
4869 #endif /* !TARGET_OS_IPHONE */
4870 }
4871
4872 if (set_new_primary(&S_primary_dns, new_primary_dns, "DNS")) {
4873 update_dns(services_info, S_primary_dns, &keys);
4874 dns_changed = TRUE;
4875 dnsinfo_changed = TRUE;
4876 }
4877 if (set_new_primary(&S_primary_proxies, new_primary_proxies, "Proxies")) {
4878 proxies_changed = TRUE;
4879 }
4880 #if !TARGET_OS_IPHONE
4881 if (set_new_primary(&S_primary_smb, new_primary_smb, "SMB")) {
4882 update_smb(services_info, S_primary_smb, &keys);
4883 smb_changed = TRUE;
4884 }
4885 #endif /* !TARGET_OS_IPHONE */
4886 }
4887 if (dnsinfo_changed || global_ipv4_changed || global_ipv6_changed) {
4888 if (S_nwi_state != NULL) {
4889 S_nwi_state->generation_count = mach_absolute_time();
4890 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4891 syslog(LOG_NOTICE, "Updating network information");
4892 _nwi_state_dump(LOG_NOTICE, S_nwi_state);
4893 }
4894 if (_nwi_state_store(S_nwi_state) == FALSE) {
4895 SCLog(TRUE, LOG_ERR, CFSTR("Notifying nwi_state_store failed"));
4896 }
4897 }
4898 }
4899 if (dnsinfo_changed) {
4900 update_dnsinfo(services_info, S_primary_dns, &keys, service_order);
4901 }
4902 if (proxies_changed ||
4903 (dnsinfo_changed && (G_supplemental_proxies_follow_dns != NULL) && CFBooleanGetValue(G_supplemental_proxies_follow_dns))
4904 ) {
4905 // if proxy change OR supplemental Proxies follow supplemental DNS
4906 update_proxies(services_info, S_primary_proxies, &keys, service_order);
4907 proxies_changed = TRUE;
4908 }
4909 my_CFRelease(&service_changes);
4910 my_CFRelease(&services_info);
4911 my_CFRelease(&if_rank_changes);
4912 changes_state = nwi_state_diff(old_nwi_state, S_nwi_state);
4913 if ((S_IPMonitor_debug & kDebugFlag2) != 0) {
4914 syslog(LOG_NOTICE, "network information diffs: ");
4915 _nwi_state_dump(LOG_NOTICE, changes_state);
4916 }
4917 network_change_msg =
4918 generate_log_changes(changes_state,
4919 dns_changed,
4920 dnsinfo_changed,
4921 old_primary_dns,
4922 proxies_changed,
4923 old_primary_proxy,
4924 #if !TARGET_OS_IPHONE
4925 smb_changed,
4926 old_primary_smb
4927 #else // !TARGET_OS_IPHONE
4928 FALSE,
4929 NULL
4930 #endif // !TARGET_OS_IPHONE
4931 );
4932 keyChangeListApplyToStore(&keys, session, network_change_msg);
4933 my_CFRelease(&old_primary_dns);
4934 my_CFRelease(&old_primary_proxy);
4935 #if !TARGET_OS_IPHONE
4936 my_CFRelease(&old_primary_smb);
4937 #endif // !TARGET_OS_IPHONE
4938 my_CFRelease(&network_change_msg);
4939
4940 if (changes_state != NULL) {
4941 nwi_state_release(changes_state);
4942 }
4943 if (old_nwi_state != NULL) {
4944 nwi_state_release(old_nwi_state);
4945 }
4946 keyChangeListFree(&keys);
4947 return;
4948 }
4949
4950 static void
4951 ip_plugin_init()
4952 {
4953 CFMutableArrayRef keys = NULL;
4954 CFStringRef pattern;
4955 CFMutableArrayRef patterns = NULL;
4956 CFRunLoopSourceRef rls = NULL;
4957
4958 if (S_is_network_boot() != 0) {
4959 S_netboot = TRUE;
4960 }
4961
4962 #ifdef RTF_IFSCOPE
4963 if (S_is_scoped_routing_enabled() != 0) {
4964 S_scopedroute = TRUE;
4965 }
4966
4967 if (S_is_scoped_v6_routing_enabled() != 0) {
4968 S_scopedroute_v6 = TRUE;
4969 }
4970 #endif /* RTF_IFSCOPE */
4971
4972 S_session = SCDynamicStoreCreate(NULL, CFSTR("IPMonitor"),
4973 IPMonitorNotify, NULL);
4974 if (S_session == NULL) {
4975 SCLog(TRUE, LOG_ERR,
4976 CFSTR("IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s"),
4977 SCErrorString(SCError()));
4978 return;
4979 }
4980 S_state_global_ipv4
4981 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
4982 kSCDynamicStoreDomainState,
4983 kSCEntNetIPv4);
4984 S_state_global_ipv6
4985 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
4986 kSCDynamicStoreDomainState,
4987 kSCEntNetIPv6);
4988 S_state_global_dns
4989 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
4990 kSCDynamicStoreDomainState,
4991 kSCEntNetDNS);
4992 S_state_global_proxies
4993 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
4994 kSCDynamicStoreDomainState,
4995 kSCEntNetProxies);
4996 #if !TARGET_OS_IPHONE
4997 S_state_global_smb
4998 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
4999 kSCDynamicStoreDomainState,
5000 kSCEntNetSMB);
5001 #endif /* !TARGET_OS_IPHONE */
5002 S_setup_global_ipv4
5003 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5004 kSCDynamicStoreDomainSetup,
5005 kSCEntNetIPv4);
5006 S_state_service_prefix
5007 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5008 kSCDynamicStoreDomainState,
5009 CFSTR(""),
5010 NULL);
5011 S_setup_service_prefix
5012 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5013 kSCDynamicStoreDomainSetup,
5014 CFSTR(""),
5015 NULL);
5016 S_service_state_dict
5017 = CFDictionaryCreateMutable(NULL, 0,
5018 &kCFTypeDictionaryKeyCallBacks,
5019 &kCFTypeDictionaryValueCallBacks);
5020
5021 S_ipv4_service_rank_dict
5022 = CFDictionaryCreateMutable(NULL, 0,
5023 &kCFTypeDictionaryKeyCallBacks,
5024 &kCFTypeDictionaryValueCallBacks);
5025
5026 S_ipv6_service_rank_dict
5027 = CFDictionaryCreateMutable(NULL, 0,
5028 &kCFTypeDictionaryKeyCallBacks,
5029 &kCFTypeDictionaryValueCallBacks);
5030
5031 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5032 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5033
5034 /* register for State: and Setup: per-service notifications */
5035 add_service_keys(kSCCompAnyRegex, keys, patterns);
5036
5037 /* register for State: per-service PPP/VPN/IPSec status notifications */
5038 add_status_keys(patterns);
5039
5040 /* register for interface rank notifications */
5041 pattern = if_rank_key_copy(kSCCompAnyRegex);
5042 CFArrayAppendValue(patterns, pattern);
5043 CFRelease(pattern);
5044
5045 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
5046 CFArrayAppendValue(keys, S_setup_global_ipv4);
5047
5048 /* add notifier for multicast DNS configuration (Bonjour/.local) */
5049 S_multicast_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
5050 kSCDynamicStoreDomainState,
5051 kSCCompNetwork,
5052 CFSTR(kDNSServiceCompMulticastDNS));
5053 CFArrayAppendValue(keys, S_multicast_resolvers);
5054
5055 /* add notifier for private DNS configuration (Back to My Mac) */
5056 S_private_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
5057 kSCDynamicStoreDomainState,
5058 kSCCompNetwork,
5059 CFSTR(kDNSServiceCompPrivateDNS));
5060 CFArrayAppendValue(keys, S_private_resolvers);
5061
5062 if (!SCDynamicStoreSetNotificationKeys(S_session, keys, patterns)) {
5063 SCLog(TRUE, LOG_ERR,
5064 CFSTR("IPMonitor ip_plugin_init "
5065 "SCDynamicStoreSetNotificationKeys failed: %s"),
5066 SCErrorString(SCError()));
5067 goto done;
5068 }
5069
5070 rls = SCDynamicStoreCreateRunLoopSource(NULL, S_session, 0);
5071 if (rls == NULL) {
5072 SCLog(TRUE, LOG_ERR,
5073 CFSTR("IPMonitor ip_plugin_init "
5074 "SCDynamicStoreCreateRunLoopSource failed: %s"),
5075 SCErrorString(SCError()));
5076 goto done;
5077 }
5078
5079 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
5080 CFRelease(rls);
5081
5082 /* initialize dns configuration */
5083 (void)dns_configuration_set(NULL, NULL, NULL, NULL, NULL);
5084 #if !TARGET_OS_IPHONE
5085 empty_dns();
5086 #endif /* !TARGET_OS_IPHONE */
5087 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_dns);
5088
5089 #if !TARGET_OS_IPHONE
5090 /* initialize SMB configuration */
5091 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_smb);
5092 #endif /* !TARGET_OS_IPHONE */
5093
5094 if_rank_dict_init();
5095
5096 done:
5097 my_CFRelease(&keys);
5098 my_CFRelease(&patterns);
5099 return;
5100 }
5101
5102 __private_extern__
5103 void
5104 prime_IPMonitor()
5105 {
5106 /* initialize multicast route */
5107 update_ipv4(NULL, NULL, NULL);
5108 return;
5109 }
5110
5111 static boolean_t
5112 S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
5113 boolean_t def)
5114 {
5115 CFBooleanRef b;
5116 boolean_t ret = def;
5117
5118 b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
5119 if (b != NULL) {
5120 ret = CFBooleanGetValue(b);
5121 }
5122 return (ret);
5123 }
5124
5125 __private_extern__
5126 void
5127 load_IPMonitor(CFBundleRef bundle, Boolean bundleVerbose)
5128 {
5129 CFDictionaryRef info_dict;
5130
5131 info_dict = CFBundleGetInfoDictionary(bundle);
5132 if (info_dict != NULL) {
5133 S_append_state
5134 = S_get_plist_boolean(info_dict,
5135 CFSTR("AppendStateArrayToSetupArray"),
5136 FALSE);
5137 }
5138 if (bundleVerbose) {
5139 S_IPMonitor_debug = kDebugFlagDefault;
5140 }
5141
5142 dns_configuration_init(bundle);
5143 proxy_configuration_init(bundle);
5144 ip_plugin_init();
5145
5146 #if !TARGET_OS_IPHONE
5147 if (S_session != NULL) {
5148 dns_configuration_monitor(S_session, IPMonitorNotify);
5149 }
5150 #endif /* !TARGET_OS_IPHONE */
5151
5152 load_hostname((S_IPMonitor_debug & kDebugFlag1) != 0);
5153 #if !TARGET_OS_IPHONE
5154 load_smb_configuration((S_IPMonitor_debug & kDebugFlag1) != 0);
5155 #endif /* !TARGET_OS_IPHONE */
5156
5157 return;
5158 }
5159
5160
5161 #pragma mark -
5162 #pragma mark Standalone test code
5163
5164
5165 #ifdef TEST_IPMONITOR
5166 #include "dns-configuration.c"
5167 #include "set-hostname.c"
5168
5169 int
5170 main(int argc, char **argv)
5171 {
5172 _sc_log = FALSE;
5173
5174 S_IPMonitor_debug = kDebugFlag1;
5175 if (argc > 1) {
5176 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
5177 }
5178
5179 load_IPMonitor(CFBundleGetMainBundle(), FALSE);
5180 prime_IPMonitor();
5181 CFRunLoopRun();
5182 /* not reached */
5183 exit(0);
5184 return 0;
5185 }
5186 #endif /* TEST_IPMONITOR */
5187
5188 #ifdef TEST_IPV4_ROUTELIST
5189 #include "dns-configuration.c"
5190 #include "set-hostname.c"
5191
5192 struct ipv4_service_contents {
5193 const char * addr;
5194 const char * mask;
5195 const char * dest;
5196 const char * router;
5197 const char * ifname;
5198 Rank rank;
5199 const CFStringRef *primaryRank;
5200 };
5201
5202 /*
5203 * addr mask dest router ifname pri primaryRank
5204 */
5205 struct ipv4_service_contents en0_10 = {
5206 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, NULL
5207 };
5208
5209 struct ipv4_service_contents en0_15 = {
5210 "10.0.0.19", "255.255.255.0", NULL, "10.0.0.1", "en0", 15, NULL
5211 };
5212
5213 struct ipv4_service_contents en0_30 = {
5214 "10.0.0.11", "255.255.255.0", NULL, "10.0.0.1", "en0", 30, NULL
5215 };
5216
5217 struct ipv4_service_contents en0_40 = {
5218 "10.0.0.12", "255.255.255.0", NULL, "10.0.0.1", "en0", 40, NULL
5219 };
5220
5221 struct ipv4_service_contents en0_50 = {
5222 "10.0.0.13", "255.255.255.0", NULL, "10.0.0.1", "en0", 50, NULL
5223 };
5224
5225 struct ipv4_service_contents en0_110 = {
5226 "192.168.2.10", "255.255.255.0", NULL, "192.168.2.1", "en0", 110, NULL
5227 };
5228
5229 struct ipv4_service_contents en0_1 = {
5230 "17.202.40.191", "255.255.252.0", NULL, "17.202.20.1", "en0", 1, NULL
5231 };
5232
5233 struct ipv4_service_contents en1_20 = {
5234 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, NULL
5235 };
5236
5237 struct ipv4_service_contents en1_2 = {
5238 "17.202.42.24", "255.255.252.0", NULL, "17.202.20.1", "en1", 2, NULL
5239 };
5240
5241 struct ipv4_service_contents en1_125 = {
5242 "192.168.2.20", "255.255.255.0", NULL, "192.168.2.1", "en1", 125, NULL
5243 };
5244
5245 struct ipv4_service_contents fw0_25 = {
5246 "192.168.2.30", "255.255.255.0", NULL, "192.168.2.1", "fw0", 25, NULL
5247 };
5248
5249 struct ipv4_service_contents fw0_21 = {
5250 "192.168.3.30", "255.255.255.0", NULL, "192.168.3.1", "fw0", 21, NULL
5251 };
5252
5253 struct ipv4_service_contents ppp0_0_1 = {
5254 "17.219.156.22", NULL, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
5255 };
5256
5257 struct ipv4_service_contents en0_test6 = {
5258 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 2, NULL
5259 };
5260 struct ipv4_service_contents en1_test6 = {
5261 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 3, NULL
5262 };
5263 struct ipv4_service_contents en2_test6 = {
5264 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL
5265 };
5266
5267 struct ipv4_service_contents en0_test7 = {
5268 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 3, NULL
5269 };
5270 struct ipv4_service_contents en1_test7 = {
5271 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 2, NULL
5272 };
5273 struct ipv4_service_contents en2_test7 = {
5274 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL
5275 };
5276 struct ipv4_service_contents fw0_test6_and_7 = {
5277 "169.254.11.33", "255.255.0.0", NULL, NULL, "fw0", 0x0ffffff, NULL
5278 };
5279
5280 struct ipv4_service_contents en0_10_last = {
5281 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
5282 };
5283
5284 struct ipv4_service_contents en0_10_never = {
5285 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
5286 };
5287
5288 struct ipv4_service_contents en1_20_first = {
5289 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
5290 };
5291
5292 struct ipv4_service_contents en1_20_never = {
5293 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
5294 };
5295
5296 struct ipv4_service_contents * test1[] = {
5297 &en0_40,
5298 &en0_15,
5299 &fw0_25,
5300 &en0_30,
5301 &en1_20,
5302 &en0_50,
5303 &en0_10,
5304 NULL
5305 };
5306
5307 struct ipv4_service_contents * test2[] = {
5308 &en0_40,
5309 &fw0_25,
5310 &en0_30,
5311 &en1_20,
5312 &en0_50,
5313 &en0_10,
5314 NULL
5315 };
5316
5317 struct ipv4_service_contents * test3[] = {
5318 &en0_40,
5319 &en1_20,
5320 &en0_50,
5321 &en0_10,
5322 &en0_110,
5323 &en1_125,
5324 &fw0_25,
5325 &fw0_21,
5326 &en0_40,
5327 &en0_30,
5328 NULL
5329 };
5330
5331 struct ipv4_service_contents * test4[] = {
5332 &en0_1,
5333 &en0_40,
5334 &en0_30,
5335 &en1_20,
5336 &en1_2,
5337 NULL
5338 };
5339
5340 struct ipv4_service_contents * test5[] = {
5341 &ppp0_0_1,
5342 &en0_1,
5343 &en0_40,
5344 &en0_30,
5345 &en1_20,
5346 &en1_2,
5347 NULL
5348 };
5349
5350 struct ipv4_service_contents * test6[] = {
5351 &en0_test6,
5352 &en1_test6,
5353 &en2_test6,
5354 &fw0_test6_and_7,
5355 NULL
5356 };
5357
5358 struct ipv4_service_contents * test7[] = {
5359 &en0_test7,
5360 &en1_test7,
5361 &en2_test7,
5362 &fw0_test6_and_7,
5363 NULL
5364 };
5365
5366 struct ipv4_service_contents * test8[] = {
5367 &en0_10,
5368 &en1_20,
5369 NULL
5370 };
5371
5372 struct ipv4_service_contents * test9[] = {
5373 &en0_10,
5374 &en1_20_first,
5375 &fw0_25,
5376 NULL
5377 };
5378
5379 struct ipv4_service_contents * test10[] = {
5380 &en0_10_last,
5381 &en1_20,
5382 &fw0_25,
5383 NULL
5384 };
5385
5386 struct ipv4_service_contents * test11[] = {
5387 &en0_10_never,
5388 &en1_20,
5389 &fw0_25,
5390 NULL
5391 };
5392
5393 struct ipv4_service_contents * test12[] = {
5394 &en0_10,
5395 &en1_20,
5396 NULL
5397 };
5398
5399 struct ipv4_service_contents * test13[] = {
5400 &en0_10,
5401 &en1_20_never,
5402 NULL
5403 };
5404
5405 struct ipv4_service_contents * test14[] = {
5406 &en1_20_never,
5407 NULL
5408 };
5409
5410 static void
5411 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
5412 const char * str)
5413 {
5414 CFStringRef prop_val;
5415
5416 if (str == NULL) {
5417 return;
5418 }
5419 prop_val = CFStringCreateWithCString(NULL,
5420 str,
5421 kCFStringEncodingASCII);
5422 CFDictionarySetValue(dict, prop_name, prop_val);
5423 CFRelease(prop_val);
5424 return;
5425 }
5426
5427 static void
5428 dict_add_string_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
5429 const char * str)
5430 {
5431 CFArrayRef array;
5432 CFStringRef prop_val;
5433
5434 if (str == NULL) {
5435 return;
5436 }
5437 prop_val = CFStringCreateWithCString(NULL,
5438 str,
5439 kCFStringEncodingASCII);
5440 array = CFArrayCreate(NULL,
5441 (const void **)&prop_val, 1,
5442 &kCFTypeArrayCallBacks);
5443 CFRelease(prop_val);
5444 CFDictionarySetValue(dict, prop_name, array);
5445 CFRelease(array);
5446 return;
5447 }
5448
5449 static CFDictionaryRef
5450 make_IPv4_dict(struct ipv4_service_contents * t)
5451 {
5452 CFMutableDictionaryRef dict;
5453
5454 dict = CFDictionaryCreateMutable(NULL, 0,
5455 &kCFTypeDictionaryKeyCallBacks,
5456 &kCFTypeDictionaryValueCallBacks);
5457 dict_add_string_as_array(dict, kSCPropNetIPv4Addresses, t->addr);
5458 dict_add_string_as_array(dict, kSCPropNetIPv4SubnetMasks, t->mask);
5459 dict_add_string_as_array(dict, kSCPropNetIPv4DestAddresses, t->dest);
5460 dict_add_string(dict, kSCPropNetIPv4Router, t->router);
5461 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
5462 return (dict);
5463 }
5464
5465 static IPv4RouteListRef
5466 make_IPv4RouteList(struct ipv4_service_contents * * this_test)
5467 {
5468 IPv4RouteListRef r;
5469 IPv4RouteListRef routes;
5470 char routes_buf[IPv4RouteListComputeSize(R_STATIC)];
5471 IPv4RouteListRef ret = NULL;
5472 struct ipv4_service_contents * * scan_test;
5473
5474 for (scan_test = this_test; *scan_test != NULL; scan_test++) {
5475 CFDictionaryRef dict;
5476
5477 dict = make_IPv4_dict(*scan_test);
5478 if (dict == NULL) {
5479 fprintf(stderr, "make_IPv4_dict failed\n");
5480 exit(1);
5481 }
5482 routes = (IPv4RouteListRef)routes_buf;
5483 routes->size = R_STATIC;
5484 routes->count = 0;
5485 r = IPv4RouteListCreateWithDictionary(routes, dict,
5486 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
5487 if (r == NULL) {
5488 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
5489 exit(1);
5490 }
5491
5492 (*scan_test)->rank = RankMake((*scan_test)->rank, kRankAssertionDefault);
5493
5494 if ((*scan_test)->primaryRank != NULL) {
5495 (*scan_test)->rank = RankMake((*scan_test)->rank,
5496 PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank));
5497 }
5498
5499 if ((*scan_test)->router == NULL) {
5500 (*scan_test)->rank = RankMake((*scan_test)->rank,
5501 PrimaryRankGetRankAssertion(kSCValNetServicePrimaryRankLast));
5502 }
5503
5504 ret = IPv4RouteListAddRouteList(ret, 1, r, (*scan_test)->rank);
5505 if (r != routes) {
5506 free(r);
5507 }
5508 CFRelease(dict);
5509 }
5510 return (ret);
5511 }
5512
5513 /*
5514 * Function: run_test
5515 * Purpose:
5516 * Runs through the given set of routes first in the forward direction,
5517 * then again backwards. We should end up with exactly the same set of
5518 * routes at the end.
5519 */
5520 static boolean_t
5521 run_test(const char * name, struct ipv4_service_contents * * this_test)
5522 {
5523 CFStringRef descr;
5524 boolean_t ret = FALSE;
5525 IPv4RouteListRef r;
5526 IPv4RouteListRef routes;
5527 char routes_buf[IPv4RouteListComputeSize(R_STATIC)];
5528 IPv4RouteListRef routes1 = NULL, routes2 = NULL;
5529 struct ipv4_service_contents * * scan_test;
5530
5531 printf("\nStarting test %s\n", name);
5532 for (scan_test = this_test; *scan_test != NULL; scan_test++) {
5533 CFDictionaryRef dict;
5534
5535 dict = make_IPv4_dict(*scan_test);
5536 if (dict == NULL) {
5537 fprintf(stderr, "make_IPv4_dict failed\n");
5538 exit(1);
5539 }
5540 routes = (IPv4RouteListRef)routes_buf;
5541 routes->size = R_STATIC;
5542 routes->count = 0;
5543 r = IPv4RouteListCreateWithDictionary(routes, dict,
5544 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
5545 if (r == NULL) {
5546 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
5547 exit(1);
5548 }
5549 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
5550 descr = IPv4RouteListCopyDescription(r);
5551 SCLog(TRUE, LOG_NOTICE, CFSTR("test: Adding %@"), descr);
5552 CFRelease(descr);
5553 }
5554
5555 (*scan_test)->rank = RankMake((*scan_test)->rank, kRankAssertionDefault);
5556
5557 if ((*scan_test)->primaryRank != NULL) {
5558 (*scan_test)->rank = RankMake((*scan_test)->rank,
5559 PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank));
5560 }
5561
5562 routes1 = IPv4RouteListAddRouteList(routes1, 1, r, (*scan_test)->rank);
5563 if (r != routes) {
5564 free(r);
5565 }
5566 CFRelease(dict);
5567 }
5568 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
5569 if (routes1 != NULL) {
5570 descr = IPv4RouteListCopyDescription(routes1);
5571 SCLog(TRUE, LOG_NOTICE, CFSTR("Routes are %@"), descr);
5572 CFRelease(descr);
5573 }
5574 }
5575 for (scan_test--; scan_test >= this_test; scan_test--) {
5576 CFDictionaryRef dict;
5577
5578 dict = make_IPv4_dict(*scan_test);
5579 if (dict == NULL) {
5580 fprintf(stderr, "make_IPv4_dict failed\n");
5581 exit(1);
5582 }
5583 routes = (IPv4RouteListRef)routes_buf;
5584 routes->size = R_STATIC;
5585 routes->count = 0;
5586 r = IPv4RouteListCreateWithDictionary(routes, dict,
5587 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
5588 if (r == NULL) {
5589 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
5590 exit(1);
5591 }
5592 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
5593 descr = IPv4RouteListCopyDescription(r);
5594 SCLog(TRUE, LOG_NOTICE, CFSTR("test: Adding %@"), descr);
5595 CFRelease(descr);
5596 }
5597 if ((*scan_test)->primaryRank != NULL) {
5598 (*scan_test)->rank = RankMake((*scan_test)->rank,
5599 PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank));
5600 }
5601
5602 routes2 = IPv4RouteListAddRouteList(routes2, 1, r, (*scan_test)->rank);
5603 if (r != routes) {
5604 free(r);
5605 }
5606 CFRelease(dict);
5607 }
5608 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
5609 if (routes2 != NULL) {
5610 descr = IPv4RouteListCopyDescription(routes2);
5611 SCLog(TRUE, LOG_NOTICE, CFSTR("Routes are %@"), descr);
5612 CFRelease(descr);
5613 }
5614 }
5615 if ((routes1 != NULL && routes2 == NULL)
5616 || (routes1 == NULL && routes2 != NULL)) {
5617 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
5618 (routes1 != NULL) ? "not " : "",
5619 (routes2 != NULL) ? "not " : "");
5620 }
5621 else if (routes1 != NULL && routes2 != NULL) {
5622 /* check if they are different */
5623 if (routes1->count != routes2->count) {
5624 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
5625 routes1->count, routes2->count);
5626 }
5627 else if (bcmp(routes1, routes2,
5628 IPv4RouteListComputeSize(routes1->count)) != 0) {
5629 fprintf(stderr, "routes1 and routes2 are different\n");
5630 }
5631 else {
5632 printf("routes1 and routes2 are the same\n");
5633 ret = TRUE;
5634 }
5635 }
5636 if (routes1 != NULL) {
5637 free(routes1);
5638 }
5639 if (routes2 != NULL) {
5640 free(routes2);
5641 }
5642 return (ret);
5643 }
5644
5645 typedef struct compare_context {
5646 IPv4RouteListRef old;
5647 IPv4RouteListRef new;
5648 } compare_context_t;
5649
5650 static void
5651 compare_callback(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg)
5652 {
5653 compare_context_t * context = (compare_context_t *)arg;
5654
5655 switch (cmd) {
5656 case kIPv4RouteListAddRouteCommand:
5657 printf("Add new[%ld] = ", route - context->new->list);
5658 IPv4RoutePrint(route);
5659 break;
5660 case kIPv4RouteListRemoveRouteCommand:
5661 printf("Remove old[%ld] = ", route - context->old->list);
5662 IPv4RoutePrint(route);
5663 break;
5664 default:
5665 break;
5666 }
5667 return;
5668 }
5669
5670 static void
5671 compare_tests(struct ipv4_service_contents * * old_test,
5672 struct ipv4_service_contents * * new_test)
5673 {
5674 IPv4RouteListRef new_routes;
5675 IPv4RouteListRef old_routes;
5676 compare_context_t context;
5677
5678 old_routes = make_IPv4RouteList(old_test);
5679 new_routes = make_IPv4RouteList(new_test);
5680
5681 if (old_routes == NULL) {
5682 printf("No Old Routes\n");
5683 }
5684 else {
5685 printf("Old Routes = ");
5686 IPv4RouteListPrint(old_routes);
5687 }
5688 if (new_routes == NULL) {
5689 printf("No New Routes\n");
5690 }
5691 else {
5692 printf("New Routes = ");
5693 IPv4RouteListPrint(new_routes);
5694 }
5695 context.old = old_routes;
5696 context.new = new_routes;
5697 IPv4RouteListApply(old_routes, new_routes, compare_callback, &context);
5698 if (old_routes != NULL) {
5699 free(old_routes);
5700 }
5701 if (new_routes != NULL) {
5702 free(new_routes);
5703 }
5704
5705 return;
5706 }
5707
5708 int
5709 main(int argc, char **argv)
5710 {
5711 _sc_log = FALSE;
5712 _sc_verbose = (argc > 1) ? TRUE : FALSE;
5713
5714 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
5715 if (argc > 1) {
5716 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
5717 }
5718
5719 if (run_test("test1", test1) == FALSE) {
5720 fprintf(stderr, "test1 failed\n");
5721 exit(1);
5722 }
5723 if (run_test("test2", test2) == FALSE) {
5724 fprintf(stderr, "test2 failed\n");
5725 exit(1);
5726 }
5727 if (run_test("test3", test4) == FALSE) {
5728 fprintf(stderr, "test3 failed\n");
5729 exit(1);
5730 }
5731 if (run_test("test4", test4) == FALSE) {
5732 fprintf(stderr, "test4 failed\n");
5733 exit(1);
5734 }
5735 if (run_test("test5", test5) == FALSE) {
5736 fprintf(stderr, "test5 failed\n");
5737 exit(1);
5738 }
5739
5740 printf("\nCompare 1 to 2:\n");
5741 compare_tests(test1, test2);
5742
5743 printf("\nCompare 2 to 1:\n");
5744 compare_tests(test2, test1);
5745
5746 printf("\nCompare 1 to 1:\n");
5747 compare_tests(test1, test1);
5748
5749 printf("\nCompare 1 to 3:\n");
5750 compare_tests(test1, test3);
5751
5752 printf("\nCompare 3 to 1:\n");
5753 compare_tests(test3, test1);
5754
5755 printf("\nCompare 2 to 3:\n");
5756 compare_tests(test2, test3);
5757
5758 printf("\nCompare 3 to 2:\n");
5759 compare_tests(test3, test2);
5760
5761 printf("\nCompare 3 to 4:\n");
5762 compare_tests(test3, test4);
5763
5764 printf("\nCompare 5 to 4:\n");
5765 compare_tests(test5, test4);
5766
5767 printf("\nCompare 6 to 7:\n");
5768 compare_tests(test6, test7);
5769
5770 printf("\nCompare 7 to 6:\n");
5771 compare_tests(test7, test6);
5772
5773 printf("\nCompare 8 to 9:\n");
5774 compare_tests(test8, test9);
5775
5776 printf("\nCompare 8 to 10:\n");
5777 compare_tests(test8, test10);
5778
5779 printf("\nCompare 8 to 11:\n");
5780 compare_tests(test8, test11);
5781
5782 printf("\nCompare 12 to 13:\n");
5783 compare_tests(test12, test13);
5784
5785 printf("\nCompare 13 to 14:\n");
5786 compare_tests(test13, test14);
5787
5788 printf("\nChecking for leaks\n");
5789 char cmd[128];
5790 sprintf(cmd, "leaks %d 2>&1", getpid());
5791 fflush(stdout);
5792 (void)system(cmd);
5793
5794 exit(0);
5795 return (0);
5796 }
5797
5798 #endif /* TEST_IPV4_ROUTELIST */
5799