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