]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/ip_plugin.c
configd-293.8.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 CFMutableDictionaryRef dict = NULL;
2958 CFStringRef if_name = NULL;
2959 char ifn[IFNAMSIZ] = { '\0' };
2960 char * ifn_p = NULL;
2961 boolean_t is_direct = FALSE;
2962 CFStringRef val_router = NULL;
2963
2964 dict = CFDictionaryCreateMutable(NULL, 0,
2965 &kCFTypeDictionaryKeyCallBacks,
2966 &kCFTypeDictionaryValueCallBacks);
2967 val_router = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Router);
2968 if (val_router != NULL) {
2969 CFArrayRef addrs;
2970
2971 /* no router if router is one of our IP addresses */
2972 addrs = CFDictionaryGetValue(ipv6_dict,
2973 kSCPropNetIPv6Addresses);
2974 is_direct = router_is_our_ipv6_address(val_router, addrs);
2975 CFDictionarySetValue(dict, kSCPropNetIPv6Router,
2976 val_router);
2977 }
2978 if_name = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
2979 if (if_name) {
2980 CFDictionarySetValue(dict,
2981 kSCDynamicStorePropNetPrimaryInterface,
2982 if_name);
2983 if (CFStringGetCString(if_name, ifn, sizeof(ifn),
2984 kCFStringEncodingASCII)) {
2985 ifn_p = ifn;
2986 }
2987 }
2988 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
2989 primary);
2990 keyChangeListSetValue(keys, S_state_global_ipv6, dict);
2991 CFRelease(dict);
2992
2993 if (val_router != NULL) { /* route add default ... */
2994 struct in6_addr router;
2995
2996 (void)cfstring_to_ip6(val_router, &router);
2997 set_ipv6_router(&router, ifn_p, is_direct);
2998 }
2999 }
3000 else {
3001 keyChangeListRemoveValue(keys, S_state_global_ipv6);
3002 set_ipv6_router(NULL, NULL, FALSE);
3003 }
3004 return;
3005 }
3006
3007 static void
3008 update_dns(CFDictionaryRef service_info,
3009 CFStringRef primary,
3010 keyChangeListRef keys)
3011 {
3012 CFDictionaryRef dict = NULL;
3013
3014 if (primary != NULL) {
3015 CFDictionaryRef service_dict;
3016
3017 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3018 if (service_dict != NULL) {
3019 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
3020 }
3021 }
3022 if (dict == NULL) {
3023 #if !TARGET_OS_IPHONE
3024 empty_dns();
3025 #endif /* !TARGET_OS_IPHONE */
3026 keyChangeListRemoveValue(keys, S_state_global_dns);
3027 }
3028 else {
3029 #if !TARGET_OS_IPHONE
3030 set_dns(CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains),
3031 CFDictionaryGetValue(dict, kSCPropNetDNSDomainName),
3032 CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses),
3033 CFDictionaryGetValue(dict, kSCPropNetDNSSortList));
3034 #endif /* !TARGET_OS_IPHONE */
3035 keyChangeListSetValue(keys, S_state_global_dns, dict);
3036 }
3037 return;
3038 }
3039
3040 static void
3041 update_dnsinfo(CFDictionaryRef service_info,
3042 CFStringRef primary,
3043 keyChangeListRef keys,
3044 CFArrayRef service_order)
3045 {
3046 CFDictionaryRef dict = NULL;
3047 CFArrayRef multicastResolvers;
3048 CFArrayRef privateResolvers;
3049
3050 multicastResolvers = CFDictionaryGetValue(service_info, S_multicast_resolvers);
3051 privateResolvers = CFDictionaryGetValue(service_info, S_private_resolvers);
3052
3053 if (primary != NULL) {
3054 CFDictionaryRef service_dict;
3055
3056 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3057 if (service_dict != NULL) {
3058 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
3059 }
3060 }
3061
3062 dns_configuration_set(dict,
3063 S_service_state_dict,
3064 service_order,
3065 multicastResolvers,
3066 privateResolvers);
3067 keyChangeListNotifyKey(keys, S_state_global_dns);
3068 return;
3069 }
3070
3071 static void
3072 update_proxies(CFDictionaryRef service_info,
3073 CFStringRef primary,
3074 keyChangeListRef keys)
3075 {
3076 CFDictionaryRef dict = NULL;
3077
3078 if (primary != NULL) {
3079 CFDictionaryRef service_dict;
3080
3081 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3082 if (service_dict != NULL) {
3083 dict = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
3084 if (dict == NULL) {
3085 dict = my_CFDictionaryGetDictionary(service_info,
3086 S_setup_global_proxies);
3087 }
3088 }
3089 }
3090 if (dict == NULL) {
3091 keyChangeListRemoveValue(keys, S_state_global_proxies);
3092 }
3093 else {
3094 keyChangeListSetValue(keys, S_state_global_proxies, dict);
3095 }
3096 return;
3097 }
3098
3099 #if !TARGET_OS_IPHONE
3100 static void
3101 update_smb(CFDictionaryRef service_info,
3102 CFStringRef primary,
3103 keyChangeListRef keys)
3104 {
3105 CFDictionaryRef dict = NULL;
3106
3107 if (primary != NULL) {
3108 CFDictionaryRef service_dict;
3109
3110 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
3111 if (service_dict != NULL) {
3112 dict = CFDictionaryGetValue(service_dict, kSCEntNetSMB);
3113 if (dict == NULL) {
3114 dict = my_CFDictionaryGetDictionary(service_info,
3115 S_setup_global_smb);
3116 }
3117 }
3118 }
3119 if (dict == NULL) {
3120 keyChangeListRemoveValue(keys, S_state_global_smb);
3121 }
3122 else {
3123 keyChangeListSetValue(keys, S_state_global_smb, dict);
3124 }
3125
3126 return;
3127 }
3128 #endif /* !TARGET_OS_IPHONE */
3129
3130 static Rank
3131 get_service_rank(CFArrayRef order, int n_order, CFStringRef serviceID)
3132 {
3133 CFIndex i;
3134 Rank rank = kRankLast;
3135
3136 if (serviceID != NULL && order != NULL && n_order > 0) {
3137 for (i = 0; i < n_order; i++) {
3138 CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(order, i));
3139
3140 if (s == NULL) {
3141 continue;
3142 }
3143 if (CFEqual(serviceID, s)) {
3144 rank = i + 1;
3145 break;
3146 }
3147 }
3148 }
3149 return (rank);
3150 }
3151
3152 /**
3153 ** Service election:
3154 **/
3155 /*
3156 * Function: rank_dict_get_service_rank
3157 * Purpose:
3158 * Retrieve the service rank in the given dictionary.
3159 */
3160 static Rank
3161 rank_dict_get_service_rank(CFDictionaryRef rank_dict, CFStringRef serviceID)
3162 {
3163 CFNumberRef rank;
3164 Rank rank_val = kRankLast;
3165
3166 rank = CFDictionaryGetValue(rank_dict, serviceID);
3167 if (rank != NULL) {
3168 CFNumberGetValue(rank, kCFNumberSInt32Type, &rank_val);
3169 }
3170 return (rank_val);
3171 }
3172
3173 /*
3174 * Function: rank_dict_set_service_rank
3175 * Purpose:
3176 * Save the results of ranking the service so we can look it up later without
3177 * repeating all of the ranking code.
3178 */
3179 static void
3180 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict,
3181 CFStringRef serviceID, Rank rank_val)
3182 {
3183 CFNumberRef rank;
3184
3185 rank = CFNumberCreate(NULL, kCFNumberSInt32Type, (const void *)&rank_val);
3186 if (rank != NULL) {
3187 CFDictionarySetValue(rank_dict, serviceID, rank);
3188 CFRelease(rank);
3189 }
3190 return;
3191 }
3192
3193 typedef struct election_info {
3194 int n_services;
3195 CFArrayRef order;
3196 int n_order;
3197 CFStringRef serviceID;
3198 CFDictionaryRef service_dict;
3199 Rank service_rank;
3200 boolean_t choose_last;
3201 } election_info_t;
3202
3203 typedef boolean_t election_func_t(void * context, election_info_t * info);
3204
3205 /*
3206 * Function: elect_ipv4
3207 * Purpose:
3208 * This function builds the list of IPv4 routes that should be active.
3209 * As elect_new_primary() invokes us with each service, we build up the
3210 * result in the passed in context, a pointer to an IPv4RouteListRef.
3211 */
3212 static boolean_t
3213 elect_ipv4(void * context, election_info_t * info)
3214 {
3215 IPv4RouteListRef * routes_p = (IPv4RouteListRef *)context;
3216 IPv4RouteListRef service_routes;
3217
3218 service_routes = service_dict_get_ipv4_routelist(info->service_dict);
3219 if (service_routes == NULL) {
3220 return (FALSE);
3221 }
3222 if ((service_routes->list->flags & kRouteChooseFirstFlag) != 0) {
3223 info->service_rank = kRankFirst;
3224 }
3225 else if (S_ppp_override_primary
3226 && (strncmp(PPP_PREFIX, service_routes->list->ifname,
3227 sizeof(PPP_PREFIX) - 1) == 0)) {
3228 /* PPP override: make ppp* look the best */
3229 /* Hack: should use interface type, not interface name */
3230 info->service_rank = kRankFirst;
3231 }
3232 else {
3233 info->service_rank = get_service_rank(info->order, info->n_order,
3234 info->serviceID);
3235 if ((service_routes->list->flags & kRouteChooseLastFlag) != 0) {
3236 info->choose_last = TRUE;
3237 }
3238 }
3239 if (routes_p != NULL) {
3240 *routes_p = IPv4RouteListAddRouteList(*routes_p,
3241 info->n_services * 3,
3242 service_routes,
3243 info->service_rank);
3244 }
3245 if ((service_routes->list->flags & kRouteChooseNeverFlag) != 0) {
3246 /* never elect as primary */
3247 return (FALSE);
3248 }
3249 rank_dict_set_service_rank(S_ipv4_service_rank_dict,
3250 info->serviceID, info->service_rank);
3251 return (TRUE);
3252 }
3253
3254 static boolean_t
3255 elect_ipv6(void * context, election_info_t * info)
3256 {
3257 CFStringRef if_name;
3258 CFStringRef primaryRank = NULL;
3259 CFDictionaryRef proto_dict;
3260 CFStringRef router;
3261 CFDictionaryRef service_options;
3262
3263 proto_dict = CFDictionaryGetValue(info->service_dict, kSCEntNetIPv6);
3264 if (proto_dict == NULL) {
3265 return (FALSE);
3266 }
3267 service_options = service_dict_get(info->serviceID, kSCEntNetService);
3268 if (service_options != NULL) {
3269 primaryRank = CFDictionaryGetValue(service_options, kSCPropNetServicePrimaryRank);
3270 if ((primaryRank != NULL)
3271 && CFEqual(primaryRank, kSCValNetServicePrimaryRankNever)) {
3272 return (FALSE);
3273 }
3274 }
3275 router = CFDictionaryGetValue(proto_dict,
3276 kSCPropNetIPv6Router);
3277 if (router == NULL) {
3278 info->choose_last = TRUE;
3279 info->service_rank = kRankLast;
3280 }
3281 else if ((primaryRank != NULL)
3282 && CFEqual(primaryRank, kSCValNetServicePrimaryRankFirst)) {
3283 info->service_rank = kRankFirst;
3284 }
3285 else if (get_override_primary(proto_dict)) {
3286 info->service_rank = kRankFirst;
3287 }
3288 else if (S_ppp_override_primary
3289 && CFDictionaryGetValueIfPresent(proto_dict,
3290 kSCPropInterfaceName,
3291 (const void **)&if_name)
3292 && CFStringHasPrefix(if_name, CFSTR(PPP_PREFIX))) {
3293 /* PPP override: make ppp* look the best */
3294 /* Hack: should use interface type, not interface name */
3295 info->service_rank = kRankFirst;
3296 }
3297 else {
3298 info->service_rank = get_service_rank(info->order, info->n_order,
3299 info->serviceID);
3300 }
3301 rank_dict_set_service_rank(S_ipv6_service_rank_dict,
3302 info->serviceID, info->service_rank);
3303 return (TRUE);
3304 }
3305
3306 /*
3307 * Function: elect_new_primary
3308 * Purpose:
3309 * Walk the list of services, passing each service dictionary to "elect_func".
3310 * "elect_func" returns rank information about the service that let us
3311 * determine the new primary.
3312 */
3313 static CFStringRef
3314 elect_new_primary(election_func_t * elect_func, void * context,
3315 CFArrayRef order, int n_order)
3316 {
3317 int count;
3318 int i;
3319 election_info_t info;
3320 void * * keys;
3321 #define N_KEYS_VALUES_STATIC 10
3322 void * keys_values_buf[N_KEYS_VALUES_STATIC * 2];
3323 CFStringRef new_primary = NULL;
3324 Rank new_primary_rank = kRankLast;
3325 boolean_t new_primary_choose_last = FALSE;
3326 void * * values;
3327
3328 count = CFDictionaryGetCount(S_service_state_dict);
3329 if (count <= N_KEYS_VALUES_STATIC) {
3330 keys = keys_values_buf;
3331 }
3332 else {
3333 keys = (void * *)malloc(sizeof(*keys) * count * 2);
3334 }
3335 values = keys + count;
3336 CFDictionaryGetKeysAndValues(S_service_state_dict,
3337 (const void * *)keys,
3338 (const void * *)values);
3339
3340 info.n_services = count;
3341 info.order = order;
3342 info.n_order = n_order;
3343 for (i = 0; i < count; i++) {
3344 boolean_t found_new_primary = FALSE;
3345
3346 info.serviceID = (CFStringRef)keys[i];
3347 info.service_dict = (CFDictionaryRef)values[i];
3348 info.service_rank = kRankLast;
3349 info.choose_last = FALSE;
3350
3351 if ((*elect_func)(context, &info) == FALSE) {
3352 continue;
3353 }
3354 if (new_primary == NULL) {
3355 found_new_primary = TRUE;
3356 }
3357 else if (info.choose_last == new_primary_choose_last) {
3358 found_new_primary = (info.service_rank < new_primary_rank);
3359 }
3360 else if (new_primary_choose_last) {
3361 found_new_primary = TRUE;
3362 }
3363 if (found_new_primary) {
3364 new_primary = info.serviceID;
3365 new_primary_rank = info.service_rank;
3366 new_primary_choose_last = info.choose_last;
3367 }
3368 }
3369 if (new_primary != NULL) {
3370 CFRetain(new_primary);
3371 }
3372 if (keys != keys_values_buf) {
3373 free(keys);
3374 }
3375 return (new_primary);
3376 }
3377
3378 static uint32_t
3379 service_changed(CFDictionaryRef services_info, CFStringRef serviceID)
3380 {
3381 uint32_t changed = 0;
3382 int i;
3383
3384 /* update service options first (e.g. rank) */
3385 if (get_rank_changes(serviceID,
3386 get_service_state_entity(services_info, serviceID,
3387 NULL),
3388 get_service_setup_entity(services_info, serviceID,
3389 NULL),
3390 services_info)) {
3391 changed |= (1 << kEntityTypeServiceOptions);
3392 }
3393 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
3394 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
3395 GetEntityChangesFuncRef func = entityChangeFunc[i];
3396 if ((*func)(serviceID,
3397 get_service_state_entity(services_info, serviceID,
3398 *entityTypeNames[i]),
3399 get_service_setup_entity(services_info, serviceID,
3400 *entityTypeNames[i]),
3401 services_info)) {
3402 changed |= (1 << i);
3403 }
3404 }
3405 return (changed);
3406 }
3407
3408 static CFArrayRef
3409 service_order_get(CFDictionaryRef services_info)
3410 {
3411 CFArrayRef order = NULL;
3412 CFDictionaryRef ipv4_dict;
3413
3414 ipv4_dict = my_CFDictionaryGetDictionary(services_info,
3415 S_setup_global_ipv4);
3416 if (ipv4_dict != NULL) {
3417 CFNumberRef ppp_override;
3418 int ppp_val = 0;
3419
3420 order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
3421 order = isA_CFArray(order);
3422
3423 /* get ppp override primary */
3424 ppp_override = CFDictionaryGetValue(ipv4_dict,
3425 kSCPropNetPPPOverridePrimary);
3426 ppp_override = isA_CFNumber(ppp_override);
3427 if (ppp_override != NULL) {
3428 CFNumberGetValue(ppp_override, kCFNumberIntType, &ppp_val);
3429 }
3430 S_ppp_override_primary = (ppp_val != 0) ? TRUE : FALSE;
3431 }
3432 else {
3433 S_ppp_override_primary = FALSE;
3434 }
3435 return (order);
3436 }
3437
3438 static boolean_t
3439 set_new_primary(CFStringRef * primary_p, CFStringRef new_primary,
3440 const char * entity)
3441 {
3442 boolean_t changed = FALSE;
3443 CFStringRef primary = *primary_p;
3444
3445 if (new_primary != NULL) {
3446 if (primary != NULL && CFEqual(new_primary, primary)) {
3447 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3448 SCLog(TRUE, LOG_NOTICE,
3449 CFSTR("IPMonitor: %@ is still primary %s"),
3450 new_primary, entity);
3451 }
3452 }
3453 else {
3454 my_CFRelease(primary_p);
3455 *primary_p = CFRetain(new_primary);
3456 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3457 SCLog(TRUE, LOG_NOTICE,
3458 CFSTR("IPMonitor: %@ is the new primary %s"),
3459 new_primary, entity);
3460 }
3461 changed = TRUE;
3462 }
3463 }
3464 else if (primary != NULL) {
3465 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3466 SCLog(TRUE, LOG_NOTICE,
3467 CFSTR("IPMonitor: %@ is no longer primary %s"),
3468 primary, entity);
3469 }
3470 my_CFRelease(primary_p);
3471 changed = TRUE;
3472 }
3473 return (changed);
3474 }
3475
3476 static Rank
3477 rank_service_entity(CFDictionaryRef rank_dict, CFStringRef serviceID,
3478 CFStringRef entity)
3479 {
3480 if (service_dict_get(serviceID, entity) == NULL) {
3481 return (kRankLast);
3482 }
3483 return (rank_dict_get_service_rank(rank_dict, serviceID));
3484 }
3485
3486 static void
3487 IPMonitorNotify(SCDynamicStoreRef session, CFArrayRef changed_keys,
3488 void * not_used)
3489 {
3490 CFIndex count;
3491 boolean_t dnsinfo_changed = FALSE;
3492 boolean_t global_ipv4_changed = FALSE;
3493 boolean_t global_ipv6_changed = FALSE;
3494 int i;
3495 keyChangeList keys;
3496 CFIndex n;
3497 int n_service_order = 0;
3498 CFArrayRef service_order;
3499 CFMutableArrayRef service_changes = NULL;
3500 CFDictionaryRef services_info = NULL;
3501
3502 count = CFArrayGetCount(changed_keys);
3503 if (count == 0) {
3504 return;
3505 }
3506
3507 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3508 SCLog(TRUE, LOG_NOTICE,
3509 CFSTR("IPMonitor: changes %@ (%d)"), changed_keys, count);
3510 }
3511
3512 keyChangeListInit(&keys);
3513 service_changes = CFArrayCreateMutable(NULL, 0,
3514 &kCFTypeArrayCallBacks);
3515 for (i = 0; i < count; i++) {
3516 CFStringRef change = CFArrayGetValueAtIndex(changed_keys, i);
3517 if (CFEqual(change, S_setup_global_ipv4)) {
3518 global_ipv4_changed = TRUE;
3519 global_ipv6_changed = TRUE;
3520 }
3521 else if (CFEqual(change, S_setup_global_proxies)) {
3522 if (S_primary_proxies != NULL) {
3523 my_CFArrayAppendUniqueValue(service_changes, S_primary_proxies);
3524 }
3525 }
3526 #if !TARGET_OS_IPHONE
3527 else if (CFEqual(change, S_setup_global_smb)) {
3528 if (S_primary_smb != NULL) {
3529 my_CFArrayAppendUniqueValue(service_changes, S_primary_smb);
3530 }
3531 }
3532 #endif /* !TARGET_OS_IPHONE */
3533 else if (CFEqual(change, S_multicast_resolvers)) {
3534 dnsinfo_changed = TRUE;
3535 }
3536 else if (CFEqual(change, S_private_resolvers)) {
3537 dnsinfo_changed = TRUE;
3538 }
3539 #if !TARGET_OS_IPHONE
3540 else if (CFEqual(change, CFSTR(_PATH_RESOLVER_DIR))) {
3541 dnsinfo_changed = TRUE;
3542 }
3543 #endif /* !TARGET_OS_IPHONE */
3544 else if (CFStringHasPrefix(change, S_state_service_prefix)) {
3545 CFStringRef serviceID = parse_component(change,
3546 S_state_service_prefix);
3547 if (serviceID) {
3548 my_CFArrayAppendUniqueValue(service_changes, serviceID);
3549 CFRelease(serviceID);
3550 }
3551 }
3552 else if (CFStringHasPrefix(change, S_setup_service_prefix)) {
3553 CFStringRef serviceID = parse_component(change,
3554 S_setup_service_prefix);
3555 if (serviceID) {
3556 my_CFArrayAppendUniqueValue(service_changes, serviceID);
3557 CFRelease(serviceID);
3558 }
3559 }
3560 }
3561
3562 /* grab a snapshot of everything we need */
3563 services_info = services_info_copy(session, service_changes);
3564 service_order = service_order_get(services_info);
3565 if (service_order != NULL) {
3566 n_service_order = CFArrayGetCount(service_order);
3567 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3568 SCLog(TRUE, LOG_NOTICE,
3569 CFSTR("IPMonitor: service_order %@ "), service_order);
3570 }
3571 }
3572 n = CFArrayGetCount(service_changes);
3573 for (i = 0; i < n; i++) {
3574 uint32_t changes;
3575 CFStringRef serviceID;
3576 Boolean wasSupplemental;
3577
3578 serviceID = CFArrayGetValueAtIndex(service_changes, i);
3579 wasSupplemental = dns_has_supplemental(serviceID);
3580 changes = service_changed(services_info, serviceID);
3581 if ((changes & (1 << kEntityTypeServiceOptions)) != 0) {
3582 /* if __Service__ (e.g. PrimaryRank) changed */
3583 global_ipv4_changed = TRUE;
3584 }
3585 else if (S_primary_ipv4 != NULL && CFEqual(S_primary_ipv4, serviceID)) {
3586 // if we are looking at the primary [IPv4] service
3587 if ((changes & (1 << kEntityTypeIPv4)) != 0) {
3588 // and something changed for THIS service
3589 global_ipv4_changed = TRUE;
3590 }
3591 }
3592 else if ((changes & (1 << kEntityTypeIPv4)) != 0) {
3593 global_ipv4_changed = TRUE;
3594 }
3595 if ((changes & (1 << kEntityTypeIPv6)) != 0) {
3596 // if we are looking at the primary [IPv6] service
3597 if (S_primary_ipv6 != NULL && CFEqual(S_primary_ipv6, serviceID)) {
3598 update_ipv6(services_info, serviceID, &keys);
3599 }
3600 // and something changed for THIS service
3601 global_ipv6_changed = TRUE;
3602 }
3603 if ((changes & (1 << kEntityTypeDNS)) != 0) {
3604 if (S_primary_dns != NULL && CFEqual(S_primary_dns, serviceID)) {
3605 update_dns(services_info, serviceID, &keys);
3606 dnsinfo_changed = TRUE;
3607 }
3608 else if (wasSupplemental || dns_has_supplemental(serviceID)) {
3609 dnsinfo_changed = TRUE;
3610 }
3611 }
3612 if ((changes & (1 << kEntityTypeProxies)) != 0) {
3613 if (S_primary_proxies != NULL && CFEqual(S_primary_proxies, serviceID)) {
3614 update_proxies(services_info, serviceID, &keys);
3615 }
3616 }
3617 #if !TARGET_OS_IPHONE
3618 if ((changes & (1 << kEntityTypeSMB)) != 0) {
3619 if (S_primary_smb != NULL && CFEqual(S_primary_smb, serviceID)) {
3620 update_smb(services_info, serviceID, &keys);
3621 }
3622 }
3623 #endif /* !TARGET_OS_IPHONE */
3624 }
3625
3626 if (global_ipv4_changed) {
3627 IPv4RouteListRef new_routelist = NULL;
3628 CFStringRef new_primary;
3629
3630 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3631 SCLog(TRUE, LOG_NOTICE,
3632 CFSTR("IPMonitor: IPv4 service election"));
3633 }
3634 new_primary = elect_new_primary(&elect_ipv4, &new_routelist,
3635 service_order, n_service_order);
3636 (void)set_new_primary(&S_primary_ipv4, new_primary, "IPv4");
3637 update_ipv4(S_primary_ipv4, new_routelist, &keys);
3638 my_CFRelease(&new_primary);
3639 }
3640 if (global_ipv6_changed) {
3641 CFStringRef new_primary;
3642
3643 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3644 SCLog(TRUE, LOG_NOTICE,
3645 CFSTR("IPMonitor: IPv6 service election"));
3646 }
3647 new_primary = elect_new_primary(&elect_ipv6, NULL,
3648 service_order, n_service_order);
3649 if (set_new_primary(&S_primary_ipv6, new_primary, "IPv6")) {
3650 update_ipv6(services_info, S_primary_ipv6, &keys);
3651 }
3652 my_CFRelease(&new_primary);
3653 }
3654 if (global_ipv4_changed || global_ipv6_changed) {
3655 CFStringRef new_primary_dns = NULL;
3656 CFStringRef new_primary_proxies = NULL;
3657 #if !TARGET_OS_IPHONE
3658 CFStringRef new_primary_smb = NULL;
3659 #endif /* !TARGET_OS_IPHONE */
3660
3661 if (S_primary_ipv4 != NULL && S_primary_ipv6 != NULL) {
3662 /* decide between IPv4 and IPv6 */
3663 if (rank_service_entity(S_ipv4_service_rank_dict,
3664 S_primary_ipv4, kSCEntNetDNS)
3665 <= rank_service_entity(S_ipv6_service_rank_dict,
3666 S_primary_ipv6, kSCEntNetDNS)) {
3667 new_primary_dns = S_primary_ipv4;
3668 }
3669 else {
3670 new_primary_dns = S_primary_ipv6;
3671 }
3672 if (rank_service_entity(S_ipv4_service_rank_dict,
3673 S_primary_ipv4, kSCEntNetProxies)
3674 <= rank_service_entity(S_ipv6_service_rank_dict,
3675 S_primary_ipv6, kSCEntNetProxies)) {
3676 new_primary_proxies = S_primary_ipv4;
3677 }
3678 else {
3679 new_primary_proxies = S_primary_ipv6;
3680 }
3681 #if !TARGET_OS_IPHONE
3682 if (rank_service_entity(S_ipv4_service_rank_dict,
3683 S_primary_ipv4, kSCEntNetSMB)
3684 <= rank_service_entity(S_ipv6_service_rank_dict,
3685 S_primary_ipv6, kSCEntNetSMB)) {
3686 new_primary_smb = S_primary_ipv4;
3687 }
3688 else {
3689 new_primary_smb = S_primary_ipv6;
3690 }
3691 #endif /* !TARGET_OS_IPHONE */
3692
3693 }
3694 else if (S_primary_ipv6 != NULL) {
3695 new_primary_dns = S_primary_ipv6;
3696 new_primary_proxies = S_primary_ipv6;
3697 #if !TARGET_OS_IPHONE
3698 new_primary_smb = S_primary_ipv6;
3699 #endif /* !TARGET_OS_IPHONE */
3700 }
3701 else if (S_primary_ipv4 != NULL) {
3702 new_primary_dns = S_primary_ipv4;
3703 new_primary_proxies = S_primary_ipv4;
3704 #if !TARGET_OS_IPHONE
3705 new_primary_smb = S_primary_ipv4;
3706 #endif /* !TARGET_OS_IPHONE */
3707 }
3708
3709 if (set_new_primary(&S_primary_dns, new_primary_dns, "DNS")) {
3710 update_dns(services_info, S_primary_dns, &keys);
3711 dnsinfo_changed = TRUE;
3712 }
3713 if (set_new_primary(&S_primary_proxies, new_primary_proxies, "Proxies")) {
3714 update_proxies(services_info, S_primary_proxies, &keys);
3715 }
3716 #if !TARGET_OS_IPHONE
3717 if (set_new_primary(&S_primary_smb, new_primary_smb, "SMB")) {
3718 update_smb(services_info, S_primary_smb, &keys);
3719 }
3720 #endif /* !TARGET_OS_IPHONE */
3721 }
3722 if (dnsinfo_changed) {
3723 update_dnsinfo(services_info, S_primary_dns, &keys, service_order);
3724 }
3725 my_CFRelease(&service_changes);
3726 my_CFRelease(&services_info);
3727 keyChangeListApplyToStore(&keys, session);
3728 keyChangeListFree(&keys);
3729 return;
3730 }
3731
3732 static void
3733 ip_plugin_init()
3734 {
3735 CFMutableArrayRef keys = NULL;
3736 CFMutableArrayRef patterns = NULL;
3737 CFRunLoopSourceRef rls = NULL;
3738
3739 if (S_is_network_boot() != 0) {
3740 S_netboot = TRUE;
3741 }
3742
3743 #ifdef RTF_IFSCOPE
3744 if (S_is_scoped_routing_enabled() != 0) {
3745 S_scopedroute = TRUE;
3746 }
3747 #endif /* RTF_IFSCOPE */
3748
3749 S_session = SCDynamicStoreCreate(NULL, CFSTR("IPMonitor"),
3750 IPMonitorNotify, NULL);
3751 if (S_session == NULL) {
3752 SCLog(TRUE, LOG_ERR,
3753 CFSTR("IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s"),
3754 SCErrorString(SCError()));
3755 return;
3756 }
3757 S_state_global_ipv4
3758 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3759 kSCDynamicStoreDomainState,
3760 kSCEntNetIPv4);
3761 S_state_global_ipv6
3762 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3763 kSCDynamicStoreDomainState,
3764 kSCEntNetIPv6);
3765 S_state_global_dns
3766 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3767 kSCDynamicStoreDomainState,
3768 kSCEntNetDNS);
3769 S_state_global_proxies
3770 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3771 kSCDynamicStoreDomainState,
3772 kSCEntNetProxies);
3773 #if !TARGET_OS_IPHONE
3774 S_state_global_smb
3775 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3776 kSCDynamicStoreDomainState,
3777 kSCEntNetSMB);
3778 #endif /* !TARGET_OS_IPHONE */
3779 S_setup_global_ipv4
3780 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3781 kSCDynamicStoreDomainSetup,
3782 kSCEntNetIPv4);
3783 S_setup_global_proxies
3784 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3785 kSCDynamicStoreDomainSetup,
3786 kSCEntNetProxies);
3787 #if !TARGET_OS_IPHONE
3788 S_setup_global_smb
3789 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3790 kSCDynamicStoreDomainSetup,
3791 kSCEntNetSMB);
3792 #endif /* !TARGET_OS_IPHONE */
3793 S_state_service_prefix
3794 = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@/"),
3795 kSCDynamicStoreDomainState,
3796 kSCCompNetwork,
3797 kSCCompService);
3798 S_setup_service_prefix
3799 = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@/"),
3800 kSCDynamicStoreDomainSetup,
3801 kSCCompNetwork,
3802 kSCCompService);
3803 S_service_state_dict
3804 = CFDictionaryCreateMutable(NULL, 0,
3805 &kCFTypeDictionaryKeyCallBacks,
3806 &kCFTypeDictionaryValueCallBacks);
3807
3808 S_ipv4_service_rank_dict
3809 = CFDictionaryCreateMutable(NULL, 0,
3810 &kCFTypeDictionaryKeyCallBacks,
3811 &kCFTypeDictionaryValueCallBacks);
3812
3813 S_ipv6_service_rank_dict
3814 = CFDictionaryCreateMutable(NULL, 0,
3815 &kCFTypeDictionaryKeyCallBacks,
3816 &kCFTypeDictionaryValueCallBacks);
3817
3818 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3819 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3820
3821 /* register for State: and Setup: per-service notifications */
3822 add_service_keys(kSCCompAnyRegex, keys, patterns);
3823
3824 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
3825 CFArrayAppendValue(keys, S_setup_global_ipv4);
3826
3827 /* add notifier for multicast DNS configuration (Bonjour/.local) */
3828 S_multicast_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
3829 kSCDynamicStoreDomainState,
3830 kSCCompNetwork,
3831 CFSTR(kDNSServiceCompMulticastDNS));
3832 CFArrayAppendValue(keys, S_multicast_resolvers);
3833
3834 /* add notifier for private DNS configuration (Back to My Mac) */
3835 S_private_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
3836 kSCDynamicStoreDomainState,
3837 kSCCompNetwork,
3838 CFSTR(kDNSServiceCompPrivateDNS));
3839 CFArrayAppendValue(keys, S_private_resolvers);
3840
3841 if (!SCDynamicStoreSetNotificationKeys(S_session, keys, patterns)) {
3842 SCLog(TRUE, LOG_ERR,
3843 CFSTR("IPMonitor ip_plugin_init "
3844 "SCDynamicStoreSetNotificationKeys failed: %s"),
3845 SCErrorString(SCError()));
3846 goto done;
3847 }
3848
3849 rls = SCDynamicStoreCreateRunLoopSource(NULL, S_session, 0);
3850 if (rls == NULL) {
3851 SCLog(TRUE, LOG_ERR,
3852 CFSTR("IPMonitor ip_plugin_init "
3853 "SCDynamicStoreCreateRunLoopSource failed: %s"),
3854 SCErrorString(SCError()));
3855 goto done;
3856 }
3857
3858 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
3859 CFRelease(rls);
3860
3861 /* initialize dns configuration */
3862 dns_configuration_set(NULL, NULL, NULL, NULL, NULL);
3863 #if !TARGET_OS_IPHONE
3864 empty_dns();
3865 #endif /* !TARGET_OS_IPHONE */
3866 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_dns);
3867
3868 #if !TARGET_OS_IPHONE
3869 /* initialize SMB configuration */
3870 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_smb);
3871 #endif /* !TARGET_OS_IPHONE */
3872
3873 done:
3874 my_CFRelease(&keys);
3875 my_CFRelease(&patterns);
3876 return;
3877 }
3878
3879 __private_extern__
3880 void
3881 prime_IPMonitor()
3882 {
3883 /* initialize multicast route */
3884 update_ipv4(NULL, NULL, NULL);
3885 return;
3886 }
3887
3888 static boolean_t
3889 S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
3890 boolean_t def)
3891 {
3892 CFBooleanRef b;
3893 boolean_t ret = def;
3894
3895 b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
3896 if (b != NULL) {
3897 ret = CFBooleanGetValue(b);
3898 }
3899 return (ret);
3900 }
3901
3902 __private_extern__
3903 void
3904 load_IPMonitor(CFBundleRef bundle, Boolean bundleVerbose)
3905 {
3906 CFDictionaryRef info_dict;
3907
3908 info_dict = CFBundleGetInfoDictionary(bundle);
3909 if (info_dict != NULL) {
3910 S_append_state
3911 = S_get_plist_boolean(info_dict,
3912 CFSTR("AppendStateArrayToSetupArray"),
3913 FALSE);
3914 }
3915 if (bundleVerbose) {
3916 S_IPMonitor_debug = kDebugFlagDefault;
3917 }
3918
3919 dns_configuration_init(bundle);
3920 ip_plugin_init();
3921
3922 #if !TARGET_OS_IPHONE
3923 if (S_session != NULL) {
3924 dns_configuration_monitor(S_session, IPMonitorNotify);
3925 }
3926 #endif /* !TARGET_OS_IPHONE */
3927
3928 load_hostname((S_IPMonitor_debug & kDebugFlag1) != 0);
3929 #if !TARGET_OS_IPHONE
3930 load_smb_configuration((S_IPMonitor_debug & kDebugFlag1) != 0);
3931 #endif /* !TARGET_OS_IPHONE */
3932
3933 return;
3934 }
3935
3936
3937 #ifdef TEST_IPMONITOR
3938 #include "dns-configuration.c"
3939 #include "set-hostname.c"
3940
3941 int
3942 main(int argc, char **argv)
3943 {
3944 _sc_log = FALSE;
3945
3946 S_IPMonitor_debug = kDebugFlag1;
3947 if (argc > 1) {
3948 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
3949 }
3950
3951 load_IPMonitor(CFBundleGetMainBundle(), FALSE);
3952 prime_IPMonitor();
3953 CFRunLoopRun();
3954 /* not reached */
3955 exit(0);
3956 return 0;
3957 }
3958 #endif /* TEST_IPMONITOR */
3959
3960 #ifdef TEST_IPV4_ROUTELIST
3961 #include "dns-configuration.c"
3962 #include "set-hostname.c"
3963
3964 struct ipv4_service_contents {
3965 const char * addr;
3966 const char * mask;
3967 const char * dest;
3968 const char * router;
3969 const char * ifname;
3970 Rank rank;
3971 const CFStringRef *primaryRank;
3972 };
3973
3974 /*
3975 * addr mask dest router ifname pri primaryRank
3976 */
3977 struct ipv4_service_contents en0_10 = {
3978 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, NULL
3979 };
3980
3981 struct ipv4_service_contents en0_15 = {
3982 "10.0.0.19", "255.255.255.0", NULL, "10.0.0.1", "en0", 15, NULL
3983 };
3984
3985 struct ipv4_service_contents en0_30 = {
3986 "10.0.0.11", "255.255.255.0", NULL, "10.0.0.1", "en0", 30, NULL
3987 };
3988
3989 struct ipv4_service_contents en0_40 = {
3990 "10.0.0.12", "255.255.255.0", NULL, "10.0.0.1", "en0", 40, NULL
3991 };
3992
3993 struct ipv4_service_contents en0_50 = {
3994 "10.0.0.13", "255.255.255.0", NULL, "10.0.0.1", "en0", 50, NULL
3995 };
3996
3997 struct ipv4_service_contents en0_110 = {
3998 "192.168.2.10", "255.255.255.0", NULL, "192.168.2.1", "en0", 110, NULL
3999 };
4000
4001 struct ipv4_service_contents en0_1 = {
4002 "17.202.40.191", "255.255.252.0", NULL, "17.202.20.1", "en0", 1, NULL
4003 };
4004
4005 struct ipv4_service_contents en1_20 = {
4006 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, NULL
4007 };
4008
4009 struct ipv4_service_contents en1_2 = {
4010 "17.202.42.24", "255.255.252.0", NULL, "17.202.20.1", "en1", 2, NULL
4011 };
4012
4013 struct ipv4_service_contents en1_125 = {
4014 "192.168.2.20", "255.255.255.0", NULL, "192.168.2.1", "en1", 125, NULL
4015 };
4016
4017 struct ipv4_service_contents fw0_25 = {
4018 "192.168.2.30", "255.255.255.0", NULL, "192.168.2.1", "fw0", 25, NULL
4019 };
4020
4021 struct ipv4_service_contents fw0_21 = {
4022 "192.168.3.30", "255.255.255.0", NULL, "192.168.3.1", "fw0", 21, NULL
4023 };
4024
4025 struct ipv4_service_contents ppp0_0_1 = {
4026 "17.219.156.22", NULL, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL
4027 };
4028
4029 struct ipv4_service_contents en0_test6 = {
4030 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 2, NULL
4031 };
4032 struct ipv4_service_contents en1_test6 = {
4033 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 3, NULL
4034 };
4035 struct ipv4_service_contents en2_test6 = {
4036 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL
4037 };
4038
4039 struct ipv4_service_contents en0_test7 = {
4040 "17.202.42.113", "255.255.252.0", NULL, "17.202.40.1", "en0", 3, NULL
4041 };
4042 struct ipv4_service_contents en1_test7 = {
4043 "17.202.42.111", "255.255.252.0", NULL, "17.202.40.1", "en1", 2, NULL
4044 };
4045 struct ipv4_service_contents en2_test7 = {
4046 "17.255.98.164", "255.255.240.0", NULL, "17.255.96.1", "en2", 1, NULL
4047 };
4048 struct ipv4_service_contents fw0_test6_and_7 = {
4049 "169.254.11.33", "255.255.0.0", NULL, NULL, "fw0", UINT_MAX, NULL
4050 };
4051
4052 struct ipv4_service_contents en0_10_last = {
4053 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast
4054 };
4055
4056 struct ipv4_service_contents en0_10_never = {
4057 "10.0.0.10", "255.255.255.0", NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever
4058 };
4059
4060 struct ipv4_service_contents en1_20_first = {
4061 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst
4062 };
4063
4064 struct ipv4_service_contents en1_20_never = {
4065 "10.0.0.20", "255.255.255.0", NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever
4066 };
4067
4068 struct ipv4_service_contents * test1[] = {
4069 &en0_40,
4070 &en0_15,
4071 &fw0_25,
4072 &en0_30,
4073 &en1_20,
4074 &en0_50,
4075 &en0_10,
4076 NULL
4077 };
4078
4079 struct ipv4_service_contents * test2[] = {
4080 &en0_40,
4081 &fw0_25,
4082 &en0_30,
4083 &en1_20,
4084 &en0_50,
4085 &en0_10,
4086 NULL
4087 };
4088
4089 struct ipv4_service_contents * test3[] = {
4090 &en0_40,
4091 &en1_20,
4092 &en0_50,
4093 &en0_10,
4094 &en0_110,
4095 &en1_125,
4096 &fw0_25,
4097 &fw0_21,
4098 &en0_40,
4099 &en0_30,
4100 NULL
4101 };
4102
4103 struct ipv4_service_contents * test4[] = {
4104 &en0_1,
4105 &en0_40,
4106 &en0_30,
4107 &en1_20,
4108 &en1_2,
4109 NULL
4110 };
4111
4112 struct ipv4_service_contents * test5[] = {
4113 &ppp0_0_1,
4114 &en0_1,
4115 &en0_40,
4116 &en0_30,
4117 &en1_20,
4118 &en1_2,
4119 NULL
4120 };
4121
4122 struct ipv4_service_contents * test6[] = {
4123 &en0_test6,
4124 &en1_test6,
4125 &en2_test6,
4126 &fw0_test6_and_7,
4127 NULL
4128 };
4129
4130 struct ipv4_service_contents * test7[] = {
4131 &en0_test7,
4132 &en1_test7,
4133 &en2_test7,
4134 &fw0_test6_and_7,
4135 NULL
4136 };
4137
4138 struct ipv4_service_contents * test8[] = {
4139 &en0_10,
4140 &en1_20,
4141 NULL
4142 };
4143
4144 struct ipv4_service_contents * test9[] = {
4145 &en0_10,
4146 &en1_20_first,
4147 &fw0_25,
4148 NULL
4149 };
4150
4151 struct ipv4_service_contents * test10[] = {
4152 &en0_10_last,
4153 &en1_20,
4154 &fw0_25,
4155 NULL
4156 };
4157
4158 struct ipv4_service_contents * test11[] = {
4159 &en0_10_never,
4160 &en1_20,
4161 &fw0_25,
4162 NULL
4163 };
4164
4165 struct ipv4_service_contents * test12[] = {
4166 &en0_10,
4167 &en1_20,
4168 NULL
4169 };
4170
4171 struct ipv4_service_contents * test13[] = {
4172 &en0_10,
4173 &en1_20_never,
4174 NULL
4175 };
4176
4177 struct ipv4_service_contents * test14[] = {
4178 &en1_20_never,
4179 NULL
4180 };
4181
4182 static void
4183 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
4184 const char * str)
4185 {
4186 CFStringRef prop_val;
4187
4188 if (str == NULL) {
4189 return;
4190 }
4191 prop_val = CFStringCreateWithCString(NULL,
4192 str,
4193 kCFStringEncodingASCII);
4194 CFDictionarySetValue(dict, prop_name, prop_val);
4195 CFRelease(prop_val);
4196 return;
4197 }
4198
4199 static void
4200 dict_add_string_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
4201 const char * str)
4202 {
4203 CFArrayRef array;
4204 CFStringRef prop_val;
4205
4206 if (str == NULL) {
4207 return;
4208 }
4209 prop_val = CFStringCreateWithCString(NULL,
4210 str,
4211 kCFStringEncodingASCII);
4212 array = CFArrayCreate(NULL,
4213 (const void **)&prop_val, 1,
4214 &kCFTypeArrayCallBacks);
4215 CFRelease(prop_val);
4216 CFDictionarySetValue(dict, prop_name, array);
4217 CFRelease(array);
4218 return;
4219 }
4220
4221 static CFDictionaryRef
4222 make_IPv4_dict(struct ipv4_service_contents * t)
4223 {
4224 CFMutableDictionaryRef dict;
4225
4226 dict = CFDictionaryCreateMutable(NULL, 0,
4227 &kCFTypeDictionaryKeyCallBacks,
4228 &kCFTypeDictionaryValueCallBacks);
4229 dict_add_string_as_array(dict, kSCPropNetIPv4Addresses, t->addr);
4230 dict_add_string_as_array(dict, kSCPropNetIPv4SubnetMasks, t->mask);
4231 dict_add_string_as_array(dict, kSCPropNetIPv4DestAddresses, t->dest);
4232 dict_add_string(dict, kSCPropNetIPv4Router, t->router);
4233 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
4234 return (dict);
4235 }
4236
4237 static IPv4RouteListRef
4238 make_IPv4RouteList(struct ipv4_service_contents * * this_test)
4239 {
4240 IPv4RouteListRef r;
4241 IPv4RouteListRef routes;
4242 char routes_buf[IPv4RouteListComputeSize(R_STATIC)];
4243 IPv4RouteListRef ret = NULL;
4244 struct ipv4_service_contents * * scan_test;
4245
4246 for (scan_test = this_test; *scan_test != NULL; scan_test++) {
4247 CFDictionaryRef dict;
4248
4249 dict = make_IPv4_dict(*scan_test);
4250 if (dict == NULL) {
4251 fprintf(stderr, "make_IPv4_dict failed\n");
4252 exit(1);
4253 }
4254 routes = (IPv4RouteListRef)routes_buf;
4255 routes->size = R_STATIC;
4256 routes->count = 0;
4257 r = IPv4RouteListCreateWithDictionary(routes, dict,
4258 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
4259 if (r == NULL) {
4260 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
4261 exit(1);
4262 }
4263 ret = IPv4RouteListAddRouteList(ret, 1, r, (*scan_test)->rank);
4264 if (r != routes) {
4265 free(r);
4266 }
4267 CFRelease(dict);
4268 }
4269 return (ret);
4270 }
4271
4272 /*
4273 * Function: run_test
4274 * Purpose:
4275 * Runs through the given set of routes first in the forward direction,
4276 * then again backwards. We should end up with exactly the same set of
4277 * routes at the end.
4278 */
4279 static boolean_t
4280 run_test(const char * name, struct ipv4_service_contents * * this_test)
4281 {
4282 CFStringRef descr;
4283 boolean_t ret = FALSE;
4284 IPv4RouteListRef r;
4285 IPv4RouteListRef routes;
4286 char routes_buf[IPv4RouteListComputeSize(R_STATIC)];
4287 IPv4RouteListRef routes1 = NULL, routes2 = NULL;
4288 struct ipv4_service_contents * * scan_test;
4289
4290 printf("\nStarting test %s\n", name);
4291 for (scan_test = this_test; *scan_test != NULL; scan_test++) {
4292 CFDictionaryRef dict;
4293
4294 dict = make_IPv4_dict(*scan_test);
4295 if (dict == NULL) {
4296 fprintf(stderr, "make_IPv4_dict failed\n");
4297 exit(1);
4298 }
4299 routes = (IPv4RouteListRef)routes_buf;
4300 routes->size = R_STATIC;
4301 routes->count = 0;
4302 r = IPv4RouteListCreateWithDictionary(routes, dict,
4303 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
4304 if (r == NULL) {
4305 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
4306 exit(1);
4307 }
4308 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
4309 descr = IPv4RouteListCopyDescription(r);
4310 SCLog(TRUE, LOG_NOTICE, CFSTR("test: Adding %@"), descr);
4311 CFRelease(descr);
4312 }
4313
4314 routes1 = IPv4RouteListAddRouteList(routes1, 1, r, (*scan_test)->rank);
4315 if (r != routes) {
4316 free(r);
4317 }
4318 CFRelease(dict);
4319 }
4320 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
4321 if (routes1 != NULL) {
4322 descr = IPv4RouteListCopyDescription(routes1);
4323 SCLog(TRUE, LOG_NOTICE, CFSTR("Routes are %@"), descr);
4324 CFRelease(descr);
4325 }
4326 }
4327 for (scan_test--; scan_test >= this_test; scan_test--) {
4328 CFDictionaryRef dict;
4329
4330 dict = make_IPv4_dict(*scan_test);
4331 if (dict == NULL) {
4332 fprintf(stderr, "make_IPv4_dict failed\n");
4333 exit(1);
4334 }
4335 routes = (IPv4RouteListRef)routes_buf;
4336 routes->size = R_STATIC;
4337 routes->count = 0;
4338 r = IPv4RouteListCreateWithDictionary(routes, dict,
4339 (*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
4340 if (r == NULL) {
4341 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
4342 exit(1);
4343 }
4344 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
4345 descr = IPv4RouteListCopyDescription(r);
4346 SCLog(TRUE, LOG_NOTICE, CFSTR("test: Adding %@"), descr);
4347 CFRelease(descr);
4348 }
4349 routes2 = IPv4RouteListAddRouteList(routes2, 1, r, (*scan_test)->rank);
4350 if (r != routes) {
4351 free(r);
4352 }
4353 CFRelease(dict);
4354 }
4355 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
4356 if (routes2 != NULL) {
4357 descr = IPv4RouteListCopyDescription(routes2);
4358 SCLog(TRUE, LOG_NOTICE, CFSTR("Routes are %@"), descr);
4359 CFRelease(descr);
4360 }
4361 }
4362 if ((routes1 != NULL && routes2 == NULL)
4363 || (routes1 == NULL && routes2 != NULL)) {
4364 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
4365 (routes1 != NULL) ? "not " : "",
4366 (routes2 != NULL) ? "not " : "");
4367 }
4368 else if (routes1 != NULL && routes2 != NULL) {
4369 /* check if they are different */
4370 if (routes1->count != routes2->count) {
4371 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
4372 routes1->count, routes2->count);
4373 }
4374 else if (bcmp(routes1, routes2,
4375 IPv4RouteListComputeSize(routes1->count)) != 0) {
4376 fprintf(stderr, "routes1 and routes2 are different\n");
4377 }
4378 else {
4379 printf("routes1 and routes2 are the same\n");
4380 ret = TRUE;
4381 }
4382 }
4383 if (routes1 != NULL) {
4384 free(routes1);
4385 }
4386 if (routes2 != NULL) {
4387 free(routes2);
4388 }
4389 return (ret);
4390 }
4391
4392 typedef struct compare_context {
4393 IPv4RouteListRef old;
4394 IPv4RouteListRef new;
4395 } compare_context_t;
4396
4397 static void
4398 compare_callback(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg)
4399 {
4400 compare_context_t * context = (compare_context_t *)arg;
4401
4402 switch (cmd) {
4403 case kIPv4RouteListAddRouteCommand:
4404 printf("Add new[%d] = ", route - context->new->list);
4405 IPv4RoutePrint(route);
4406 printf("\n");
4407 break;
4408 case kIPv4RouteListRemoveRouteCommand:
4409 printf("Remove old[%d] = ", route - context->old->list);
4410 IPv4RoutePrint(route);
4411 printf("\n");
4412 break;
4413 default:
4414 break;
4415 }
4416 return;
4417 }
4418
4419 static void
4420 compare_tests(struct ipv4_service_contents * * old_test,
4421 struct ipv4_service_contents * * new_test)
4422 {
4423 IPv4RouteListRef new_routes;
4424 IPv4RouteListRef old_routes;
4425 compare_context_t context;
4426
4427 old_routes = make_IPv4RouteList(old_test);
4428 new_routes = make_IPv4RouteList(new_test);
4429
4430 if (old_routes == NULL) {
4431 printf("No Old Routes\n");
4432 }
4433 else {
4434 printf("Old Routes = ");
4435 IPv4RouteListPrint(old_routes);
4436 }
4437 if (new_routes == NULL) {
4438 printf("No New Routes\n");
4439 }
4440 else {
4441 printf("New Routes = ");
4442 IPv4RouteListPrint(new_routes);
4443 }
4444 context.old = old_routes;
4445 context.new = new_routes;
4446 IPv4RouteListApply(old_routes, new_routes, compare_callback, &context);
4447 if (old_routes != NULL) {
4448 free(old_routes);
4449 }
4450 if (new_routes != NULL) {
4451 free(new_routes);
4452 }
4453
4454 return;
4455 }
4456
4457 int
4458 main(int argc, char **argv)
4459 {
4460 _sc_log = FALSE;
4461 _sc_verbose = (argc > 1) ? TRUE : FALSE;
4462
4463 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
4464 if (argc > 1) {
4465 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
4466 }
4467
4468 if (run_test("test1", test1) == FALSE) {
4469 fprintf(stderr, "test1 failed\n");
4470 exit(1);
4471 }
4472 if (run_test("test2", test2) == FALSE) {
4473 fprintf(stderr, "test2 failed\n");
4474 exit(1);
4475 }
4476 if (run_test("test3", test4) == FALSE) {
4477 fprintf(stderr, "test3 failed\n");
4478 exit(1);
4479 }
4480 if (run_test("test4", test4) == FALSE) {
4481 fprintf(stderr, "test4 failed\n");
4482 exit(1);
4483 }
4484 if (run_test("test5", test5) == FALSE) {
4485 fprintf(stderr, "test5 failed\n");
4486 exit(1);
4487 }
4488
4489 printf("\nCompare 1 to 2:\n");
4490 compare_tests(test1, test2);
4491
4492 printf("\nCompare 2 to 1:\n");
4493 compare_tests(test2, test1);
4494
4495 printf("\nCompare 1 to 1:\n");
4496 compare_tests(test1, test1);
4497
4498 printf("\nCompare 1 to 3:\n");
4499 compare_tests(test1, test3);
4500
4501 printf("\nCompare 3 to 1:\n");
4502 compare_tests(test3, test1);
4503
4504 printf("\nCompare 2 to 3:\n");
4505 compare_tests(test2, test3);
4506
4507 printf("\nCompare 3 to 2:\n");
4508 compare_tests(test3, test2);
4509
4510 printf("\nCompare 3 to 4:\n");
4511 compare_tests(test3, test4);
4512
4513 printf("\nCompare 5 to 4:\n");
4514 compare_tests(test5, test4);
4515
4516 printf("\nCompare 6 to 7:\n");
4517 compare_tests(test6, test7);
4518
4519 printf("\nCompare 7 to 6:\n");
4520 compare_tests(test7, test6);
4521
4522 printf("\nCompare 8 to 9:\n");
4523 compare_tests(test8, test9);
4524
4525 printf("\nCompare 8 to 10:\n");
4526 compare_tests(test8, test10);
4527
4528 printf("\nCompare 8 to 11:\n");
4529 compare_tests(test8, test11);
4530
4531 printf("\nCompare 12 to 13:\n");
4532 compare_tests(test12, test13);
4533
4534 printf("\nCompare 13 to 14:\n");
4535 compare_tests(test13, test14);
4536
4537 printf("\nChecking for leaks\n");
4538 char cmd[128];
4539 sprintf(cmd, "leaks %d 2>&1", getpid());
4540 fflush(stdout);
4541 (void)system(cmd);
4542
4543 exit(0);
4544 return (0);
4545 }
4546
4547 #endif /* TEST_IPV4_ROUTELIST */
4548