]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/ip_plugin.c
configd-293.6.tar.gz
[apple/configd.git] / Plugins / IPMonitor / ip_plugin.c
1 /*
2 * Copyright (c) 2000-2009 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/types.h>
77 #include <sys/socket.h>
78 #include <net/route.h>
79 #include <net/if.h>
80 #include <net/if_dl.h>
81 #include <netinet/in.h>
82 #include <arpa/inet.h>
83 #include <sys/sysctl.h>
84 #include <limits.h>
85 #include <notify.h>
86
87 #include <SystemConfiguration/SystemConfiguration.h>
88 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
89 #include <SystemConfiguration/SCValidation.h>
90 #include <SystemConfiguration/SCPrivate.h> /* for SCLog() */
91 #include <dnsinfo.h>
92 #if !TARGET_OS_IPHONE
93 #include <dnsinfo_create.h>
94 #endif /* !TARGET_OS_IPHONE */
95
96 #include <dns_sd.h>
97 #ifndef kDNSServiceCompMulticastDNS
98 #define kDNSServiceCompMulticastDNS "MulticastDNS"
99 #endif
100 #ifndef kDNSServiceCompPrivateDNS
101 #define kDNSServiceCompPrivateDNS "PrivateDNS"
102 #endif
103
104 enum {
105 kProtocolFlagsNone = 0x0,
106 kProtocolFlagsIPv4 = 0x1,
107 kProtocolFlagsIPv6 = 0x2
108 };
109 typedef uint8_t ProtocolFlags;
110
111 enum {
112 kDebugFlag1 = 0x00000001,
113 kDebugFlag2 = 0x00000002,
114 kDebugFlag4 = 0x00000004,
115 kDebugFlag8 = 0x00000008,
116 kDebugFlagDefault = kDebugFlag1,
117 kDebugFlagAll = 0xffffffff
118 };
119
120 #ifdef TEST_IPV4_ROUTELIST
121 #define ROUTELIST_DEBUG(a, f) { if ((S_IPMonitor_debug & (f)) != 0) printf a ;}
122 #else
123 #define ROUTELIST_DEBUG(a, f)
124 #endif
125
126 #include "set-hostname.h"
127 #include "dns-configuration.h"
128 #if !TARGET_OS_IPHONE
129 #include "smb-configuration.h"
130 #endif /* !TARGET_OS_IPHONE */
131
132 #define PPP_PREFIX "ppp"
133
134 #define IP_FORMAT "%d.%d.%d.%d"
135 #define IP_CH(ip) ((u_char *)(ip))
136 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
137
138 typedef uint32_t Rank;
139
140 enum {
141 kRankFirst = 0,
142 kRankLast = UINT_MAX
143 };
144
145 /*
146 * IPv4 Route management
147 */
148
149 typedef uint32_t RouteFlags;
150
151 enum {
152 kRouteIsDirectToInterfaceFlag = 0x00000001,
153 kRouteIsNotSubnetLocalFlag = 0x00000002,
154 kRouteChooseFirstFlag = 0x00000004,
155 kRouteChooseLastFlag = 0x00000008,
156 kRouteChooseNeverFlag = 0x00000010,
157 kRouteIsScopedFlag = 0x00000020,
158 kRouteWantScopedFlag = (kRouteChooseNeverFlag|kRouteIsScopedFlag),
159 };
160
161 typedef struct {
162 struct in_addr dest;
163 struct in_addr mask;
164 struct in_addr gateway;
165 char ifname[IFNAMSIZ];
166 unsigned int ifindex;
167 struct in_addr ifa;
168 Rank rank;
169 RouteFlags flags;
170 } IPv4Route, *IPv4RouteRef;
171
172 typedef struct {
173 int count;
174 int size;
175 IPv4Route list[1]; /* variable length */
176 } IPv4RouteList, *IPv4RouteListRef;
177
178 enum {
179 kIPv4RouteListAddRouteCommand,
180 kIPv4RouteListRemoveRouteCommand
181 };
182
183 typedef uint32_t IPv4RouteListApplyCommand;
184
185 typedef void IPv4RouteListApplyCallBackFunc(IPv4RouteListApplyCommand cmd,
186 IPv4RouteRef route, void * arg);
187 typedef IPv4RouteListApplyCallBackFunc * IPv4RouteListApplyCallBackFuncPtr;
188
189 /* SCDynamicStore session */
190 static SCDynamicStoreRef S_session = NULL;
191
192 /* debug output flags */
193 static uint32_t S_IPMonitor_debug = 0;
194
195 /* are we netbooted? If so, don't touch the default route */
196 static boolean_t S_netboot = FALSE;
197
198 /* is scoped routing enabled? */
199 #ifdef RTF_IFSCOPE
200 static boolean_t S_scopedroute = FALSE;
201 #endif /* RTF_IFSCOPE */
202
203 /* dictionary to hold per-service state: key is the serviceID */
204 static CFMutableDictionaryRef S_service_state_dict = NULL;
205 static CFMutableDictionaryRef S_ipv4_service_rank_dict = NULL;
206 static CFMutableDictionaryRef S_ipv6_service_rank_dict = NULL;
207
208 /* if set, a PPP interface overrides the primary */
209 static boolean_t S_ppp_override_primary = FALSE;
210
211 /* the current primary serviceID's */
212 static CFStringRef S_primary_ipv4 = NULL;
213 static CFStringRef S_primary_ipv6 = NULL;
214 static CFStringRef S_primary_dns = NULL;
215 static CFStringRef S_primary_proxies = NULL;
216
217 static CFStringRef S_state_global_ipv4 = NULL;
218 static CFStringRef S_state_global_ipv6 = NULL;
219 static CFStringRef S_state_global_dns = NULL;
220 static CFStringRef S_state_global_proxies = NULL;
221 static CFStringRef S_state_service_prefix = NULL;
222 static CFStringRef S_setup_global_ipv4 = NULL;
223 static CFStringRef S_setup_global_proxies = NULL;
224 static CFStringRef S_setup_service_prefix = NULL;
225
226 static CFStringRef S_multicast_resolvers = NULL;
227 static CFStringRef S_private_resolvers = NULL;
228
229 static IPv4RouteListRef S_ipv4_routelist = NULL;
230
231 static const struct in_addr S_ip_zeros = { 0 };
232 static const struct in6_addr S_ip6_zeros = IN6ADDR_ANY_INIT;
233
234 static boolean_t S_append_state = FALSE;
235
236 #if !TARGET_OS_IPHONE
237 static CFStringRef S_primary_smb = NULL;
238 static CFStringRef S_state_global_smb = NULL;
239 static CFStringRef S_setup_global_smb = NULL;
240 #endif /* !TARGET_OS_IPHONE */
241
242 #if !TARGET_OS_IPHONE
243 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
244 #endif /* !TARGET_OS_IPHONE */
245
246 #ifndef KERN_NETBOOT
247 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
248 #endif KERN_NETBOOT
249
250 /**
251 ** entityType*, GetEntityChanges*
252 ** - definitions for the entity types we handle
253 **/
254 enum {
255 kEntityTypeIPv4 = 0,
256 kEntityTypeIPv6,
257 kEntityTypeDNS,
258 kEntityTypeProxies,
259 #if !TARGET_OS_IPHONE
260 kEntityTypeSMB,
261 #endif /* !TARGET_OS_IPHONE */
262 ENTITY_TYPES_COUNT,
263 kEntityTypeServiceOptions = 31
264 };
265 typedef uint32_t EntityType;
266
267 static const CFStringRef *entityTypeNames[ENTITY_TYPES_COUNT] = {
268 &kSCEntNetIPv4, /* 0 */
269 &kSCEntNetIPv6, /* 1 */
270 &kSCEntNetDNS, /* 2 */
271 &kSCEntNetProxies, /* 3 */
272 #if !TARGET_OS_IPHONE
273 &kSCEntNetSMB, /* 4 */
274 #endif /* !TARGET_OS_IPHONE */
275 };
276
277 typedef boolean_t GetEntityChangesFunc(CFStringRef serviceID,
278 CFDictionaryRef state_dict,
279 CFDictionaryRef setup_dict,
280 CFDictionaryRef info);
281 typedef GetEntityChangesFunc * GetEntityChangesFuncRef;
282
283 static GetEntityChangesFunc get_ipv4_changes;
284 static GetEntityChangesFunc get_ipv6_changes;
285 static GetEntityChangesFunc get_dns_changes;
286 static GetEntityChangesFunc get_proxies_changes;
287 #if !TARGET_OS_IPHONE
288 static GetEntityChangesFunc get_smb_changes;
289 #endif /* !TARGET_OS_IPHONE */
290
291 static void
292 my_CFRelease(void * t);
293
294 static void
295 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new);
296
297 static void
298 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key);
299
300 static const GetEntityChangesFuncRef entityChangeFunc[ENTITY_TYPES_COUNT] = {
301 get_ipv4_changes, /* 0 */
302 get_ipv6_changes, /* 1 */
303 get_dns_changes, /* 2 */
304 get_proxies_changes,/* 3 */
305 #if !TARGET_OS_IPHONE
306 get_smb_changes, /* 4 */
307 #endif /* !TARGET_OS_IPHONE */
308 };
309
310 /**
311 ** keyChangeList
312 ** - mechanism to do an atomic update of the SCDynamicStore
313 ** when the content needs to be changed across multiple functions
314 **/
315 typedef struct {
316 CFMutableArrayRef notify;
317 CFMutableArrayRef remove;
318 CFMutableDictionaryRef set;
319 } keyChangeList, * keyChangeListRef;
320
321 static void
322 keyChangeListInit(keyChangeListRef keys)
323 {
324 keys->notify = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
325 keys->remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
326 keys->set = CFDictionaryCreateMutable(NULL, 0,
327 &kCFTypeDictionaryKeyCallBacks,
328 &kCFTypeDictionaryValueCallBacks);
329 return;
330 }
331
332 static void
333 keyChangeListFree(keyChangeListRef keys)
334 {
335 my_CFRelease(&keys->notify);
336 my_CFRelease(&keys->remove);
337 my_CFRelease(&keys->set);
338 return;
339 }
340
341 static void
342 keyChangeListNotifyKey(keyChangeListRef keys, CFStringRef key)
343 {
344 my_CFArrayAppendUniqueValue(keys->notify, key);
345 return;
346 }
347
348 static void
349 keyChangeListRemoveValue(keyChangeListRef keys, CFStringRef key)
350 {
351 my_CFArrayAppendUniqueValue(keys->remove, key);
352 CFDictionaryRemoveValue(keys->set, key);
353 return;
354 }
355
356 static void
357 keyChangeListSetValue(keyChangeListRef keys, CFStringRef key, CFTypeRef value)
358 {
359 my_CFArrayRemoveValue(keys->remove, key);
360 CFDictionarySetValue(keys->set, key, value);
361 return;
362 }
363
364 static void
365 keyChangeListApplyToStore(keyChangeListRef keys, SCDynamicStoreRef session)
366 {
367 CFArrayRef notify = keys->notify;
368 CFArrayRef remove = keys->remove;
369 CFDictionaryRef set = keys->set;
370 int status;
371
372 if (CFArrayGetCount(notify) == 0) {
373 notify = NULL;
374 }
375 if (CFArrayGetCount(remove) == 0) {
376 remove = NULL;
377 }
378 if (CFDictionaryGetCount(set) == 0) {
379 set = NULL;
380 }
381 if (set == NULL && remove == NULL && notify == NULL) {
382 return;
383 }
384 if (S_IPMonitor_debug & kDebugFlag1) {
385 if (set != NULL) {
386 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: Setting:\n%@"),
387 set);
388 }
389 if (remove != NULL) {
390 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: Removing:\n%@"),
391 remove);
392 }
393 if (notify != NULL) {
394 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: Notifying:\n%@"),
395 notify);
396 }
397 }
398 (void)SCDynamicStoreSetMultiple(session, set, remove, notify);
399
400 status = notify_post("com.apple.system.config.network_change");
401 if (status == NOTIFY_STATUS_OK) {
402 SCLog(TRUE, LOG_NOTICE, CFSTR("network configuration changed."));
403 } else {
404 SCLog(TRUE, LOG_NOTICE,
405 CFSTR("IPMonitor: notify_post() failed: error=%ld"), status);
406 }
407
408 return;
409 }
410
411 static boolean_t
412 S_is_network_boot()
413 {
414 int mib[2];
415 size_t len;
416 int netboot = 0;
417
418 mib[0] = CTL_KERN;
419 mib[1] = KERN_NETBOOT;
420 len = sizeof(netboot);
421 sysctl(mib, 2, &netboot, &len, NULL, 0);
422 return (netboot);
423 }
424
425 #ifdef RTF_IFSCOPE
426 static boolean_t
427 S_is_scoped_routing_enabled()
428 {
429 int scopedroute = 0;
430 size_t len = sizeof(scopedroute);
431
432 if ((sysctlbyname("net.inet.ip.scopedroute",
433 &scopedroute, &len,
434 NULL, 0) == -1)
435 && (errno != ENOENT)) {
436 SCLog(TRUE, LOG_ERR, CFSTR("sysctlbyname() failed: %s"), strerror(errno));
437 }
438 return (scopedroute);
439 }
440 #endif /* RTF_IFSCOPE */
441
442 static void
443 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new)
444 {
445 CFIndex n = CFArrayGetCount(arr);
446
447 if (CFArrayContainsValue(arr, CFRangeMake(0, n), new)) {
448 return;
449 }
450 CFArrayAppendValue(arr, new);
451 return;
452 }
453
454 static void
455 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key)
456 {
457 CFIndex i;
458
459 i = CFArrayGetFirstIndexOfValue(arr,
460 CFRangeMake(0, CFArrayGetCount(arr)),
461 key);
462 if (i != kCFNotFound) {
463 CFArrayRemoveValueAtIndex(arr, i);
464 }
465 return;
466 }
467
468 static void
469 my_CFRelease(void * t)
470 {
471 void * * obj = (void * *)t;
472
473 if (obj && *obj) {
474 CFRelease(*obj);
475 *obj = NULL;
476 }
477 return;
478 }
479
480 static CFDictionaryRef
481 my_CFDictionaryGetDictionary(CFDictionaryRef dict, CFStringRef key)
482 {
483 if (isA_CFDictionary(dict) == NULL) {
484 return (NULL);
485 }
486 return (isA_CFDictionary(CFDictionaryGetValue(dict, key)));
487 }
488
489 static CFArrayRef
490 my_CFDictionaryGetArray(CFDictionaryRef dict, CFStringRef key)
491 {
492 if (isA_CFDictionary(dict) == NULL) {
493 return (NULL);
494 }
495 return (isA_CFArray(CFDictionaryGetValue(dict, key)));
496 }
497
498 static boolean_t
499 cfstring_to_ipvx(int family, CFStringRef str, void * addr, int addr_size)
500 {
501 char buf[128];
502
503 if (isA_CFString(str) == NULL) {
504 goto done;
505 }
506
507 switch (family) {
508 case AF_INET:
509 if (addr_size < sizeof(struct in_addr)) {
510 goto done;
511 }
512 break;
513 case AF_INET6:
514 if (addr_size < sizeof(struct in6_addr)) {
515 goto done;
516 }
517 break;
518 default:
519 goto done;
520 }
521 (void)_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII);
522 if (inet_pton(family, buf, addr) == 1) {
523 return (TRUE);
524 }
525 done:
526 bzero(addr, addr_size);
527 return (FALSE);
528 }
529
530 static boolean_t
531 cfstring_to_ip(CFStringRef str, struct in_addr * ip_p)
532 {
533 return (cfstring_to_ipvx(AF_INET, str, ip_p, sizeof(*ip_p)));
534 }
535
536 static boolean_t
537 cfstring_to_ip6(CFStringRef str, struct in6_addr * ip6_p)
538 {
539 return (cfstring_to_ipvx(AF_INET6, str, ip6_p, sizeof(*ip6_p)));
540 }
541
542 static CFStringRef
543 setup_service_key(CFStringRef serviceID, CFStringRef entity)
544 {
545 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
546 kSCDynamicStoreDomainSetup,
547 serviceID,
548 entity));
549 }
550
551 static CFStringRef
552 state_service_key(CFStringRef serviceID, CFStringRef entity)
553 {
554 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
555 kSCDynamicStoreDomainState,
556 serviceID,
557 entity));
558 }
559
560 static CFDictionaryRef
561 get_service_setup_entity(CFDictionaryRef service_info, CFStringRef serviceID,
562 CFStringRef entity)
563 {
564 CFStringRef setup_key;
565 CFDictionaryRef setup_dict;
566
567 setup_key = setup_service_key(serviceID, entity);
568 setup_dict = my_CFDictionaryGetDictionary(service_info, setup_key);
569 my_CFRelease(&setup_key);
570 return (setup_dict);
571 }
572
573 static CFDictionaryRef
574 get_service_state_entity(CFDictionaryRef service_info, CFStringRef serviceID,
575 CFStringRef entity)
576 {
577 CFStringRef state_key;
578 CFDictionaryRef state_dict;
579
580 state_key = state_service_key(serviceID, entity);
581 state_dict = my_CFDictionaryGetDictionary(service_info, state_key);
582 my_CFRelease(&state_key);
583 return (state_dict);
584 }
585
586 static boolean_t
587 dict_get_first_ip(CFDictionaryRef dict, CFStringRef prop, struct in_addr * ip_p)
588 {
589 CFArrayRef ip_list;
590
591 ip_list = CFDictionaryGetValue(dict, prop);
592 if (isA_CFArray(ip_list) != NULL
593 && CFArrayGetCount(ip_list) > 0
594 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list, 0), ip_p)) {
595 return (TRUE);
596 }
597 return (FALSE);
598 }
599
600 static boolean_t
601 get_override_primary(CFDictionaryRef dict)
602 {
603 CFTypeRef override;
604
605 override = CFDictionaryGetValue(dict, kSCPropNetOverridePrimary);
606 if (isA_CFNumber(override) != NULL) {
607 int val = 0;
608
609 CFNumberGetValue((CFNumberRef)override, kCFNumberIntType, &val);
610 if (val != 0) {
611 return (TRUE);
612 }
613 }
614 else if (isA_CFBoolean(override) != NULL) {
615 if (CFBooleanGetValue(override)) {
616 return (TRUE);
617 }
618 }
619 return (FALSE);
620 }
621
622 /**
623 ** IPv4Route*
624 **/
625
626 static __inline__ struct in_addr
627 subnet_addr(struct in_addr addr, struct in_addr mask)
628 {
629 struct in_addr net;
630
631 net.s_addr = addr.s_addr & mask.s_addr;
632 return (net);
633 }
634
635 static __inline__ int
636 uint32_cmp(uint32_t a, uint32_t b)
637 {
638 int ret;
639
640 if (a == b) {
641 ret = 0;
642 }
643 else if (a < b) {
644 ret = -1;
645 }
646 else {
647 ret = 1;
648 }
649 return (ret);
650 }
651
652 static __inline__ int
653 in_addr_cmp(struct in_addr a, struct in_addr b)
654 {
655 return (uint32_cmp(ntohl(a.s_addr), ntohl(b.s_addr)));
656 }
657
658 static __inline__ int
659 RankCompare(Rank a, Rank b)
660 {
661 return (uint32_cmp(a, b));
662 }
663
664 static __inline__ int
665 RouteFlagsCompare(RouteFlags a, RouteFlags b)
666 {
667 return (uint32_cmp(a, b));
668 }
669
670 static void
671 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r, CFMutableStringRef str)
672 {
673 CFStringAppendFormat(str, NULL,
674 CFSTR("Dest " IP_FORMAT
675 " Mask " IP_FORMAT
676 " Gate " IP_FORMAT
677 " Ifp %s Ifa " IP_FORMAT),
678 IP_LIST(&r->dest),
679 IP_LIST(&r->mask),
680 IP_LIST(&r->gateway),
681 (r->ifname[0] != '\0') ? r->ifname : "<none>",
682 IP_LIST(&r->ifa));
683 if ((r->flags & kRouteIsNotSubnetLocalFlag) != 0) {
684 CFStringAppend(str, CFSTR(" [non-local]"));
685 }
686 else if ((r->flags & kRouteIsDirectToInterfaceFlag) != 0) {
687 CFStringAppend(str, CFSTR(" [direct]"));
688 }
689 if ((r->flags & kRouteChooseFirstFlag) != 0) {
690 CFStringAppend(str, CFSTR(" [first]"));
691 }
692 if ((r->flags & kRouteChooseLastFlag) != 0) {
693 CFStringAppend(str, CFSTR(" [last]"));
694 }
695 if ((r->flags & kRouteChooseNeverFlag) != 0) {
696 CFStringAppend(str, CFSTR(" [never]"));
697 }
698 if ((r->flags & kRouteIsScopedFlag) != 0) {
699 CFStringAppend(str, CFSTR(" [SCOPED]"));
700 }
701 else if ((r->flags & kRouteWantScopedFlag) != 0) {
702 CFStringAppend(str, CFSTR(" [SCOPED*]"));
703 }
704 return;
705 }
706
707 static CFStringRef
708 IPv4RouteCopyDescription(IPv4RouteRef r)
709 {
710 CFMutableStringRef str;
711
712 str = CFStringCreateMutable(NULL, 0);
713 IPv4RouteCopyDescriptionWithString(r, str);
714 return (str);
715 }
716
717 static __inline__ void
718 IPv4RoutePrint(IPv4RouteRef route)
719 {
720 CFStringRef str = IPv4RouteCopyDescription(route);
721
722 SCPrint(TRUE, stdout, CFSTR("%@"), str);
723 CFRelease(str);
724 return;
725 }
726
727 static __inline__ void
728 IPv4RouteLog(IPv4RouteRef route)
729 {
730 CFStringRef str = IPv4RouteCopyDescription(route);
731
732 SCLog(TRUE, LOG_NOTICE, CFSTR("%@"), str);
733 CFRelease(str);
734 return;
735 }
736
737 static int
738 IPv4RouteCompare(IPv4RouteRef a, Rank a_rank,
739 IPv4RouteRef b, Rank b_rank, boolean_t * same_dest)
740 {
741 int cmp;
742
743 *same_dest = FALSE;
744 cmp = in_addr_cmp(a->dest, b->dest);
745 if (cmp == 0) {
746 cmp = in_addr_cmp(a->mask, b->mask);
747 if (cmp == 0) {
748 int name_cmp = strcmp(a->ifname, b->ifname);
749
750 if (name_cmp == 0) {
751 cmp = 0;
752 }
753 else {
754 boolean_t a_never = (a->flags & kRouteChooseNeverFlag) != 0;
755 boolean_t b_never = (b->flags & kRouteChooseNeverFlag) != 0;
756 *same_dest = TRUE;
757
758 if (a_never != b_never) {
759 if (a_never) {
760 cmp = 1;
761 }
762 else {
763 cmp = -1;
764 }
765 }
766 else {
767 boolean_t a_last = (a->flags & kRouteChooseLastFlag) != 0;
768 boolean_t b_last = (b->flags & kRouteChooseLastFlag) != 0;
769
770 if (a_last != b_last) {
771 if (a_last) {
772 cmp = 1;
773 }
774 else {
775 cmp = -1;
776 }
777 }
778 else {
779 boolean_t a_first = (a->flags & kRouteChooseFirstFlag) != 0;
780 boolean_t b_first = (b->flags & kRouteChooseFirstFlag) != 0;
781
782 if (a_first != b_first) {
783 if (a_first) {
784 cmp = -1;
785 }
786 else {
787 cmp = 1;
788 }
789 }
790 else {
791 cmp = RankCompare(a_rank, b_rank);
792 if (cmp == 0) {
793 cmp = name_cmp;
794 }
795 }
796 }
797 }
798 }
799 }
800 }
801 if ((S_IPMonitor_debug & kDebugFlag8) != 0) {
802 CFStringRef a_str;
803 CFStringRef b_str;
804 char ch;
805
806 if (cmp < 0) {
807 ch = '<';
808 }
809 else if (cmp == 0) {
810 ch = '=';
811 }
812 else {
813 ch = '>';
814 }
815 a_str = IPv4RouteCopyDescription(a);
816 b_str = IPv4RouteCopyDescription(b);
817 SCLog(TRUE, LOG_NOTICE, CFSTR("%@ rank %u %c %@ rank %u"),
818 a_str, a_rank, ch, b_str, b_rank);
819 CFRelease(a_str);
820 CFRelease(b_str);
821 }
822 return (cmp);
823 }
824
825 static CFStringRef
826 IPv4RouteListCopyDescription(IPv4RouteListRef routes)
827 {
828 int i;
829 IPv4RouteRef r;
830 CFMutableStringRef str;
831
832 str = CFStringCreateMutable(NULL, 0);
833 CFStringAppendFormat(str, NULL, CFSTR("<IPv4RouteList[%d]> = {"),
834 routes->count);
835 for (i = 0, r = routes->list; i < routes->count; i++, r++) {
836 CFStringAppendFormat(str, NULL, CFSTR("\n%2d. "), i);
837 IPv4RouteCopyDescriptionWithString(r, str);
838 }
839 CFStringAppend(str, CFSTR("\n}\n"));
840 return (str);
841 }
842
843 static __inline__ void
844 IPv4RouteListPrint(IPv4RouteListRef routes)
845 {
846 CFStringRef str = IPv4RouteListCopyDescription(routes);
847
848 SCPrint(TRUE, stdout, CFSTR("%@"), str);
849 CFRelease(str);
850 return;
851 }
852
853 static __inline__ void
854 IPv4RouteListLog(IPv4RouteListRef routes)
855 {
856 CFStringRef str = IPv4RouteListCopyDescription(routes);
857
858 SCLog(TRUE, LOG_NOTICE, CFSTR("%@"), str);
859 CFRelease(str);
860 return;
861 }
862
863 static __inline__ unsigned int
864 IPv4RouteListComputeSize(unsigned int n)
865 {
866 return (offsetof(IPv4RouteList, list[n]));
867 }
868
869 static IPv4RouteRef
870 IPv4RouteListFindRoute(IPv4RouteListRef routes, IPv4RouteRef route)
871 {
872 int i;
873 IPv4RouteRef scan_result = NULL;
874 IPv4RouteRef scan;
875
876 for (i = 0, scan = routes->list; i < routes->count; i++, scan++) {
877 if ((scan->dest.s_addr == route->dest.s_addr)
878 && (scan->mask.s_addr == route->mask.s_addr)
879 && (strcmp(scan->ifname, route->ifname) == 0)
880 && (scan->ifa.s_addr == route->ifa.s_addr)
881 && (scan->gateway.s_addr == route->gateway.s_addr)) {
882 /*
883 * So far, the routes look the same. If the flags
884 * are also equiv than we've found a match.
885 */
886 RouteFlags r_flags;
887 RouteFlags s_flags;
888
889 s_flags = scan->flags;
890 if ((s_flags & kRouteWantScopedFlag) != 0) {
891 s_flags |= kRouteWantScopedFlag;
892 }
893 r_flags = route->flags;
894 if ((r_flags & kRouteWantScopedFlag) != 0) {
895 r_flags |= kRouteWantScopedFlag;
896 }
897 if (s_flags == r_flags) {
898 scan_result = scan;
899 break;
900 }
901 }
902 }
903 return (scan_result);
904 }
905
906 static void
907 IPv4RouteListApply(IPv4RouteListRef old_routes, IPv4RouteListRef new_routes,
908 IPv4RouteListApplyCallBackFuncPtr func, void * arg)
909 {
910 int i;
911 IPv4RouteRef scan;
912
913 if (old_routes == new_routes && old_routes == NULL) {
914 /* both old and new are NULL, so there's nothing to do */
915 return;
916 }
917 if (old_routes != NULL) {
918 for (i = 0, scan = old_routes->list;
919 i < old_routes->count;
920 i++, scan++) {
921 IPv4RouteRef new_route = NULL;
922
923 if (new_routes != NULL) {
924 new_route = IPv4RouteListFindRoute(new_routes, scan);
925 }
926 if (new_route == NULL) {
927 if (func != NULL) {
928 (*func)(kIPv4RouteListRemoveRouteCommand, scan, arg);
929 }
930 }
931 }
932 }
933 if (new_routes != NULL) {
934 for (i = 0, scan = new_routes->list;
935 i < new_routes->count;
936 i++, scan++) {
937 IPv4RouteRef old_route = NULL;
938
939 if (old_routes != NULL) {
940 old_route = IPv4RouteListFindRoute(old_routes, scan);
941 }
942 if (old_route == NULL) {
943 if (func != NULL) {
944 (*func)(kIPv4RouteListAddRouteCommand, scan, arg);
945 }
946 }
947 }
948 }
949 return;
950 }
951
952 /*
953 * Function: IPv4RouteListAddRoute
954 *
955 * Purpose:
956 * Add the given IPv4Route to the list of routes, eliminating lower-ranked
957 * duplicates on the same interface, and marking any lower ranked duplicates
958 * on other interfaces with kRouteIsScopedFlag.
959 *
960 * This routine assumes that if routes is not NULL, it is malloc'd memory.
961 *
962 * Returns:
963 * Route list updated with the given route, possibly a different pointer,
964 * due to using realloc'd memory.
965 */
966
967 enum {
968 kScopeNone = 0,
969 kScopeThis = 1,
970 kScopeNext = 2
971 };
972
973 static IPv4RouteListRef
974 IPv4RouteListAddRoute(IPv4RouteListRef routes, int init_size,
975 IPv4RouteRef this_route, Rank this_rank)
976 {
977 int i;
978 int scope_which = kScopeNone;
979 IPv4RouteRef scan;
980 int where = -1;
981
982 if (routes == NULL) {
983 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(init_size));
984 routes->size = init_size;
985 routes->count = 0;
986 }
987 for (i = 0, scan = routes->list; i < routes->count;
988 i++, scan++) {
989 int cmp;
990 boolean_t same_dest;
991
992 cmp = IPv4RouteCompare(this_route, this_rank, scan, scan->rank, &same_dest);
993 if (cmp < 0) {
994 if (where == -1) {
995 if (same_dest == TRUE) {
996 if ((scan->flags & kRouteIsScopedFlag) != 0) {
997 ROUTELIST_DEBUG(("Hit 1: set scope on self\n"),
998 kDebugFlag8);
999 scope_which = kScopeThis;
1000 }
1001 else {
1002 ROUTELIST_DEBUG(("Hit 2: set scope on next\n"),
1003 kDebugFlag8);
1004 scope_which = kScopeNext;
1005 }
1006 }
1007 /* remember our insertion point, but keep going to find a dup */
1008 where = i;
1009 }
1010 }
1011 else if (cmp == 0) {
1012 /* exact match */
1013 if (where != -1) {
1014 /* this route is a duplicate */
1015 ROUTELIST_DEBUG(("Hit 3: removing [%d]\n", i), kDebugFlag8);
1016 routes->count--;
1017 if (i == routes->count) {
1018 /* last slot, decrementing gets rid of it */
1019 }
1020 else {
1021 bcopy(routes->list + i + 1,
1022 routes->list + i,
1023 sizeof(routes->list[0]) * (routes->count - i));
1024 }
1025 break;
1026 }
1027 /* resolve conflict using rank */
1028 if (this_rank < scan->rank) {
1029 boolean_t is_scoped = FALSE;
1030
1031 if (scan->flags & kRouteIsScopedFlag) {
1032 is_scoped = TRUE;
1033 }
1034 ROUTELIST_DEBUG(("Hit 4:replacing [%d] rank %u < %u\n", i,
1035 this_rank,
1036 scan->rank), kDebugFlag8);
1037 *scan = *this_route;
1038 scan->rank = this_rank;
1039 if (is_scoped) {
1040 /* preserve whether route was scoped */
1041 ROUTELIST_DEBUG(("Hit 5: preserved scope\n"), kDebugFlag8);
1042 scan->flags |= kRouteIsScopedFlag;
1043 }
1044 }
1045 /* we're done */
1046 goto done;
1047 }
1048 else {
1049 if (same_dest == TRUE) {
1050 if (scope_which == kScopeNone) {
1051 ROUTELIST_DEBUG(("Hit 10: set scope on self\n"),
1052 kDebugFlag8);
1053 scope_which = kScopeThis;
1054 }
1055 }
1056 #ifdef TEST_IPV4_ROUTELIST
1057 else if (where != -1) {
1058 /* not possible because we maintain a sorted list */
1059 ROUTELIST_DEBUG(("Hit 11: moved past routes - can't happen\n"),
1060 kDebugFlag8);
1061 break;
1062 }
1063 #endif /* TEST_IPV4_ROUTELIST */
1064 }
1065 }
1066 if (routes->size == routes->count) {
1067 int how_many;
1068 IPv4RouteListRef new_routes;
1069 int old_size;
1070
1071 /* double the size */
1072 old_size = routes->size;
1073 how_many = old_size * 2;
1074 new_routes = (IPv4RouteListRef)
1075 realloc(routes, IPv4RouteListComputeSize(how_many));
1076 if (new_routes == NULL) {
1077 /* no memory */
1078 goto done;
1079 }
1080 ROUTELIST_DEBUG(("increasing size from %d to %d\n", old_size,
1081 how_many), kDebugFlag8);
1082 new_routes->size = how_many;
1083 routes = new_routes;
1084 }
1085 if (where == -1) {
1086 /* add it to the end */
1087 where = routes->count;
1088 }
1089 else {
1090 /* insert it at [where] */
1091 bcopy(routes->list + where,
1092 routes->list + where + 1,
1093 sizeof(routes->list[0]) * (routes->count - where));
1094 }
1095 /* copy the route */
1096 routes->list[where] = *this_route;
1097 routes->list[where].rank = this_rank;
1098
1099 /* set the scope */
1100 switch (scope_which) {
1101 case kScopeThis:
1102 routes->list[where].flags |= kRouteIsScopedFlag;
1103 break;
1104 case kScopeNext:
1105 routes->list[where + 1].flags |= kRouteIsScopedFlag;
1106 break;
1107 default:
1108 case kScopeNone:
1109 break;
1110 }
1111 routes->count++;
1112 done:
1113 return (routes);
1114 }
1115
1116 /*
1117 * Function: IPv4RouteListAddRouteList
1118 *
1119 * Purpose:
1120 * Invoke IPv4RouteListAddRoute for each route in the given list.
1121 *
1122 * Returns:
1123 * See IPv4RouteListAddRoute for more information.
1124 */
1125 static IPv4RouteListRef
1126 IPv4RouteListAddRouteList(IPv4RouteListRef routes, int init_size,
1127 IPv4RouteListRef service_routes, Rank rank)
1128 {
1129 int i;
1130 IPv4RouteRef scan;
1131
1132 for (i = 0, scan = service_routes->list;
1133 i < service_routes->count; i++, scan++) {
1134 routes = IPv4RouteListAddRoute(routes, init_size, scan, rank);
1135 }
1136 return (routes);
1137 }
1138
1139 static boolean_t
1140 plist_get_cstring(CFDictionaryRef dict, CFStringRef prop_name,
1141 char * buf, int buf_size)
1142 {
1143 CFStringRef val;
1144
1145 val = CFDictionaryGetValue(dict, prop_name);
1146 if (isA_CFString(val) == NULL) {
1147 return (FALSE);
1148 }
1149 if (CFStringGetCString(val, buf, buf_size, kCFStringEncodingASCII)
1150 == FALSE) {
1151 return (FALSE);
1152 }
1153 return (TRUE);
1154 }
1155
1156 /*
1157 * Function: IPv4RouteListCreateWithDictionary
1158 *
1159 * Purpose:
1160 * Given the service ipv4 entity dictionary, generate the list of routes.
1161 * Currently, this includes just the default route and subnet route,
1162 * if the service has a subnet mask.
1163 *
1164 * Returns:
1165 * If the passed in route_list is NULL or too small, this routine
1166 * allocates malloc'd memory to hold the routes.
1167 */
1168 static IPv4RouteListRef
1169 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes,
1170 CFDictionaryRef dict,
1171 CFStringRef primaryRank)
1172 {
1173 struct in_addr addr = { 0 };
1174 RouteFlags flags = 0;
1175 unsigned int ifindex;
1176 char ifn[IFNAMSIZ];
1177 struct in_addr mask = { 0 };
1178 int n;
1179 IPv4RouteRef r;
1180 struct in_addr subnet = { 0 };
1181 struct in_addr router = { 0 };
1182
1183 if (dict == NULL) {
1184 return (NULL);
1185 }
1186 if (plist_get_cstring(dict, kSCPropInterfaceName, ifn, sizeof(ifn))
1187 == FALSE) {
1188 return (NULL);
1189 }
1190 #ifdef TEST_IPV4_ROUTELIST
1191 ifindex = 0;
1192 #else /* TEST_IPV4_ROUTELIST */
1193 ifindex = if_nametoindex(ifn);
1194 if (ifindex == 0) {
1195 /* interface doesn't exist */
1196 return (NULL);
1197 }
1198 #endif /* TEST_IPV4_ROUTELIST */
1199 if (cfstring_to_ip(CFDictionaryGetValue(dict, kSCPropNetIPv4Router),
1200 &router) == 0) {
1201 (void)dict_get_first_ip(dict, kSCPropNetIPv4DestAddresses, &router);
1202 }
1203 n = 1;
1204 if (dict_get_first_ip(dict, kSCPropNetIPv4Addresses, &addr)
1205 && dict_get_first_ip(dict, kSCPropNetIPv4SubnetMasks, &mask)) {
1206 /* subnet route */
1207 subnet = subnet_addr(addr, mask);
1208 /* ignore link-local subnets, let IPConfiguration handle them for now */
1209 if (ntohl(subnet.s_addr) != IN_LINKLOCALNETNUM) {
1210 n++;
1211 }
1212 }
1213 if (addr.s_addr == 0) {
1214 /* thanks for playing */
1215 return (NULL);
1216 }
1217 if (router.s_addr == 0) {
1218 flags |= kRouteIsDirectToInterfaceFlag | kRouteChooseLastFlag;
1219 }
1220 else {
1221 /*
1222 * If the router address is our address and the subnet mask is
1223 * not 255.255.255.255, assume all routes are local to the interface.
1224 */
1225 if (addr.s_addr == router.s_addr
1226 && ifn[0] != '\0' && mask.s_addr != INADDR_BROADCAST) {
1227 flags |= kRouteIsDirectToInterfaceFlag;
1228 }
1229 if (primaryRank != NULL) {
1230 if (CFEqual(primaryRank, kSCValNetServicePrimaryRankNever)) {
1231 flags |= kRouteChooseNeverFlag;
1232 } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankFirst)) {
1233 flags |= kRouteChooseFirstFlag;
1234 } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankLast)) {
1235 flags |= kRouteChooseLastFlag;
1236 }
1237 } else if (get_override_primary(dict)) {
1238 flags |= kRouteChooseFirstFlag;
1239 }
1240 }
1241 if (n > 1 && (flags & kRouteIsDirectToInterfaceFlag) == 0
1242 && subnet.s_addr != subnet_addr(router, mask).s_addr) {
1243 flags |= kRouteIsNotSubnetLocalFlag;
1244 }
1245
1246 if (routes == NULL || routes->size < n) {
1247 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(n));
1248 routes->size = n;
1249 }
1250 bzero(routes, IPv4RouteListComputeSize(n));
1251 routes->count = n;
1252
1253 /* start at the beginning */
1254 r = routes->list;
1255
1256 /* add the default route */
1257 r->ifindex = ifindex;
1258 strlcpy(r->ifname, ifn, sizeof(r->ifname));
1259 r->ifa = addr;
1260 r->flags = flags;
1261 if ((flags & kRouteIsDirectToInterfaceFlag) == 0) {
1262 r->gateway = router;
1263 }
1264 else {
1265 r->gateway = addr;
1266 }
1267 r++;
1268 n--;
1269
1270 /* add the subnet route */
1271 if (n > 0) {
1272 r->ifindex = ifindex;
1273 r->gateway = addr;
1274 r->dest = subnet;
1275 r->mask = mask;
1276 strlcpy(r->ifname, ifn, sizeof(r->ifname));
1277 r->ifa = addr;
1278 r->flags = flags & (kRouteChooseFirstFlag|kRouteChooseLastFlag|kRouteChooseNeverFlag);
1279 }
1280
1281 return (routes);
1282 }
1283
1284 /*
1285 * Function: parse_component
1286 * Purpose:
1287 * Given a string 'key' and a string prefix 'prefix',
1288 * return the next component in the slash '/' separated
1289 * key.
1290 *
1291 * Examples:
1292 * 1. key = "a/b/c" prefix = "a/"
1293 * returns "b"
1294 * 2. key = "a/b/c" prefix = "a/b/"
1295 * returns "c"
1296 */
1297 static CFStringRef
1298 parse_component(CFStringRef key, CFStringRef prefix)
1299 {
1300 CFMutableStringRef comp;
1301 CFRange range;
1302
1303 if (CFStringHasPrefix(key, prefix) == FALSE) {
1304 return (NULL);
1305 }
1306 comp = CFStringCreateMutableCopy(NULL, 0, key);
1307 if (comp == NULL) {
1308 return (NULL);
1309 }
1310 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
1311 range = CFStringFind(comp, CFSTR("/"), 0);
1312 if (range.location == kCFNotFound) {
1313 return (comp);
1314 }
1315 range.length = CFStringGetLength(comp) - range.location;
1316 CFStringDelete(comp, range);
1317 return (comp);
1318 }
1319
1320 static CFMutableDictionaryRef
1321 service_dict_copy(CFStringRef serviceID)
1322 {
1323 CFDictionaryRef d = NULL;
1324 CFMutableDictionaryRef service_dict;
1325
1326 /* create a modifyable dictionary, a copy or a new one */
1327 d = CFDictionaryGetValue(S_service_state_dict, serviceID);
1328 if (d == NULL) {
1329 service_dict
1330 = CFDictionaryCreateMutable(NULL, 0,
1331 &kCFTypeDictionaryKeyCallBacks,
1332 &kCFTypeDictionaryValueCallBacks);
1333 }
1334 else {
1335 service_dict = CFDictionaryCreateMutableCopy(NULL, 0, d);
1336 }
1337 return (service_dict);
1338 }
1339
1340 static void
1341 dump_service_entity(CFStringRef serviceID, CFStringRef entity,
1342 CFStringRef operation, CFTypeRef val)
1343 {
1344 CFStringRef this_val = NULL;
1345
1346 if (isA_CFData(val) && CFEqual(entity, kSCEntNetIPv4)) {
1347 this_val = IPv4RouteListCopyDescription((IPv4RouteListRef)
1348 CFDataGetBytePtr(val));
1349 if (this_val != NULL) {
1350 val = this_val;
1351 }
1352 }
1353 if (val == NULL) {
1354 val = CFSTR("<none>");
1355 }
1356 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: serviceID %@ %@ %@ value = %@"),
1357 serviceID, operation, entity, val);
1358 my_CFRelease(&this_val);
1359 return;
1360 }
1361
1362 static boolean_t
1363 service_dict_set(CFStringRef serviceID, CFStringRef entity,
1364 CFTypeRef new_val)
1365 {
1366 boolean_t changed = FALSE;
1367 CFTypeRef old_val;
1368 CFMutableDictionaryRef service_dict;
1369
1370 service_dict = service_dict_copy(serviceID);
1371 old_val = CFDictionaryGetValue(service_dict, entity);
1372 if (new_val == NULL) {
1373 if (old_val != NULL) {
1374 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1375 dump_service_entity(serviceID, entity, CFSTR("Removed:"),
1376 old_val);
1377 }
1378 CFDictionaryRemoveValue(service_dict, entity);
1379 changed = TRUE;
1380 }
1381 }
1382 else {
1383 if (old_val == NULL || CFEqual(new_val, old_val) == FALSE) {
1384 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1385 dump_service_entity(serviceID, entity,
1386 CFSTR("Changed: old"), old_val);
1387 dump_service_entity(serviceID, entity,
1388 CFSTR("Changed: new"), new_val);
1389 }
1390 CFDictionarySetValue(service_dict, entity, new_val);
1391 changed = TRUE;
1392 }
1393 }
1394 if (CFDictionaryGetCount(service_dict) == 0) {
1395 CFDictionaryRemoveValue(S_service_state_dict, serviceID);
1396 }
1397 else {
1398 CFDictionarySetValue(S_service_state_dict, serviceID, service_dict);
1399 }
1400 my_CFRelease(&service_dict);
1401 return (changed);
1402 }
1403
1404 static CFDictionaryRef
1405 service_dict_get(CFStringRef serviceID, CFStringRef entity)
1406 {
1407 CFDictionaryRef service_dict;
1408
1409 service_dict = CFDictionaryGetValue(S_service_state_dict, serviceID);
1410 if (service_dict == NULL) {
1411 return (NULL);
1412 }
1413 return (CFDictionaryGetValue(service_dict, entity));
1414 }
1415
1416 #define ALLOW_EMPTY_STRING 0x1
1417
1418 static CFTypeRef
1419 sanitize_prop(CFTypeRef val, uint32_t flags)
1420 {
1421 if (val != NULL) {
1422 if (isA_CFString(val)) {
1423 CFMutableStringRef str;
1424
1425 str = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)val);
1426 CFStringTrimWhitespace(str);
1427 if (!(flags & ALLOW_EMPTY_STRING) && (CFStringGetLength(str) == 0)) {
1428 CFRelease(str);
1429 str = NULL;
1430 }
1431 val = str;
1432 } else {
1433 CFRetain(val);
1434 }
1435 }
1436
1437 return val;
1438 }
1439
1440 static void
1441 merge_array_prop(CFMutableDictionaryRef dict,
1442 CFStringRef key,
1443 CFDictionaryRef state_dict,
1444 CFDictionaryRef setup_dict,
1445 uint32_t flags,
1446 Boolean append)
1447 {
1448 CFMutableArrayRef merge_prop;
1449 CFArrayRef setup_prop = NULL;
1450 CFArrayRef state_prop = NULL;
1451
1452 if (setup_dict != NULL) {
1453 setup_prop = isA_CFArray(CFDictionaryGetValue(setup_dict, key));
1454 }
1455 if (state_dict != NULL) {
1456 state_prop = isA_CFArray(CFDictionaryGetValue(state_dict, key));
1457 }
1458
1459 if ((setup_prop == NULL) && (state_prop == NULL)) {
1460 return;
1461 }
1462
1463 merge_prop = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1464 if (setup_prop != NULL) {
1465 CFIndex i;
1466 CFIndex n;
1467
1468 n = CFArrayGetCount(setup_prop);
1469 for (i = 0; i < n; i++) {
1470 CFTypeRef val;
1471
1472 val = CFArrayGetValueAtIndex(setup_prop, i);
1473 val = sanitize_prop(val, flags);
1474 if (val != NULL) {
1475 CFArrayAppendValue(merge_prop, val);
1476 CFRelease(val);
1477 }
1478 }
1479 }
1480 if (state_prop != NULL
1481 && (setup_prop == NULL || S_append_state)) {
1482 CFIndex i;
1483 CFIndex n;
1484 CFRange setup_range = CFRangeMake(0, CFArrayGetCount(merge_prop));
1485
1486 n = CFArrayGetCount(state_prop);
1487 for (i = 0; i < n; i++) {
1488 CFTypeRef val;
1489
1490 val = CFArrayGetValueAtIndex(state_prop, i);
1491 val = sanitize_prop(val, flags);
1492 if (val != NULL) {
1493 if (append || !CFArrayContainsValue(merge_prop, setup_range, val)) {
1494 CFArrayAppendValue(merge_prop, val);
1495 }
1496 CFRelease(val);
1497 }
1498 }
1499 }
1500 if (CFArrayGetCount(merge_prop) > 0) {
1501 CFDictionarySetValue(dict, key, merge_prop);
1502 }
1503 CFRelease(merge_prop);
1504 return;
1505 }
1506
1507 static void
1508 pick_prop(CFMutableDictionaryRef dict,
1509 CFStringRef key,
1510 CFDictionaryRef state_dict,
1511 CFDictionaryRef setup_dict,
1512 uint32_t flags)
1513 {
1514 CFTypeRef val = NULL;
1515
1516 if (setup_dict != NULL) {
1517 val = CFDictionaryGetValue(setup_dict, key);
1518 val = sanitize_prop(val, flags);
1519 }
1520 if (val == NULL && state_dict != NULL) {
1521 val = CFDictionaryGetValue(state_dict, key);
1522 val = sanitize_prop(val, flags);
1523 }
1524 if (val != NULL) {
1525 CFDictionarySetValue(dict, key, val);
1526 CFRelease(val);
1527 }
1528
1529 return;
1530 }
1531
1532 /**
1533 ** GetEntityChangesFunc functions
1534 **/
1535 static boolean_t
1536 get_ipv4_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
1537 CFDictionaryRef setup_dict, CFDictionaryRef info)
1538 {
1539 boolean_t changed = FALSE;
1540 CFMutableDictionaryRef dict = NULL;
1541 CFStringRef primaryRank = NULL;
1542 IPv4RouteListRef r;
1543 #define R_STATIC 3
1544 IPv4RouteListRef routes;
1545 char routes_buf[IPv4RouteListComputeSize(R_STATIC)];
1546 CFDataRef routes_data = NULL;
1547 CFDictionaryRef service_options;
1548
1549 if (state_dict == NULL) {
1550 goto done;
1551 }
1552 service_options = service_dict_get(serviceID, kSCEntNetService);
1553 if (service_options != NULL) {
1554 primaryRank = CFDictionaryGetValue(service_options, kSCPropNetServicePrimaryRank);
1555 }
1556 dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
1557 if (setup_dict != NULL) {
1558 CFStringRef router;
1559 struct in_addr router_ip;
1560
1561 router = CFDictionaryGetValue(setup_dict,
1562 kSCPropNetIPv4Router);
1563 if (router != NULL
1564 && cfstring_to_ip(router, &router_ip)) {
1565 CFDictionarySetValue(dict,
1566 kSCPropNetIPv4Router,
1567 router);
1568 }
1569 }
1570 routes = (IPv4RouteListRef)routes_buf;
1571 routes->size = R_STATIC;
1572 routes->count = 0;
1573 r = IPv4RouteListCreateWithDictionary(routes, dict, primaryRank);
1574 if (r != NULL) {
1575 routes_data = CFDataCreate(NULL,
1576 (const void *)r,
1577 IPv4RouteListComputeSize(r->count));
1578 if (r != routes) {
1579 free(r);
1580 }
1581 }
1582 else {
1583 SCLog(TRUE, LOG_NOTICE,
1584 CFSTR("IPMonitor: %@ invalid IPv4 dictionary = %@"),
1585 serviceID,
1586 dict);
1587 }
1588 done:
1589 changed = service_dict_set(serviceID, kSCEntNetIPv4, routes_data);
1590 if (routes_data == NULL) {
1591 /* clean up the rank too */
1592 CFDictionaryRemoveValue(S_ipv4_service_rank_dict, serviceID);
1593 }
1594 my_CFRelease(&dict);
1595 my_CFRelease(&routes_data);
1596 return (changed);
1597 }
1598
1599 static boolean_t
1600 get_ipv6_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
1601 CFDictionaryRef setup_dict, CFDictionaryRef info)
1602 {
1603 struct in6_addr addr;
1604 CFArrayRef addrs;
1605 boolean_t changed = FALSE;
1606 CFMutableDictionaryRef dict = NULL;
1607 CFDictionaryRef new_dict = NULL;
1608 CFStringRef router = NULL;
1609 struct in6_addr router_ip;
1610 boolean_t valid_ip = FALSE;
1611
1612 if (state_dict == NULL) {
1613 goto done;
1614 }
1615 addrs = isA_CFArray(CFDictionaryGetValue(state_dict,
1616 kSCPropNetIPv6Addresses));
1617 if (addrs != NULL && CFArrayGetCount(addrs) > 0) {
1618 valid_ip = cfstring_to_ip6(CFArrayGetValueAtIndex(addrs, 0), &addr);
1619 }
1620 if (valid_ip == FALSE) {
1621 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1622 SCLog(TRUE, LOG_NOTICE,
1623 CFSTR("IPMonitor: %@ has no valid IPv6 address, ignoring"),
1624 serviceID);
1625 }
1626 goto done;
1627 }
1628 dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
1629 if (setup_dict != NULL) {
1630 router = CFDictionaryGetValue(setup_dict,
1631 kSCPropNetIPv6Router);
1632 if (router != NULL && cfstring_to_ip6(router, &router_ip)) {
1633 CFDictionarySetValue(dict,
1634 kSCPropNetIPv6Router,
1635 router);
1636 }
1637 }
1638 else {
1639 router = CFDictionaryGetValue(dict,
1640 kSCPropNetIPv6Router);
1641 if (router != NULL
1642 && cfstring_to_ip6(router, &router_ip) == FALSE) {
1643 CFDictionaryRemoveValue(dict, kSCPropNetIPv6Router);
1644 }
1645 }
1646 new_dict = dict;
1647 done:
1648 changed = service_dict_set(serviceID, kSCEntNetIPv6, new_dict);
1649 if (new_dict == NULL) {
1650 /* clean up the rank too */
1651 CFDictionaryRemoveValue(S_ipv6_service_rank_dict, serviceID);
1652 }
1653 my_CFRelease(&new_dict);
1654 return (changed);
1655 }
1656
1657 static boolean_t
1658 dns_has_supplemental(CFStringRef serviceID)
1659 {
1660 CFDictionaryRef dns_dict;
1661 CFDictionaryRef service_dict;
1662
1663 service_dict = CFDictionaryGetValue(S_service_state_dict, serviceID);
1664 if (service_dict == NULL) {
1665 return FALSE;
1666 }
1667
1668 dns_dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
1669 if (dns_dict == NULL) {
1670 return FALSE;
1671 }
1672
1673 return CFDictionaryContainsKey(dns_dict, kSCPropNetDNSSupplementalMatchDomains);
1674 }
1675
1676 static void
1677 accumulate_dns_servers(CFArrayRef in_servers, ProtocolFlags active_protos,
1678 CFMutableArrayRef out_servers)
1679 {
1680 int count;
1681 int i;
1682
1683 count = CFArrayGetCount(in_servers);
1684 for (i = 0; i < count; i++) {
1685 CFStringRef addr = CFArrayGetValueAtIndex(in_servers, i);
1686 struct in6_addr ipv6_addr;
1687 struct in_addr ip_addr;
1688
1689 if (cfstring_to_ip(addr, &ip_addr)) {
1690 /* IPv4 address */
1691 if ((active_protos & kProtocolFlagsIPv4) == 0
1692 && ntohl(ip_addr.s_addr) != INADDR_LOOPBACK) {
1693 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1694 syslog(LOG_NOTICE,
1695 "IPMonitor: no IPv4 connectivity, "
1696 "ignoring DNS server address " IP_FORMAT,
1697 IP_LIST(&ip_addr));
1698 }
1699 continue;
1700 }
1701 }
1702 else if (cfstring_to_ip6(addr, &ipv6_addr)) {
1703 /* IPv6 address */
1704 if ((active_protos & kProtocolFlagsIPv6) == 0
1705 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr)) {
1706 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1707 char str[128];
1708
1709 str[0] = '\0';
1710 inet_ntop(AF_INET6, &ipv6_addr, str, sizeof(str));
1711 syslog(LOG_NOTICE,
1712 "IPMonitor: no IPv6 connectivity, "
1713 "ignoring DNS server address %s",
1714 str);
1715 }
1716 continue;
1717 }
1718 }
1719 else {
1720 /* bad IP address */
1721 SCLog(TRUE, LOG_NOTICE,
1722 CFSTR("IPMonitor: ignoring bad DNS server address '%@'"),
1723 addr);
1724 continue;
1725 }
1726 /* DNS server is valid and one we want */
1727 CFArrayAppendValue(out_servers, addr);
1728 }
1729 return;
1730 }
1731
1732 static void
1733 merge_dns_servers(CFMutableDictionaryRef new_dict,
1734 CFArrayRef state_servers,
1735 CFArrayRef setup_servers,
1736 ProtocolFlags active_protos)
1737 {
1738 CFMutableArrayRef dns_servers;
1739
1740 if (state_servers == NULL && setup_servers == NULL) {
1741 /* no DNS servers */
1742 return;
1743 }
1744 dns_servers = CFArrayCreateMutable(NULL, 0,
1745 &kCFTypeArrayCallBacks);
1746 if (setup_servers != NULL) {
1747 accumulate_dns_servers(setup_servers, active_protos,
1748 dns_servers);
1749 }
1750 if ((CFArrayGetCount(dns_servers) == 0 || S_append_state)
1751 && state_servers != NULL) {
1752 accumulate_dns_servers(state_servers, active_protos,
1753 dns_servers);
1754 }
1755 if (CFArrayGetCount(dns_servers) != 0) {
1756 CFDictionarySetValue(new_dict,
1757 kSCPropNetDNSServerAddresses, dns_servers);
1758 }
1759 my_CFRelease(&dns_servers);
1760 return;
1761 }
1762
1763
1764 static boolean_t
1765 get_dns_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
1766 CFDictionaryRef setup_dict, CFDictionaryRef info)
1767 {
1768 ProtocolFlags active_protos = kProtocolFlagsNone;
1769 boolean_t changed = FALSE;
1770 CFStringRef domain;
1771 int i;
1772 struct {
1773 CFStringRef key;
1774 uint32_t flags;
1775 Boolean append;
1776 } merge_list[] = {
1777 { kSCPropNetDNSSearchDomains, 0, FALSE },
1778 { kSCPropNetDNSSortList, 0, FALSE },
1779 { kSCPropNetDNSSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE },
1780 { kSCPropNetDNSSupplementalMatchOrders, 0, TRUE },
1781 };
1782 CFMutableDictionaryRef new_dict = NULL;
1783 CFStringRef pick_list[] = {
1784 kSCPropNetDNSDomainName,
1785 kSCPropNetDNSOptions,
1786 kSCPropNetDNSSearchOrder,
1787 kSCPropNetDNSServerPort,
1788 kSCPropNetDNSServerTimeout,
1789 };
1790
1791 if ((state_dict == NULL) && (setup_dict == NULL)) {
1792 /* there is no DNS */
1793 goto done;
1794 }
1795
1796 if (service_dict_get(serviceID, kSCEntNetIPv4) != NULL) {
1797 active_protos |= kProtocolFlagsIPv4;
1798 }
1799 if (service_dict_get(serviceID, kSCEntNetIPv6) != NULL) {
1800 active_protos |= kProtocolFlagsIPv6;
1801 }
1802 /* merge DNS configuration */
1803 new_dict = CFDictionaryCreateMutable(NULL, 0,
1804 &kCFTypeDictionaryKeyCallBacks,
1805 &kCFTypeDictionaryValueCallBacks);
1806 if (active_protos == kProtocolFlagsNone) {
1807 /* there is no IPv4 nor IPv6 */
1808 if (state_dict == NULL) {
1809 /* no DNS information at all */
1810 goto done;
1811 }
1812 merge_dns_servers(new_dict,
1813 my_CFDictionaryGetArray(state_dict,
1814 kSCPropNetDNSServerAddresses),
1815 NULL,
1816 kProtocolFlagsIPv4 | kProtocolFlagsIPv6);
1817 setup_dict = NULL;
1818 }
1819 else {
1820 merge_dns_servers(new_dict,
1821 my_CFDictionaryGetArray(state_dict,
1822 kSCPropNetDNSServerAddresses),
1823 my_CFDictionaryGetArray(setup_dict,
1824 kSCPropNetDNSServerAddresses),
1825 active_protos);
1826 }
1827
1828 for (i = 0; i < sizeof(merge_list)/sizeof(merge_list[0]); i++) {
1829 merge_array_prop(new_dict,
1830 merge_list[i].key,
1831 state_dict,
1832 setup_dict,
1833 merge_list[i].flags,
1834 merge_list[i].append);
1835 }
1836 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
1837 pick_prop(new_dict,
1838 pick_list[i],
1839 state_dict,
1840 setup_dict,
1841 0);
1842 }
1843
1844 if (active_protos == kProtocolFlagsNone) {
1845 /* there is no IPv4 nor IPv6, only supplemental DNS */
1846 if (CFDictionaryContainsKey(new_dict,
1847 kSCPropNetDNSSupplementalMatchDomains)) {
1848 /* only keep State: supplemental */
1849 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSDomainName);
1850 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchDomains);
1851 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchOrder);
1852 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSortList);
1853 } else {
1854 goto done;
1855 }
1856 }
1857 if (CFDictionaryGetCount(new_dict) == 0) {
1858 my_CFRelease(&new_dict);
1859 goto done;
1860 }
1861
1862 if (S_append_state) {
1863 /*
1864 * ensure any specified domain name (e.g. the domain returned by
1865 * a DHCP server) is in the search list.
1866 */
1867 domain = CFDictionaryGetValue(new_dict, kSCPropNetDNSDomainName);
1868 if (isA_CFString(domain)) {
1869 CFArrayRef search;
1870
1871 search = CFDictionaryGetValue(new_dict, kSCPropNetDNSSearchDomains);
1872 if (isA_CFArray(search) &&
1873 !CFArrayContainsValue(search, CFRangeMake(0, CFArrayGetCount(search)), domain)) {
1874 CFMutableArrayRef new_search;
1875
1876 new_search = CFArrayCreateMutableCopy(NULL, 0, search);
1877 CFArrayAppendValue(new_search, domain);
1878 CFDictionarySetValue(new_dict, kSCPropNetDNSSearchDomains, new_search);
1879 my_CFRelease(&new_search);
1880 }
1881 }
1882 }
1883
1884 done:
1885 changed = service_dict_set(serviceID, kSCEntNetDNS, new_dict);
1886 my_CFRelease(&new_dict);
1887 return (changed);
1888 }
1889
1890 static void
1891 merge_dict(const void *key, const void *value, void *context)
1892 {
1893 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context;
1894
1895 CFDictionarySetValue(dict, key, value);
1896 return;
1897 }
1898
1899 #define PROXY_AUTO_DISCOVERY_URL 252
1900
1901 static CFStringRef
1902 wpadURL_dhcp(CFDictionaryRef dhcp_options)
1903 {
1904 CFStringRef urlString = NULL;
1905
1906 if (isA_CFDictionary(dhcp_options)) {
1907 CFDataRef data;
1908
1909 data = DHCPInfoGetOptionData(dhcp_options, PROXY_AUTO_DISCOVERY_URL);
1910 if (data != NULL) {
1911 CFURLRef url;
1912
1913 url = CFURLCreateWithBytes(NULL,
1914 CFDataGetBytePtr(data),
1915 CFDataGetLength(data),
1916 kCFStringEncodingUTF8,
1917 NULL);
1918 if (url != NULL) {
1919 urlString = CFURLGetString(url);
1920 if (urlString != NULL) {
1921 CFRetain(urlString);
1922 }
1923 CFRelease(url);
1924 }
1925 }
1926 }
1927
1928 return urlString;
1929 }
1930
1931 static CFStringRef
1932 wpadURL_dns(void)
1933 {
1934 CFURLRef url;
1935 CFStringRef urlString = NULL;
1936
1937 url = CFURLCreateWithString(NULL, CFSTR("http://wpad/wpad.dat"), NULL);
1938 if (url != NULL) {
1939 urlString = CFURLGetString(url);
1940 if (urlString != NULL) {
1941 CFRetain(urlString);
1942 }
1943 CFRelease(url);
1944 }
1945
1946 return urlString;
1947 }
1948
1949 static boolean_t
1950 get_proxies_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
1951 CFDictionaryRef setup_dict, CFDictionaryRef info)
1952 {
1953 boolean_t changed = FALSE;
1954 CFMutableDictionaryRef new_dict = NULL;
1955 struct {
1956 CFStringRef key1; /* an "enable" key */
1957 CFStringRef key2;
1958 CFStringRef key3;
1959 } pick_list[] = {
1960 { kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort },
1961 { kSCPropNetProxiesGopherEnable, kSCPropNetProxiesGopherProxy, kSCPropNetProxiesGopherPort },
1962 { kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort },
1963 { kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort },
1964 { kSCPropNetProxiesRTSPEnable, kSCPropNetProxiesRTSPProxy, kSCPropNetProxiesRTSPPort },
1965 { kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort },
1966 { kSCPropNetProxiesProxyAutoConfigEnable,
1967 kSCPropNetProxiesProxyAutoConfigURLString,
1968 NULL, },
1969 { kSCPropNetProxiesProxyAutoDiscoveryEnable,
1970 NULL,
1971 NULL, }
1972 };
1973
1974 if ((service_dict_get(serviceID, kSCEntNetIPv4) == NULL) &&
1975 (service_dict_get(serviceID, kSCEntNetIPv6) == NULL)) {
1976 /* there is no IPv4 nor IPv6 */
1977 goto done;
1978 }
1979
1980 if ((setup_dict != NULL) && (state_dict != NULL)) {
1981 CFIndex i;
1982 CFMutableDictionaryRef setup_copy;
1983
1984 /*
1985 * Merge the per-service "Setup:" and "State:" proxy information with
1986 * the "Setup:" information always taking precedence. Additionally,
1987 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
1988 * Port) is defined than all of the values for that group will be
1989 * used. That is, we don't allow mixing some of the values from
1990 * the "Setup:" keys and others from the "State:" keys.
1991 */
1992 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
1993 setup_copy = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
1994 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
1995 if (CFDictionaryContainsKey(setup_copy, pick_list[i].key1)) {
1996 /*
1997 * if a "Setup:" enabled key has been provided than we want to
1998 * ignore all of the "State:" keys
1999 */
2000 CFDictionaryRemoveValue(new_dict, pick_list[i].key1);
2001 if (pick_list[i].key2 != NULL) {
2002 CFDictionaryRemoveValue(new_dict, pick_list[i].key2);
2003 }
2004 if (pick_list[i].key3 != NULL) {
2005 CFDictionaryRemoveValue(new_dict, pick_list[i].key3);
2006 }
2007 } else if (CFDictionaryContainsKey(state_dict, pick_list[i].key1) ||
2008 ((pick_list[i].key2 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key2)) ||
2009 ((pick_list[i].key3 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key3))) {
2010 /*
2011 * if a "Setup:" enabled key has not been provided and we have
2012 * some" "State:" keys than we remove all of of "Setup:" keys
2013 */
2014 CFDictionaryRemoveValue(setup_copy, pick_list[i].key1);
2015 if (pick_list[i].key2 != NULL) {
2016 CFDictionaryRemoveValue(setup_copy, pick_list[i].key2);
2017 }
2018 if (pick_list[i].key3 != NULL) {
2019 CFDictionaryRemoveValue(setup_copy, pick_list[i].key3);
2020 }
2021 }
2022 }
2023
2024 /* merge the "Setup:" keys */
2025 CFDictionaryApplyFunction(setup_copy, merge_dict, new_dict);
2026 CFRelease(setup_copy);
2027
2028 if (CFDictionaryGetCount(new_dict) == 0) {
2029 CFRelease(new_dict);
2030 new_dict = NULL;
2031 }
2032 }
2033 else if (setup_dict != NULL) {
2034 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
2035 }
2036 else if (state_dict != NULL) {
2037 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2038 }
2039
2040 /* process WPAD */
2041 if (new_dict != NULL) {
2042 CFDictionaryRef dhcp_options;
2043 CFNumberRef num;
2044 CFNumberRef wpad = NULL;
2045 int wpadEnabled = 0;
2046 CFStringRef wpadURL = NULL;
2047
2048 if (CFDictionaryGetValueIfPresent(new_dict,
2049 kSCPropNetProxiesProxyAutoDiscoveryEnable,
2050 (const void **)&num) &&
2051 isA_CFNumber(num)) {
2052 /* if we have a WPAD key */
2053 wpad = num;
2054 if (!CFNumberGetValue(num, kCFNumberIntType, &wpadEnabled)) {
2055 /* if we don't like the enabled key/value */
2056 wpadEnabled = 0;
2057 }
2058 }
2059
2060 if (wpadEnabled) {
2061 int pacEnabled = 0;
2062
2063 num = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigEnable);
2064 if (!isA_CFNumber(num) ||
2065 !CFNumberGetValue(num, kCFNumberIntType, &pacEnabled)) {
2066 /* if we don't like the enabled key/value */
2067 pacEnabled = 0;
2068 }
2069
2070 if (pacEnabled) {
2071 CFStringRef pacURL;
2072
2073 pacURL = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigURLString);
2074 if (!isA_CFString(pacURL)) {
2075 /* if we don't like the PAC URL */
2076 pacEnabled = 0;
2077 }
2078 }
2079
2080 if (pacEnabled) {
2081 /*
2082 * we already have a PAC URL so disable WPAD.
2083 */
2084 wpadEnabled = 0;
2085 goto setWPAD;
2086 }
2087
2088 /*
2089 * if WPAD is enabled and we don't already have a PAC URL then
2090 * we check for a DHCP provided URL. If not available, we use
2091 * a PAC URL pointing to a well-known file (wpad.dat) on a
2092 * well-known host (wpad.<domain>).
2093 */
2094 dhcp_options = get_service_state_entity(info, serviceID, kSCEntNetDHCP);
2095 wpadURL = wpadURL_dhcp(dhcp_options);
2096 if (wpadURL == NULL) {
2097 wpadURL = wpadURL_dns();
2098 }
2099 if (wpadURL == NULL) {
2100 wpadEnabled = 0; /* if we don't have a WPAD URL */
2101 goto setWPAD;
2102 }
2103
2104 pacEnabled = 1;
2105 num = CFNumberCreate(NULL, kCFNumberIntType, &pacEnabled);
2106 CFDictionarySetValue(new_dict,
2107 kSCPropNetProxiesProxyAutoConfigEnable,
2108 num);
2109 CFRelease(num);
2110 CFDictionarySetValue(new_dict,
2111 kSCPropNetProxiesProxyAutoConfigURLString,
2112 wpadURL);
2113 CFRelease(wpadURL);
2114 }
2115
2116 setWPAD:
2117 if (wpad != NULL) {
2118 num = CFNumberCreate(NULL, kCFNumberIntType, &wpadEnabled);
2119 CFDictionarySetValue(new_dict,
2120 kSCPropNetProxiesProxyAutoDiscoveryEnable,
2121 num);
2122 CFRelease(num);
2123 }
2124 }
2125
2126 done:
2127 changed = service_dict_set(serviceID, kSCEntNetProxies, new_dict);
2128 if (new_dict != NULL) CFRelease(new_dict);
2129 return (changed);
2130 }
2131
2132 #if !TARGET_OS_IPHONE
2133 static boolean_t
2134 get_smb_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2135 CFDictionaryRef setup_dict, CFDictionaryRef info)
2136 {
2137 boolean_t changed = FALSE;
2138 int i;
2139 CFMutableDictionaryRef new_dict = NULL;
2140 CFStringRef pick_list[] = {
2141 kSCPropNetSMBNetBIOSName,
2142 kSCPropNetSMBNetBIOSNodeType,
2143 kSCPropNetSMBNetBIOSScope,
2144 kSCPropNetSMBWorkgroup,
2145 };
2146
2147 if (service_dict_get(serviceID, kSCEntNetIPv4) == NULL) {
2148 /* there is no IPv4 */
2149 goto done;
2150 }
2151
2152 if (state_dict == NULL && setup_dict == NULL) {
2153 /* there is no SMB */
2154 goto done;
2155 }
2156
2157 /* merge SMB configuration */
2158 new_dict = CFDictionaryCreateMutable(NULL, 0,
2159 &kCFTypeDictionaryKeyCallBacks,
2160 &kCFTypeDictionaryValueCallBacks);
2161
2162 merge_array_prop(new_dict,
2163 kSCPropNetSMBWINSAddresses,
2164 state_dict,
2165 setup_dict,
2166 0,
2167 FALSE);
2168 for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2169 pick_prop(new_dict,
2170 pick_list[i],
2171 state_dict,
2172 setup_dict,
2173 0);
2174 }
2175
2176 if (CFDictionaryGetCount(new_dict) == 0) {
2177 my_CFRelease(&new_dict);
2178 goto done;
2179 }
2180
2181 done:
2182 changed = service_dict_set(serviceID, kSCEntNetSMB, new_dict);
2183 my_CFRelease(&new_dict);
2184 return (changed);
2185 }
2186 #endif /* !TARGET_OS_IPHONE */
2187
2188 static boolean_t
2189 get_rank_changes(CFStringRef serviceID, CFDictionaryRef state_options,
2190 CFDictionaryRef setup_options, CFDictionaryRef info)
2191 {
2192 boolean_t changed = FALSE;
2193 CFMutableDictionaryRef new_dict = NULL;
2194 CFStringRef new_rank = NULL;
2195 CFStringRef setup_rank = NULL;
2196 CFStringRef state_rank = NULL;
2197
2198
2199 /*
2200 * Check "PrimaryRank" setting
2201 *
2202 * Note: Rank Never > Rank Last > Rank First > Rank None
2203 */
2204 if (isA_CFDictionary(setup_options)) {
2205 setup_rank = CFDictionaryGetValue(setup_options, kSCPropNetServicePrimaryRank);
2206 setup_rank = isA_CFString(setup_rank);
2207 }
2208 if (isA_CFDictionary(state_options)) {
2209 state_rank = CFDictionaryGetValue(state_options, kSCPropNetServicePrimaryRank);
2210 state_rank = isA_CFString(state_rank);
2211 }
2212
2213 if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankNever)) ||
2214 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankNever))) {
2215 new_rank = kSCValNetServicePrimaryRankNever;
2216 }
2217 else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankLast)) ||
2218 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankLast))) {
2219 new_rank = kSCValNetServicePrimaryRankLast;
2220 }
2221 else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankFirst)) ||
2222 ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankFirst))) {
2223 new_rank = kSCValNetServicePrimaryRankFirst;
2224 }
2225
2226 if (new_rank != NULL) {
2227 new_dict = CFDictionaryCreateMutable(NULL, 0,
2228 &kCFTypeDictionaryKeyCallBacks,
2229 &kCFTypeDictionaryValueCallBacks);
2230 CFDictionarySetValue(new_dict, kSCPropNetServicePrimaryRank, new_rank);
2231 }
2232
2233 changed = service_dict_set(serviceID, kSCEntNetService, new_dict);
2234 my_CFRelease(&new_dict);
2235 return (changed);
2236 }
2237
2238 static void
2239 add_service_keys(CFStringRef serviceID, CFMutableArrayRef keys, CFMutableArrayRef patterns)
2240 {
2241 int i;
2242 CFStringRef key;
2243
2244 if (CFEqual(serviceID, kSCCompAnyRegex)) {
2245 keys = patterns;
2246 }
2247
2248 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
2249 key = setup_service_key(serviceID, *entityTypeNames[i]);
2250 CFArrayAppendValue(keys, key);
2251 CFRelease(key);
2252 key = state_service_key(serviceID, *entityTypeNames[i]);
2253 CFArrayAppendValue(keys, key);
2254 CFRelease(key);
2255 }
2256
2257 key = state_service_key(serviceID, kSCEntNetDHCP);
2258 CFArrayAppendValue(patterns, key);
2259 CFRelease(key);
2260
2261 key = setup_service_key(serviceID, NULL);
2262 CFArrayAppendValue(patterns, key);
2263 CFRelease(key);
2264 key = state_service_key(serviceID, NULL);
2265 CFArrayAppendValue(patterns, key);
2266 CFRelease(key);
2267
2268
2269 return;
2270 }
2271
2272 static CFDictionaryRef
2273 services_info_copy(SCDynamicStoreRef session, CFArrayRef service_list)
2274 {
2275 int count;
2276 CFMutableArrayRef get_keys;
2277 CFMutableArrayRef get_patterns;
2278 CFDictionaryRef info;
2279 int s;
2280
2281 count = CFArrayGetCount(service_list);
2282 get_keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2283 get_patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2284
2285 CFArrayAppendValue(get_keys, S_setup_global_ipv4);
2286 CFArrayAppendValue(get_keys, S_setup_global_proxies);
2287 #if !TARGET_OS_IPHONE
2288 CFArrayAppendValue(get_keys, S_setup_global_smb);
2289 #endif /* !TARGET_OS_IPHONE */
2290 CFArrayAppendValue(get_keys, S_multicast_resolvers);
2291 CFArrayAppendValue(get_keys, S_private_resolvers);
2292
2293 for (s = 0; s < count; s++) {
2294 CFStringRef serviceID = CFArrayGetValueAtIndex(service_list, s);
2295
2296 add_service_keys(serviceID, get_keys, get_patterns);
2297 }
2298
2299 info = SCDynamicStoreCopyMultiple(session, get_keys, get_patterns);
2300 my_CFRelease(&get_keys);
2301 my_CFRelease(&get_patterns);
2302 return (info);
2303 }
2304
2305 static int rtm_seq = 0;
2306
2307 static int
2308 ipv4_route_open_socket(void)
2309 {
2310 int sockfd;
2311
2312 if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) == -1) {
2313 SCLog(TRUE, LOG_NOTICE,
2314 CFSTR("IPMonitor: ipv4_route_open_socket: socket failed, %s"),
2315 strerror(errno));
2316 }
2317 return (sockfd);
2318 }
2319
2320 /*
2321 * Define: ROUTE_MSG_ADDRS_SPACE
2322 * Purpose:
2323 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2324 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2325 * someone changes the code and doesn't think to modify this.
2326 */
2327 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2328 + 2 * sizeof(struct sockaddr_dl) \
2329 + 128)
2330 typedef struct {
2331 struct rt_msghdr hdr;
2332 char addrs[ROUTE_MSG_ADDRS_SPACE];
2333 } route_msg;
2334
2335 static int
2336 ipv4_route(int sockfd,
2337 int cmd, struct in_addr gateway, struct in_addr netaddr,
2338 struct in_addr netmask, char * ifname, unsigned int ifindex,
2339 struct in_addr ifa, RouteFlags flags)
2340 {
2341 boolean_t default_route = (netaddr.s_addr == 0);
2342 int len;
2343 int ret = 0;
2344 route_msg rtmsg;
2345 union {
2346 struct sockaddr_in * in_p;
2347 struct sockaddr_dl * dl_p;
2348 void * ptr;
2349 } rtaddr;
2350
2351 if (default_route && S_netboot) {
2352 return (0);
2353 }
2354
2355 if (ifname == NULL) {
2356 /* this should not happen, but rather than crash, return an error */
2357 syslog(LOG_NOTICE,
2358 "IPMonitor: ipv4_route ifname is NULL on network address %s",
2359 inet_ntoa(netaddr));
2360 return (EBADF);
2361 }
2362 memset(&rtmsg, 0, sizeof(rtmsg));
2363 rtmsg.hdr.rtm_type = cmd;
2364 rtmsg.hdr.rtm_version = RTM_VERSION;
2365 rtmsg.hdr.rtm_seq = ++rtm_seq;
2366 rtmsg.hdr.rtm_addrs
2367 = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP | RTA_IFA;
2368 if (default_route
2369 && (flags & kRouteIsDirectToInterfaceFlag) == 0) {
2370 rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
2371 }
2372 else {
2373 rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC;
2374 }
2375 if ((flags & kRouteWantScopedFlag) != 0) {
2376 #ifdef RTF_IFSCOPE
2377 if (!S_scopedroute) {
2378 return (0);
2379 }
2380 if (ifindex == 0) {
2381 /* specifically asked for a scoped route, yet no index supplied */
2382 syslog(LOG_NOTICE,
2383 "IPMonitor: ipv4_route index is 0 on %s-scoped route %s",
2384 ifname, inet_ntoa(netaddr));
2385 return (EBADF);
2386 }
2387 rtmsg.hdr.rtm_index = ifindex;
2388 rtmsg.hdr.rtm_flags |= RTF_IFSCOPE;
2389 #else /* RTF_IFSCOPE */
2390 return (0);
2391 #endif /* RTF_IFSCOPE */
2392 }
2393
2394 rtaddr.ptr = rtmsg.addrs;
2395
2396 /* dest */
2397 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2398 rtaddr.in_p->sin_family = AF_INET;
2399 rtaddr.in_p->sin_addr = netaddr;
2400 rtaddr.ptr += sizeof(*rtaddr.in_p);
2401
2402 /* gateway */
2403 if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) {
2404 /* gateway is an IP address */
2405 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2406 rtaddr.in_p->sin_family = AF_INET;
2407 rtaddr.in_p->sin_addr = gateway;
2408 rtaddr.ptr += sizeof(*rtaddr.in_p);
2409 }
2410 else {
2411 /* gateway is the interface itself */
2412 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
2413 rtaddr.dl_p->sdl_family = AF_LINK;
2414 rtaddr.dl_p->sdl_nlen = strlen(ifname);
2415 bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen);
2416 rtaddr.ptr += sizeof(*rtaddr.dl_p);
2417 }
2418
2419 /* mask */
2420 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2421 rtaddr.in_p->sin_family = AF_INET;
2422 rtaddr.in_p->sin_addr = netmask;
2423 rtaddr.ptr += sizeof(*rtaddr.in_p);
2424
2425 /* interface name */
2426 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
2427 rtaddr.dl_p->sdl_family = AF_LINK;
2428 rtaddr.dl_p->sdl_nlen = strlen(ifname);
2429 bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen);
2430 rtaddr.ptr += sizeof(*rtaddr.dl_p);
2431
2432 /* interface address */
2433 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2434 rtaddr.in_p->sin_family = AF_INET;
2435 rtaddr.in_p->sin_addr = ifa;
2436 rtaddr.ptr += sizeof(*rtaddr.in_p);
2437
2438 len = sizeof(rtmsg.hdr) + (rtaddr.ptr - (void *)rtmsg.addrs);
2439 rtmsg.hdr.rtm_msglen = len;
2440 if (write(sockfd, &rtmsg, len) == -1) {
2441 ret = errno;
2442 }
2443 return (ret);
2444 }
2445
2446 static boolean_t
2447 ipv6_route(int cmd, struct in6_addr gateway, struct in6_addr netaddr,
2448 struct in6_addr netmask, char * ifname, boolean_t is_direct)
2449 {
2450 boolean_t default_route;
2451 int len;
2452 boolean_t ret = TRUE;
2453 struct {
2454 struct rt_msghdr hdr;
2455 struct sockaddr_in6 dst;
2456 struct sockaddr_in6 gway;
2457 struct sockaddr_in6 mask;
2458 struct sockaddr_dl ifp;
2459 } rtmsg;
2460 int sockfd = -1;
2461 struct in6_addr zeroes = IN6ADDR_ANY_INIT;
2462
2463 default_route = (bcmp(&zeroes, &netaddr, sizeof(netaddr)) == 0);
2464
2465 if (IN6_IS_ADDR_LINKLOCAL(&gateway) && ifname != NULL) {
2466 unsigned int index = if_nametoindex(ifname);
2467
2468 /* add the scope id to the link local address */
2469 gateway.__u6_addr.__u6_addr16[1] = (uint16_t)htons(index);
2470 }
2471 if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) == -1) {
2472 SCLog(TRUE, LOG_NOTICE,
2473 CFSTR("IPMonitor ipv6_route: open routing socket failed, %s"),
2474 strerror(errno));
2475 return (FALSE);
2476 }
2477 memset(&rtmsg, 0, sizeof(rtmsg));
2478 rtmsg.hdr.rtm_type = cmd;
2479 if (default_route) {
2480 if (is_direct) {
2481 /* if router is directly reachable, don't set the gateway flag */
2482 rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC;
2483 }
2484 else {
2485 rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
2486 }
2487 }
2488 else {
2489 rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC;
2490 }
2491 rtmsg.hdr.rtm_version = RTM_VERSION;
2492 rtmsg.hdr.rtm_seq = ++rtm_seq;
2493 rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
2494 rtmsg.dst.sin6_len = sizeof(rtmsg.dst);
2495 rtmsg.dst.sin6_family = AF_INET6;
2496 rtmsg.dst.sin6_addr = netaddr;
2497 rtmsg.gway.sin6_len = sizeof(rtmsg.gway);
2498 rtmsg.gway.sin6_family = AF_INET6;
2499 rtmsg.gway.sin6_addr = gateway;
2500 rtmsg.mask.sin6_len = sizeof(rtmsg.mask);
2501 rtmsg.mask.sin6_family = AF_INET6;
2502 rtmsg.mask.sin6_addr = netmask;
2503
2504 len = sizeof(rtmsg);
2505 if (ifname) {
2506 rtmsg.ifp.sdl_len = sizeof(rtmsg.ifp);
2507 rtmsg.ifp.sdl_family = AF_LINK;
2508 rtmsg.ifp.sdl_nlen = strlen(ifname);
2509 rtmsg.hdr.rtm_addrs |= RTA_IFP;
2510 bcopy(ifname, rtmsg.ifp.sdl_data, rtmsg.ifp.sdl_nlen);
2511 }
2512 else {
2513 /* no ifp information */
2514 len -= sizeof(rtmsg.ifp);
2515 }
2516 rtmsg.hdr.rtm_msglen = len;
2517 if (write(sockfd, &rtmsg, len) == -1) {
2518 if ((cmd == RTM_ADD) && (errno == EEXIST)) {
2519 /* no sense complaining about a route that already exists */
2520 }
2521 else if ((cmd == RTM_DELETE) && (errno == ESRCH)) {
2522 /* no sense complaining about a route that isn't there */
2523 }
2524 else {
2525 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2526 SCLog(TRUE, LOG_NOTICE,
2527 CFSTR("IPMonitor ipv6_route: write routing"
2528 " socket failed, %s"), strerror(errno));
2529 }
2530 ret = FALSE;
2531 }
2532 }
2533
2534 close(sockfd);
2535 return (ret);
2536 }
2537
2538 static boolean_t
2539 ipv6_default_route_delete(void)
2540 {
2541 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2542 SCLog(TRUE, LOG_NOTICE, CFSTR("IPMonitor: IPv6 route delete default"));
2543 }
2544 return (ipv6_route(RTM_DELETE, S_ip6_zeros, S_ip6_zeros, S_ip6_zeros,
2545 NULL, FALSE));
2546 }
2547
2548 static boolean_t
2549 ipv6_default_route_add(struct in6_addr router, char * ifname,
2550 boolean_t is_direct)
2551 {
2552 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2553 char str[128];
2554
2555 str[0] = '\0';
2556
2557 inet_ntop(AF_INET6, &router, str, sizeof(str));
2558 SCLog(TRUE, LOG_NOTICE,
2559 CFSTR("IPMonitor: IPv6 route add default"
2560 " %s interface %s direct %d"),
2561 str, ifname, is_direct);
2562 }
2563 return (ipv6_route(RTM_ADD, router, S_ip6_zeros, S_ip6_zeros,
2564 ifname, is_direct));
2565 }
2566
2567
2568 static int
2569 multicast_route_delete(int sockfd)
2570 {
2571 struct in_addr gateway = { htonl(INADDR_LOOPBACK) };
2572 struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) };
2573 struct in_addr netmask = { htonl(IN_CLASSD_NET) };
2574
2575 return (ipv4_route(sockfd, RTM_DELETE, gateway, netaddr, netmask, "lo0", 0,
2576 gateway, 0));
2577 }
2578
2579 static int
2580 multicast_route_add(int sockfd)
2581 {
2582 struct in_addr gateway = { htonl(INADDR_LOOPBACK) };
2583 struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) };
2584 struct in_addr netmask = { htonl(IN_CLASSD_NET) };
2585
2586 return (ipv4_route(sockfd, RTM_ADD, gateway, netaddr, netmask, "lo0", 0,
2587 gateway, 0));
2588 }
2589
2590 static void
2591 set_ipv6_router(struct in6_addr * router, char * ifname, boolean_t is_direct)
2592 {
2593 /* assign the new default route, ensure local multicast route available */
2594 (void)ipv6_default_route_delete();
2595 if (router != NULL) {
2596 (void)ipv6_default_route_add(*router, ifname, is_direct);
2597 }
2598 return;
2599 }
2600
2601 #if !TARGET_OS_IPHONE
2602 static __inline__ void
2603 empty_dns()
2604 {
2605 (void)unlink(VAR_RUN_RESOLV_CONF);
2606 }
2607
2608 static void
2609 set_dns(CFArrayRef val_search_domains,
2610 CFStringRef val_domain_name,
2611 CFArrayRef val_servers,
2612 CFArrayRef val_sortlist)
2613 {
2614 FILE * f = fopen(VAR_RUN_RESOLV_CONF "-", "w");
2615
2616 /* publish new resolv.conf */
2617 if (f) {
2618 CFIndex i;
2619 CFIndex n;
2620
2621 SCPrint(TRUE, f, CFSTR("#\n"));
2622 SCPrint(TRUE, f, CFSTR("# Mac OS X Notice\n"));
2623 SCPrint(TRUE, f, CFSTR("#\n"));
2624 SCPrint(TRUE, f, CFSTR("# This file is not used by the host name and address resolution\n"));
2625 SCPrint(TRUE, f, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
2626 SCPrint(TRUE, f, CFSTR("# this Mac OS X system.\n"));
2627 SCPrint(TRUE, f, CFSTR("#\n"));
2628 SCPrint(TRUE, f, CFSTR("# This file is automatically generated.\n"));
2629 SCPrint(TRUE, f, CFSTR("#\n"));
2630
2631 if (isA_CFString(val_domain_name)) {
2632 SCPrint(TRUE, f, CFSTR("domain %@\n"), val_domain_name);
2633 }
2634
2635 if (isA_CFArray(val_search_domains)) {
2636 SCPrint(TRUE, f, CFSTR("search"));
2637 n = CFArrayGetCount(val_search_domains);
2638 for (i = 0; i < n; i++) {
2639 CFStringRef domain;
2640
2641 domain = CFArrayGetValueAtIndex(val_search_domains, i);
2642 if (isA_CFString(domain)) {
2643 SCPrint(TRUE, f, CFSTR(" %@"), domain);
2644 }
2645 }
2646 SCPrint(TRUE, f, CFSTR("\n"));
2647 }
2648
2649 if (isA_CFArray(val_servers)) {
2650 n = CFArrayGetCount(val_servers);
2651 for (i = 0; i < n; i++) {
2652 CFStringRef nameserver;
2653
2654 nameserver = CFArrayGetValueAtIndex(val_servers, i);
2655 if (isA_CFString(nameserver)) {
2656 SCPrint(TRUE, f, CFSTR("nameserver %@\n"), nameserver);
2657 }
2658 }
2659 }
2660
2661 if (isA_CFArray(val_sortlist)) {
2662 SCPrint(TRUE, f, CFSTR("sortlist"));
2663 n = CFArrayGetCount(val_sortlist);
2664 for (i = 0; i < n; i++) {
2665 CFStringRef address;
2666
2667 address = CFArrayGetValueAtIndex(val_sortlist, i);
2668 if (isA_CFString(address)) {
2669 SCPrint(TRUE, f, CFSTR(" %@"), address);
2670 }
2671 }
2672 SCPrint(TRUE, f, CFSTR("\n"));
2673 }
2674
2675 fclose(f);
2676 rename(VAR_RUN_RESOLV_CONF "-", VAR_RUN_RESOLV_CONF);
2677 }
2678 return;
2679 }
2680 #endif /* !TARGET_OS_IPHONE */
2681
2682 static boolean_t
2683 router_is_our_ipv6_address(CFStringRef router, CFArrayRef addr_list)
2684 {
2685 CFIndex i;
2686 CFIndex n = CFArrayGetCount(addr_list);
2687 struct in6_addr r;
2688
2689 (void)cfstring_to_ip6(router, &r);
2690 for (i = 0; i < n; i++) {
2691 struct in6_addr ip;
2692
2693 if (cfstring_to_ip6(CFArrayGetValueAtIndex(addr_list, i), &ip)
2694 && bcmp(&r, &ip, sizeof(r)) == 0) {
2695 return (TRUE);
2696 }
2697 }
2698 return (FALSE);
2699 }
2700
2701 static IPv4RouteListRef
2702 service_dict_get_ipv4_routelist(CFDictionaryRef service_dict)
2703 {
2704 CFDataRef data;
2705 IPv4RouteListRef routes = NULL;
2706
2707 data = (CFDataRef)CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
2708 if (data != NULL) {
2709 routes = (IPv4RouteListRef)CFDataGetBytePtr(data);
2710 }
2711 return (routes);
2712 }
2713
2714 typedef struct apply_ipv4_route_context {
2715 IPv4RouteListRef old;
2716 IPv4RouteListRef new;
2717 int sockfd;
2718 } apply_ipv4_route_context_t;
2719
2720 /* add/remove a router/32 subnet */
2721 static int
2722 ipv4_route_gateway(int sockfd, int cmd, char * ifn_p,
2723 IPv4RouteRef def_route)
2724 {
2725 struct in_addr mask;
2726
2727 mask.s_addr = htonl(INADDR_BROADCAST);
2728 return (ipv4_route(sockfd, cmd, def_route->ifa,
2729 def_route->gateway, mask, ifn_p, def_route->ifindex,
2730 def_route->ifa,
2731 (def_route->flags & kRouteWantScopedFlag)));
2732 }
2733
2734 /*
2735 * Function: apply_ipv4_route
2736 * Purpose:
2737 * Callback function that adds/removes the specified route.
2738 */
2739 static void
2740 apply_ipv4_route(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg)
2741 {
2742 apply_ipv4_route_context_t *context = (apply_ipv4_route_context_t *)arg;
2743 char * ifn_p;
2744 int retval;
2745
2746 ifn_p = route->ifname;
2747 switch (cmd) {
2748 case kIPv4RouteListAddRouteCommand:
2749 if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) {
2750 retval = ipv4_route_gateway(context->sockfd, RTM_ADD,
2751 ifn_p, route);
2752 if (retval == EEXIST) {
2753 /* delete and add again */
2754 (void)ipv4_route_gateway(context->sockfd, RTM_DELETE,
2755 ifn_p, route);
2756 retval = ipv4_route_gateway(context->sockfd, RTM_ADD,
2757 ifn_p, route);
2758 }
2759 if (retval != 0) {
2760 SCLog(TRUE, LOG_NOTICE,
2761 CFSTR("IPMonitor apply_ipv4_route failed to add"
2762 " %s/32 route, %s"),
2763 inet_ntoa(route->gateway), strerror(retval));
2764 }
2765 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2766 SCLog(TRUE, LOG_NOTICE, CFSTR("Added IPv4 Route %s/32"),
2767 inet_ntoa(route->gateway));
2768 }
2769 }
2770 retval = ipv4_route(context->sockfd,
2771 RTM_ADD, route->gateway,
2772 route->dest, route->mask, ifn_p, route->ifindex,
2773 route->ifa, route->flags);
2774 if (retval == EEXIST) {
2775 /* delete and add again */
2776 (void)ipv4_route(context->sockfd,
2777 RTM_DELETE, route->gateway,
2778 route->dest, route->mask, ifn_p, route->ifindex,
2779 route->ifa, route->flags);
2780 retval = ipv4_route(context->sockfd,
2781 RTM_ADD, route->gateway,
2782 route->dest, route->mask,
2783 ifn_p, route->ifindex,
2784 route->ifa, route->flags);
2785 }
2786 if (retval != 0) {
2787 SCLog(TRUE, LOG_NOTICE,
2788 CFSTR("IPMonitor apply_ipv4_route failed to add"
2789 " route, %s:"), strerror(retval));
2790 IPv4RouteLog(route);
2791 }
2792 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2793 SCLog(TRUE, LOG_NOTICE,
2794 CFSTR("Added IPv4 route new[%d] = "),
2795 route - context->new->list);
2796 IPv4RouteLog(route);
2797 }
2798 break;
2799 case kIPv4RouteListRemoveRouteCommand:
2800 retval = ipv4_route(context->sockfd,
2801 RTM_DELETE, route->gateway,
2802 route->dest, route->mask, ifn_p, route->ifindex,
2803 route->ifa, route->flags);
2804 if (retval != 0) {
2805 if (retval != ESRCH) {
2806 SCLog(TRUE, LOG_NOTICE,
2807 CFSTR("IPMonitor apply_ipv4_route failed to remove"
2808 " route, %s: "), strerror(retval));
2809 IPv4RouteLog(route);
2810 }
2811 }
2812 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2813 SCLog(TRUE, LOG_NOTICE,
2814 CFSTR("Removed IPv4 route old[%d] = "),
2815 route - context->old->list);
2816 IPv4RouteLog(route);
2817 }
2818 if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) {
2819 retval = ipv4_route_gateway(context->sockfd, RTM_DELETE,
2820 ifn_p, route);
2821 if (retval != 0) {
2822 SCLog(TRUE, LOG_NOTICE,
2823 CFSTR("IPMonitor apply_ipv4_route failed to remove"
2824 " %s/32 route, %s: "),
2825 inet_ntoa(route->gateway), strerror(retval));
2826 }
2827 else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2828 SCLog(TRUE, LOG_NOTICE, CFSTR("Removed IPv4 Route %s/32"),
2829 inet_ntoa(route->gateway));
2830 }
2831 }
2832 break;
2833 default:
2834 break;
2835 }
2836 return;
2837 }
2838
2839 /*
2840 * Function: update_ipv4
2841 *
2842 * Purpose:
2843 * Update the IPv4 configuration based on the latest information.
2844 * Publish the State:/Network/Global/IPv4 information, and update the
2845 * IPv4 routing table. IPv4RouteListApply() invokes our callback,
2846 * apply_ipv4_route(), to install/remove the routes.
2847 */
2848 static void
2849 update_ipv4(CFStringRef primary,
2850 IPv4RouteListRef new_routelist,
2851 keyChangeListRef keys)
2852 {
2853 apply_ipv4_route_context_t context;
2854
2855 if (keys != NULL) {
2856 if (new_routelist != NULL && primary != NULL) {
2857 char * ifn_p = NULL;
2858 IPv4RouteRef r;
2859 CFMutableDictionaryRef dict = NULL;
2860
2861 dict = CFDictionaryCreateMutable(NULL, 0,
2862 &kCFTypeDictionaryKeyCallBacks,
2863 &kCFTypeDictionaryValueCallBacks);
2864 /* the first entry is the default route */
2865 r = new_routelist->list;
2866 if (r->gateway.s_addr != 0) {
2867 CFStringRef router;
2868
2869 router = CFStringCreateWithCString(NULL,
2870 inet_ntoa(r->gateway),
2871 kCFStringEncodingASCII);
2872 if (router != NULL) {
2873 CFDictionarySetValue(dict, kSCPropNetIPv4Router, router);
2874 CFRelease(router);
2875 }
2876 }
2877 if (r->ifname[0] != '\0') {
2878 ifn_p = r->ifname;
2879 }
2880 if (ifn_p != NULL) {
2881 CFStringRef ifname_cf;
2882
2883 ifname_cf = CFStringCreateWithCString(NULL,
2884 ifn_p,
2885 kCFStringEncodingASCII);
2886 if (ifname_cf != NULL) {
2887 CFDictionarySetValue(dict,
2888 kSCDynamicStorePropNetPrimaryInterface,
2889 ifname_cf);
2890 CFRelease(ifname_cf);
2891 }
2892 }
2893 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
2894 primary);
2895 keyChangeListSetValue(keys, S_state_global_ipv4, dict);
2896 CFRelease(dict);
2897 }
2898 else {
2899 keyChangeListRemoveValue(keys, S_state_global_ipv4);
2900 }
2901 }
2902
2903 bzero(&context, sizeof(context));
2904 context.sockfd = ipv4_route_open_socket();
2905 if (context.sockfd != -1) {
2906 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2907 if (S_ipv4_routelist == NULL) {
2908 SCLog(TRUE, LOG_NOTICE, CFSTR("Old Routes = <none>"));
2909 }
2910 else {
2911 SCLog(TRUE, LOG_NOTICE, CFSTR("Old Routes = "));
2912 IPv4RouteListLog(S_ipv4_routelist);
2913 }
2914 if (new_routelist == NULL) {
2915 SCLog(TRUE, LOG_NOTICE, CFSTR("New Routes = <none>"));
2916 }
2917 else {
2918 SCLog(TRUE, LOG_NOTICE, CFSTR("New Routes = "));
2919 IPv4RouteListLog(new_routelist);
2920 }
2921 }
2922 context.old = S_ipv4_routelist;
2923 context.new = new_routelist;
2924 IPv4RouteListApply(S_ipv4_routelist, new_routelist,
2925 &apply_ipv4_route, (void *)&context);
2926 if (new_routelist != NULL) {
2927 (void)multicast_route_delete(context.sockfd);
2928 }
2929 else {
2930 (void)multicast_route_add(context.sockfd);
2931 }
2932 close(context.sockfd);
2933 }
2934 if (S_ipv4_routelist != NULL) {
2935 free(S_ipv4_routelist);
2936 }
2937 S_ipv4_routelist = new_routelist;
2938 return;
2939 }
2940
2941 static void
2942 update_ipv6(CFDictionaryRef service_info,
2943 CFStringRef primary,
2944 keyChangeListRef keys)
2945 {
2946 CFDictionaryRef ipv6_dict = NULL;
2947
2948 if (primary != NULL) {
2949 CFDictionaryRef service_dict;
2950
2951 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
2952 if (service_dict != NULL) {
2953 ipv6_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
2954 }
2955 }
2956 if (ipv6_dict != NULL) {
2957 CFArrayRef addrs;
2958 CFMutableDictionaryRef dict = NULL;
2959 CFStringRef if_name = NULL;
2960 char ifn[IFNAMSIZ] = { '\0' };
2961 char * ifn_p = NULL;
2962 boolean_t is_direct = FALSE;
2963 CFStringRef val_router = NULL;
2964
2965 dict = CFDictionaryCreateMutable(NULL, 0,
2966 &kCFTypeDictionaryKeyCallBacks,
2967 &kCFTypeDictionaryValueCallBacks);
2968 val_router = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Router);
2969 addrs = CFDictionaryGetValue(ipv6_dict,
2970 kSCPropNetIPv6Addresses);
2971 if (val_router != NULL) {
2972 /* no router if router is one of our IP addresses */
2973 is_direct = router_is_our_ipv6_address(val_router, addrs);
2974 CFDictionarySetValue(dict, kSCPropNetIPv6Router,
2975 val_router);
2976 }
2977 else {
2978 val_router = CFArrayGetValueAtIndex(addrs, 0);
2979 is_direct = TRUE;
2980 }
2981 if_name = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
2982 if (if_name) {
2983 CFDictionarySetValue(dict,
2984 kSCDynamicStorePropNetPrimaryInterface,
2985 if_name);
2986 if (CFStringGetCString(if_name, ifn, sizeof(ifn),
2987 kCFStringEncodingASCII)) {
2988 ifn_p = ifn;
2989 }
2990 }
2991 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
2992 primary);
2993 keyChangeListSetValue(keys, S_state_global_ipv6, dict);
2994 CFRelease(dict);
2995
2996 { /* route add default ... */
2997 struct in6_addr router;
2998
2999 (void)cfstring_to_ip6(val_router, &router);
3000 set_ipv6_router(&router, ifn_p, is_direct);
3001 }
3002 }
3003 else {
3004 keyChangeListRemoveValue(keys, S_state_global_ipv6);
3005 set_ipv6_router(NULL, NULL, FALSE);
3006 }
3007 return;
3008 }
3009
3010 static void
3011 update_dns(CFDictionaryRef service_info,
3012 CFStringRef primary,
3013 keyChangeListRef keys)
3014 {
3015 CFDictionaryRef dict = NULL;
3016
3017 if (primary != NULL) {
3018 CFDictionaryRef service_dict;
3019
3020 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3021 if (service_dict != NULL) {
3022 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
3023 }
3024 }
3025 if (dict == NULL) {
3026 #if !TARGET_OS_IPHONE
3027 empty_dns();
3028 #endif /* !TARGET_OS_IPHONE */
3029 keyChangeListRemoveValue(keys, S_state_global_dns);
3030 }
3031 else {
3032 #if !TARGET_OS_IPHONE
3033 set_dns(CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains),
3034 CFDictionaryGetValue(dict, kSCPropNetDNSDomainName),
3035 CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses),
3036 CFDictionaryGetValue(dict, kSCPropNetDNSSortList));
3037 #endif /* !TARGET_OS_IPHONE */
3038 keyChangeListSetValue(keys, S_state_global_dns, dict);
3039 }
3040 return;
3041 }
3042
3043 static void
3044 update_dnsinfo(CFDictionaryRef service_info,
3045 CFStringRef primary,
3046 keyChangeListRef keys,
3047 CFArrayRef service_order)
3048 {
3049 CFDictionaryRef dict = NULL;
3050 CFArrayRef multicastResolvers;
3051 CFArrayRef privateResolvers;
3052
3053 multicastResolvers = CFDictionaryGetValue(service_info, S_multicast_resolvers);
3054 privateResolvers = CFDictionaryGetValue(service_info, S_private_resolvers);
3055
3056 if (primary != NULL) {
3057 CFDictionaryRef service_dict;
3058
3059 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3060 if (service_dict != NULL) {
3061 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
3062 }
3063 }
3064
3065 dns_configuration_set(dict,
3066 S_service_state_dict,
3067 service_order,
3068 multicastResolvers,
3069 privateResolvers);
3070 keyChangeListNotifyKey(keys, S_state_global_dns);
3071 return;
3072 }
3073
3074 static void
3075 update_proxies(CFDictionaryRef service_info,
3076 CFStringRef primary,
3077 keyChangeListRef keys)
3078 {
3079 CFDictionaryRef dict = NULL;
3080
3081 if (primary != NULL) {
3082 CFDictionaryRef service_dict;
3083
3084 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3085 if (service_dict != NULL) {
3086 dict = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
3087 if (dict == NULL) {
3088 dict = my_CFDictionaryGetDictionary(service_info,
3089 S_setup_global_proxies);
3090 }
3091 }
3092 }
3093 if (dict == NULL) {
3094 keyChangeListRemoveValue(keys, S_state_global_proxies);
3095 }
3096 else {
3097 keyChangeListSetValue(keys, S_state_global_proxies, dict);
3098 }
3099 return;
3100 }
3101
3102 #if !TARGET_OS_IPHONE
3103 static void
3104 update_smb(CFDictionaryRef service_info,
3105 CFStringRef primary,
3106 keyChangeListRef keys)
3107 {
3108 CFDictionaryRef dict = NULL;
3109
3110 if (primary != NULL) {
3111 CFDictionaryRef service_dict;
3112
3113 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3114 if (service_dict != NULL) {
3115 dict = CFDictionaryGetValue(service_dict, kSCEntNetSMB);
3116 if (dict == NULL) {
3117 dict = my_CFDictionaryGetDictionary(service_info,
3118 S_setup_global_smb);
3119 }
3120 }
3121 }
3122 if (dict == NULL) {
3123 keyChangeListRemoveValue(keys, S_state_global_smb);
3124 }
3125 else {
3126 keyChangeListSetValue(keys, S_state_global_smb, dict);
3127 }
3128
3129 return;
3130 }
3131 #endif /* !TARGET_OS_IPHONE */
3132
3133 static Rank
3134 get_service_rank(CFArrayRef order, int n_order, CFStringRef serviceID)
3135 {
3136 CFIndex i;
3137 Rank rank = kRankLast;
3138
3139 if (serviceID != NULL && order != NULL && n_order > 0) {
3140 for (i = 0; i < n_order; i++) {
3141 CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(order, i));
3142
3143 if (s == NULL) {
3144 continue;
3145 }
3146 if (CFEqual(serviceID, s)) {
3147 rank = i + 1;
3148 break;
3149 }
3150 }
3151 }
3152 return (rank);
3153 }
3154
3155 /**
3156 ** Service election:
3157 **/
3158 /*
3159 * Function: rank_dict_get_service_rank
3160 * Purpose:
3161 * Retrieve the service rank in the given dictionary.
3162 */
3163 static Rank
3164 rank_dict_get_service_rank(CFDictionaryRef rank_dict, CFStringRef serviceID)
3165 {
3166 CFNumberRef rank;
3167 Rank rank_val = kRankLast;
3168
3169 rank = CFDictionaryGetValue(rank_dict, serviceID);
3170 if (rank != NULL) {
3171 CFNumberGetValue(rank, kCFNumberSInt32Type, &rank_val);
3172 }
3173 return (rank_val);
3174 }
3175
3176 /*
3177 * Function: rank_dict_set_service_rank
3178 * Purpose:
3179 * Save the results of ranking the service so we can look it up later without
3180 * repeating all of the ranking code.
3181 */
3182 static void
3183 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict,
3184 CFStringRef serviceID, Rank rank_val)
3185 {
3186 CFNumberRef rank;
3187
3188 rank = CFNumberCreate(NULL, kCFNumberSInt32Type, (const void *)&rank_val);
3189 if (rank != NULL) {
3190 CFDictionarySetValue(rank_dict, serviceID, rank);
3191 CFRelease(rank);
3192 }
3193 return;
3194 }
3195
3196 typedef struct election_info {
3197 int n_services;
3198 CFArrayRef order;
3199 int n_order;
3200 CFStringRef serviceID;
3201 CFDictionaryRef service_dict;
3202 Rank service_rank;
3203 boolean_t choose_last;
3204 } election_info_t;
3205
3206 typedef boolean_t election_func_t(void * context, election_info_t * info);
3207
3208 /*
3209 * Function: elect_ipv4
3210 * Purpose:
3211 * This function builds the list of IPv4 routes that should be active.
3212 * As elect_new_primary() invokes us with each service, we build up the
3213 * result in the passed in context, a pointer to an IPv4RouteListRef.
3214 */
3215 static boolean_t
3216 elect_ipv4(void * context, election_info_t * info)
3217 {
3218 IPv4RouteListRef * routes_p = (IPv4RouteListRef *)context;
3219 IPv4RouteListRef service_routes;
3220
3221 service_routes = service_dict_get_ipv4_routelist(info->service_dict);
3222 if (service_routes == NULL) {
3223 return (FALSE);
3224 }
3225 if ((service_routes->list->flags & kRouteChooseFirstFlag) != 0) {
3226 info->service_rank = kRankFirst;
3227 }
3228 else if (S_ppp_override_primary
3229 && (strncmp(PPP_PREFIX, service_routes->list->ifname,
3230 sizeof(PPP_PREFIX) - 1) == 0)) {
3231 /* PPP override: make ppp* look the best */
3232 /* Hack: should use interface type, not interface name */
3233 info->service_rank = kRankFirst;
3234 }
3235 else {
3236 info->service_rank = get_service_rank(info->order, info->n_order,
3237 info->serviceID);
3238 if ((service_routes->list->flags & kRouteChooseLastFlag) != 0) {
3239 info->choose_last = TRUE;
3240 }
3241 }
3242 if (routes_p != NULL) {
3243 *routes_p = IPv4RouteListAddRouteList(*routes_p,
3244 info->n_services * 3,
3245 service_routes,
3246 info->service_rank);
3247 }
3248 if ((service_routes->list->flags & kRouteChooseNeverFlag) != 0) {
3249 /* never elect as primary */
3250 return (FALSE);
3251 }
3252 rank_dict_set_service_rank(S_ipv4_service_rank_dict,
3253 info->serviceID, info->service_rank);
3254 return (TRUE);
3255 }
3256
3257 static boolean_t
3258 elect_ipv6(void * context, election_info_t * info)
3259 {
3260 CFStringRef if_name;
3261 CFStringRef primaryRank = NULL;
3262 CFDictionaryRef proto_dict;
3263 CFStringRef router;
3264 CFDictionaryRef service_options;
3265
3266 proto_dict = CFDictionaryGetValue(info->service_dict, kSCEntNetIPv6);
3267 if (proto_dict == NULL) {
3268 return (FALSE);
3269 }
3270 service_options = service_dict_get(info->serviceID, kSCEntNetService);
3271 if (service_options != NULL) {
3272 primaryRank = CFDictionaryGetValue(service_options, kSCPropNetServicePrimaryRank);
3273 if ((primaryRank != NULL)
3274 && CFEqual(primaryRank, kSCValNetServicePrimaryRankNever)) {
3275 return (FALSE);
3276 }
3277 }
3278 router = CFDictionaryGetValue(proto_dict,
3279 kSCPropNetIPv6Router);
3280 if (router == NULL) {
3281 info->choose_last = TRUE;
3282 info->service_rank = kRankLast;
3283 }
3284 else if ((primaryRank != NULL)
3285 && CFEqual(primaryRank, kSCValNetServicePrimaryRankFirst)) {
3286 info->service_rank = kRankFirst;
3287 }
3288 else if (get_override_primary(proto_dict)) {
3289 info->service_rank = kRankFirst;
3290 }
3291 else if (S_ppp_override_primary
3292 && CFDictionaryGetValueIfPresent(proto_dict,
3293 kSCPropInterfaceName,
3294 (const void **)&if_name)
3295 && CFStringHasPrefix(if_name, CFSTR(PPP_PREFIX))) {
3296 /* PPP override: make ppp* look the best */
3297 /* Hack: should use interface type, not interface name */
3298 info->service_rank = kRankFirst;
3299 }
3300 else {
3301 info->service_rank = get_service_rank(info->order, info->n_order,
3302 info->serviceID);
3303 }
3304 rank_dict_set_service_rank(S_ipv6_service_rank_dict,
3305 info->serviceID, info->service_rank);
3306 return (TRUE);
3307 }
3308
3309 /*
3310 * Function: elect_new_primary
3311 * Purpose:
3312 * Walk the list of services, passing each service dictionary to "elect_func".
3313 * "elect_func" returns rank information about the service that let us
3314 * determine the new primary.
3315 */
3316 static CFStringRef
3317 elect_new_primary(election_func_t * elect_func, void * context,
3318 CFArrayRef order, int n_order)
3319 {
3320 int count;
3321 int i;
3322 election_info_t info;
3323 void * * keys;
3324 #define N_KEYS_VALUES_STATIC 10
3325 void * keys_values_buf[N_KEYS_VALUES_STATIC * 2];
3326 CFStringRef new_primary = NULL;
3327 Rank new_primary_rank = kRankLast;
3328 boolean_t new_primary_choose_last = FALSE;
3329 void * * values;
3330
3331 count = CFDictionaryGetCount(S_service_state_dict);
3332 if (count <= N_KEYS_VALUES_STATIC) {
3333 keys = keys_values_buf;
3334 }
3335 else {
3336 keys = (void * *)malloc(sizeof(*keys) * count * 2);
3337 }
3338 values = keys + count;
3339 CFDictionaryGetKeysAndValues(S_service_state_dict,
3340 (const void * *)keys,
3341 (const void * *)values);
3342
3343 info.n_services = count;
3344 info.order = order;
3345 info.n_order = n_order;
3346 for (i = 0; i < count; i++) {
3347 boolean_t found_new_primary = FALSE;
3348
3349 info.serviceID = (CFStringRef)keys[i];
3350 info.service_dict = (CFDictionaryRef)values[i];
3351 info.service_rank = kRankLast;
3352 info.choose_last = FALSE;
3353
3354 if ((*elect_func)(context, &info) == FALSE) {
3355 continue;
3356 }
3357 if (new_primary == NULL) {
3358 found_new_primary = TRUE;
3359 }
3360 else if (info.choose_last == new_primary_choose_last) {
3361 found_new_primary = (info.service_rank < new_primary_rank);
3362 }
3363 else if (new_primary_choose_last) {
3364 found_new_primary = TRUE;
3365 }
3366 if (found_new_primary) {
3367 new_primary = info.serviceID;
3368 new_primary_rank = info.service_rank;
3369 new_primary_choose_last = info.choose_last;
3370 }
3371 }
3372 if (new_primary != NULL) {
3373 CFRetain(new_primary);
3374 }
3375 if (keys != keys_values_buf) {
3376 free(keys);
3377 }
3378 return (new_primary);
3379 }
3380
3381 static uint32_t
3382 service_changed(CFDictionaryRef services_info, CFStringRef serviceID)
3383 {
3384 uint32_t changed = 0;
3385 int i;
3386
3387 /* update service options first (e.g. rank) */
3388 if (get_rank_changes(serviceID,
3389 get_service_state_entity(services_info, serviceID,
3390 NULL),
3391 get_service_setup_entity(services_info, serviceID,
3392 NULL),
3393 services_info)) {
3394 changed |= (1 << kEntityTypeServiceOptions);
3395 }
3396 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
3397 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
3398 GetEntityChangesFuncRef func = entityChangeFunc[i];
3399 if ((*func)(serviceID,
3400 get_service_state_entity(services_info, serviceID,
3401 *entityTypeNames[i]),
3402 get_service_setup_entity(services_info, serviceID,
3403 *entityTypeNames[i]),
3404 services_info)) {
3405 changed |= (1 << i);
3406 }
3407 }
3408 return (changed);
3409 }
3410
3411 static CFArrayRef
3412 service_order_get(CFDictionaryRef services_info)
3413 {
3414 CFArrayRef order = NULL;
3415 CFDictionaryRef ipv4_dict;
3416
3417 ipv4_dict = my_CFDictionaryGetDictionary(services_info,
3418 S_setup_global_ipv4);
3419 if (ipv4_dict != NULL) {
3420 CFNumberRef ppp_override;
3421 int ppp_val = 0;
3422
3423 order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
3424 order = isA_CFArray(order);
3425
3426 /* get ppp override primary */
3427 ppp_override = CFDictionaryGetValue(ipv4_dict,
3428 kSCPropNetPPPOverridePrimary);
3429 ppp_override = isA_CFNumber(ppp_override);
3430 if (ppp_override != NULL) {
3431 CFNumberGetValue(ppp_override, kCFNumberIntType, &ppp_val);
3432 }
3433 S_ppp_override_primary = (ppp_val != 0) ? TRUE : FALSE;
3434 }
3435 else {
3436 S_ppp_override_primary = FALSE;
3437 }
3438 return (order);
3439 }
3440
3441 static boolean_t
3442 set_new_primary(CFStringRef * primary_p, CFStringRef new_primary,
3443 const char * entity)
3444 {
3445 boolean_t changed = FALSE;
3446 CFStringRef primary = *primary_p;
3447
3448 if (new_primary != NULL) {
3449 if (primary != NULL && CFEqual(new_primary, primary)) {
3450 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3451 SCLog(TRUE, LOG_NOTICE,
3452 CFSTR("IPMonitor: %@ is still primary %s"),
3453 new_primary, entity);
3454 }
3455 }
3456 else {
3457 my_CFRelease(primary_p);
3458 *primary_p = CFRetain(new_primary);
3459 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3460 SCLog(TRUE, LOG_NOTICE,
3461 CFSTR("IPMonitor: %@ is the new primary %s"),
3462 new_primary, entity);
3463 }
3464 changed = TRUE;
3465 }
3466 }
3467 else if (primary != NULL) {
3468 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3469 SCLog(TRUE, LOG_NOTICE,
3470 CFSTR("IPMonitor: %@ is no longer primary %s"),
3471 primary, entity);
3472 }
3473 my_CFRelease(primary_p);
3474 changed = TRUE;
3475 }
3476 return (changed);
3477 }
3478
3479 static Rank
3480 rank_service_entity(CFDictionaryRef rank_dict, CFStringRef serviceID,
3481 CFStringRef entity)
3482 {
3483 if (service_dict_get(serviceID, entity) == NULL) {
3484 return (kRankLast);
3485 }
3486 return (rank_dict_get_service_rank(rank_dict, serviceID));
3487 }
3488
3489 static void
3490 IPMonitorNotify(SCDynamicStoreRef session, CFArrayRef changed_keys,
3491 void * not_used)
3492 {
3493 CFIndex count;
3494 boolean_t dnsinfo_changed = FALSE;
3495 boolean_t global_ipv4_changed = FALSE;
3496 boolean_t global_ipv6_changed = FALSE;
3497 int i;
3498 keyChangeList keys;
3499 CFIndex n;
3500 int n_service_order = 0;
3501 CFArrayRef service_order;
3502 CFMutableArrayRef service_changes = NULL;
3503 CFDictionaryRef services_info = NULL;
3504
3505 count = CFArrayGetCount(changed_keys);
3506 if (count == 0) {
3507 return;
3508 }
3509
3510 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3511 SCLog(TRUE, LOG_NOTICE,
3512 CFSTR("IPMonitor: changes %@ (%d)"), changed_keys, count);
3513 }
3514
3515 keyChangeListInit(&keys);
3516 service_changes = CFArrayCreateMutable(NULL, 0,
3517 &kCFTypeArrayCallBacks);
3518 for (i = 0; i < count; i++) {
3519 CFStringRef change = CFArrayGetValueAtIndex(changed_keys, i);
3520 if (CFEqual(change, S_setup_global_ipv4)) {
3521 global_ipv4_changed = TRUE;
3522 global_ipv6_changed = TRUE;
3523 }
3524 else if (CFEqual(change, S_setup_global_proxies)) {
3525 if (S_primary_proxies != NULL) {
3526 my_CFArrayAppendUniqueValue(service_changes, S_primary_proxies);
3527 }
3528 }
3529 #if !TARGET_OS_IPHONE
3530 else if (CFEqual(change, S_setup_global_smb)) {
3531 if (S_primary_smb != NULL) {
3532 my_CFArrayAppendUniqueValue(service_changes, S_primary_smb);
3533 }
3534 }
3535 #endif /* !TARGET_OS_IPHONE */
3536 else if (CFEqual(change, S_multicast_resolvers)) {
3537 dnsinfo_changed = TRUE;
3538 }
3539 else if (CFEqual(change, S_private_resolvers)) {
3540 dnsinfo_changed = TRUE;
3541 }
3542 #if !TARGET_OS_IPHONE
3543 else if (CFEqual(change, CFSTR(_PATH_RESOLVER_DIR))) {
3544 dnsinfo_changed = TRUE;
3545 }
3546 #endif /* !TARGET_OS_IPHONE */
3547 else if (CFStringHasPrefix(change, S_state_service_prefix)) {
3548 CFStringRef serviceID = parse_component(change,
3549 S_state_service_prefix);
3550 if (serviceID) {
3551 my_CFArrayAppendUniqueValue(service_changes, serviceID);
3552 CFRelease(serviceID);
3553 }
3554 }
3555 else if (CFStringHasPrefix(change, S_setup_service_prefix)) {
3556 CFStringRef serviceID = parse_component(change,
3557 S_setup_service_prefix);
3558 if (serviceID) {
3559 my_CFArrayAppendUniqueValue(service_changes, serviceID);
3560 CFRelease(serviceID);
3561 }
3562 }
3563 }
3564
3565 /* grab a snapshot of everything we need */
3566 services_info = services_info_copy(session, service_changes);
3567 service_order = service_order_get(services_info);
3568 if (service_order != NULL) {
3569 n_service_order = CFArrayGetCount(service_order);
3570 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3571 SCLog(TRUE, LOG_NOTICE,
3572 CFSTR("IPMonitor: service_order %@ "), service_order);
3573 }
3574 }
3575 n = CFArrayGetCount(service_changes);
3576 for (i = 0; i < n; i++) {
3577 uint32_t changes;
3578 CFStringRef serviceID;
3579 Boolean wasSupplemental;
3580
3581 serviceID = CFArrayGetValueAtIndex(service_changes, i);
3582 wasSupplemental = dns_has_supplemental(serviceID);
3583 changes = service_changed(services_info, serviceID);
3584 if ((changes & (1 << kEntityTypeServiceOptions)) != 0) {
3585 /* if __Service__ (e.g. PrimaryRank) changed */
3586 global_ipv4_changed = TRUE;
3587 }
3588 else if (S_primary_ipv4 != NULL && CFEqual(S_primary_ipv4, serviceID)) {
3589 // if we are looking at the primary [IPv4] service
3590 if ((changes & (1 << kEntityTypeIPv4)) != 0) {
3591 // and something changed for THIS service
3592 global_ipv4_changed = TRUE;
3593 }
3594 }
3595 else if ((changes & (1 << kEntityTypeIPv4)) != 0) {
3596 global_ipv4_changed = TRUE;
3597 }
3598 if ((changes & (1 << kEntityTypeIPv6)) != 0) {
3599 // if we are looking at the primary [IPv6] service
3600 if (S_primary_ipv6 != NULL && CFEqual(S_primary_ipv6, serviceID)) {
3601 update_ipv6(services_info, serviceID, &keys);
3602 }
3603 // and something changed for THIS service
3604 global_ipv6_changed = TRUE;
3605 }
3606 if ((changes & (1 << kEntityTypeDNS)) != 0) {
3607 if (S_primary_dns != NULL && CFEqual(S_primary_dns, serviceID)) {
3608 update_dns(services_info, serviceID, &keys);
3609 dnsinfo_changed = TRUE;
3610 }
3611 else if (wasSupplemental || dns_has_supplemental(serviceID)) {
3612 dnsinfo_changed = TRUE;
3613 }
3614 }
3615 if ((changes & (1 << kEntityTypeProxies)) != 0) {
3616 if (S_primary_proxies != NULL && CFEqual(S_primary_proxies, serviceID)) {
3617 update_proxies(services_info, serviceID, &keys);
3618 }
3619 }
3620 #if !TARGET_OS_IPHONE
3621 if ((changes & (1 << kEntityTypeSMB)) != 0) {
3622 if (S_primary_smb != NULL && CFEqual(S_primary_smb, serviceID)) {
3623 update_smb(services_info, serviceID, &keys);
3624 }
3625 }
3626 #endif /* !TARGET_OS_IPHONE */
3627 }
3628
3629 if (global_ipv4_changed) {
3630 IPv4RouteListRef new_routelist = NULL;
3631 CFStringRef new_primary;
3632
3633 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3634 SCLog(TRUE, LOG_NOTICE,
3635 CFSTR("IPMonitor: IPv4 service election"));
3636 }
3637 new_primary = elect_new_primary(&elect_ipv4, &new_routelist,
3638 service_order, n_service_order);
3639 (void)set_new_primary(&S_primary_ipv4, new_primary, "IPv4");
3640 update_ipv4(S_primary_ipv4, new_routelist, &keys);
3641 my_CFRelease(&new_primary);
3642 }
3643 if (global_ipv6_changed) {
3644 CFStringRef new_primary;
3645
3646 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3647 SCLog(TRUE, LOG_NOTICE,
3648 CFSTR("IPMonitor: IPv6 service election"));
3649 }
3650 new_primary = elect_new_primary(&elect_ipv6, NULL,
3651 service_order, n_service_order);
3652 if (set_new_primary(&S_primary_ipv6, new_primary, "IPv6")) {
3653 update_ipv6(services_info, S_primary_ipv6, &keys);
3654 }
3655 my_CFRelease(&new_primary);
3656 }
3657 if (global_ipv4_changed || global_ipv6_changed) {
3658 CFStringRef new_primary_dns = NULL;
3659 CFStringRef new_primary_proxies = NULL;
3660 #if !TARGET_OS_IPHONE
3661 CFStringRef new_primary_smb = NULL;
3662 #endif /* !TARGET_OS_IPHONE */
3663
3664 if (S_primary_ipv4 != NULL && S_primary_ipv6 != NULL) {
3665 /* decide between IPv4 and IPv6 */
3666 if (rank_service_entity(S_ipv4_service_rank_dict,
3667 S_primary_ipv4, kSCEntNetDNS)
3668 <= rank_service_entity(S_ipv6_service_rank_dict,
3669 S_primary_ipv6, kSCEntNetDNS)) {
3670 new_primary_dns = S_primary_ipv4;
3671 }
3672 else {
3673 new_primary_dns = S_primary_ipv6;
3674 }
3675 if (rank_service_entity(S_ipv4_service_rank_dict,
3676 S_primary_ipv4, kSCEntNetProxies)
3677 <= rank_service_entity(S_ipv6_service_rank_dict,
3678 S_primary_ipv6, kSCEntNetProxies)) {
3679 new_primary_proxies = S_primary_ipv4;
3680 }
3681 else {
3682 new_primary_proxies = S_primary_ipv6;
3683 }
3684 #if !TARGET_OS_IPHONE
3685 if (rank_service_entity(S_ipv4_service_rank_dict,
3686 S_primary_ipv4, kSCEntNetSMB)
3687 <= rank_service_entity(S_ipv6_service_rank_dict,
3688 S_primary_ipv6, kSCEntNetSMB)) {
3689 new_primary_smb = S_primary_ipv4;
3690 }
3691 else {
3692 new_primary_smb = S_primary_ipv6;
3693 }
3694 #endif /* !TARGET_OS_IPHONE */
3695
3696 }
3697 else if (S_primary_ipv6 != NULL) {
3698 new_primary_dns = S_primary_ipv6;
3699 new_primary_proxies = S_primary_ipv6;
3700 #if !TARGET_OS_IPHONE
3701 new_primary_smb = S_primary_ipv6;
3702 #endif /* !TARGET_OS_IPHONE */
3703 }
3704 else if (S_primary_ipv4 != NULL) {
3705 new_primary_dns = S_primary_ipv4;
3706 new_primary_proxies = S_primary_ipv4;
3707 #if !TARGET_OS_IPHONE
3708 new_primary_smb = S_primary_ipv4;
3709 #endif /* !TARGET_OS_IPHONE */
3710 }
3711
3712 if (set_new_primary(&S_primary_dns, new_primary_dns, "DNS")) {
3713 update_dns(services_info, S_primary_dns, &keys);
3714 dnsinfo_changed = TRUE;
3715 }
3716 if (set_new_primary(&S_primary_proxies, new_primary_proxies, "Proxies")) {
3717 update_proxies(services_info, S_primary_proxies, &keys);
3718 }
3719 #if !TARGET_OS_IPHONE
3720 if (set_new_primary(&S_primary_smb, new_primary_smb, "SMB")) {
3721 update_smb(services_info, S_primary_smb, &keys);
3722 }
3723 #endif /* !TARGET_OS_IPHONE */
3724 }
3725 if (dnsinfo_changed) {
3726 update_dnsinfo(services_info, S_primary_dns, &keys, service_order);
3727 }
3728 my_CFRelease(&service_changes);
3729 my_CFRelease(&services_info);
3730 keyChangeListApplyToStore(&keys, session);
3731 keyChangeListFree(&keys);
3732 return;
3733 }
3734
3735 static void
3736 ip_plugin_init()
3737 {
3738 CFMutableArrayRef keys = NULL;
3739 CFMutableArrayRef patterns = NULL;
3740 CFRunLoopSourceRef rls = NULL;
3741
3742 if (S_is_network_boot() != 0) {
3743 S_netboot = TRUE;
3744 }
3745
3746 #ifdef RTF_IFSCOPE
3747 if (S_is_scoped_routing_enabled() != 0) {
3748 S_scopedroute = TRUE;
3749 }
3750 #endif /* RTF_IFSCOPE */
3751
3752 S_session = SCDynamicStoreCreate(NULL, CFSTR("IPMonitor"),
3753 IPMonitorNotify, NULL);
3754 if (S_session == NULL) {
3755 SCLog(TRUE, LOG_ERR,
3756 CFSTR("IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s"),
3757 SCErrorString(SCError()));
3758 return;
3759 }
3760 S_state_global_ipv4
3761 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3762 kSCDynamicStoreDomainState,
3763 kSCEntNetIPv4);
3764 S_state_global_ipv6
3765 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3766 kSCDynamicStoreDomainState,
3767 kSCEntNetIPv6);
3768 S_state_global_dns
3769 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3770 kSCDynamicStoreDomainState,
3771 kSCEntNetDNS);
3772 S_state_global_proxies
3773 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3774 kSCDynamicStoreDomainState,
3775 kSCEntNetProxies);
3776 #if !TARGET_OS_IPHONE
3777 S_state_global_smb
3778 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3779 kSCDynamicStoreDomainState,
3780 kSCEntNetSMB);
3781 #endif /* !TARGET_OS_IPHONE */
3782 S_setup_global_ipv4
3783 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3784 kSCDynamicStoreDomainSetup,
3785 kSCEntNetIPv4);
3786 S_setup_global_proxies
3787 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3788 kSCDynamicStoreDomainSetup,
3789 kSCEntNetProxies);
3790 #if !TARGET_OS_IPHONE
3791 S_setup_global_smb
3792 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3793 kSCDynamicStoreDomainSetup,
3794 kSCEntNetSMB);
3795 #endif /* !TARGET_OS_IPHONE */
3796 S_state_service_prefix
3797 = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@/"),
3798 kSCDynamicStoreDomainState,
3799 kSCCompNetwork,
3800 kSCCompService);
3801 S_setup_service_prefix
3802 = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@/"),
3803 kSCDynamicStoreDomainSetup,
3804 kSCCompNetwork,
3805 kSCCompService);
3806 S_service_state_dict
3807 = CFDictionaryCreateMutable(NULL, 0,
3808 &kCFTypeDictionaryKeyCallBacks,
3809 &kCFTypeDictionaryValueCallBacks);
3810
3811 S_ipv4_service_rank_dict
3812 = CFDictionaryCreateMutable(NULL, 0,
3813 &kCFTypeDictionaryKeyCallBacks,
3814 &kCFTypeDictionaryValueCallBacks);
3815
3816 S_ipv6_service_rank_dict
3817 = CFDictionaryCreateMutable(NULL, 0,
3818 &kCFTypeDictionaryKeyCallBacks,
3819 &kCFTypeDictionaryValueCallBacks);
3820
3821 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3822 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3823
3824 /* register for State: and Setup: per-service notifications */
3825 add_service_keys(kSCCompAnyRegex, keys, patterns);
3826
3827 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
3828 CFArrayAppendValue(keys, S_setup_global_ipv4);
3829
3830 /* add notifier for multicast DNS configuration (Bonjour/.local) */
3831 S_multicast_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
3832 kSCDynamicStoreDomainState,
3833 kSCCompNetwork,
3834 CFSTR(kDNSServiceCompMulticastDNS));
3835 CFArrayAppendValue(keys, S_multicast_resolvers);
3836
3837 /* add notifier for private DNS configuration (Back to My Mac) */
3838 S_private_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
3839 kSCDynamicStoreDomainState,
3840 kSCCompNetwork,
3841 CFSTR(kDNSServiceCompPrivateDNS));
3842 CFArrayAppendValue(keys, S_private_resolvers);
3843
3844 if (!SCDynamicStoreSetNotificationKeys(S_session, keys, patterns)) {
3845 SCLog(TRUE, LOG_ERR,
3846 CFSTR("IPMonitor ip_plugin_init "
3847 "SCDynamicStoreSetNotificationKeys failed: %s"),
3848 SCErrorString(SCError()));
3849 goto done;
3850 }
3851
3852 rls = SCDynamicStoreCreateRunLoopSource(NULL, S_session, 0);
3853 if (rls == NULL) {
3854 SCLog(TRUE, LOG_ERR,
3855 CFSTR("IPMonitor ip_plugin_init "
3856 "SCDynamicStoreCreateRunLoopSource failed: %s"),
3857 SCErrorString(SCError()));
3858 goto done;
3859 }
3860
3861 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
3862 CFRelease(rls);
3863
3864 /* initialize dns configuration */
3865 dns_configuration_set(NULL, NULL, NULL, NULL, NULL);
3866 #if !TARGET_OS_IPHONE
3867 empty_dns();
3868 #endif /* !TARGET_OS_IPHONE */
3869 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_dns);
3870
3871 #if !TARGET_OS_IPHONE
3872 /* initialize SMB configuration */
3873 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_smb);
3874 #endif /* !TARGET_OS_IPHONE */
3875
3876 done:
3877 my_CFRelease(&keys);
3878 my_CFRelease(&patterns);
3879 return;
3880 }
3881
3882 __private_extern__
3883 void
3884 prime_IPMonitor()
3885 {
3886 /* initialize multicast route */
3887 update_ipv4(NULL, NULL, NULL);
3888 return;
3889 }
3890
3891 static boolean_t
3892 S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
3893 boolean_t def)
3894 {
3895 CFBooleanRef b;
3896 boolean_t ret = def;
3897
3898 b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
3899 if (b != NULL) {
3900 ret = CFBooleanGetValue(b);
3901 }
3902 return (ret);
3903 }
3904
3905 __private_extern__
3906 void
3907 load_IPMonitor(CFBundleRef bundle, Boolean bundleVerbose)
3908 {
3909 CFDictionaryRef info_dict;
3910
3911 info_dict = CFBundleGetInfoDictionary(bundle);
3912 if (info_dict != NULL) {
3913 S_append_state
3914 = S_get_plist_boolean(info_dict,
3915 CFSTR("AppendStateArrayToSetupArray"),
3916 FALSE);
3917 }
3918 if (bundleVerbose) {
3919 S_IPMonitor_debug = kDebugFlagDefault;
3920 }
3921
3922 dns_configuration_init(bundle);
3923 ip_plugin_init();
3924
3925 #if !TARGET_OS_IPHONE
3926 if (S_session != NULL) {
3927 dns_configuration_monitor(S_session, IPMonitorNotify);
3928 }
3929 #endif /* !TARGET_OS_IPHONE */
3930
3931 load_hostname((S_IPMonitor_debug & kDebugFlag1) != 0);
3932 #if !TARGET_OS_IPHONE
3933 load_smb_configuration((S_IPMonitor_debug & kDebugFlag1) != 0);
3934 #endif /* !TARGET_OS_IPHONE */
3935
3936 return;
3937 }
3938
3939
3940 #ifdef TEST_IPMONITOR
3941 #include "dns-configuration.c"
3942 #include "set-hostname.c"
3943
3944 int
3945 main(int argc, char **argv)
3946 {
3947 _sc_log = FALSE;
3948
3949 S_IPMonitor_debug = kDebugFlag1;
3950 if (argc > 1) {
3951 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
3952 }
3953
3954 load_IPMonitor(CFBundleGetMainBundle(), FALSE);
3955 prime_IPMonitor();
3956 CFRunLoopRun();
3957 /* not reached */
3958 exit(0);
3959 return 0;
3960 }
3961 #endif /* TEST_IPMONITOR */
3962
3963 #ifdef TEST_IPV4_ROUTELIST
3964 #include "dns-configuration.c"
3965 #include "set-hostname.c"
3966
3967 struct ipv4_service_contents {
3968 const char * addr;
3969 const char * mask;
3970 const char * dest;
3971 const char * router;
3972 const char * ifname;
3973 Rank rank;
3974 const CFStringRef *primaryRank;
3975 };
3976
3977 /*
3978 * addr mask dest router ifname pri primaryRank
3979 */
3980 struct ipv4_service_contents en0_10 = {
3981 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, NULL
3982 };
3983
3984 struct ipv4_service_contents en0_15 = {
3985 "10.0.0.19", "255.255.255.0", NULL, "10.0.0.1", "en0", 15, NULL
3986 };
3987
3988 struct ipv4_service_contents en0_30 = {
3989 "10.0.0.11", "255.255.255.0", NULL, "10.0.0.1", "en0", 30, NULL
3990 };
3991
3992 struct ipv4_service_contents en0_40 = {
3993 "10.0.0.12", "255.255.255.0", NULL, "10.0.0.1", "en0", 40, NULL
3994 };
3995
3996 struct ipv4_service_contents en0_50 = {
3997 "10.0.0.13", "255.255.255.0", NULL, "10.0.0.1", "en0", 50, NULL
3998 };
3999
4000 struct ipv4_service_contents en0_110 = {
4001 "192.168.2.10", "255.255.255.0", NULL, "192.168.2.1", "en0", 110, NULL
4002 };
4003
4004 struct ipv4_service_contents en0_1 = {
4005 "17.202.40.191", "255.255.252.0", NULL, "17.202.20.1", "en0", 1, NULL
4006 };
4007
4008 struct ipv4_service_contents en1_20 = {
4009 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, NULL
4010 };
4011
4012 struct ipv4_service_contents en1_2 = {
4013 "17.202.42.24", "255.255.252.0", NULL, "17.202.20.1", "en1", 2, NULL
4014 };
4015
4016 struct ipv4_service_contents en1_125 = {
4017 "192.168.2.20", "255.255.255.0", NULL, "192.168.2.1", "en1", 125, NULL
4018 };
4019
4020 struct ipv4_service_contents fw0_25 = {
4021 "192.168.2.30", "255.255.255.0", NULL, "192.168.2.1", "fw0", 25, NULL
4022 };
4023
4024 struct ipv4_service_contents fw0_21 = {
4025 "192.168.3.30", "255.255.255.0", NULL, "192.168.3.1", "fw0", 21, NULL
4026 };
4027
4028 struct ipv4_service_contents ppp0_0_1 = {
4029 "17.219.156.22", NULL, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
4030 };
4031
4032 struct ipv4_service_contents en0_test6 = {
4033 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 2, NULL
4034 };
4035 struct ipv4_service_contents en1_test6 = {
4036 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 3, NULL
4037 };
4038 struct ipv4_service_contents en2_test6 = {
4039 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL
4040 };
4041
4042 struct ipv4_service_contents en0_test7 = {
4043 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 3, NULL
4044 };
4045 struct ipv4_service_contents en1_test7 = {
4046 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 2, NULL
4047 };
4048 struct ipv4_service_contents en2_test7 = {
4049 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL
4050 };
4051 struct ipv4_service_contents fw0_test6_and_7 = {
4052 "169.254.11.33", "255.255.0.0", NULL, NULL, "fw0", UINT_MAX, NULL
4053 };
4054
4055 struct ipv4_service_contents en0_10_last = {
4056 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
4057 };
4058
4059 struct ipv4_service_contents en0_10_never = {
4060 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
4061 };
4062
4063 struct ipv4_service_contents en1_20_first = {
4064 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
4065 };
4066
4067 struct ipv4_service_contents en1_20_never = {
4068 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
4069 };
4070
4071 struct ipv4_service_contents * test1[] = {
4072 &en0_40,
4073 &en0_15,
4074 &fw0_25,
4075 &en0_30,
4076 &en1_20,
4077 &en0_50,
4078 &en0_10,
4079 NULL
4080 };
4081
4082 struct ipv4_service_contents * test2[] = {
4083 &en0_40,
4084 &fw0_25,
4085 &en0_30,
4086 &en1_20,
4087 &en0_50,
4088 &en0_10,
4089 NULL
4090 };
4091
4092 struct ipv4_service_contents * test3[] = {
4093 &en0_40,
4094 &en1_20,
4095 &en0_50,
4096 &en0_10,
4097 &en0_110,
4098 &en1_125,
4099 &fw0_25,
4100 &fw0_21,
4101 &en0_40,
4102 &en0_30,
4103 NULL
4104 };
4105
4106 struct ipv4_service_contents * test4[] = {
4107 &en0_1,
4108 &en0_40,
4109 &en0_30,
4110 &en1_20,
4111 &en1_2,
4112 NULL
4113 };
4114
4115 struct ipv4_service_contents * test5[] = {
4116 &ppp0_0_1,
4117 &en0_1,
4118 &en0_40,
4119 &en0_30,
4120 &en1_20,
4121 &en1_2,
4122 NULL
4123 };
4124
4125 struct ipv4_service_contents * test6[] = {
4126 &en0_test6,
4127 &en1_test6,
4128 &en2_test6,
4129 &fw0_test6_and_7,
4130 NULL
4131 };
4132
4133 struct ipv4_service_contents * test7[] = {
4134 &en0_test7,
4135 &en1_test7,
4136 &en2_test7,
4137 &fw0_test6_and_7,
4138 NULL
4139 };
4140
4141 struct ipv4_service_contents * test8[] = {
4142 &en0_10,
4143 &en1_20,
4144 NULL
4145 };
4146
4147 struct ipv4_service_contents * test9[] = {
4148 &en0_10,
4149 &en1_20_first,
4150 &fw0_25,
4151 NULL
4152 };
4153
4154 struct ipv4_service_contents * test10[] = {
4155 &en0_10_last,
4156 &en1_20,
4157 &fw0_25,
4158 NULL
4159 };
4160
4161 struct ipv4_service_contents * test11[] = {
4162 &en0_10_never,
4163 &en1_20,
4164 &fw0_25,
4165 NULL
4166 };
4167
4168 struct ipv4_service_contents * test12[] = {
4169 &en0_10,
4170 &en1_20,
4171 NULL
4172 };
4173
4174 struct ipv4_service_contents * test13[] = {
4175 &en0_10,
4176 &en1_20_never,
4177 NULL
4178 };
4179
4180 struct ipv4_service_contents * test14[] = {
4181 &en1_20_never,
4182 NULL
4183 };
4184
4185 static void
4186 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
4187 const char * str)
4188 {
4189 CFStringRef prop_val;
4190
4191 if (str == NULL) {
4192 return;
4193 }
4194 prop_val = CFStringCreateWithCString(NULL,
4195 str,
4196 kCFStringEncodingASCII);
4197 CFDictionarySetValue(dict, prop_name, prop_val);
4198 CFRelease(prop_val);
4199 return;
4200 }
4201
4202 static void
4203 dict_add_string_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
4204 const char * str)
4205 {
4206 CFArrayRef array;
4207 CFStringRef prop_val;
4208
4209 if (str == NULL) {
4210 return;
4211 }
4212 prop_val = CFStringCreateWithCString(NULL,
4213 str,
4214 kCFStringEncodingASCII);
4215 array = CFArrayCreate(NULL,
4216 (const void **)&prop_val, 1,
4217 &kCFTypeArrayCallBacks);
4218 CFRelease(prop_val);
4219 CFDictionarySetValue(dict, prop_name, array);
4220 CFRelease(array);
4221 return;
4222 }
4223
4224 static CFDictionaryRef
4225 make_IPv4_dict(struct ipv4_service_contents * t)
4226 {
4227 CFMutableDictionaryRef dict;
4228
4229 dict = CFDictionaryCreateMutable(NULL, 0,
4230 &kCFTypeDictionaryKeyCallBacks,
4231 &kCFTypeDictionaryValueCallBacks);
4232 dict_add_string_as_array(dict, kSCPropNetIPv4Addresses, t->addr);
4233 dict_add_string_as_array(dict, kSCPropNetIPv4SubnetMasks, t->mask);
4234 dict_add_string_as_array(dict, kSCPropNetIPv4DestAddresses, t->dest);
4235 dict_add_string(dict, kSCPropNetIPv4Router, t->router);
4236 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
4237 return (dict);
4238 }
4239
4240 static IPv4RouteListRef
4241 make_IPv4RouteList(struct ipv4_service_contents * * this_test)
4242 {
4243 IPv4RouteListRef r;
4244 IPv4RouteListRef routes;
4245 char routes_buf[IPv4RouteListComputeSize(R_STATIC)];
4246 IPv4RouteListRef ret = NULL;
4247 struct ipv4_service_contents * * scan_test;
4248
4249 for (scan_test = this_test; *scan_test != NULL; scan_test++) {
4250 CFDictionaryRef dict;
4251
4252 dict = make_IPv4_dict(*scan_test);
4253 if (dict == NULL) {
4254 fprintf(stderr, "make_IPv4_dict failed\n");
4255 exit(1);
4256 }
4257 routes = (IPv4RouteListRef)routes_buf;
4258 routes->size = R_STATIC;
4259 routes->count = 0;
4260 r = IPv4RouteListCreateWithDictionary(routes, dict,
4261 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
4262 if (r == NULL) {
4263 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
4264 exit(1);
4265 }
4266 ret = IPv4RouteListAddRouteList(ret, 1, r, (*scan_test)->rank);
4267 if (r != routes) {
4268 free(r);
4269 }
4270 CFRelease(dict);
4271 }
4272 return (ret);
4273 }
4274
4275 /*
4276 * Function: run_test
4277 * Purpose:
4278 * Runs through the given set of routes first in the forward direction,
4279 * then again backwards. We should end up with exactly the same set of
4280 * routes at the end.
4281 */
4282 static boolean_t
4283 run_test(const char * name, struct ipv4_service_contents * * this_test)
4284 {
4285 CFStringRef descr;
4286 boolean_t ret = FALSE;
4287 IPv4RouteListRef r;
4288 IPv4RouteListRef routes;
4289 char routes_buf[IPv4RouteListComputeSize(R_STATIC)];
4290 IPv4RouteListRef routes1 = NULL, routes2 = NULL;
4291 struct ipv4_service_contents * * scan_test;
4292
4293 printf("\nStarting test %s\n", name);
4294 for (scan_test = this_test; *scan_test != NULL; scan_test++) {
4295 CFDictionaryRef dict;
4296
4297 dict = make_IPv4_dict(*scan_test);
4298 if (dict == NULL) {
4299 fprintf(stderr, "make_IPv4_dict failed\n");
4300 exit(1);
4301 }
4302 routes = (IPv4RouteListRef)routes_buf;
4303 routes->size = R_STATIC;
4304 routes->count = 0;
4305 r = IPv4RouteListCreateWithDictionary(routes, dict,
4306 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
4307 if (r == NULL) {
4308 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
4309 exit(1);
4310 }
4311 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
4312 descr = IPv4RouteListCopyDescription(r);
4313 SCLog(TRUE, LOG_NOTICE, CFSTR("test: Adding %@"), descr);
4314 CFRelease(descr);
4315 }
4316
4317 routes1 = IPv4RouteListAddRouteList(routes1, 1, r, (*scan_test)->rank);
4318 if (r != routes) {
4319 free(r);
4320 }
4321 CFRelease(dict);
4322 }
4323 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
4324 if (routes1 != NULL) {
4325 descr = IPv4RouteListCopyDescription(routes1);
4326 SCLog(TRUE, LOG_NOTICE, CFSTR("Routes are %@"), descr);
4327 CFRelease(descr);
4328 }
4329 }
4330 for (scan_test--; scan_test >= this_test; scan_test--) {
4331 CFDictionaryRef dict;
4332
4333 dict = make_IPv4_dict(*scan_test);
4334 if (dict == NULL) {
4335 fprintf(stderr, "make_IPv4_dict failed\n");
4336 exit(1);
4337 }
4338 routes = (IPv4RouteListRef)routes_buf;
4339 routes->size = R_STATIC;
4340 routes->count = 0;
4341 r = IPv4RouteListCreateWithDictionary(routes, dict,
4342 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
4343 if (r == NULL) {
4344 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
4345 exit(1);
4346 }
4347 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
4348 descr = IPv4RouteListCopyDescription(r);
4349 SCLog(TRUE, LOG_NOTICE, CFSTR("test: Adding %@"), descr);
4350 CFRelease(descr);
4351 }
4352 routes2 = IPv4RouteListAddRouteList(routes2, 1, r, (*scan_test)->rank);
4353 if (r != routes) {
4354 free(r);
4355 }
4356 CFRelease(dict);
4357 }
4358 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
4359 if (routes2 != NULL) {
4360 descr = IPv4RouteListCopyDescription(routes2);
4361 SCLog(TRUE, LOG_NOTICE, CFSTR("Routes are %@"), descr);
4362 CFRelease(descr);
4363 }
4364 }
4365 if ((routes1 != NULL && routes2 == NULL)
4366 || (routes1 == NULL && routes2 != NULL)) {
4367 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
4368 (routes1 != NULL) ? "not " : "",
4369 (routes2 != NULL) ? "not " : "");
4370 }
4371 else if (routes1 != NULL && routes2 != NULL) {
4372 /* check if they are different */
4373 if (routes1->count != routes2->count) {
4374 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
4375 routes1->count, routes2->count);
4376 }
4377 else if (bcmp(routes1, routes2,
4378 IPv4RouteListComputeSize(routes1->count)) != 0) {
4379 fprintf(stderr, "routes1 and routes2 are different\n");
4380 }
4381 else {
4382 printf("routes1 and routes2 are the same\n");
4383 ret = TRUE;
4384 }
4385 }
4386 if (routes1 != NULL) {
4387 free(routes1);
4388 }
4389 if (routes2 != NULL) {
4390 free(routes2);
4391 }
4392 return (ret);
4393 }
4394
4395 typedef struct compare_context {
4396 IPv4RouteListRef old;
4397 IPv4RouteListRef new;
4398 } compare_context_t;
4399
4400 static void
4401 compare_callback(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg)
4402 {
4403 compare_context_t * context = (compare_context_t *)arg;
4404
4405 switch (cmd) {
4406 case kIPv4RouteListAddRouteCommand:
4407 printf("Add new[%d] = ", route - context->new->list);
4408 IPv4RoutePrint(route);
4409 printf("\n");
4410 break;
4411 case kIPv4RouteListRemoveRouteCommand:
4412 printf("Remove old[%d] = ", route - context->old->list);
4413 IPv4RoutePrint(route);
4414 printf("\n");
4415 break;
4416 default:
4417 break;
4418 }
4419 return;
4420 }
4421
4422 static void
4423 compare_tests(struct ipv4_service_contents * * old_test,
4424 struct ipv4_service_contents * * new_test)
4425 {
4426 IPv4RouteListRef new_routes;
4427 IPv4RouteListRef old_routes;
4428 compare_context_t context;
4429
4430 old_routes = make_IPv4RouteList(old_test);
4431 new_routes = make_IPv4RouteList(new_test);
4432
4433 if (old_routes == NULL) {
4434 printf("No Old Routes\n");
4435 }
4436 else {
4437 printf("Old Routes = ");
4438 IPv4RouteListPrint(old_routes);
4439 }
4440 if (new_routes == NULL) {
4441 printf("No New Routes\n");
4442 }
4443 else {
4444 printf("New Routes = ");
4445 IPv4RouteListPrint(new_routes);
4446 }
4447 context.old = old_routes;
4448 context.new = new_routes;
4449 IPv4RouteListApply(old_routes, new_routes, compare_callback, &context);
4450 if (old_routes != NULL) {
4451 free(old_routes);
4452 }
4453 if (new_routes != NULL) {
4454 free(new_routes);
4455 }
4456
4457 return;
4458 }
4459
4460 int
4461 main(int argc, char **argv)
4462 {
4463 _sc_log = FALSE;
4464 _sc_verbose = (argc > 1) ? TRUE : FALSE;
4465
4466 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
4467 if (argc > 1) {
4468 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
4469 }
4470
4471 if (run_test("test1", test1) == FALSE) {
4472 fprintf(stderr, "test1 failed\n");
4473 exit(1);
4474 }
4475 if (run_test("test2", test2) == FALSE) {
4476 fprintf(stderr, "test2 failed\n");
4477 exit(1);
4478 }
4479 if (run_test("test3", test4) == FALSE) {
4480 fprintf(stderr, "test3 failed\n");
4481 exit(1);
4482 }
4483 if (run_test("test4", test4) == FALSE) {
4484 fprintf(stderr, "test4 failed\n");
4485 exit(1);
4486 }
4487 if (run_test("test5", test5) == FALSE) {
4488 fprintf(stderr, "test5 failed\n");
4489 exit(1);
4490 }
4491
4492 printf("\nCompare 1 to 2:\n");
4493 compare_tests(test1, test2);
4494
4495 printf("\nCompare 2 to 1:\n");
4496 compare_tests(test2, test1);
4497
4498 printf("\nCompare 1 to 1:\n");
4499 compare_tests(test1, test1);
4500
4501 printf("\nCompare 1 to 3:\n");
4502 compare_tests(test1, test3);
4503
4504 printf("\nCompare 3 to 1:\n");
4505 compare_tests(test3, test1);
4506
4507 printf("\nCompare 2 to 3:\n");
4508 compare_tests(test2, test3);
4509
4510 printf("\nCompare 3 to 2:\n");
4511 compare_tests(test3, test2);
4512
4513 printf("\nCompare 3 to 4:\n");
4514 compare_tests(test3, test4);
4515
4516 printf("\nCompare 5 to 4:\n");
4517 compare_tests(test5, test4);
4518
4519 printf("\nCompare 6 to 7:\n");
4520 compare_tests(test6, test7);
4521
4522 printf("\nCompare 7 to 6:\n");
4523 compare_tests(test7, test6);
4524
4525 printf("\nCompare 8 to 9:\n");
4526 compare_tests(test8, test9);
4527
4528 printf("\nCompare 8 to 10:\n");
4529 compare_tests(test8, test10);
4530
4531 printf("\nCompare 8 to 11:\n");
4532 compare_tests(test8, test11);
4533
4534 printf("\nCompare 12 to 13:\n");
4535 compare_tests(test12, test13);
4536
4537 printf("\nCompare 13 to 14:\n");
4538 compare_tests(test13, test14);
4539
4540 printf("\nChecking for leaks\n");
4541 char cmd[128];
4542 sprintf(cmd, "leaks %d 2>&1", getpid());
4543 fflush(stdout);
4544 (void)system(cmd);
4545
4546 exit(0);
4547 return (0);
4548 }
4549
4550 #endif /* TEST_IPV4_ROUTELIST */
4551