]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/ip_plugin.c
configd-699.1.5.tar.gz
[apple/configd.git] / Plugins / IPMonitor / ip_plugin.c
1 /*
2 * Copyright (c) 2000-2014 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 * November 13, 2013 Dieter Siegmund (dieter@apple.com)
71 * - added generic IPv4 routing support
72 */
73
74 #include <stdlib.h>
75 #include <unistd.h>
76 #include <string.h>
77 #include <stdio.h>
78 #include <sys/fcntl.h>
79 #include <sys/ioctl.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <net/route.h>
83 #include <net/if.h>
84 #include <net/if_dl.h>
85 #include <netinet/in.h>
86 #include <netinet/icmp6.h>
87 #include <netinet6/in6_var.h>
88 #include <netinet6/nd6.h>
89 #include <arpa/inet.h>
90 #include <sys/sysctl.h>
91 #include <limits.h>
92 #include <notify.h>
93 #include <mach/mach_time.h>
94 #include <dispatch/dispatch.h>
95 #include <CommonCrypto/CommonDigest.h>
96
97 #include <SystemConfiguration/SystemConfiguration.h>
98 #include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
99 #include <SystemConfiguration/SCValidation.h>
100 #include <SystemConfiguration/scprefs_observer.h>
101 #include <SystemConfiguration/SCPrivate.h> /* for SCLog() */
102 #include "SCNetworkReachabilityInternal.h"
103 #include "SCNetworkSignaturePrivate.h"
104 #include <dnsinfo.h>
105 #include "dnsinfo_server.h"
106
107 #include <ppp/PPPControllerPriv.h>
108
109 #include <dns_sd.h>
110 #ifndef kDNSServiceCompMulticastDNS
111 #define kDNSServiceCompMulticastDNS "MulticastDNS"
112 #endif
113 #ifndef kDNSServiceCompPrivateDNS
114 #define kDNSServiceCompPrivateDNS "PrivateDNS"
115 #endif
116 #include <network_information.h>
117 #include "network_information_priv.h"
118 #include "network_information_server.h"
119 #include <ppp/ppp_msg.h>
120 #include "ip_plugin.h"
121 #if !TARGET_IPHONE_SIMULATOR
122 #include "set-hostname.h"
123 #endif /* !TARGET_IPHONE_SIMULATOR */
124
125 #include "dns-configuration.h"
126 #include "proxy-configuration.h"
127
128 #if !TARGET_OS_IPHONE
129 #include "smb-configuration.h"
130 #endif /* !TARGET_OS_IPHONE */
131
132 #define kLoopbackInterface "lo0"
133 #define EROUTENOTAPPLIED 1001
134
135 typedef CF_ENUM(uint8_t, ProtocolFlags) {
136 kProtocolFlagsNone = 0x0,
137 kProtocolFlagsIPv4 = 0x1,
138 kProtocolFlagsIPv6 = 0x2
139 };
140
141 enum {
142 kDebugFlag1 = 0x00000001,
143 kDebugFlag2 = 0x00000002,
144 kDebugFlag4 = 0x00000004,
145 kDebugFlag8 = 0x00000008,
146 kDebugFlagDefault = kDebugFlag1,
147 kDebugFlagAll = 0xffffffff
148 };
149
150 typedef unsigned int IFIndex;
151
152 #ifndef TEST_ROUTELIST
153
154 #define ROUTELIST_DEBUG(flag, fmt, ...)
155
156 static struct if_nameindex * S_if_nameindex_cache;
157
158 __private_extern__ IFIndex
159 my_if_nametoindex(const char * ifname)
160 {
161 IFIndex idx = 0;
162 struct if_nameindex * scan;
163
164 if (S_if_nameindex_cache == NULL) {
165 return (if_nametoindex(ifname));
166 }
167 for (scan = S_if_nameindex_cache;
168 scan->if_index != 0 && scan->if_name != NULL;
169 scan++) {
170 if (strcmp(scan->if_name, ifname) == 0) {
171 idx = scan->if_index;
172 break;
173 }
174 }
175 return (idx);
176 }
177
178 __private_extern__ const char *
179 my_if_indextoname(IFIndex idx, char if_name[IFNAMSIZ])
180 {
181 const char * name = NULL;
182 struct if_nameindex * scan;
183
184 if (S_if_nameindex_cache == NULL) {
185 return (if_indextoname(idx, if_name));
186 }
187 for (scan = S_if_nameindex_cache;
188 scan->if_index != 0 && scan->if_name != NULL;
189 scan++) {
190 if (scan->if_index == idx) {
191 name = if_name;
192 strlcpy(if_name, scan->if_name, IFNAMSIZ);
193 break;
194 }
195 }
196 return (name);
197 }
198
199 static void
200 my_if_freenameindex(void)
201 {
202 if (S_if_nameindex_cache != NULL) {
203 if_freenameindex(S_if_nameindex_cache);
204 S_if_nameindex_cache = NULL;
205 }
206 return;
207 }
208
209 static void
210 my_if_nameindex(void)
211 {
212 my_if_freenameindex();
213 S_if_nameindex_cache = if_nameindex();
214 return;
215 }
216
217
218 #else /* TEST_ROUTELIST */
219
220 #define ROUTELIST_DEBUG(flags, format, ...) { if (((S_IPMonitor_debug & (flags)) != 0)) printf((format), ## __VA_ARGS__ ); }
221
222
223 static const char * * list;
224 static int list_count;
225 static int list_size;
226
227 __private_extern__ IFIndex
228 my_if_nametoindex(const char * ifname)
229 {
230 IFIndex ret;
231
232 if (list == NULL) {
233 list_size = 4;
234 list_count = 2;
235 list = (const char * *)malloc(sizeof(*list) * list_size);
236 list[0] = strdup("");
237 list[1] = strdup(kLoopbackInterface);
238 }
239 else {
240 int i;
241
242 for (i = 1; i < list_count; i++) {
243 if (strcmp(list[i], ifname) == 0) {
244 ret = i;
245 goto done;
246 }
247 }
248 }
249 if (list_count == list_size) {
250 list_size += 2;
251 list = (const char * *)realloc(list, sizeof(*list) * list_size);
252 }
253 list[list_count] = strdup(ifname);
254 ret = list_count;
255 list_count++;
256 done:
257 return (ret);
258 }
259
260 __private_extern__ const char *
261 my_if_indextoname(IFIndex idx, char if_name[IFNAMSIZ])
262 {
263 const char * name = NULL;
264
265 if (idx < list_count) {
266 name = if_name;
267 strlcpy(if_name, list[idx], IFNAMSIZ);
268 }
269 return (name);
270 }
271
272 static void
273 my_if_nameindex(void)
274 {
275 }
276
277 static void
278 my_if_freenameindex(void)
279 {
280 }
281
282 #endif /* TEST_ROUTELIST */
283
284 static const char *
285 my_if_indextoname2(IFIndex ifindex, char ifname[IFNAMSIZ])
286 {
287 if (ifindex == 0) {
288 return (NULL);
289 }
290 if (my_if_indextoname(ifindex, ifname) == NULL) {
291 snprintf(ifname, IFNAMSIZ, "[%d]", ifindex);
292 }
293 return (ifname);
294 }
295
296
297 static IFIndex
298 lo0_ifindex(void)
299 {
300 static IFIndex idx;
301
302 if (idx == 0) {
303 idx = my_if_nametoindex(kLoopbackInterface);
304 }
305 return (idx);
306 }
307
308
309 /*
310 * Property: kServiceOptionRankAssertion
311 * Purpose:
312 * Key used in the service options dictionary to hold the RankAssertion
313 * derived from the kSCPropNetServicePrimaryRank string.
314 */
315 #define kServiceOptionRankAssertion CFSTR("RankAssertion") /* number */
316
317 /*
318 * Property: kIPIsCoupled
319 * Purpose:
320 * Used to indicate that the IPv4 and IPv6 services are coupled.
321 * Neither the IPv4 part nor the IPv6 part of a coupled service
322 * may become primary if IPv4 or IPv6 is primary for another interface.
323 *
324 * For example, if the service over en3 is "coupled" and has IPv6,
325 * and en0 is primary for just IPv4, IPv6 over en3 is not eligible
326 * to become primary for IPv6.
327 */
328 #define kIPIsCoupled CFSTR("IPIsCoupled")
329
330 #define PPP_PREFIX "ppp"
331
332 #define IP_FORMAT "%d.%d.%d.%d"
333 #define IP_CH(ip) ((u_char *)(ip))
334 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
335
336 static SCLoggerRef S_IPMonitor_logger;
337
338 static Boolean S_bundle_logging_verbose;
339
340 /*
341 * IPv4 Route management
342 */
343
344 typedef CF_ENUM(uint16_t, RouteFlags) {
345 kRouteFlagsIsScoped = 0x0001,
346 kRouteFlagsHasGateway = 0x0002,
347 kRouteFlagsIsHost = 0x0004,
348 kRouteFlagsIsNULL = 0x0008,
349 kRouteFlagsKernelManaged = 0x0010
350 };
351
352 typedef CF_ENUM(uint16_t, ControlFlags) {
353 kControlFlagsProcessed = 0x0001,
354 kControlFlagsAdded = 0x0002,
355 };
356
357 #define ROUTE_COMMON \
358 int prefix_length; \
359 IFIndex ifindex; \
360 IFIndex exclude_ifindex; \
361 Rank rank; \
362 RouteFlags flags; \
363 ControlFlags control_flags;
364
365 typedef struct {
366 ROUTE_COMMON
367 } Route, * RouteRef;
368
369 typedef struct {
370 ROUTE_COMMON
371 struct in_addr dest;
372 struct in_addr mask;
373 struct in_addr gateway;
374 struct in_addr ifa;
375 } IPv4Route, * IPv4RouteRef;
376
377 typedef struct {
378 ROUTE_COMMON
379 struct in6_addr dest;
380 struct in6_addr gateway;
381 struct in6_addr ifa;
382 } IPv6Route, * IPv6RouteRef;
383
384 typedef CF_ENUM(uint16_t, RouteListFlags) {
385 kRouteListFlagsExcludeNWI = 0x0001,
386 kRouteListFlagsHasDefault = 0x0002
387 };
388
389 #define ROUTELIST_COMMON \
390 int count; \
391 int size; \
392 RouteListFlags flags;
393
394 typedef struct {
395 ROUTELIST_COMMON
396 } RouteListCommon, * RouteListRef;
397
398 typedef struct {
399 ROUTELIST_COMMON
400 IPv4Route list[1]; /* variable length */
401 } IPv4RouteList, * IPv4RouteListRef;
402
403 typedef struct {
404 ROUTELIST_COMMON
405 IPv6Route list[1]; /* variable length */
406 } IPv6RouteList, * IPv6RouteListRef;
407
408 typedef union {
409 void * ptr;
410 RouteListRef common;
411 IPv4RouteListRef v4;
412 IPv6RouteListRef v6;
413 } RouteListUnion;
414
415 typedef enum {
416 kRouteCommandAdd,
417 kRouteCommandRemove
418 } RouteCommand;
419
420 /*
421 * Election Information
422 * - information about the current best services
423 */
424 typedef union {
425 struct in_addr v4;
426 struct in6_addr v6;
427 } in_addr;
428
429 typedef union {
430 struct sockaddr_in v4;
431 struct sockaddr_in6 v6;
432 } in_sockaddr;
433
434 typedef struct Candidate {
435 CFStringRef serviceID;
436 CFStringRef if_name;
437 Rank rank;
438 boolean_t ip_is_coupled;
439 SCNetworkReachabilityFlags reachability_flags;
440 in_addr addr;
441 in_sockaddr vpn_server_addr;
442 CFStringRef signature;
443 } Candidate, * CandidateRef;
444
445 typedef struct ElectionResults {
446 int af;
447 int count;
448 int size;
449 Candidate candidates[1];
450 } ElectionResults, * ElectionResultsRef;
451
452 static __inline__ size_t
453 ElectionResultsComputeSize(unsigned int n)
454 {
455 return (offsetof(ElectionResults, candidates[n]));
456 }
457
458 /*
459 * Type: Rank
460 * Purpose:
461 * A 32-bit value to encode the relative rank of a service.
462 *
463 * The top 8 bits are used to hold the rank assertion (first, default, last,
464 * never, scoped);
465 *
466 * The bottom 24 bits are used to store the service index (i.e. the
467 * position within the service order array).
468 */
469 #define RANK_ASSERTION_MAKE(r) ((Rank)(r) << 24)
470 #define kRankAssertionFirst RANK_ASSERTION_MAKE(0)
471 #define kRankAssertionDefault RANK_ASSERTION_MAKE(1)
472 #define kRankAssertionLast RANK_ASSERTION_MAKE(2)
473 #define kRankAssertionNever RANK_ASSERTION_MAKE(3)
474 #define kRankAssertionScoped RANK_ASSERTION_MAKE(4)
475 #define kRankAssertionMask RANK_ASSERTION_MAKE(0xff)
476 #define RANK_ASSERTION_MASK(r) ((Rank)(r) & kRankAssertionMask)
477 #define RANK_ASSERTION_GET(r) ((Rank)(r) >> 24)
478 #define RANK_INDEX_MAKE(r) ((Rank)(r))
479 #define kRankIndexMask RANK_INDEX_MAKE(0xffffff)
480 #define RANK_INDEX_MASK(r) ((Rank)(r) & kRankIndexMask)
481
482 static __inline__ Rank
483 RankMake(uint32_t service_index, Rank primary_rank)
484 {
485 return (RANK_INDEX_MASK(service_index) | RANK_ASSERTION_MASK(primary_rank));
486 }
487
488 static Rank
489 InterfaceRankGetRankAssertion(CFNumberRef rank_cf, Boolean * ret_is_set)
490 {
491 SCNetworkServicePrimaryRank if_rank;
492 Boolean is_set = FALSE;
493 Rank rank = kRankAssertionDefault;
494
495 if (rank_cf != NULL
496 && CFNumberGetValue(rank_cf, kCFNumberSInt32Type, &if_rank)
497 && if_rank != kSCNetworkServicePrimaryRankDefault) {
498 if (if_rank == kSCNetworkServicePrimaryRankFirst) {
499 rank = kRankAssertionFirst;
500 }
501 else {
502 rank = RANK_ASSERTION_MAKE(if_rank);
503 }
504 is_set = TRUE;
505 }
506 if (ret_is_set != NULL) {
507 *ret_is_set = is_set;
508 }
509 return (rank);
510 }
511
512 static Rank
513 PrimaryRankGetRankAssertion(CFStringRef rank_str, Boolean * is_set)
514 {
515 int i;
516 struct {
517 const CFStringRef * name;
518 Rank rank_assertion;
519 } values[] = {
520 { &kSCValNetServicePrimaryRankFirst, kRankAssertionFirst },
521 { &kSCValNetServicePrimaryRankLast, kRankAssertionLast },
522 { &kSCValNetServicePrimaryRankNever, kRankAssertionNever },
523 { &kSCValNetServicePrimaryRankScoped, kRankAssertionScoped }
524 };
525
526 if (rank_str != NULL) {
527 for (i = 0; i < countof(values); i++) {
528 if (CFEqual(rank_str, *(values[i].name))) {
529 if (is_set != NULL) {
530 *is_set = TRUE;
531 }
532 return (values[i].rank_assertion);
533 }
534 }
535 }
536 if (is_set != NULL) {
537 *is_set = FALSE;
538 }
539 return (kRankAssertionDefault);
540 }
541
542 /* SCDynamicStore session */
543 static SCDynamicStoreRef S_session = NULL;
544
545 /* debug output flags */
546 static uint32_t S_IPMonitor_debug = 0;
547 static Boolean S_IPMonitor_verbose = FALSE;
548
549 /* are we netbooted? If so, don't touch the default route */
550 static boolean_t S_netboot = FALSE;
551
552 /* is scoped routing enabled? */
553 static boolean_t S_scopedroute = FALSE;
554 static boolean_t S_scopedroute_v6 = FALSE;
555
556 /* dictionary to hold per-service state: key is the serviceID */
557 static CFMutableDictionaryRef S_service_state_dict = NULL;
558 static CFMutableDictionaryRef S_ipv4_service_rank_dict = NULL;
559 static CFMutableDictionaryRef S_ipv6_service_rank_dict = NULL;
560
561 /* dictionary to hold per-interface rank information */
562 static CFDictionaryRef S_if_rank_dict;
563
564 /* if set, a PPP interface overrides the primary */
565 static boolean_t S_ppp_override_primary = FALSE;
566
567 /* the current primary serviceID's */
568 static CFStringRef S_primary_ipv4 = NULL;
569 static CFStringRef S_primary_ipv6 = NULL;
570 static CFStringRef S_primary_dns = NULL;
571 static CFStringRef S_primary_proxies = NULL;
572
573 /* the current election results */
574 static ElectionResultsRef S_ipv4_results;
575 static ElectionResultsRef S_ipv6_results;
576
577 static CFStringRef S_state_global_ipv4 = NULL;
578 static CFStringRef S_state_global_ipv6 = NULL;
579 static CFStringRef S_state_global_dns = NULL;
580 static CFStringRef S_state_global_proxies = NULL;
581 static CFStringRef S_state_service_prefix = NULL;
582 static CFStringRef S_setup_global_ipv4 = NULL;
583 static CFStringRef S_setup_service_prefix = NULL;
584
585 static CFStringRef S_multicast_resolvers = NULL;
586 static CFStringRef S_private_resolvers = NULL;
587
588 #if !TARGET_IPHONE_SIMULATOR
589 static IPv4RouteListRef S_ipv4_routelist = NULL;
590 static IPv6RouteListRef S_ipv6_routelist = NULL;
591
592 #endif /* !TARGET_IPHONE_SIMULATOR */
593
594 static boolean_t S_append_state = FALSE;
595
596 static CFDictionaryRef S_dns_dict = NULL;
597
598 static Boolean S_dnsinfo_synced = TRUE;
599
600 static nwi_state_t S_nwi_state = NULL;
601 static Boolean S_nwi_synced = TRUE;
602
603 static CFDictionaryRef S_proxies_dict = NULL;
604
605 // Note: access should be gated with __network_change_queue()
606 static uint32_t S_network_change_needed = 0;
607 #define NETWORK_CHANGE_NET 1<<0
608 #define NETWORK_CHANGE_DNS 1<<1
609 #define NETWORK_CHANGE_PROXY 1<<2
610 #if !TARGET_OS_IPHONE
611 #define NETWORK_CHANGE_SMB 1<<3
612 #endif /* !TARGET_OS_IPHONE */
613 static struct timeval S_network_change_start;
614 static Boolean S_network_change_timeout = FALSE;
615 static dispatch_source_t S_network_change_timer = NULL;
616
617 #if !TARGET_OS_IPHONE
618 static CFStringRef S_primary_smb = NULL;
619 static CFStringRef S_state_global_smb = NULL;
620 static CFDictionaryRef S_smb_dict = NULL;
621 #endif /* !TARGET_OS_IPHONE */
622
623 #if !TARGET_OS_IPHONE
624 #define VAR_RUN_RESOLV_CONF "/var/run/resolv.conf"
625 #endif /* !TARGET_OS_IPHONE */
626
627 #ifndef KERN_NETBOOT
628 #define KERN_NETBOOT 40 /* int: are we netbooted? 1=yes,0=no */
629 #endif /* KERN_NETBOOT */
630
631 /**
632 ** entityType*, GetEntityChanges*
633 ** - definitions for the entity types we handle
634 **/
635 typedef enum {
636 kEntityTypeIPv4 = 0,
637 kEntityTypeIPv6,
638 kEntityTypeDNS,
639 kEntityTypeProxies,
640 #if !TARGET_OS_IPHONE
641 kEntityTypeSMB,
642 #endif /* !TARGET_OS_IPHONE */
643 ENTITY_TYPES_COUNT,
644 kEntityTypeTransientStatus,
645 kEntityTypeServiceOptions = 31
646 } EntityType;
647
648 static const CFStringRef *entityTypeNames[ENTITY_TYPES_COUNT] = {
649 &kSCEntNetIPv4, /* 0 */
650 &kSCEntNetIPv6, /* 1 */
651 &kSCEntNetDNS, /* 2 */
652 &kSCEntNetProxies, /* 3 */
653 #if !TARGET_OS_IPHONE
654 &kSCEntNetSMB, /* 4 */
655 #endif /* !TARGET_OS_IPHONE */
656 };
657
658 static Boolean
659 S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value);
660
661 static __inline__ char
662 ipvx_char(int af)
663 {
664 return ((af == AF_INET) ? '4' : '6');
665 }
666
667 static __inline__ char
668 ipvx_other_char(int af)
669 {
670 return ((af == AF_INET) ? '6' : '4');
671 }
672
673 /*
674 * IPv4/IPv6 Service Dict keys: kIPDictRoutes, IPDictService
675 *
676 * The IPv4/IPv6 service dictionary contains two sub-dictionaries:
677 * Routes CFData containing IPv4RouteList/IPv6RouteList
678 * Service dictionary containing kSCEntNetIPv[46] service entity
679 */
680 #define kIPDictRoutes CFSTR("Routes") /* data */
681 #define kIPDictService CFSTR("Service") /* dict */
682
683 static CFDictionaryRef
684 ipdict_create(CFDictionaryRef dict, CFDataRef routes_data)
685 {
686 CFStringRef keys[2];
687 CFTypeRef values[2];
688
689 keys[0] = kIPDictService;
690 values[0] = dict;
691 keys[1] = kIPDictRoutes;
692 values[1] = routes_data;
693 return (CFDictionaryCreate(NULL,
694 (const void * *)keys,
695 values,
696 countof(keys),
697 &kCFTypeDictionaryKeyCallBacks,
698 &kCFTypeDictionaryValueCallBacks));
699 }
700
701 static void *
702 ipdict_get_routelist(CFDictionaryRef dict)
703 {
704 void * routes_list = NULL;
705
706 if (dict != NULL) {
707 CFDataRef routes;
708
709 routes = CFDictionaryGetValue(dict, kIPDictRoutes);
710 if (routes != NULL) {
711 routes_list = (void *)CFDataGetBytePtr(routes);
712 }
713 }
714 return (routes_list);
715 }
716
717 static CFDictionaryRef
718 ipdict_get_service(CFDictionaryRef dict)
719 {
720 CFDictionaryRef ip_dict = NULL;
721
722 if (dict != NULL) {
723 ip_dict = CFDictionaryGetValue(dict, kIPDictService);
724 }
725 return (ip_dict);
726 }
727
728 static CFStringRef
729 ipdict_get_ifname(CFDictionaryRef dict)
730 {
731 CFStringRef ifname = NULL;
732 CFDictionaryRef ip_dict;
733
734 ip_dict = ipdict_get_service(dict);
735 if (ip_dict != NULL) {
736 ifname = CFDictionaryGetValue(ip_dict, kSCPropInterfaceName);
737 }
738 return (ifname);
739 }
740
741 typedef boolean_t GetEntityChangesFunc(CFStringRef serviceID,
742 CFDictionaryRef state_dict,
743 CFDictionaryRef setup_dict,
744 CFDictionaryRef info);
745 typedef GetEntityChangesFunc * GetEntityChangesFuncRef;
746
747 static GetEntityChangesFunc get_ipv4_changes;
748 static GetEntityChangesFunc get_ipv6_changes;
749 static GetEntityChangesFunc get_dns_changes;
750 static GetEntityChangesFunc get_proxies_changes;
751 #if !TARGET_OS_IPHONE
752 static GetEntityChangesFunc get_smb_changes;
753 #endif /* !TARGET_OS_IPHONE */
754
755 static void
756 my_CFRelease(void * t);
757
758 static void
759 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new);
760
761 static void
762 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key);
763
764 static const GetEntityChangesFuncRef entityChangeFunc[ENTITY_TYPES_COUNT] = {
765 get_ipv4_changes, /* 0 */
766 get_ipv6_changes, /* 1 */
767 get_dns_changes, /* 2 */
768 get_proxies_changes,/* 3 */
769 #if !TARGET_OS_IPHONE
770 get_smb_changes, /* 4 */
771 #endif /* !TARGET_OS_IPHONE */
772 };
773
774 /**
775 ** keyChangeList
776 ** - mechanism to do an atomic update of the SCDynamicStore
777 ** when the content needs to be changed across multiple functions
778 **/
779 typedef struct {
780 CFMutableArrayRef notify;
781 CFMutableArrayRef remove;
782 CFMutableDictionaryRef set;
783 } keyChangeList, * keyChangeListRef;
784
785 static void
786 keyChangeListInit(keyChangeListRef keys)
787 {
788 keys->notify = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
789 keys->remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
790 keys->set = CFDictionaryCreateMutable(NULL, 0,
791 &kCFTypeDictionaryKeyCallBacks,
792 &kCFTypeDictionaryValueCallBacks);
793 return;
794 }
795
796 static void
797 keyChangeListFree(keyChangeListRef keys)
798 {
799 my_CFRelease(&keys->notify);
800 my_CFRelease(&keys->remove);
801 my_CFRelease(&keys->set);
802 return;
803 }
804
805 static Boolean
806 keyChangeListActive(keyChangeListRef keys)
807 {
808 return ((CFDictionaryGetCount(keys->set) > 0) ||
809 (CFArrayGetCount(keys->remove) > 0) ||
810 (CFArrayGetCount(keys->notify) > 0));
811 }
812
813 static void
814 keyChangeListNotifyKey(keyChangeListRef keys, CFStringRef key)
815 {
816 my_CFArrayAppendUniqueValue(keys->notify, key);
817 return;
818 }
819
820 static void
821 keyChangeListRemoveValue(keyChangeListRef keys, CFStringRef key)
822 {
823 my_CFArrayAppendUniqueValue(keys->remove, key);
824 CFDictionaryRemoveValue(keys->set, key);
825 return;
826 }
827
828 static void
829 keyChangeListSetValue(keyChangeListRef keys, CFStringRef key, CFTypeRef value)
830 {
831 my_CFArrayRemoveValue(keys->remove, key);
832 CFDictionarySetValue(keys->set, key, value);
833 return;
834 }
835
836 static void
837 keyChangeListApplyToStore(keyChangeListRef keys, SCDynamicStoreRef session)
838 {
839 CFArrayRef notify = keys->notify;
840 CFArrayRef remove = keys->remove;
841 CFDictionaryRef set = keys->set;
842
843 if (CFArrayGetCount(notify) == 0) {
844 notify = NULL;
845 }
846 if (CFArrayGetCount(remove) == 0) {
847 remove = NULL;
848 }
849 if (CFDictionaryGetCount(set) == 0) {
850 set = NULL;
851 }
852 if (set == NULL && remove == NULL && notify == NULL) {
853 return;
854 }
855 if (S_IPMonitor_debug & kDebugFlag1) {
856 if (set != NULL) {
857 my_log(LOG_DEBUG, "IPMonitor: Setting:\n%@", set);
858 }
859 if (remove != NULL) {
860 my_log(LOG_DEBUG, "IPMonitor: Removing:\n%@", remove);
861 }
862 if (notify != NULL) {
863 my_log(LOG_DEBUG, "IPMonitor: Notifying:\n%@", notify);
864 }
865 }
866 (void)SCDynamicStoreSetMultiple(session, set, remove, notify);
867
868 return;
869 }
870
871 static void
872 S_nwi_ifstate_dump(nwi_ifstate_t ifstate, int i)
873 {
874 const char * addr_str;
875 void * address;
876 char ntopbuf[INET6_ADDRSTRLEN];
877 char vpn_ntopbuf[INET6_ADDRSTRLEN];
878 const struct sockaddr * vpn_addr;
879
880 address = nwi_ifstate_get_address(ifstate);
881 addr_str = inet_ntop(ifstate->af, address, ntopbuf, sizeof(ntopbuf));
882 vpn_addr = nwi_ifstate_get_vpn_server(ifstate);
883 if (vpn_addr != NULL) {
884 _SC_sockaddr_to_string(nwi_ifstate_get_vpn_server(ifstate),
885 vpn_ntopbuf,
886 sizeof(vpn_ntopbuf));
887 }
888 my_log(LOG_DEBUG,
889 " [%d]: %s%s%s%s rank 0x%x iaddr %s%s%s reach_flags 0x%x",
890 i, ifstate->ifname,
891 ifstate->diff_str != NULL ? ifstate->diff_str : "",
892 (ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0
893 ? " dns" : "",
894 (ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0
895 ? " never" : "",
896 ifstate->rank,
897 addr_str,
898 (vpn_addr != NULL) ? " vpn_server_addr: " : "",
899 (vpn_addr != NULL) ? vpn_ntopbuf : "",
900 ifstate->reach_flags);
901 return;
902 }
903
904 static void
905 S_nwi_state_dump(nwi_state_t state)
906 {
907 int i;
908 nwi_ifstate_t scan;
909
910 if (state == NULL) {
911 my_log(LOG_DEBUG, "nwi_state = <none>");
912 return;
913 }
914 my_log(LOG_DEBUG,
915 "nwi_state = { "
916 "gen=%llu size=%u #v4=%u #v6=%u "
917 "reach_flags=(v4=0x%x, v6=0x%x) }",
918 state->generation_count,
919 state->size,
920 state->ipv4_count,
921 state->ipv6_count,
922 nwi_state_get_reachability_flags(state, AF_INET),
923 nwi_state_get_reachability_flags(state, AF_INET6));
924 if (state->ipv4_count) {
925 my_log(LOG_DEBUG, "IPv4:");
926 for (i = 0, scan = state->nwi_ifstates;
927 i < state->ipv4_count; i++, scan++) {
928 S_nwi_ifstate_dump(scan, i);
929 }
930 }
931 if (state->ipv6_count) {
932 my_log(LOG_DEBUG, "IPv6:");
933 for (i = 0, scan = state->nwi_ifstates + state->ipv6_start;
934 i < state->ipv6_count; i++, scan++) {
935 S_nwi_ifstate_dump(scan, i);
936 }
937 }
938 return;
939 }
940
941 static boolean_t
942 S_is_network_boot()
943 {
944 int mib[2];
945 size_t len;
946 int netboot = 0;
947
948 mib[0] = CTL_KERN;
949 mib[1] = KERN_NETBOOT;
950 len = sizeof(netboot);
951 sysctl(mib, 2, &netboot, &len, NULL, 0);
952 return (netboot);
953 }
954
955 static int rtm_seq = 0;
956
957 #if !TARGET_IPHONE_SIMULATOR
958 static int
959 open_routing_socket(void)
960 {
961 int sockfd;
962
963 if ((sockfd = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE)) == -1) {
964 my_log(LOG_NOTICE,
965 "IPMonitor: open_routing_socket: socket failed, %s",
966 strerror(errno));
967 }
968 return (sockfd);
969 }
970
971 static __inline__ int
972 inet6_dgram_socket()
973 {
974 return (socket(AF_INET6, SOCK_DGRAM, 0));
975 }
976
977 static int
978 siocdradd_in6(int s, int if_index, const struct in6_addr * addr, u_char flags)
979 {
980 struct in6_defrouter dr;
981 struct sockaddr_in6 * sin6;
982
983 bzero(&dr, sizeof(dr));
984 sin6 = &dr.rtaddr;
985 sin6->sin6_len = sizeof(struct sockaddr_in6);
986 sin6->sin6_family = AF_INET6;
987 sin6->sin6_addr = *addr;
988 dr.flags = flags;
989 dr.if_index = if_index;
990 return (ioctl(s, SIOCDRADD_IN6, &dr));
991 }
992
993 static int
994 siocdrdel_in6(int s, int if_index, const struct in6_addr * addr)
995 {
996 struct in6_defrouter dr;
997 struct sockaddr_in6 * sin6;
998
999 bzero(&dr, sizeof(dr));
1000 sin6 = &dr.rtaddr;
1001 sin6->sin6_len = sizeof(struct sockaddr_in6);
1002 sin6->sin6_family = AF_INET6;
1003 sin6->sin6_addr = *addr;
1004 dr.if_index = if_index;
1005 return (ioctl(s, SIOCDRDEL_IN6, &dr));
1006 }
1007
1008 #endif /* !TARGET_IPHONE_SIMULATOR */
1009
1010 static boolean_t
1011 S_is_scoped_routing_enabled()
1012 {
1013 int scopedroute = 0;
1014 size_t len = sizeof(scopedroute);
1015
1016 if ((sysctlbyname("net.inet.ip.scopedroute",
1017 &scopedroute, &len,
1018 NULL, 0) == -1)
1019 && (errno != ENOENT)) {
1020 my_log(LOG_ERR, "sysctlbyname() failed: %s", strerror(errno));
1021 }
1022 return (scopedroute);
1023 }
1024
1025 static boolean_t
1026 S_is_scoped_v6_routing_enabled()
1027 {
1028 int scopedroute_v6 = 0;
1029 size_t len = sizeof(scopedroute_v6);
1030
1031 if ((sysctlbyname("net.inet6.ip6.scopedroute",
1032 &scopedroute_v6, &len,
1033 NULL, 0) == -1)
1034 && (errno != ENOENT)) {
1035 my_log(LOG_ERR, "sysctlbyname() failed: %s", strerror(errno));
1036 }
1037 return (scopedroute_v6);
1038 }
1039
1040 static void
1041 my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new)
1042 {
1043 CFIndex n = CFArrayGetCount(arr);
1044
1045 if (CFArrayContainsValue(arr, CFRangeMake(0, n), new)) {
1046 return;
1047 }
1048 CFArrayAppendValue(arr, new);
1049 return;
1050 }
1051
1052 static void
1053 my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key)
1054 {
1055 CFIndex i;
1056
1057 i = CFArrayGetFirstIndexOfValue(arr,
1058 CFRangeMake(0, CFArrayGetCount(arr)),
1059 key);
1060 if (i != kCFNotFound) {
1061 CFArrayRemoveValueAtIndex(arr, i);
1062 }
1063 return;
1064 }
1065
1066 static CFArrayRef
1067 my_CFArrayCreateCombinedArray(CFArrayRef array1, CFArrayRef array2)
1068 {
1069 CFMutableArrayRef combined;
1070
1071 combined = CFArrayCreateMutableCopy(NULL, 0, array1);
1072 CFArrayAppendArray(combined,
1073 array2,
1074 CFRangeMake(0, CFArrayGetCount(array2)));
1075 return (combined);
1076 }
1077
1078 static void
1079 my_CFRelease(void * t)
1080 {
1081 void * * obj = (void * *)t;
1082
1083 if (obj && *obj) {
1084 CFRelease(*obj);
1085 *obj = NULL;
1086 }
1087 return;
1088 }
1089
1090 static CFDictionaryRef
1091 my_CFDictionaryGetDictionary(CFDictionaryRef dict, CFStringRef key)
1092 {
1093 if (isA_CFDictionary(dict) == NULL) {
1094 return (NULL);
1095 }
1096 return (isA_CFDictionary(CFDictionaryGetValue(dict, key)));
1097 }
1098
1099 static CFArrayRef
1100 my_CFDictionaryGetArray(CFDictionaryRef dict, CFStringRef key)
1101 {
1102 if (isA_CFDictionary(dict) == NULL) {
1103 return (NULL);
1104 }
1105 return (isA_CFArray(CFDictionaryGetValue(dict, key)));
1106 }
1107
1108 static boolean_t
1109 cfstring_to_ipvx(int family, CFStringRef str, void * addr, int addr_size)
1110 {
1111 char buf[128];
1112
1113 if (isA_CFString(str) == NULL) {
1114 goto done;
1115 }
1116
1117 switch (family) {
1118 case AF_INET:
1119 if (addr_size < sizeof(struct in_addr)) {
1120 goto done;
1121 }
1122 break;
1123 case AF_INET6:
1124 if (addr_size < sizeof(struct in6_addr)) {
1125 goto done;
1126 }
1127 break;
1128 default:
1129 goto done;
1130 }
1131 (void)_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII);
1132 if (inet_pton(family, buf, addr) == 1) {
1133 return (TRUE);
1134 }
1135 done:
1136 bzero(addr, addr_size);
1137 return (FALSE);
1138 }
1139
1140 __private_extern__
1141 boolean_t
1142 cfstring_to_ip(CFStringRef str, struct in_addr * ip_p)
1143 {
1144 return (cfstring_to_ipvx(AF_INET, str, ip_p, sizeof(*ip_p)));
1145 }
1146
1147 __private_extern__
1148 boolean_t
1149 cfstring_to_ip6(CFStringRef str, struct in6_addr * ip6_p)
1150 {
1151 return (cfstring_to_ipvx(AF_INET6, str, ip6_p, sizeof(*ip6_p)));
1152 }
1153
1154 static boolean_t
1155 cfnumber_to_int(CFNumberRef num, int * int_val)
1156 {
1157 if (isA_CFNumber(num) == NULL) {
1158 return (FALSE);
1159 }
1160 return (CFNumberGetValue(num, kCFNumberIntType, int_val));
1161 }
1162
1163 static CF_RETURNS_RETAINED CFStringRef
1164 setup_service_key(CFStringRef serviceID, CFStringRef entity)
1165 {
1166 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1167 kSCDynamicStoreDomainSetup,
1168 serviceID,
1169 entity));
1170 }
1171
1172 static CF_RETURNS_RETAINED CFStringRef
1173 state_service_key(CFStringRef serviceID, CFStringRef entity)
1174 {
1175 return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1176 kSCDynamicStoreDomainState,
1177 serviceID,
1178 entity));
1179 }
1180
1181 static CFStringRef
1182 interface_entity_key_copy(CFStringRef ifname, CFStringRef entity)
1183 {
1184 return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
1185 kSCDynamicStoreDomainState,
1186 ifname,
1187 entity));
1188 }
1189
1190 static CFDictionaryRef
1191 get_service_setup_entity(CFDictionaryRef services_info, CFStringRef serviceID,
1192 CFStringRef entity)
1193 {
1194 CFStringRef setup_key;
1195 CFDictionaryRef setup_dict;
1196
1197 setup_key = setup_service_key(serviceID, entity);
1198 setup_dict = my_CFDictionaryGetDictionary(services_info, setup_key);
1199 my_CFRelease(&setup_key);
1200 return (setup_dict);
1201 }
1202
1203 static CFDictionaryRef
1204 get_service_state_entity(CFDictionaryRef services_info, CFStringRef serviceID,
1205 CFStringRef entity)
1206 {
1207 CFStringRef state_key;
1208 CFDictionaryRef state_dict;
1209
1210 state_key = state_service_key(serviceID, entity);
1211 state_dict = my_CFDictionaryGetDictionary(services_info, state_key);
1212 my_CFRelease(&state_key);
1213 return (state_dict);
1214 }
1215
1216 static boolean_t
1217 dict_get_first_ip(CFDictionaryRef dict, CFStringRef prop, struct in_addr * ip_p)
1218 {
1219 CFArrayRef ip_list;
1220
1221 ip_list = CFDictionaryGetValue(dict, prop);
1222 if (isA_CFArray(ip_list) != NULL
1223 && CFArrayGetCount(ip_list) > 0
1224 && cfstring_to_ip(CFArrayGetValueAtIndex(ip_list, 0), ip_p)) {
1225 return (TRUE);
1226 }
1227 return (FALSE);
1228 }
1229
1230 static boolean_t
1231 dict_get_first_ipv6(CFDictionaryRef dict, CFStringRef prop,
1232 struct in6_addr * ip_p)
1233 {
1234 CFArrayRef ip_list;
1235
1236 ip_list = CFDictionaryGetValue(dict, prop);
1237 if (isA_CFArray(ip_list) != NULL
1238 && CFArrayGetCount(ip_list) > 0
1239 && cfstring_to_ip6(CFArrayGetValueAtIndex(ip_list, 0), ip_p)) {
1240 return (TRUE);
1241 }
1242 return (FALSE);
1243 }
1244
1245 static boolean_t
1246 dict_get_first_int(CFDictionaryRef dict, CFStringRef prop,
1247 int * val)
1248 {
1249 CFArrayRef list;
1250
1251 list = CFDictionaryGetValue(dict, prop);
1252 if (isA_CFArray(list) != NULL
1253 && CFArrayGetCount(list) > 0
1254 && cfnumber_to_int(CFArrayGetValueAtIndex(list, 0), val)) {
1255 return (TRUE);
1256 }
1257 return (FALSE);
1258 }
1259
1260 static boolean_t
1261 dict_get_ip(CFDictionaryRef dict, CFStringRef prop, struct in_addr * ip_p)
1262 {
1263 CFStringRef val;
1264
1265 val = CFDictionaryGetValue(dict, prop);
1266 return (cfstring_to_ip(val, ip_p));
1267 }
1268
1269 static boolean_t
1270 dict_get_ipv6(CFDictionaryRef dict, CFStringRef prop, struct in6_addr * ip_p)
1271 {
1272 CFStringRef val;
1273
1274 val = CFDictionaryGetValue(dict, prop);
1275 return (cfstring_to_ip6(val, ip_p));
1276 }
1277
1278 static boolean_t
1279 dict_get_int(CFDictionaryRef dict, CFStringRef prop, int * intval)
1280 {
1281 CFNumberRef val;
1282
1283 val = CFDictionaryGetValue(dict, prop);
1284 return (cfnumber_to_int(val, intval));
1285 }
1286
1287 static boolean_t
1288 get_override_primary(CFDictionaryRef dict)
1289 {
1290 CFTypeRef override;
1291
1292 override = CFDictionaryGetValue(dict, kSCPropNetOverridePrimary);
1293 if (isA_CFNumber(override) != NULL) {
1294 int val = 0;
1295
1296 CFNumberGetValue((CFNumberRef)override, kCFNumberIntType, &val);
1297 if (val != 0) {
1298 return (TRUE);
1299 }
1300 }
1301 else if (isA_CFBoolean(override) != NULL) {
1302 if (CFBooleanGetValue(override)) {
1303 return (TRUE);
1304 }
1305 }
1306 return (FALSE);
1307 }
1308
1309 /**
1310 ** Route*
1311 **/
1312
1313 typedef size_t
1314 (*RouteListComputeSize)(CFIndex n);
1315
1316 typedef boolean_t
1317 (*RouteIsEqual)(RouteRef a, RouteRef b);
1318
1319 typedef int
1320 (*RouteApply)(RouteRef route, int cmd, int sockfd);
1321
1322 typedef const void *
1323 (*RouteGateway)(RouteRef route);
1324
1325 typedef void
1326 (*RouteSetGateway)(RouteRef route, const void * address);
1327
1328 typedef const void *
1329 (*RouteDestination)(RouteRef route);
1330
1331 typedef boolean_t
1332 (*RouteSameSubnet)(RouteRef route, const void * address);
1333
1334 typedef CFStringRef
1335 (*RouteCopyDescription)(RouteRef route);
1336
1337 typedef void
1338 (*RouteLog)(int priority, RouteRef route, const char * msg);
1339
1340 typedef struct {
1341 RouteListComputeSize list_compute_size;
1342
1343 RouteIsEqual route_equal;
1344 RouteApply route_apply;
1345 RouteGateway route_gateway;
1346 RouteSetGateway route_set_gateway;
1347 RouteDestination route_destination;
1348 RouteSameSubnet route_same_subnet;
1349 RouteLog route_log;
1350 RouteCopyDescription route_copy_description;
1351
1352 int element_size;
1353 int address_size;
1354 int all_bits_set;
1355 } RouteListInfo;
1356
1357 typedef const RouteListInfo * RouteListInfoRef;
1358
1359 typedef struct {
1360 RouteListInfoRef info;
1361 RouteListRef old_routes;
1362 RouteListRef new_routes;
1363 int sockfd;
1364 int depth;
1365 } RouteListApplyContext, * RouteListApplyContextRef;
1366
1367
1368 static int
1369 RouteAddressCompare(RouteListInfoRef info,
1370 const void * addr1,
1371 const void * addr2)
1372 {
1373 return (memcmp(addr1, addr2, info->address_size));
1374 }
1375
1376 static int
1377 RouteCompare(RouteListInfoRef info,
1378 RouteRef a, Rank a_rank,
1379 RouteRef b, Rank b_rank, boolean_t * same_dest)
1380 {
1381 int cmp;
1382 RouteDestination route_destination;
1383 RouteCopyDescription route_copy_description;
1384
1385 *same_dest = FALSE;
1386 route_destination = info->route_destination;
1387 route_copy_description = info->route_copy_description;
1388 cmp = RouteAddressCompare(info,
1389 (*route_destination)(a),
1390 (*route_destination)(b));
1391 if (cmp == 0) {
1392 cmp = a->prefix_length - b->prefix_length;
1393 if (cmp == 0) {
1394 int index_cmp = a->ifindex - b->ifindex;
1395
1396 if (index_cmp == 0) {
1397 cmp = 0;
1398 }
1399 else if ((a->ifindex == 0 || b->ifindex == 0)
1400 && (a->flags & kRouteFlagsIsScoped) == 0
1401 && (b->flags & kRouteFlagsIsScoped) == 0) {
1402 /*
1403 * Either of the routes specifies no interface and neither
1404 * route is scoped. Claim they are equal to eliminate the
1405 * duplicate route.
1406 */
1407 cmp = 0;
1408 }
1409 else {
1410 *same_dest = TRUE;
1411 cmp = RankCompare(a_rank, b_rank);
1412 if (cmp == 0) {
1413 cmp = index_cmp;
1414 }
1415 }
1416 }
1417 }
1418 if ((S_IPMonitor_debug & kDebugFlag8) != 0) {
1419 CFStringRef a_str;
1420 CFStringRef b_str;
1421 char ch;
1422
1423 if (cmp < 0) {
1424 ch = '<';
1425 }
1426 else if (cmp == 0) {
1427 ch = '=';
1428 }
1429 else {
1430 ch = '>';
1431 }
1432 a_str = (*route_copy_description)(a);
1433 b_str = (*route_copy_description)(b);
1434 my_log(LOG_DEBUG, "%@ rank 0x%x %c %@ rank 0x%x",
1435 a_str, a_rank, ch, b_str, b_rank);
1436 CFRelease(a_str);
1437 CFRelease(b_str);
1438 }
1439 return (cmp);
1440 }
1441
1442 static RouteRef
1443 RouteListGetRouteAtIndexSimple(RouteListInfoRef info, RouteListRef routes,
1444 CFIndex where)
1445 {
1446 return ((void *)routes + (*info->list_compute_size)(where));
1447 }
1448
1449 static RouteRef
1450 RouteListGetRouteAtIndex(RouteListInfoRef info, RouteListRef routes,
1451 CFIndex where)
1452 {
1453 if (routes->count == 0
1454 || where >= routes->count) {
1455 return (NULL);
1456 }
1457 return (RouteListGetRouteAtIndexSimple(info, routes, where));
1458 }
1459
1460 static RouteRef
1461 RouteListGetFirstRoute(RouteListInfoRef info, RouteListRef routes)
1462 {
1463 return (RouteListGetRouteAtIndexSimple(info, routes, 0));
1464 }
1465
1466 #if !TARGET_IPHONE_SIMULATOR
1467 static CFIndex
1468 RouteListRouteIndex(RouteListInfoRef info, RouteListRef routes,
1469 RouteRef route)
1470 {
1471 return (((void *)route
1472 - (void *)RouteListGetFirstRoute(info, routes))
1473 / info->element_size);
1474 }
1475 #endif /* !TARGET_IPHONE_SIMULATOR */
1476
1477 static RouteRef
1478 RouteGetNextRoute(RouteListInfoRef info, RouteRef route)
1479 {
1480 return ((RouteRef)(((void *)route) + info->element_size));
1481 }
1482
1483 static RouteRef
1484 RouteListAddRouteAtIndex(RouteListInfoRef info, RouteListRef routes,
1485 RouteRef this_route, CFIndex where)
1486 {
1487 RouteRef insert_route;
1488
1489 if (where == kCFNotFound) {
1490 /* add it to the end */
1491 insert_route
1492 = RouteListGetRouteAtIndexSimple(info, routes, routes->count);
1493 }
1494 else {
1495 /* make space at [where] */
1496 insert_route = RouteListGetRouteAtIndexSimple(info, routes, where);
1497 bcopy(insert_route,
1498 (void *)insert_route + info->element_size,
1499 info->element_size * (routes->count - where));
1500 }
1501 /* copy the route */
1502 bcopy(this_route, insert_route, info->element_size);
1503 routes->count++;
1504 return (insert_route);
1505 }
1506
1507 static void
1508 RouteListRemoveRouteAtIndex(RouteListInfoRef info, RouteListRef routes,
1509 CFIndex where)
1510 {
1511 if (routes->count == 0
1512 || where >= routes->count) {
1513 return;
1514 }
1515 routes->count--;
1516 if (where == routes->count) {
1517 /* last slot, decrementing gets rid of it */
1518 }
1519 else {
1520 RouteRef remove_route;
1521
1522 remove_route = RouteListGetRouteAtIndexSimple(info, routes, where);
1523 bcopy((void *)remove_route + info->element_size,
1524 remove_route,
1525 info->element_size * (routes->count - where));
1526 }
1527 return;
1528 }
1529
1530 /*
1531 * Function: RouteListAddRoute
1532 *
1533 * Purpose:
1534 * Add the given route to the list of routes, eliminating lower-ranked
1535 * duplicates on the same interface, and marking any lower ranked duplicates
1536 * on other interfaces with kRouteFlagsIsScoped.
1537 *
1538 * This routine assumes that if routes is not NULL, it is malloc'd memory.
1539 *
1540 * Returns:
1541 * Route list updated with the given route, possibly a different pointer,
1542 * due to using realloc'd memory.
1543 */
1544
1545 typedef enum {
1546 kScopeNone = 0,
1547 kScopeThis = 1,
1548 kScopeNext = 2
1549 } Scope;
1550
1551 static RouteListRef
1552 RouteListAddRoute(RouteListInfoRef info,
1553 RouteListRef routes, int init_size,
1554 RouteRef this_route, Rank this_rank)
1555 {
1556 CFIndex i;
1557 RouteRef first_scan = NULL;
1558 RouteFlags flags;
1559 RouteRef scan;
1560 Scope scope_which = kScopeNone;
1561 CFIndex where = kCFNotFound;
1562
1563 if (routes == NULL) {
1564 size_t alloc_size = (*info->list_compute_size)(init_size);
1565
1566 routes = (RouteListRef)malloc(alloc_size);
1567 bzero(routes, sizeof(*routes));
1568 routes->size = init_size;
1569 }
1570 for (i = 0, scan = RouteListGetFirstRoute(info, routes);
1571 i < routes->count;
1572 i++, scan = RouteGetNextRoute(info, scan)) {
1573 int cmp;
1574 boolean_t same_dest;
1575
1576 cmp = RouteCompare(info, this_route, this_rank, scan, scan->rank,
1577 &same_dest);
1578 if (same_dest == TRUE && first_scan == NULL) {
1579 first_scan = scan;
1580 }
1581 if (cmp < 0) {
1582 if (where == kCFNotFound) {
1583 if (same_dest == TRUE
1584 && (first_scan->flags & kRouteFlagsIsScoped) == 0) {
1585 if ((scan->flags & kRouteFlagsIsScoped) != 0) {
1586 ROUTELIST_DEBUG(kDebugFlag8,
1587 "Hit 1: set scope on self\n");
1588 scope_which = kScopeThis;
1589 }
1590 else {
1591 ROUTELIST_DEBUG(kDebugFlag8,
1592 "Hit 2: set scope on next\n");
1593 scope_which = kScopeNext;
1594 }
1595 }
1596 /* remember our insertion point, but keep going to find a dup */
1597 where = i;
1598 }
1599 }
1600 else if (cmp == 0) {
1601 /* exact match */
1602 /* exact match */
1603 if (where != kCFNotFound
1604 && scan->ifindex == this_route->ifindex
1605 && scan->exclude_ifindex == 0
1606 && this_route->exclude_ifindex == 0) {
1607 /* this route is a duplicate */
1608 ROUTELIST_DEBUG(kDebugFlag8, "Hit 3: removing [%ld]\n", i);
1609 RouteListRemoveRouteAtIndex(info, routes, i);
1610 break;
1611 }
1612 /*
1613 * this_route is "better" than scan if this_route is not excluded
1614 * and scan is excluded or this_route sorts ahead of scan
1615 */
1616 if (this_route->exclude_ifindex == 0
1617 && (scan->exclude_ifindex != 0 || this_rank < scan->rank)) {
1618 IFIndex ifindex = 0;
1619 boolean_t is_scoped = FALSE;
1620
1621 if (scan->flags & kRouteFlagsIsScoped) {
1622 is_scoped = TRUE;
1623 }
1624 if (this_rank < scan->rank) {
1625 ROUTELIST_DEBUG(kDebugFlag8,
1626 "Hit 4a: replacing [%ld]"
1627 " rank 0x%x < 0x%x\n",
1628 i, this_rank, scan->rank);
1629 }
1630 else {
1631 ROUTELIST_DEBUG(kDebugFlag8,
1632 "Hit 4b: replacing [%ld] excluded route\n",
1633 i);
1634 }
1635 if (scan->ifindex != 0) {
1636 ifindex = scan->ifindex;
1637 }
1638 else if (this_route->ifindex != 0) {
1639 ifindex = this_route->ifindex;
1640 }
1641 bcopy(this_route, scan, info->element_size);
1642 scan->rank = this_rank;
1643 scan->ifindex = ifindex;
1644 scan->exclude_ifindex = 0;
1645 if (is_scoped) {
1646 /* preserve whether route was scoped */
1647 ROUTELIST_DEBUG(kDebugFlag8, "Hit 5: preserved scope\n");
1648 scan->flags |= kRouteFlagsIsScoped;
1649 }
1650 }
1651 /* we're done */
1652 goto done;
1653 }
1654 else {
1655 if (same_dest == TRUE) {
1656 if (scope_which == kScopeNone) {
1657 ROUTELIST_DEBUG(kDebugFlag8, "Hit 6: set scope on self\n");
1658 scope_which = kScopeThis;
1659 }
1660 }
1661 #ifdef TEST_ROUTELIST
1662 else if (where != kCFNotFound) {
1663 /* not possible because we maintain a sorted list */
1664 fprintf(stderr,
1665 "Hit 7: moved past routes - can't happen\n");
1666 exit(2);
1667 break;
1668 }
1669 #endif /* TEST_ROUTELIST */
1670 }
1671 }
1672
1673 if (routes->size == routes->count) {
1674 int how_many;
1675 RouteListRef new_routes;
1676 int old_size;
1677
1678 /* double the size */
1679 old_size = routes->size;
1680 how_many = old_size * 2;
1681 new_routes = (RouteListRef)
1682 reallocf(routes, (*info->list_compute_size)(how_many));
1683 if (new_routes == NULL) {
1684 /* no memory */
1685 routes = NULL;
1686 goto done;
1687 }
1688 ROUTELIST_DEBUG(kDebugFlag8, "increasing size from %d to %d\n",
1689 old_size, how_many);
1690 new_routes->size = how_many;
1691 routes = new_routes;
1692 }
1693
1694 /* add/insert the new route */
1695 this_route = RouteListAddRouteAtIndex(info, routes, this_route, where);
1696 this_route->rank = this_rank;
1697 flags = 0;
1698 if (RANK_ASSERTION_MASK(this_rank) == kRankAssertionNever) {
1699 flags |= kRouteFlagsIsScoped;
1700 }
1701 switch (scope_which) {
1702 case kScopeThis:
1703 flags |= kRouteFlagsIsScoped;
1704 break;
1705 case kScopeNext:
1706 this_route = RouteListGetRouteAtIndex(info, routes, where + 1);
1707 flags |= kRouteFlagsIsScoped;
1708 break;
1709 default:
1710 case kScopeNone:
1711 break;
1712 }
1713 if (this_route != NULL && flags != 0) {
1714 this_route->flags |= flags;
1715 }
1716
1717 done:
1718 return (routes);
1719 }
1720
1721 /*
1722 * Function: RouteListAddRouteList
1723 * Purpose:
1724 * Invoke RouteListAddRoute for each route in the given list
1725 * 'service_routes' combining them into a combined list 'routes'.
1726 *
1727 * Returns:
1728 * See RouteListAddRoute for more information.
1729 */
1730 static RouteListRef
1731 RouteListAddRouteList(RouteListInfoRef info,
1732 RouteListRef routes, int init_size,
1733 RouteListRef service_routes, Rank rank)
1734 {
1735 int i;
1736 RouteRef scan;
1737
1738 for (i = 0, scan = RouteListGetFirstRoute(info, service_routes);
1739 i < service_routes->count;
1740 i++, scan = RouteGetNextRoute(info, scan)) {
1741 Rank this_rank;
1742
1743 if (i == 0
1744 && (service_routes->flags & kRouteListFlagsHasDefault) != 0) {
1745 /* only apply rank to first element of the list (default route) */
1746 this_rank = rank;
1747 }
1748 else {
1749 this_rank = RANK_INDEX_MASK(rank) | RANK_ASSERTION_MASK(scan->rank);
1750 }
1751 routes = RouteListAddRoute(info, routes, init_size, scan, this_rank);
1752 }
1753 return (routes);
1754 }
1755
1756 static void
1757 RouteAddInterfaceToDescription(RouteRef r, CFMutableStringRef str)
1758 {
1759 char if_name[IFNAMSIZ];
1760
1761 if (my_if_indextoname2(r->ifindex, if_name) != NULL) {
1762 CFStringAppendFormat(str, NULL,
1763 CFSTR(" Ifp %s"),
1764 if_name);
1765 }
1766 if (my_if_indextoname2(r->exclude_ifindex, if_name) != NULL) {
1767 CFStringAppendFormat(str, NULL,
1768 CFSTR(" !Ifp %s"),
1769 if_name);
1770 }
1771 return;
1772 }
1773
1774 static void
1775 RouteAddFlagsToDescription(RouteRef r, CFMutableStringRef str)
1776 {
1777 if ((r->flags & kRouteFlagsIsNULL) != 0) {
1778 CFStringAppend(str, CFSTR(" [null]"));
1779 }
1780 else {
1781 Rank rank_assertion = RANK_ASSERTION_MASK(r->rank);
1782
1783 switch (rank_assertion) {
1784 case kRankAssertionFirst:
1785 CFStringAppend(str, CFSTR(" [first]"));
1786 break;
1787 case kRankAssertionLast:
1788 CFStringAppend(str, CFSTR(" [last]"));
1789 break;
1790 case kRankAssertionNever:
1791 CFStringAppend(str, CFSTR(" [never]"));
1792 break;
1793 default:
1794 break;
1795 }
1796 if ((r->flags & kRouteFlagsKernelManaged) != 0) {
1797 CFStringAppend(str, CFSTR(" [kern]"));
1798 }
1799 if ((r->flags & kRouteFlagsIsScoped) != 0) {
1800 CFStringAppend(str, CFSTR(" [SCOPED]"));
1801 }
1802 }
1803 return;
1804 }
1805
1806 #if !TARGET_IPHONE_SIMULATOR
1807 static RouteRef
1808 RouteListFindRoute(RouteListInfoRef info, RouteListRef routes, RouteRef route)
1809 {
1810 int i;
1811 RouteRef match = NULL;
1812 RouteRef scan;
1813
1814 for (i = 0, scan = RouteListGetFirstRoute(info, routes);
1815 i < routes->count;
1816 i++, scan = RouteGetNextRoute(info, scan)) {
1817 if ((*info->route_equal)(scan, route)) {
1818 match = scan;
1819 break;
1820 }
1821
1822 }
1823 return (match);
1824 }
1825
1826 typedef enum {
1827 kRouteLookupFlagsNone = 0x0,
1828 kRouteLookupFlagsExcludeInterface = 0x1
1829 } RouteLookupFlags;
1830
1831 static RouteRef
1832 RouteListLookup(RouteListInfoRef info,
1833 RouteListRef routes,
1834 const void * address,
1835 int n_bits,
1836 IFIndex ifindex,
1837 RouteLookupFlags lookup_flags)
1838 {
1839 RouteRef best_match = NULL;
1840 RouteRef candidate;
1841 int i;
1842
1843 for (i = 0, candidate = RouteListGetFirstRoute(info, routes);
1844 i < routes->count;
1845 i++, candidate = RouteGetNextRoute(info, candidate)) {
1846 if (candidate->ifindex == 0 || candidate->exclude_ifindex != 0) {
1847 /* ignore exclude routes */
1848 continue;
1849 }
1850 if ((lookup_flags & kRouteLookupFlagsExcludeInterface) != 0) {
1851 /* exclude interfaces with the same interface index */
1852 if (ifindex == candidate->ifindex) {
1853 continue;
1854 }
1855 }
1856 else if (ifindex != candidate->ifindex) {
1857 continue;
1858 }
1859 if ((candidate->flags & kRouteFlagsHasGateway) != 0
1860 && RouteAddressCompare(info,
1861 (*info->route_gateway)(candidate),
1862 address) == 0) {
1863 /* skip route whose gateway is the address we're looking for */
1864 continue;
1865 }
1866 if ((candidate->flags & kRouteFlagsIsHost) != 0) {
1867 /* if host route and we're looking for an exact match */
1868 if (n_bits == info->all_bits_set
1869 && RouteAddressCompare(info,
1870 (*info->route_destination)(candidate),
1871 address) == 0) {
1872 /* found exact match */
1873 best_match = candidate;
1874 break;
1875 }
1876 /* skip it */
1877 continue;
1878 }
1879 /* verify that address is on the same subnet */
1880 if ((*info->route_same_subnet)(candidate, address) == FALSE) {
1881 /* different subnet */
1882 continue;
1883 }
1884
1885 if (candidate->prefix_length == n_bits) {
1886 /* exact match */
1887 best_match = candidate;
1888 break;
1889 }
1890 if (candidate->prefix_length > n_bits) {
1891 /* matched too many bits */
1892 continue;
1893 }
1894 if (best_match == NULL
1895 || candidate->prefix_length > best_match->prefix_length) {
1896 best_match = candidate;
1897 }
1898 }
1899 return (best_match);
1900 }
1901
1902
1903 /*
1904 * Function: RouteProcess
1905 * Purpose:
1906 * Function to process adding or removing the specified route.
1907 * In the case of adding, that may involve first processing the gateway
1908 * route (recursively).
1909 */
1910 static boolean_t
1911 RouteProcess(RouteRef route,
1912 RouteCommand cmd,
1913 RouteListApplyContextRef context)
1914 {
1915 RouteLog route_log = context->info->route_log;
1916 RouteApply route_apply = context->info->route_apply;
1917 RouteGateway route_gateway = context->info->route_gateway;
1918 int retval;
1919
1920 switch (cmd) {
1921 case kRouteCommandAdd:
1922 if ((route->control_flags & kControlFlagsProcessed) != 0) {
1923 return ((route->control_flags & kControlFlagsAdded) != 0);
1924 }
1925 route->control_flags |= kControlFlagsProcessed;
1926 if ((route->flags & kRouteFlagsHasGateway) != 0) {
1927 boolean_t added;
1928 RouteRef gateway_route;
1929
1930 gateway_route
1931 = RouteListLookup(context->info,
1932 context->new_routes,
1933 (*route_gateway)(route),
1934 context->info->all_bits_set,
1935 route->ifindex,
1936 kRouteLookupFlagsNone);
1937 if (gateway_route == NULL) {
1938 (*route_log)(LOG_NOTICE, route,
1939 "IPMonitor RouteProcess: no gateway route");
1940 }
1941 else {
1942 #define MAX_RECURSE_DEPTH 10
1943 /* avoid infinite recursion */
1944 if (context->depth == MAX_RECURSE_DEPTH) {
1945 (*route_log)(LOG_NOTICE, route,
1946 "IPMonitor RouteProcess: "
1947 "routing loop detected, not adding");
1948 return (FALSE);
1949 }
1950 /* recurse to add gateway route */
1951 context->depth++;
1952 added = RouteProcess(gateway_route,
1953 kRouteCommandAdd,
1954 context);
1955 context->depth--;
1956 if (added == FALSE) {
1957 (*route_log)(LOG_NOTICE, route,
1958 "IPMonitor RouteProcess: failed to add");
1959 return (FALSE);
1960 }
1961 }
1962 }
1963 retval = (*route_apply)(route, RTM_ADD, context->sockfd);
1964 if (retval == EEXIST) {
1965 /* delete and add again */
1966 (void)(*route_apply)(route, RTM_DELETE, context->sockfd);
1967 retval = (*route_apply)(route, RTM_ADD, context->sockfd);
1968 }
1969 switch (retval) {
1970 default:
1971 my_log(LOG_NOTICE,
1972 "IPMonitor RouteProcess failed to add route, %s:",
1973 strerror(retval));
1974 (*route_log)(LOG_NOTICE, route, NULL);
1975 break;
1976 case 0:
1977 case EROUTENOTAPPLIED:
1978 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1979 char buf[64];
1980 const char * str;
1981
1982 str = (retval == EROUTENOTAPPLIED) ? "!" : "";
1983 snprintf(buf, sizeof(buf), "%sAdd new[%ld]",
1984 str,
1985 RouteListRouteIndex(context->info,
1986 context->new_routes,
1987 route));
1988 (*route_log)(LOG_DEBUG, route, buf);
1989 }
1990 route->control_flags |= kControlFlagsAdded;
1991 break;
1992 }
1993 break;
1994 case kRouteCommandRemove:
1995 retval = (*route_apply)(route, RTM_DELETE, context->sockfd);
1996 switch (retval) {
1997 case 0:
1998 case ESRCH:
1999 case EROUTENOTAPPLIED:
2000 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2001 char buf[64];
2002 const char * str;
2003
2004 str = (retval == EROUTENOTAPPLIED) ? "!" : "";
2005 snprintf(buf, sizeof(buf), "%sRemove old[%ld]%s",
2006 str,
2007 RouteListRouteIndex(context->info,
2008 context->old_routes,
2009 route),
2010 (retval == ESRCH) ? "(ESRCH)" : "");
2011 (*route_log)(LOG_DEBUG, route, buf);
2012 }
2013 break;
2014 default:
2015 my_log(LOG_NOTICE,
2016 "IPMonitor RouteProcess failed to remove"
2017 " route, %s", strerror(retval));
2018 (*route_log)(LOG_NOTICE, route, NULL);
2019 break;
2020 }
2021 break;
2022 default:
2023 break;
2024 }
2025 return (TRUE);
2026 }
2027
2028 static void
2029 RouteListApply(RouteListInfoRef info,
2030 RouteListRef old_routes, RouteListRef new_routes,
2031 int sockfd)
2032 {
2033 RouteListApplyContext context;
2034 int i;
2035 RouteRef scan;
2036
2037 if (old_routes == new_routes && old_routes == NULL) {
2038 /* both old and new are NULL, so there's nothing to do */
2039 return;
2040 }
2041 bzero(&context, sizeof(context));
2042 context.old_routes = old_routes;
2043 context.new_routes = new_routes;
2044 context.sockfd = sockfd;
2045 context.info = info;
2046 if (old_routes != NULL) {
2047 for (i = 0, scan = RouteListGetFirstRoute(info, old_routes);
2048 i < old_routes->count;
2049 i++, scan = RouteGetNextRoute(info, scan)) {
2050 RouteRef new_route = NULL;
2051
2052 if (new_routes != NULL) {
2053 new_route = RouteListFindRoute(info, new_routes, scan);
2054 }
2055 if (new_route == NULL) {
2056 if ((scan->control_flags & kControlFlagsAdded) != 0) {
2057 RouteProcess(scan, kRouteCommandRemove, &context);
2058 }
2059 }
2060 }
2061 }
2062 if (new_routes != NULL) {
2063 if (old_routes != NULL) {
2064 /* preserve the control flags from any old routes */
2065 for (i = 0, scan = RouteListGetFirstRoute(info, new_routes);
2066 i < new_routes->count;
2067 i++, scan = RouteGetNextRoute(info, scan)) {
2068 RouteRef old_route = NULL;
2069
2070 old_route = RouteListFindRoute(info, old_routes, scan);
2071 if (old_route != NULL) {
2072 /* preserve the control state in the new route */
2073 scan->control_flags = old_route->control_flags;
2074 }
2075 }
2076 }
2077 /* add any routes that need to be added */
2078 for (i = 0, scan = RouteListGetFirstRoute(info, new_routes);
2079 i < new_routes->count;
2080 i++, scan = RouteGetNextRoute(info, scan)) {
2081 if ((scan->control_flags & kControlFlagsProcessed) != 0) {
2082 continue;
2083 }
2084 RouteProcess(scan, kRouteCommandAdd, &context);
2085 }
2086 }
2087 return;
2088 }
2089 /*
2090 * Function: RouteListFinalize
2091 * Purpose:
2092 * Look for excluded routes. If the excluded route does not have an assigned
2093 * interface, search for a route that *does not* go over the excluded
2094 * interface.
2095 *
2096 * If the excluded route does have an assigned interface, search for a route
2097 * that *does* go over the assigned interface.
2098 *
2099 * Set the gateway on the excluded route to match the gateway of the found
2100 * route.
2101 */
2102 static void
2103 RouteListFinalize(RouteListInfoRef info, RouteListRef routes)
2104 {
2105 int i;
2106 RouteRef scan;
2107
2108 if (routes == NULL) {
2109 return;
2110 }
2111 for (i = 0, scan = RouteListGetFirstRoute(info, routes);
2112 i < routes->count;
2113 i++, scan = RouteGetNextRoute(info, scan)) {
2114 RouteRef route;
2115 IFIndex ifindex;
2116 RouteLookupFlags flags;
2117
2118 if (scan->exclude_ifindex == 0) {
2119 continue;
2120 }
2121 if (scan->ifindex == 0) {
2122 ifindex = scan->exclude_ifindex;
2123 flags = kRouteLookupFlagsExcludeInterface;
2124 }
2125 else {
2126 ifindex = scan->ifindex;
2127 flags = kRouteLookupFlagsNone;
2128 }
2129 route = RouteListLookup(info, routes,
2130 (*info->route_destination)(scan),
2131 scan->prefix_length, ifindex, flags);
2132 if (route == NULL) {
2133 (*info->route_log)(LOG_NOTICE, (RouteRef)scan,
2134 "IPMonitor: can't resolve excluded route");
2135 }
2136 else {
2137 if ((S_IPMonitor_debug & kDebugFlag8) != 0) {
2138 (*info->route_log)(LOG_DEBUG, (RouteRef)scan, "Excluded route");
2139 (*info->route_log)(LOG_DEBUG, (RouteRef)route, "Resolved to");
2140 }
2141 scan->ifindex = route->ifindex;
2142 if ((route->flags & kRouteFlagsHasGateway) != 0) {
2143 (*info->route_set_gateway)(scan, (*info->route_gateway)(route));
2144 scan->flags |= kRouteFlagsHasGateway;
2145 if (scan->prefix_length == info->all_bits_set) {
2146 scan->flags |= kRouteFlagsIsHost;
2147 }
2148 }
2149 else {
2150 /* routes directly to interface */
2151 scan->flags &= ~(kRouteFlagsHasGateway | kRouteFlagsIsHost);
2152 }
2153 }
2154 }
2155 return;
2156 }
2157 #endif /* !TARGET_IPHONE_SIMULATOR */
2158
2159 /**
2160 ** IPv4Route*
2161 **/
2162
2163 #define IPV4_ROUTE_ALL_BITS_SET 32
2164
2165 static __inline__ struct in_addr
2166 subnet_addr(struct in_addr addr, struct in_addr mask)
2167 {
2168 struct in_addr net;
2169
2170 net.s_addr = addr.s_addr & mask.s_addr;
2171 return (net);
2172 }
2173
2174 static void
2175 IPv4RouteCopyDescriptionWithString(IPv4RouteRef r, CFMutableStringRef str)
2176 {
2177 if ((r->flags & kRouteFlagsIsHost) != 0) {
2178 CFStringAppendFormat(str, NULL,
2179 CFSTR("Host " IP_FORMAT),
2180 IP_LIST(&r->dest));
2181 }
2182 else {
2183 CFStringAppendFormat(str, NULL,
2184 CFSTR("Net " IP_FORMAT),
2185 IP_LIST(&r->dest));
2186 CFStringAppendFormat(str, NULL, CFSTR("/%d"),
2187 r->prefix_length);
2188 }
2189 if ((r->flags & kRouteFlagsHasGateway) != 0) {
2190 CFStringAppendFormat(str, NULL,
2191 CFSTR(" Gate " IP_FORMAT),
2192 IP_LIST(&r->gateway));
2193 }
2194 RouteAddInterfaceToDescription((RouteRef)r, str);
2195 if (r->ifa.s_addr != 0) {
2196 CFStringAppendFormat(str, NULL,
2197 CFSTR(" Ifa " IP_FORMAT),
2198 IP_LIST(&r->ifa));
2199 }
2200 RouteAddFlagsToDescription((RouteRef)r, str);
2201 return;
2202 }
2203
2204 static CFStringRef
2205 IPv4RouteCopyDescription(RouteRef r)
2206 {
2207 CFMutableStringRef str;
2208
2209 str = CFStringCreateMutable(NULL, 0);
2210 IPv4RouteCopyDescriptionWithString((IPv4RouteRef)r, str);
2211 return (str);
2212 }
2213
2214 #ifdef TEST_IPV4_ROUTELIST
2215 static CFMutableStringRef
2216 IPv4RouteListCopyDescription(IPv4RouteListRef routes);
2217
2218 static void
2219 IPv4RouteLog(int level, RouteRef route, const char * msg)
2220 {
2221 CFStringRef str = IPv4RouteCopyDescription(route);
2222
2223 if (msg == NULL) {
2224 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
2225 }
2226 else {
2227 SCPrint(TRUE, stdout, CFSTR("%s: %@\n"), msg, str);
2228 }
2229 CFRelease(str);
2230 return;
2231 }
2232
2233 static __inline__ void
2234 IPv4RouteListPrint(IPv4RouteListRef routes)
2235 {
2236 CFStringRef str = IPv4RouteListCopyDescription(routes);
2237
2238 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
2239 CFRelease(str);
2240 return;
2241 }
2242
2243 #else /* TEST_IPV4_ROUTELIST */
2244
2245 static __inline__ void
2246 IPv4RouteLog(int level, RouteRef route, const char * msg)
2247 {
2248 CFStringRef str = IPv4RouteCopyDescription(route);
2249
2250 if (msg == NULL) {
2251 my_log(level, "%@", str);
2252 }
2253 else {
2254 my_log(level, "%s: %@", msg, str);
2255 }
2256 CFRelease(str);
2257 return;
2258 }
2259
2260 #endif /* TEST_IPV4_ROUTELIST */
2261
2262 static boolean_t
2263 IPv4RouteIsEqual(RouteRef r_scan, RouteRef r_route)
2264 {
2265 IPv4RouteRef route = (IPv4RouteRef)r_route;
2266 IPv4RouteRef scan = (IPv4RouteRef)r_scan;
2267
2268 return ((scan->dest.s_addr == route->dest.s_addr)
2269 && (scan->mask.s_addr == route->mask.s_addr)
2270 && (scan->ifindex == route->ifindex)
2271 && (scan->ifa.s_addr == route->ifa.s_addr)
2272 && (scan->gateway.s_addr == route->gateway.s_addr)
2273 && (scan->flags == route->flags));
2274 }
2275
2276 static CFMutableStringRef
2277 IPv4RouteListCopyDescription(IPv4RouteListRef routes)
2278 {
2279 int i;
2280 IPv4RouteRef r;
2281 CFMutableStringRef str;
2282
2283 str = CFStringCreateMutable(NULL, 0);
2284 CFStringAppendFormat(str, NULL, CFSTR("<IPv4RouteList[%d]> = {"),
2285 routes->count);
2286 for (i = 0, r = routes->list; i < routes->count; i++, r++) {
2287 CFStringAppendFormat(str, NULL, CFSTR("\n%2d. "), i);
2288 IPv4RouteCopyDescriptionWithString(r, str);
2289 }
2290 CFStringAppend(str, CFSTR("\n}"));
2291 return (str);
2292 }
2293
2294 static size_t
2295 IPv4RouteListComputeSize(CFIndex n)
2296 {
2297 return (offsetof(IPv4RouteList, list[n]));
2298 }
2299
2300 static int
2301 count_prefix_bits_set(uint32_t n)
2302 {
2303 int count;
2304 const static int8_t bits[16] = {
2305 0, /* 0000 */
2306 -1, /* 0001 */
2307 -1, /* 0010 */
2308 -1, /* 0011 */
2309 -1, /* 0100 */
2310 -1, /* 0101 */
2311 -1, /* 0110 */
2312 -1, /* 0111 */
2313 1, /* 1000 */
2314 -1, /* 1001 */
2315 -1, /* 1010 */
2316 -1, /* 1011 */
2317 2, /* 1100 */
2318 -1, /* 1101 */
2319 3, /* 1110 */
2320 4, /* 1111 */
2321 };
2322
2323 for (count = 0; n != 0; n >>= 4) {
2324 int nbits = bits[n & 0x0f];
2325
2326 if (nbits < 0) {
2327 return (-1);
2328 }
2329 count += nbits;
2330 }
2331 return (count);
2332 }
2333
2334 static uint32_t
2335 prefix_to_mask32(unsigned int prefix_length)
2336 {
2337 if (prefix_length > 32 || prefix_length == 0) {
2338 return (0);
2339 }
2340 return (0xffffffff << (32 - prefix_length));
2341 }
2342
2343 static int
2344 mask_get_prefix_length(struct in_addr mask)
2345 {
2346 int count;
2347
2348 count = count_prefix_bits_set(mask.s_addr);
2349 if (count >= 0) {
2350 uint32_t val;
2351
2352 val = prefix_to_mask32(count);
2353 if (ntohl(mask.s_addr) != val) {
2354 /* expected mask based on prefix length doesn't match */
2355 return (-1);
2356 }
2357 }
2358 return (count);
2359 }
2360
2361 static boolean_t
2362 IPv4RouteSetPrefixLength(IPv4RouteRef route)
2363 {
2364 int length;
2365
2366 length = mask_get_prefix_length(route->mask);
2367 if (length < 0) {
2368 return (FALSE);
2369 }
2370 route->prefix_length = length;
2371 return (TRUE);
2372 }
2373
2374 static const void *
2375 IPv4RouteGateway(RouteRef r_route)
2376 {
2377 IPv4RouteRef route = (IPv4RouteRef)r_route;
2378 return (&route->gateway);
2379 }
2380
2381 static void
2382 IPv4RouteSetGateway(RouteRef r_route, const void * address)
2383 {
2384 IPv4RouteRef route = (IPv4RouteRef)r_route;
2385
2386 route->gateway = *((struct in_addr *)address);
2387 return;
2388 }
2389
2390 static const void *
2391 IPv4RouteDestination(RouteRef r_route)
2392 {
2393 IPv4RouteRef route = (IPv4RouteRef)r_route;
2394 return (&route->dest);
2395 }
2396
2397 static boolean_t
2398 IPv4RouteSameSubnet(RouteRef r_route, const void * addr)
2399 {
2400 const struct in_addr * address;
2401 IPv4RouteRef route = (IPv4RouteRef)r_route;
2402
2403 address = (const struct in_addr *)addr;
2404 return ((address->s_addr & route->mask.s_addr) == route->dest.s_addr);
2405 }
2406
2407 /*
2408 * Define: ROUTE_MSG_ADDRS_SPACE
2409 * Purpose:
2410 * Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
2411 * 3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
2412 * someone changes the code and doesn't think to modify this.
2413 */
2414 #define ROUTE_MSG_ADDRS_SPACE (3 * sizeof(struct sockaddr_in) \
2415 + 2 * sizeof(struct sockaddr_dl) \
2416 + 128)
2417 typedef struct {
2418 struct rt_msghdr hdr;
2419 char addrs[ROUTE_MSG_ADDRS_SPACE];
2420 } route_msg;
2421
2422 /*
2423 * Function: IPv4RouteApply
2424 * Purpose:
2425 * Add or remove the specified route to/from the kernel routing table.
2426 */
2427 static int
2428 IPv4RouteApply(RouteRef r_route, int cmd, int sockfd)
2429 {
2430 int len;
2431 int ret = 0;
2432 IPv4RouteRef route = (IPv4RouteRef)r_route;
2433 route_msg rtmsg;
2434 union {
2435 struct sockaddr_in * in_p;
2436 struct sockaddr_dl * dl_p;
2437 void * ptr;
2438 } rtaddr;
2439
2440 if (S_netboot && route->dest.s_addr == 0) {
2441 /* don't touch the default route */
2442 return (EROUTENOTAPPLIED);
2443 }
2444 if ((route->flags & kRouteFlagsIsScoped) != 0
2445 && !S_scopedroute) {
2446 return (EROUTENOTAPPLIED);
2447 }
2448 if ((route->flags & kRouteFlagsIsNULL) != 0) {
2449 return (EROUTENOTAPPLIED);
2450 }
2451 if (route->ifindex == 0) {
2452 my_log(LOG_NOTICE,
2453 "IPMonitor IPv4RouteApply: " IP_FORMAT
2454 " no interface specified, ignoring",
2455 IP_LIST(&route->dest));
2456 return (ENXIO);
2457 }
2458 if (sockfd == -1) {
2459 #ifdef TEST_IPV4_ROUTELIST
2460 return (0);
2461 #else /* TEST_IPV4_ROUTELIST */
2462 return (EBADF);
2463 #endif /* TEST_IPV4_ROUTELIST */
2464 }
2465 memset(&rtmsg, 0, sizeof(rtmsg));
2466 rtmsg.hdr.rtm_type = cmd;
2467 rtmsg.hdr.rtm_version = RTM_VERSION;
2468 rtmsg.hdr.rtm_seq = ++rtm_seq;
2469 rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_IFP;
2470 if (route->ifa.s_addr != 0) {
2471 rtmsg.hdr.rtm_addrs |= RTA_IFA;
2472 }
2473 rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC;
2474 if ((route->flags & kRouteFlagsIsHost) != 0) {
2475 rtmsg.hdr.rtm_flags |= RTF_HOST;
2476 }
2477 else {
2478 rtmsg.hdr.rtm_addrs |= RTA_NETMASK;
2479 if ((route->flags & kRouteFlagsHasGateway) == 0) {
2480 rtmsg.hdr.rtm_flags |= RTF_CLONING;
2481 }
2482 }
2483 if ((route->flags & kRouteFlagsHasGateway) != 0) {
2484 rtmsg.hdr.rtm_flags |= RTF_GATEWAY;
2485 }
2486 if ((route->flags & kRouteFlagsIsScoped) != 0) {
2487 rtmsg.hdr.rtm_index = route->ifindex;
2488 rtmsg.hdr.rtm_flags |= RTF_IFSCOPE;
2489 }
2490
2491 rtaddr.ptr = rtmsg.addrs;
2492
2493 /* dest */
2494 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2495 rtaddr.in_p->sin_family = AF_INET;
2496 rtaddr.in_p->sin_addr = route->dest;
2497 rtaddr.ptr += sizeof(*rtaddr.in_p);
2498
2499 /* gateway */
2500 if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) {
2501 /* gateway is an IP address */
2502 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2503 rtaddr.in_p->sin_family = AF_INET;
2504 rtaddr.in_p->sin_addr = route->gateway;
2505 rtaddr.ptr += sizeof(*rtaddr.in_p);
2506 }
2507 else {
2508 /* gateway is the interface itself */
2509 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
2510 rtaddr.dl_p->sdl_family = AF_LINK;
2511 rtaddr.dl_p->sdl_index = route->ifindex;
2512 rtaddr.ptr += sizeof(*rtaddr.dl_p);
2513 }
2514
2515 /* mask */
2516 if ((rtmsg.hdr.rtm_addrs & RTA_NETMASK) != 0) {
2517 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2518 rtaddr.in_p->sin_family = AF_INET;
2519 rtaddr.in_p->sin_addr = route->mask;
2520 rtaddr.ptr += sizeof(*rtaddr.in_p);
2521 }
2522
2523 /* interface */
2524 if ((rtmsg.hdr.rtm_addrs & RTA_IFP) != 0) {
2525 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
2526 rtaddr.dl_p->sdl_family = AF_LINK;
2527 rtaddr.dl_p->sdl_index = route->ifindex;
2528 rtaddr.ptr += sizeof(*rtaddr.dl_p);
2529 }
2530 /* interface address */
2531 if ((rtmsg.hdr.rtm_addrs & RTA_IFA) != 0) {
2532 rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
2533 rtaddr.in_p->sin_family = AF_INET;
2534 rtaddr.in_p->sin_addr = route->ifa;
2535 rtaddr.ptr += sizeof(*rtaddr.in_p);
2536 }
2537
2538 /* apply the route */
2539 len = (int)(sizeof(rtmsg.hdr) + (rtaddr.ptr - (void *)rtmsg.addrs));
2540 rtmsg.hdr.rtm_msglen = len;
2541 if (write(sockfd, &rtmsg, len) == -1) {
2542 ret = errno;
2543 }
2544 return (ret);
2545 }
2546
2547 static const RouteListInfo IPv4RouteListInfo = {
2548 IPv4RouteListComputeSize,
2549
2550 IPv4RouteIsEqual,
2551 IPv4RouteApply,
2552 IPv4RouteGateway,
2553 IPv4RouteSetGateway,
2554 IPv4RouteDestination,
2555 IPv4RouteSameSubnet,
2556 IPv4RouteLog,
2557 IPv4RouteCopyDescription,
2558
2559 sizeof(IPv4Route),
2560 sizeof(struct in_addr),
2561 IPV4_ROUTE_ALL_BITS_SET
2562 };
2563
2564 #if !TARGET_IPHONE_SIMULATOR
2565 static __inline__ void
2566 IPv4RouteListLog(int level, IPv4RouteListRef routes)
2567 {
2568 CFStringRef str = IPv4RouteListCopyDescription(routes);
2569
2570 my_log(level, "%@", str);
2571 CFRelease(str);
2572 return;
2573 }
2574
2575 static void
2576 IPv4RouteListApply(IPv4RouteListRef old_routes, IPv4RouteListRef new_routes,
2577 int sockfd)
2578 {
2579 RouteListApply(&IPv4RouteListInfo,
2580 (RouteListRef)old_routes, (RouteListRef)new_routes,
2581 sockfd);
2582 return;
2583 }
2584
2585 static void
2586 IPv4RouteListFinalize(IPv4RouteListRef routes)
2587 {
2588 RouteListFinalize(&IPv4RouteListInfo, (RouteListRef)routes);
2589 return;
2590 }
2591 #endif /* !TARGET_IPHONE_SIMULATOR */
2592
2593 #ifdef TEST_IPV4_ROUTELIST
2594 static IPv4RouteListRef
2595 IPv4RouteListAddRouteList(IPv4RouteListRef routes, int init_size,
2596 IPv4RouteListRef service_routes, Rank rank)
2597 {
2598 return ((IPv4RouteListRef)
2599 RouteListAddRouteList(&IPv4RouteListInfo,
2600 (RouteListRef)routes, init_size,
2601 (RouteListRef)service_routes, rank));
2602 }
2603 #endif /* TEST_IPV4_ROUTELIST */
2604
2605 static CFStringRef
2606 plist_get_string(CFDictionaryRef dict, CFStringRef prop_name,
2607 char * buf, int buf_size)
2608 {
2609 CFStringRef val;
2610
2611 val = CFDictionaryGetValue(dict, prop_name);
2612 if (isA_CFString(val) == NULL) {
2613 return (NULL);
2614 }
2615 if (CFStringGetCString(val, buf, buf_size, kCFStringEncodingUTF8)
2616 == FALSE) {
2617 return (NULL);
2618 }
2619 return (val);
2620 }
2621
2622 typedef struct {
2623 struct in_addr addr;
2624 int * count_p;
2625 IFIndex ifindex;
2626 IFIndex exclude_ifindex;
2627 IPv4RouteRef * route_p;
2628 Rank rank;
2629 const char * descr;
2630 } AddIPv4RouteContext, * AddIPv4RouteContextRef;
2631
2632 static void
2633 AddIPv4Route(const void * value, void * context)
2634 {
2635 AddIPv4RouteContextRef ctx = (AddIPv4RouteContextRef)context;
2636 CFDictionaryRef dict = (CFDictionaryRef)value;
2637 IPv4RouteRef r = *ctx->route_p;
2638
2639 dict = isA_CFDictionary(dict);
2640 if (dict == NULL
2641 || !dict_get_ip(dict, kSCPropNetIPv4RouteDestinationAddress, &r->dest)
2642 || !dict_get_ip(dict, kSCPropNetIPv4RouteSubnetMask, &r->mask)) {
2643 /* one less route than we expected */
2644 if (dict == NULL) {
2645 my_log(LOG_NOTICE, "IPMonitor: %s route is not a dictionary",
2646 ctx->descr);
2647 }
2648 else {
2649 my_log(LOG_NOTICE, "IPMonitor: %s route is invalid, %@",
2650 ctx->descr, dict);
2651 }
2652 goto skip;
2653 }
2654 if (IPv4RouteSetPrefixLength(r) == FALSE) {
2655 my_log(LOG_NOTICE, "IPMonitor: %s route has invalid subnet mask, %@",
2656 ctx->descr, dict);
2657 goto skip;
2658 }
2659 r->rank = ctx->rank;
2660 r->exclude_ifindex = ctx->exclude_ifindex;
2661 if (ctx->ifindex != 0) {
2662 r->ifindex = ctx->ifindex;
2663 r->ifa = ctx->addr;
2664 if (ctx->exclude_ifindex == 0
2665 && dict_get_ip(dict,
2666 kSCPropNetIPv4RouteGatewayAddress,
2667 &r->gateway)) {
2668 r->flags |= kRouteFlagsHasGateway;
2669 if (r->prefix_length == IPV4_ROUTE_ALL_BITS_SET) {
2670 r->flags |= kRouteFlagsIsHost;
2671 }
2672 }
2673 }
2674 else {
2675 char ifname[IFNAMSIZ];
2676
2677 if (plist_get_string(dict, kSCPropNetIPv4RouteInterfaceName,
2678 ifname, sizeof(ifname)) != NULL) {
2679 IFIndex ifindex;
2680
2681 ifindex = my_if_nametoindex(ifname);
2682 if (ifindex == 0) {
2683 my_log(LOG_NOTICE,
2684 "IPMonitor %s: interface %s does not exist, %@",
2685 ctx->descr, ifname, dict);
2686 goto skip;
2687 }
2688 else if (ifindex == ctx->ifindex) {
2689 my_log(LOG_NOTICE,
2690 "IPMonitor %s: interface %s unexpected, %@",
2691 ctx->descr, ifname, dict);
2692 goto skip;
2693 }
2694 r->ifindex = ifindex;
2695 }
2696 }
2697 (*ctx->route_p)++;
2698 return;
2699
2700 skip:
2701 (*ctx->count_p)--;
2702 return;
2703
2704 }
2705
2706 static boolean_t
2707 confirm_interface_name(CFDictionaryRef dict, CFStringRef ifname)
2708 {
2709 CFStringRef confirmed_ifname;
2710 boolean_t confirmed;
2711
2712 confirmed_ifname
2713 = CFDictionaryGetValue(dict, kSCPropConfirmedInterfaceName);
2714 if (isA_CFString(confirmed_ifname) != NULL) {
2715 confirmed = CFEqual(confirmed_ifname, ifname);
2716 }
2717 else {
2718 confirmed = TRUE;
2719 }
2720 return (confirmed);
2721 }
2722
2723 /*
2724 * Function: IPv4RouteListCreateWithDictionary
2725 *
2726 * Purpose:
2727 * Given the service ipv4 entity dictionary, generate the list of routes.
2728 * Currently, this includes just the default route and subnet route,
2729 * if the service has a subnet mask.
2730 *
2731 * Returns:
2732 * If the passed in route_list is NULL or too small, this routine
2733 * allocates malloc'd memory to hold the routes.
2734 */
2735 static IPv4RouteListRef
2736 IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes,
2737 CFDictionaryRef dict,
2738 CFNumberRef rank_assertion)
2739 {
2740 boolean_t add_default = FALSE;
2741 boolean_t add_router_subnet = FALSE;
2742 boolean_t add_subnet = FALSE;
2743 struct in_addr addr = { 0 };
2744 CFArrayRef additional_routes = NULL;
2745 CFIndex additional_routes_count;
2746 boolean_t allow_additional_routes = FALSE;
2747 boolean_t exclude_from_nwi = FALSE;
2748 CFArrayRef excluded_routes = NULL;
2749 CFIndex excluded_routes_count;
2750 RouteFlags flags = 0;
2751 IFIndex ifindex;
2752 char ifname[IFNAMSIZ];
2753 CFStringRef ifname_cf;
2754 struct in_addr mask = { 0 };
2755 int n = 0;
2756 int prefix_length = 0;
2757 Rank primary_rank = kRankAssertionDefault;
2758 IPv4RouteRef r;
2759 Rank rank = kRankAssertionDefault;
2760 struct in_addr router = { 0 };
2761 struct in_addr subnet = { 0 };
2762
2763 if (dict == NULL) {
2764 return (NULL);
2765 }
2766 ifname_cf = plist_get_string(dict, kSCPropInterfaceName,
2767 ifname, sizeof(ifname));
2768 if (ifname_cf == NULL) {
2769 return (NULL);
2770 }
2771 ifindex = my_if_nametoindex(ifname);
2772 if (ifindex == 0) {
2773 /* interface doesn't exist */
2774 return (NULL);
2775 }
2776 allow_additional_routes = confirm_interface_name(dict, ifname_cf);
2777 if (dict_get_ip(dict, kSCPropNetIPv4Router, &router) == FALSE) {
2778 (void)dict_get_first_ip(dict, kSCPropNetIPv4DestAddresses, &router);
2779 }
2780 if (dict_get_first_ip(dict, kSCPropNetIPv4Addresses, &addr)
2781 && dict_get_first_ip(dict, kSCPropNetIPv4SubnetMasks, &mask)) {
2782 /* subnet route */
2783 subnet = subnet_addr(addr, mask);
2784 /* ignore link-local subnets, let IPConfiguration handle them for now */
2785 if (ntohl(subnet.s_addr) != IN_LINKLOCALNETNUM) {
2786 prefix_length = mask_get_prefix_length(mask);
2787 if (prefix_length < 0) {
2788 my_log(LOG_NOTICE,
2789 "IPMonitor: ignoring bad subnet mask "
2790 IP_FORMAT " on %s",
2791 IP_LIST(&mask), ifname);
2792 }
2793 else {
2794 add_subnet = TRUE;
2795 n++;
2796 }
2797 }
2798 else if (router.s_addr == 0) {
2799 exclude_from_nwi = TRUE;
2800 }
2801 }
2802 if (addr.s_addr == 0) {
2803 /* invalid/non-existent address */
2804 return (NULL);
2805 }
2806 if (rank_assertion != NULL) {
2807 (void)CFNumberGetValue(rank_assertion, kCFNumberSInt32Type,
2808 &primary_rank);
2809 }
2810 if (router.s_addr == 0) {
2811 /* if no router is configured, demote the rank if necessary */
2812 switch (primary_rank) {
2813 case kRankAssertionLast:
2814 case kRankAssertionNever:
2815 case kRankAssertionScoped:
2816 /* rank is already demoted */
2817 break;
2818 default:
2819 /* demote to RankLast */
2820 primary_rank = kRankAssertionLast;
2821 break;
2822 }
2823 }
2824 else {
2825 /*
2826 * If the router address is our address and the subnet mask is
2827 * not 255.255.255.255, assume all routes are local to the interface.
2828 */
2829 if (addr.s_addr == router.s_addr
2830 && mask.s_addr != INADDR_BROADCAST) {
2831 ; /* all routes local */
2832 }
2833 else {
2834 flags |= kRouteFlagsHasGateway;
2835 }
2836 if (rank_assertion == NULL && get_override_primary(dict)) {
2837 primary_rank = kRankAssertionFirst;
2838 }
2839 }
2840
2841 if (S_dict_get_boolean(dict, kIsNULL, FALSE)) {
2842 exclude_from_nwi = TRUE;
2843 flags |= kRouteFlagsIsNULL;
2844 }
2845
2846 switch (primary_rank) {
2847 case kRankAssertionScoped:
2848 /* Scoped means all routes for the service get scoped */
2849 primary_rank = rank = kRankAssertionNever;
2850 flags |= kRouteFlagsIsScoped;
2851 break;
2852 case kRankAssertionNever:
2853 /* Never means just the default route gets scoped */
2854 rank = kRankAssertionLast;
2855 flags |= kRouteFlagsIsScoped;
2856 break;
2857 default:
2858 rank = primary_rank;
2859 break;
2860 }
2861
2862 if ((flags & kRouteFlagsHasGateway) != 0) {
2863 add_router_subnet = TRUE;
2864 n++;
2865 }
2866
2867 if (ifindex != lo0_ifindex()) {
2868 add_default = TRUE;
2869 n++;
2870 }
2871 if (allow_additional_routes) {
2872 additional_routes
2873 = CFDictionaryGetValue(dict, kSCPropNetIPv4AdditionalRoutes);
2874 additional_routes = isA_CFArray(additional_routes);
2875 if (additional_routes != NULL) {
2876 additional_routes_count = CFArrayGetCount(additional_routes);
2877 n += additional_routes_count;
2878 }
2879 excluded_routes
2880 = CFDictionaryGetValue(dict, kSCPropNetIPv4ExcludedRoutes);
2881 excluded_routes = isA_CFArray(excluded_routes);
2882 if (excluded_routes != NULL) {
2883 excluded_routes_count = CFArrayGetCount(excluded_routes);
2884 n += excluded_routes_count;
2885 }
2886 }
2887 if (routes == NULL || routes->size < n) {
2888 routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(n));
2889 routes->size = n;
2890 }
2891 bzero(routes, IPv4RouteListComputeSize(n));
2892 routes->count = n;
2893 if (exclude_from_nwi) {
2894 routes->flags |= kRouteListFlagsExcludeNWI;
2895 }
2896
2897 /* start at the beginning */
2898 r = routes->list;
2899
2900 if (add_default) {
2901 /* add the default route */
2902 routes->flags |= kRouteListFlagsHasDefault;
2903 r->ifindex = ifindex;
2904 r->ifa = addr;
2905 r->flags = flags;
2906 if ((flags & kRouteFlagsHasGateway) != 0) {
2907 r->gateway = router;
2908 }
2909 else {
2910 r->gateway = addr;
2911 }
2912 r->rank = primary_rank;
2913 r++;
2914 }
2915
2916 /* add the subnet route */
2917 if (add_subnet) {
2918 if ((flags & kRouteFlagsIsNULL) != 0) {
2919 r->flags |= kRouteFlagsIsNULL;
2920 }
2921 r->ifindex = ifindex;
2922 r->gateway = addr;
2923 r->dest = subnet;
2924 r->mask = mask;
2925 r->prefix_length = prefix_length;
2926 r->ifa = addr;
2927 r->rank = rank;
2928 r++;
2929 }
2930
2931 /* add the router subnet route */
2932 if (add_router_subnet) {
2933 if ((flags & kRouteFlagsIsNULL) != 0) {
2934 r->flags |= kRouteFlagsIsNULL;
2935 }
2936 r->ifindex = ifindex;
2937 r->gateway = addr;
2938 r->dest = router;
2939 r->mask.s_addr = INADDR_BROADCAST;
2940 r->prefix_length = IPV4_ROUTE_ALL_BITS_SET;
2941 r->ifa = addr;
2942 r->rank = rank;
2943 r++;
2944 }
2945
2946 if (additional_routes != NULL || excluded_routes != NULL) {
2947 AddIPv4RouteContext context;
2948
2949 bzero(&context, sizeof(context));
2950 context.count_p = &routes->count;
2951 context.route_p = &r;
2952 context.rank = rank;
2953
2954 /* additional routes */
2955 if (additional_routes != NULL) {
2956 context.ifindex = ifindex;
2957 context.addr = addr;
2958 context.descr = "AdditionalRoutes";
2959 CFArrayApplyFunction(additional_routes,
2960 CFRangeMake(0, additional_routes_count),
2961 AddIPv4Route, &context);
2962 }
2963 /* excluded routes */
2964 if (excluded_routes != NULL) {
2965 context.descr = "ExcludedRoutes";
2966 /* exclude this interface */
2967 context.ifindex = 0;
2968 context.exclude_ifindex = ifindex;
2969 CFArrayApplyFunction(excluded_routes,
2970 CFRangeMake(0, excluded_routes_count),
2971 AddIPv4Route, &context);
2972 }
2973 }
2974 return (routes);
2975 }
2976
2977 /**
2978 ** IPv6Route*
2979 **/
2980 #define IPV6_ROUTE_ALL_BITS_SET 128
2981
2982 static boolean_t
2983 ipv6_prefix_length_is_valid(int prefix_length)
2984 {
2985 if (prefix_length < 0 || prefix_length > IPV6_ROUTE_ALL_BITS_SET) {
2986 return (FALSE);
2987 }
2988 return (TRUE);
2989 }
2990
2991 /*
2992 * from netinet6/in6.c
2993 */
2994 static void
2995 in6_len2mask(struct in6_addr * mask, int len)
2996 {
2997 int i;
2998
2999 bzero(mask, sizeof(*mask));
3000 for (i = 0; i < len / 8; i++)
3001 mask->s6_addr[i] = 0xff;
3002 if (len % 8)
3003 mask->s6_addr[i] = (0xff00 >> (len % 8)) & 0xff;
3004 }
3005
3006 static void
3007 in6_maskaddr(struct in6_addr * addr, const struct in6_addr * mask)
3008 {
3009 int i;
3010
3011 for (i = 0; i < sizeof(addr->s6_addr); i++) {
3012 addr->s6_addr[i] &= mask->s6_addr[i];
3013 }
3014 return;
3015 }
3016
3017 static void
3018 in6_netaddr(struct in6_addr * addr, int len)
3019 {
3020 struct in6_addr mask;
3021
3022 in6_len2mask(&mask, len);
3023 in6_maskaddr(addr, &mask);
3024 return;
3025 }
3026
3027 static void
3028 in6_addr_scope_linklocal(struct in6_addr * addr, IFIndex ifindex)
3029 {
3030 if (IN6_IS_ADDR_LINKLOCAL(addr)) {
3031 addr->__u6_addr.__u6_addr16[1] = htons(ifindex);
3032 }
3033 return;
3034 }
3035
3036 static void
3037 string_append_in6_addr(CFMutableStringRef str, const struct in6_addr * addr)
3038 {
3039 char ntopbuf[INET6_ADDRSTRLEN];
3040
3041 CFStringAppendCString(str,
3042 inet_ntop(AF_INET6, addr, ntopbuf, sizeof(ntopbuf)),
3043 kCFStringEncodingASCII);
3044 return;
3045 }
3046
3047 static void
3048 IPv6RouteCopyDescriptionWithString(IPv6RouteRef r, CFMutableStringRef str)
3049 {
3050 if ((r->flags & kRouteFlagsIsHost) != 0) {
3051 CFStringAppend(str, CFSTR("Host "));
3052 string_append_in6_addr(str, &r->dest);
3053 }
3054 else {
3055 CFStringAppend(str, CFSTR("Net "));
3056 string_append_in6_addr(str, &r->dest);
3057 CFStringAppendFormat(str, NULL, CFSTR("/%d"),
3058 r->prefix_length);
3059 }
3060 if ((r->flags & kRouteFlagsHasGateway) != 0) {
3061 CFStringAppend(str, CFSTR(" Gate "));
3062 string_append_in6_addr(str, &r->gateway);
3063 }
3064 RouteAddInterfaceToDescription((RouteRef)r, str);
3065 if (!IN6_ARE_ADDR_EQUAL(&r->ifa, &in6addr_any)) {
3066 CFStringAppend(str, CFSTR(" Ifa "));
3067 string_append_in6_addr(str, &r->ifa);
3068 }
3069 RouteAddFlagsToDescription((RouteRef)r, str);
3070 return;
3071 }
3072
3073 static CFStringRef
3074 IPv6RouteCopyDescription(RouteRef r)
3075 {
3076 CFMutableStringRef str;
3077
3078 str = CFStringCreateMutable(NULL, 0);
3079 IPv6RouteCopyDescriptionWithString((IPv6RouteRef)r, str);
3080 return (str);
3081 }
3082
3083 static CFMutableStringRef
3084 IPv6RouteListCopyDescription(IPv6RouteListRef routes)
3085 {
3086 int i;
3087 IPv6RouteRef r;
3088 CFMutableStringRef str;
3089
3090 str = CFStringCreateMutable(NULL, 0);
3091 CFStringAppendFormat(str, NULL, CFSTR("<IPv6RouteList[%d]> = {"),
3092 routes->count);
3093 for (i = 0, r = routes->list; i < routes->count; i++, r++) {
3094 CFStringAppendFormat(str, NULL, CFSTR("\n%2d. "), i);
3095 IPv6RouteCopyDescriptionWithString(r, str);
3096 }
3097 CFStringAppend(str, CFSTR("\n}"));
3098 return (str);
3099 }
3100
3101 #ifdef TEST_IPV6_ROUTELIST
3102
3103 static void
3104 IPv6RouteLog(int level, RouteRef route, const char * msg)
3105 {
3106 CFStringRef str = IPv6RouteCopyDescription(route);
3107
3108 if (msg == NULL) {
3109 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
3110 }
3111 else {
3112 SCPrint(TRUE, stdout, CFSTR("%s: %@\n"), msg, str);
3113 }
3114 CFRelease(str);
3115 return;
3116 }
3117
3118 static __inline__ void
3119 IPv6RouteListPrint(IPv6RouteListRef routes)
3120 {
3121 CFStringRef str = IPv6RouteListCopyDescription(routes);
3122
3123 SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
3124 CFRelease(str);
3125 return;
3126 }
3127
3128 #else /* TEST_IPV6_ROUTELIST */
3129
3130 static __inline__ void
3131 IPv6RouteLog(int level, RouteRef route, const char * msg)
3132 {
3133 CFStringRef str = IPv6RouteCopyDescription(route);
3134
3135 if (msg == NULL) {
3136 my_log(level, "%@", str);
3137 }
3138 else {
3139 my_log(level, "%s: %@", msg, str);
3140 }
3141 CFRelease(str);
3142 return;
3143 }
3144
3145 #endif /* TEST_IPV6_ROUTELIST */
3146
3147 static size_t
3148 IPv6RouteListComputeSize(CFIndex n)
3149 {
3150 return (offsetof(IPv6RouteList, list[n]));
3151 }
3152
3153
3154 typedef struct {
3155 struct in6_addr * addr;
3156 int * count_p;
3157 IFIndex ifindex;
3158 IFIndex exclude_ifindex;
3159 IPv6RouteRef * route_p;
3160 Rank rank;
3161 const char * descr;
3162 } AddIPv6RouteContext, * AddIPv6RouteContextRef;
3163
3164 static void
3165 AddIPv6Route(const void * value, void * context)
3166 {
3167 AddIPv6RouteContextRef ctx = (AddIPv6RouteContextRef)context;
3168 CFDictionaryRef dict = (CFDictionaryRef)value;
3169 IPv6RouteRef r = *ctx->route_p;
3170
3171 dict = isA_CFDictionary(dict);
3172 if (dict == NULL
3173 || !dict_get_ipv6(dict, kSCPropNetIPv6RouteDestinationAddress, &r->dest)
3174 || !dict_get_int(dict, kSCPropNetIPv6RoutePrefixLength,
3175 &r->prefix_length)
3176 || !ipv6_prefix_length_is_valid(r->prefix_length)) {
3177 /* one less route than we expected */
3178 if (dict == NULL) {
3179 my_log(LOG_NOTICE, "IPMonitor: %s route is not a dictionary",
3180 ctx->descr);
3181 }
3182 else {
3183 my_log(LOG_NOTICE, "IPMonitor: %s route is invalid, %@",
3184 ctx->descr, dict);
3185 }
3186 goto skip;
3187 }
3188 r->rank = ctx->rank;
3189 r->exclude_ifindex = ctx->exclude_ifindex;
3190 if (ctx->ifindex != 0) {
3191 r->ifindex = ctx->ifindex;
3192 r->ifa = *ctx->addr;
3193 if (ctx->exclude_ifindex == 0
3194 && dict_get_ipv6(dict,
3195 kSCPropNetIPv6RouteGatewayAddress,
3196 &r->gateway)) {
3197 r->flags |= kRouteFlagsHasGateway;
3198 if (r->prefix_length == IPV6_ROUTE_ALL_BITS_SET) {
3199 r->flags |= kRouteFlagsIsHost;
3200 }
3201 }
3202 }
3203 else {
3204 char ifname[IFNAMSIZ];
3205
3206 if (plist_get_string(dict, kSCPropNetIPv6RouteInterfaceName,
3207 ifname, sizeof(ifname)) != NULL) {
3208 IFIndex ifindex;
3209
3210 ifindex = my_if_nametoindex(ifname);
3211 if (ifindex == 0) {
3212 my_log(LOG_NOTICE,
3213 "IPMonitor %s: interface %s does not exist, %@",
3214 ctx->descr, ifname, dict);
3215 goto skip;
3216 }
3217 else if (ifindex == ctx->ifindex) {
3218 my_log(LOG_NOTICE,
3219 "IPMonitor %s: interface %s unexpected, %@",
3220 ctx->descr, ifname, dict);
3221 goto skip;
3222 }
3223 r->ifindex = ifindex;
3224 }
3225 }
3226 (*ctx->route_p)++;
3227 return;
3228
3229 skip:
3230 (*ctx->count_p)--;
3231 return;
3232
3233 }
3234
3235 /*
3236 * Function: IPv6RouteListCreateWithDictionary
3237 *
3238 * Purpose:
3239 * Given the service IPv6 entity dictionary, generate the list of routes.
3240 *
3241 * Returns:
3242 * If the passed in route_list is NULL or too small, this routine
3243 * allocates malloc'd memory to hold the routes.
3244 */
3245 static IPv6RouteListRef
3246 IPv6RouteListCreateWithDictionary(IPv6RouteListRef routes,
3247 CFDictionaryRef dict,
3248 CFNumberRef rank_assertion)
3249 {
3250 boolean_t add_default = FALSE;
3251 boolean_t add_prefix = FALSE;
3252 struct in6_addr addr;
3253 CFArrayRef additional_routes = NULL;
3254 CFIndex additional_routes_count;
3255 boolean_t allow_additional_routes = FALSE;
3256 boolean_t exclude_from_nwi = FALSE;
3257 CFArrayRef excluded_routes = NULL;
3258 CFIndex excluded_routes_count;
3259 RouteFlags flags = 0;
3260 IFIndex ifindex;
3261 char ifname[IFNAMSIZ];
3262 CFStringRef ifname_cf;
3263 int n = 0;
3264 int prefix_length = 0;
3265 Rank primary_rank = kRankAssertionDefault;
3266 IPv6RouteRef r;
3267 Rank rank = kRankAssertionDefault;
3268 struct in6_addr router = in6addr_any;
3269
3270 if (dict == NULL) {
3271 return (NULL);
3272 }
3273 ifname_cf = plist_get_string(dict, kSCPropInterfaceName,
3274 ifname, sizeof(ifname));
3275 if (ifname_cf == NULL) {
3276 return (NULL);
3277 }
3278 ifindex = my_if_nametoindex(ifname);
3279 if (ifindex == 0) {
3280 /* interface doesn't exist */
3281 return (NULL);
3282 }
3283 allow_additional_routes = confirm_interface_name(dict, ifname_cf);
3284 if (dict_get_ipv6(dict, kSCPropNetIPv6Router, &router) == FALSE) {
3285 (void)dict_get_first_ipv6(dict, kSCPropNetIPv6DestAddresses, &router);
3286 }
3287 if (dict_get_first_ipv6(dict, kSCPropNetIPv6Addresses, &addr)) {
3288 if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
3289 return (NULL);
3290 }
3291 if (dict_get_first_int(dict, kSCPropNetIPv6PrefixLength,
3292 &prefix_length)
3293 && !IN6_IS_ADDR_LINKLOCAL(&addr)
3294 && ipv6_prefix_length_is_valid(prefix_length)) {
3295 add_prefix = TRUE;
3296 n++;
3297 }
3298 else {
3299 prefix_length = 0;
3300 }
3301 }
3302 else {
3303 /* no addresses */
3304 return (NULL);
3305 }
3306 if (rank_assertion != NULL) {
3307 (void)CFNumberGetValue(rank_assertion, kCFNumberSInt32Type,
3308 &primary_rank);
3309 }
3310 if (!IN6_IS_ADDR_UNSPECIFIED(&router)) {
3311 if (ifindex != lo0_ifindex()) {
3312 add_default = TRUE;
3313 n++;
3314 }
3315 /*
3316 * If the router address is our address and the prefix length is
3317 * not 128, assume all routes are local to the interface.
3318 */
3319 if (IN6_ARE_ADDR_EQUAL(&router, &addr)
3320 && prefix_length != IPV6_ROUTE_ALL_BITS_SET) {
3321 ; /* all routes local */
3322 }
3323 else {
3324 flags |= kRouteFlagsHasGateway;
3325 }
3326 if (rank_assertion == NULL && get_override_primary(dict)) {
3327 primary_rank = kRankAssertionFirst;
3328 }
3329 }
3330 if (S_dict_get_boolean(dict, kIsNULL, FALSE)) {
3331 exclude_from_nwi = TRUE;
3332 flags |= kRouteFlagsIsNULL;
3333 }
3334
3335 switch (primary_rank) {
3336 case kRankAssertionScoped:
3337 /* Scoped means all routes for the service get scoped */
3338 primary_rank = rank = kRankAssertionNever;
3339 flags |= kRouteFlagsIsScoped;
3340 break;
3341 case kRankAssertionNever:
3342 /* Never means just the default route gets scoped */
3343 rank = kRankAssertionLast;
3344 flags |= kRouteFlagsIsScoped;
3345 break;
3346 default:
3347 rank = primary_rank;
3348 break;
3349 }
3350
3351 if (allow_additional_routes) {
3352 additional_routes
3353 = CFDictionaryGetValue(dict, kSCPropNetIPv6AdditionalRoutes);
3354 additional_routes = isA_CFArray(additional_routes);
3355 if (additional_routes != NULL) {
3356 additional_routes_count = CFArrayGetCount(additional_routes);
3357 n += additional_routes_count;
3358 }
3359 excluded_routes = CFDictionaryGetValue(dict,
3360 kSCPropNetIPv6ExcludedRoutes);
3361 excluded_routes = isA_CFArray(excluded_routes);
3362 if (excluded_routes != NULL) {
3363 excluded_routes_count = CFArrayGetCount(excluded_routes);
3364 n += excluded_routes_count;
3365 }
3366 }
3367 if (n == 0) {
3368 return (NULL);
3369 }
3370
3371 /* need IPv6LL subnet route */
3372 n++;
3373
3374 if (routes == NULL || routes->size < n) {
3375 routes = (IPv6RouteListRef)malloc(IPv6RouteListComputeSize(n));
3376 routes->size = n;
3377 }
3378 bzero(routes, IPv6RouteListComputeSize(n));
3379 routes->count = n;
3380 if (exclude_from_nwi) {
3381 routes->flags |= kRouteListFlagsExcludeNWI;
3382 }
3383
3384 /* start at the beginning */
3385 r = routes->list;
3386 if (add_default) {
3387 /* add the default route */
3388 routes->flags |= kRouteListFlagsHasDefault;
3389 r->ifindex = ifindex;
3390 r->ifa = addr;
3391 r->flags = flags;
3392 if ((flags & kRouteFlagsHasGateway) != 0) {
3393 r->gateway = router;
3394 }
3395 else {
3396 r->gateway = addr;
3397 }
3398 r->rank = primary_rank;
3399 if (S_scopedroute_v6) {
3400 r->flags |= kRouteFlagsKernelManaged;
3401 }
3402 r++;
3403 }
3404
3405
3406 /* add IPv6LL route */
3407 r->ifindex = ifindex;
3408 r->dest.s6_addr[0] = 0xfe;
3409 r->dest.s6_addr[1] = 0x80;
3410 r->prefix_length = 64;
3411 r->rank = rank;
3412 r->flags |= kRouteFlagsKernelManaged;
3413 r++;
3414
3415
3416 /* add the prefix route(s) */
3417 if (add_prefix) {
3418 r->flags |= kRouteFlagsKernelManaged;
3419 if ((flags & kRouteFlagsIsNULL) != 0) {
3420 r->flags |= kRouteFlagsIsNULL;
3421 }
3422 r->ifindex = ifindex;
3423 r->gateway = addr;
3424 r->dest = addr;
3425 in6_netaddr(&r->dest, prefix_length);
3426 r->prefix_length = prefix_length;
3427 r->ifa = addr;
3428 r->rank = rank;
3429 r++;
3430 }
3431
3432 if (additional_routes != NULL || excluded_routes != NULL) {
3433 AddIPv6RouteContext context;
3434
3435 bzero(&context, sizeof(context));
3436 context.count_p = &routes->count;
3437 context.route_p = &r;
3438 context.rank = rank;
3439
3440 /* additional routes */
3441 if (additional_routes != NULL) {
3442 context.ifindex = ifindex;
3443 context.addr = &addr;
3444 context.descr = "AdditionalRoutes";
3445 CFArrayApplyFunction(additional_routes,
3446 CFRangeMake(0, additional_routes_count),
3447 AddIPv6Route, &context);
3448 }
3449 /* excluded routes */
3450 if (excluded_routes != NULL) {
3451 context.descr = "ExcludedRoutes";
3452 /* exclude this interface */
3453 context.ifindex = 0;
3454 context.exclude_ifindex = ifindex;
3455 context.addr = NULL;
3456 CFArrayApplyFunction(excluded_routes,
3457 CFRangeMake(0, excluded_routes_count),
3458 AddIPv6Route, &context);
3459 }
3460 }
3461 return (routes);
3462 }
3463
3464 static const void *
3465 IPv6RouteGateway(RouteRef r_route)
3466 {
3467 IPv6RouteRef route = (IPv6RouteRef)r_route;
3468 return (&route->gateway);
3469 }
3470
3471 static void
3472 IPv6RouteSetGateway(RouteRef r_route, const void * address)
3473 {
3474 IPv6RouteRef route = (IPv6RouteRef)r_route;
3475
3476 route->gateway = *((struct in6_addr *)address);
3477 return;
3478 }
3479
3480 static const void *
3481 IPv6RouteDestination(RouteRef r_route)
3482 {
3483 IPv6RouteRef route = (IPv6RouteRef)r_route;
3484 return (&route->dest);
3485 }
3486
3487 static __inline__ int
3488 in6_addr_cmp(const struct in6_addr * a, const struct in6_addr * b)
3489 {
3490 return (memcmp(a->s6_addr, b->s6_addr, sizeof(struct in6_addr)));
3491 }
3492
3493 static boolean_t
3494 IPv6RouteIsEqual(RouteRef r_route1, RouteRef r_route2)
3495 {
3496 IPv6RouteRef route1 = (IPv6RouteRef)r_route1;
3497 IPv6RouteRef route2 = (IPv6RouteRef)r_route2;
3498
3499 return (route1->prefix_length == route2->prefix_length
3500 && route1->ifindex == route2->ifindex
3501 && route1->flags == route2->flags
3502 && in6_addr_cmp(&route1->dest, &route2->dest) == 0
3503 && in6_addr_cmp(&route1->ifa, &route2->ifa) == 0
3504 && in6_addr_cmp(&route1->gateway, &route2->gateway) == 0);
3505 }
3506
3507 static boolean_t
3508 IPv6RouteSameSubnet(RouteRef r_route, const void * addr)
3509 {
3510 const struct in6_addr * address = (const struct in6_addr *)addr;
3511 struct in6_addr netaddr;
3512 IPv6RouteRef route = (IPv6RouteRef)r_route;
3513
3514 netaddr = *address;
3515 in6_netaddr(&netaddr, route->prefix_length);
3516 return (in6_addr_cmp(&netaddr, &route->dest) == 0);
3517 }
3518
3519
3520 #define V6_ROUTE_MSG_ADDRS_SPACE (5 * sizeof(struct sockaddr_dl) + 128)
3521
3522 typedef struct {
3523 struct rt_msghdr hdr;
3524 char addrs[V6_ROUTE_MSG_ADDRS_SPACE];
3525 } v6_route_msg;
3526
3527 /*
3528 * Function: IPv6RouteApply
3529 * Purpose:
3530 * Add or remove the specified route to/from the kernel routing table.
3531 */
3532 static int
3533 IPv6RouteApply(RouteRef r_route, int cmd, int sockfd)
3534 {
3535 int len;
3536 int ret = 0;
3537 IPv6RouteRef route = (IPv6RouteRef)r_route;
3538 v6_route_msg rtmsg;
3539 union {
3540 struct sockaddr_in6 * in_p;
3541 struct sockaddr_dl * dl_p;
3542 void * ptr;
3543 } rtaddr;
3544
3545 if ((route->flags & kRouteFlagsIsScoped) != 0
3546 && !S_scopedroute_v6) {
3547 return (EROUTENOTAPPLIED);
3548 }
3549 if ((route->flags & kRouteFlagsKernelManaged) != 0) {
3550 /* the kernel manages this route, don't touch it */
3551 return (EROUTENOTAPPLIED);
3552 }
3553 if ((route->flags & kRouteFlagsIsNULL) != 0) {
3554 return (EROUTENOTAPPLIED);
3555 }
3556 if (route->ifindex == 0) {
3557 IPv6RouteLog(LOG_NOTICE, (RouteRef)route,
3558 "IPMonitor IPv6RouteApply: no interface specified");
3559 return (ENXIO);
3560 }
3561 if (sockfd == -1) {
3562 #ifdef TEST_IPV6_ROUTELIST
3563 return (0);
3564 #else /* TEST_IPV6_ROUTELIST */
3565 return (EBADF);
3566 #endif /* TEST_IPV6_ROUTELIST */
3567 }
3568 memset(&rtmsg, 0, sizeof(rtmsg));
3569 rtmsg.hdr.rtm_type = cmd;
3570 rtmsg.hdr.rtm_version = RTM_VERSION;
3571 rtmsg.hdr.rtm_seq = ++rtm_seq;
3572 rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_IFP;
3573 if (!IN6_IS_ADDR_UNSPECIFIED(&route->ifa)) {
3574 rtmsg.hdr.rtm_addrs |= RTA_IFA;
3575 }
3576 rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC;
3577 if ((route->flags & kRouteFlagsIsHost) != 0) {
3578 rtmsg.hdr.rtm_flags |= RTF_HOST;
3579 }
3580 else {
3581 rtmsg.hdr.rtm_addrs |= RTA_NETMASK;
3582 if ((route->flags & kRouteFlagsHasGateway) == 0) {
3583 rtmsg.hdr.rtm_flags |= RTF_CLONING;
3584 }
3585 }
3586 if ((route->flags & kRouteFlagsHasGateway) != 0) {
3587 rtmsg.hdr.rtm_flags |= RTF_GATEWAY;
3588 }
3589 if ((route->flags & kRouteFlagsIsScoped) != 0) {
3590 rtmsg.hdr.rtm_index = route->ifindex;
3591 rtmsg.hdr.rtm_flags |= RTF_IFSCOPE;
3592 }
3593
3594 rtaddr.ptr = rtmsg.addrs;
3595
3596 /* dest */
3597 rtaddr.in_p->sin6_len = sizeof(*rtaddr.in_p);
3598 rtaddr.in_p->sin6_family = AF_INET6;
3599 rtaddr.in_p->sin6_addr = route->dest;
3600 in6_addr_scope_linklocal(&rtaddr.in_p->sin6_addr, route->ifindex);
3601 rtaddr.ptr += sizeof(*rtaddr.in_p);
3602
3603 /* gateway */
3604 if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) {
3605 /* gateway is an IP address */
3606 rtaddr.in_p->sin6_len = sizeof(*rtaddr.in_p);
3607 rtaddr.in_p->sin6_family = AF_INET6;
3608 rtaddr.in_p->sin6_addr = route->gateway;
3609 in6_addr_scope_linklocal(&rtaddr.in_p->sin6_addr, route->ifindex);
3610 rtaddr.ptr += sizeof(*rtaddr.in_p);
3611 }
3612 else {
3613 /* gateway is the interface itself */
3614 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3615 rtaddr.dl_p->sdl_family = AF_LINK;
3616 rtaddr.dl_p->sdl_index = route->ifindex;
3617 rtaddr.ptr += sizeof(*rtaddr.dl_p);
3618 }
3619
3620 /* mask */
3621 if ((rtmsg.hdr.rtm_addrs & RTA_NETMASK) != 0) {
3622 rtaddr.in_p->sin6_len = sizeof(*rtaddr.in_p);
3623 rtaddr.in_p->sin6_family = AF_INET6;
3624 in6_len2mask(&rtaddr.in_p->sin6_addr, route->prefix_length);
3625 rtaddr.ptr += sizeof(*rtaddr.in_p);
3626 }
3627
3628 /* interface */
3629 if ((rtmsg.hdr.rtm_addrs & RTA_IFP) != 0) {
3630 rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3631 rtaddr.dl_p->sdl_family = AF_LINK;
3632 rtaddr.dl_p->sdl_index = route->ifindex;
3633 rtaddr.ptr += sizeof(*rtaddr.dl_p);
3634 }
3635 /* interface address */
3636 if ((rtmsg.hdr.rtm_addrs & RTA_IFA) != 0) {
3637 rtaddr.in_p->sin6_len = sizeof(*rtaddr.in_p);
3638 rtaddr.in_p->sin6_family = AF_INET6;
3639 rtaddr.in_p->sin6_addr = route->ifa;
3640 rtaddr.ptr += sizeof(*rtaddr.in_p);
3641 }
3642
3643 /* apply the route */
3644 len = (int)(sizeof(rtmsg.hdr) + (rtaddr.ptr - (void *)rtmsg.addrs));
3645 rtmsg.hdr.rtm_msglen = len;
3646 if (write(sockfd, &rtmsg, len) == -1) {
3647 ret = errno;
3648 }
3649 return (ret);
3650 }
3651
3652 static const RouteListInfo IPv6RouteListInfo = {
3653 IPv6RouteListComputeSize,
3654
3655 IPv6RouteIsEqual,
3656 IPv6RouteApply,
3657 IPv6RouteGateway,
3658 IPv6RouteSetGateway,
3659 IPv6RouteDestination,
3660 IPv6RouteSameSubnet,
3661 IPv6RouteLog,
3662 IPv6RouteCopyDescription,
3663
3664 sizeof(IPv6Route),
3665 sizeof(struct in6_addr),
3666 IPV6_ROUTE_ALL_BITS_SET
3667 };
3668
3669 #ifdef TEST_IPV6_ROUTELIST
3670 static IPv6RouteListRef
3671 IPv6RouteListAddRouteList(IPv6RouteListRef routes, int init_size,
3672 IPv6RouteListRef service_routes, Rank rank)
3673 {
3674 return ((IPv6RouteListRef)
3675 RouteListAddRouteList(&IPv6RouteListInfo,
3676 (RouteListRef)routes, init_size,
3677 (RouteListRef)service_routes, rank));
3678 }
3679 #endif /* TEST_IPV6_ROUTELIST */
3680
3681 #if !TARGET_IPHONE_SIMULATOR
3682 static __inline__ void
3683 IPv6RouteListLog(int level, IPv6RouteListRef routes)
3684 {
3685 CFStringRef str = IPv6RouteListCopyDescription(routes);
3686
3687 my_log(level, "%@", str);
3688 CFRelease(str);
3689 return;
3690 }
3691
3692 static void
3693 IPv6RouteListFinalize(IPv6RouteListRef routes)
3694 {
3695 RouteListFinalize(&IPv6RouteListInfo, (RouteListRef)routes);
3696 return;
3697 }
3698
3699 static void
3700 IPv6RouteListApply(IPv6RouteListRef old_routes, IPv6RouteListRef new_routes,
3701 int sockfd)
3702 {
3703 RouteListApply(&IPv6RouteListInfo,
3704 (RouteListRef)old_routes, (RouteListRef)new_routes,
3705 sockfd);
3706 return;
3707 }
3708 #endif /* !TARGET_IPHONE_SIMULATOR */
3709
3710 /*
3711 * Function: parse_component
3712 * Purpose:
3713 * Given a string 'key' and a string prefix 'prefix',
3714 * return the next component in the slash '/' separated
3715 * key.
3716 *
3717 * Examples:
3718 * 1. key = "a/b/c" prefix = "a/"
3719 * returns "b"
3720 * 2. key = "a/b/c" prefix = "a/b/"
3721 * returns "c"
3722 */
3723 static CF_RETURNS_RETAINED CFStringRef
3724 parse_component(CFStringRef key, CFStringRef prefix)
3725 {
3726 CFMutableStringRef comp;
3727 CFRange range;
3728
3729 if (CFStringHasPrefix(key, prefix) == FALSE) {
3730 return (NULL);
3731 }
3732 comp = CFStringCreateMutableCopy(NULL, 0, key);
3733 if (comp == NULL) {
3734 return (NULL);
3735 }
3736 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
3737 range = CFStringFind(comp, CFSTR("/"), 0);
3738 if (range.location == kCFNotFound) {
3739 return (comp);
3740 }
3741 range.length = CFStringGetLength(comp) - range.location;
3742 CFStringDelete(comp, range);
3743 return (comp);
3744 }
3745
3746 __private_extern__ boolean_t
3747 service_contains_protocol(CFDictionaryRef service, int af)
3748 {
3749 boolean_t contains_protocol = FALSE;
3750 CFStringRef entity;
3751 RouteListRef routes;
3752 CFDictionaryRef dict;
3753
3754 entity = (af == AF_INET) ? kSCEntNetIPv4 : kSCEntNetIPv6;
3755 dict = CFDictionaryGetValue(service, entity);
3756 if (dict == NULL) {
3757 goto done;
3758 }
3759 routes = ipdict_get_routelist(dict);
3760 if (routes == NULL) {
3761 goto done;
3762 }
3763 if ((routes->flags & kRouteListFlagsExcludeNWI) != 0) {
3764 goto done;
3765 }
3766 contains_protocol = TRUE;
3767
3768 done:
3769 return (contains_protocol);
3770 }
3771
3772
3773 static CFMutableDictionaryRef
3774 service_dict_copy(CFStringRef serviceID)
3775 {
3776 CFDictionaryRef d = NULL;
3777 CFMutableDictionaryRef service_dict;
3778
3779 /* create a modifyable dictionary, a copy or a new one */
3780 d = CFDictionaryGetValue(S_service_state_dict, serviceID);
3781 if (d == NULL) {
3782 service_dict
3783 = CFDictionaryCreateMutable(NULL, 0,
3784 &kCFTypeDictionaryKeyCallBacks,
3785 &kCFTypeDictionaryValueCallBacks);
3786 }
3787 else {
3788 service_dict = CFDictionaryCreateMutableCopy(NULL, 0, d);
3789 }
3790 return (service_dict);
3791 }
3792
3793 static void
3794 log_service_entity(int level, CFStringRef serviceID, CFStringRef entity,
3795 CFStringRef operation, CFTypeRef val)
3796 {
3797 CFMutableStringRef this_val = NULL;
3798
3799 if (val != NULL) {
3800 boolean_t is_ipv4;
3801 boolean_t is_ipv6;
3802
3803 if ((is_ipv4 = CFEqual(entity, kSCEntNetIPv4))
3804 || (is_ipv6 = CFEqual(entity, kSCEntNetIPv6))) {
3805 RouteListUnion routes;
3806
3807 routes.ptr = ipdict_get_routelist(val);
3808 if (routes.ptr != NULL) {
3809 CFDictionaryRef service_dict = NULL;
3810
3811 if (is_ipv4) {
3812 this_val = IPv4RouteListCopyDescription(routes.v4);
3813 }
3814 else {
3815 this_val = IPv6RouteListCopyDescription(routes.v6);
3816 }
3817 service_dict = ipdict_get_service(val);
3818 if (service_dict != NULL) {
3819 CFStringAppendFormat(this_val, NULL,
3820 CFSTR("\n<Service> = %@"),
3821 service_dict);
3822 }
3823 val = this_val;
3824 }
3825 }
3826 }
3827 if (val == NULL) {
3828 val = CFSTR("<none>");
3829 }
3830 my_log(level, "IPMonitor: serviceID %@ %@ %@ value = %@",
3831 serviceID, operation, entity, val);
3832 my_CFRelease(&this_val);
3833 return;
3834 }
3835
3836 static boolean_t
3837 service_dict_set(CFStringRef serviceID, CFStringRef entity,
3838 CFTypeRef new_val)
3839 {
3840 boolean_t changed = FALSE;
3841 CFTypeRef old_val;
3842 CFMutableDictionaryRef service_dict;
3843
3844 service_dict = service_dict_copy(serviceID);
3845 old_val = CFDictionaryGetValue(service_dict, entity);
3846 if (new_val == NULL) {
3847 if (old_val != NULL) {
3848 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3849 log_service_entity(LOG_DEBUG, serviceID, entity,
3850 CFSTR("Removed:"), old_val);
3851 }
3852 CFDictionaryRemoveValue(service_dict, entity);
3853 changed = TRUE;
3854 }
3855 }
3856 else {
3857 if (old_val == NULL || CFEqual(new_val, old_val) == FALSE) {
3858 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3859 log_service_entity(LOG_DEBUG, serviceID, entity,
3860 CFSTR("Changed: old"), old_val);
3861 log_service_entity(LOG_DEBUG, serviceID, entity,
3862 CFSTR("Changed: new"), new_val);
3863 }
3864 CFDictionarySetValue(service_dict, entity, new_val);
3865 changed = TRUE;
3866 }
3867 }
3868 if (CFDictionaryGetCount(service_dict) == 0) {
3869 CFDictionaryRemoveValue(S_service_state_dict, serviceID);
3870 }
3871 else {
3872 CFDictionarySetValue(S_service_state_dict, serviceID, service_dict);
3873 }
3874 my_CFRelease(&service_dict);
3875 return (changed);
3876 }
3877
3878 static CFDictionaryRef
3879 service_dict_get(CFStringRef serviceID, CFStringRef entity)
3880 {
3881 CFDictionaryRef service_dict;
3882
3883 service_dict = CFDictionaryGetValue(S_service_state_dict, serviceID);
3884 if (service_dict == NULL) {
3885 return (NULL);
3886 }
3887 return (CFDictionaryGetValue(service_dict, entity));
3888 }
3889
3890 #ifndef kSCPropNetHostname
3891 #define kSCPropNetHostname CFSTR("Hostname")
3892 #endif
3893
3894 __private_extern__
3895 CFStringRef
3896 copy_dhcp_hostname(CFStringRef serviceID)
3897 {
3898 CFDictionaryRef dict = NULL;
3899 CFStringRef hostname = NULL;
3900 CFDictionaryRef service_dict = NULL;
3901
3902 dict = service_dict_get(serviceID, kSCEntNetIPv4);
3903 if (dict == NULL) {
3904 return (NULL);
3905 }
3906 service_dict = ipdict_get_service(dict);
3907 if (service_dict == NULL) {
3908 return (NULL);
3909 }
3910 hostname = CFDictionaryGetValue(service_dict, kSCPropNetHostname);
3911 if (hostname != NULL) {
3912 CFRetain(hostname);
3913 }
3914 return (hostname);
3915 }
3916
3917 #if !TARGET_IPHONE_SIMULATOR
3918
3919 static struct in6_addr *
3920 ipv6_service_get_router(CFDictionaryRef service,
3921 IFIndex * ifindex_p, CFStringRef * ifname_p)
3922 {
3923 IPv6RouteListRef routes;
3924 struct in6_addr * router = NULL;
3925
3926 routes = ipdict_get_routelist(service);
3927 if (routes != NULL
3928 && (routes->flags & kRouteListFlagsExcludeNWI) == 0
3929 && (routes->flags & kRouteListFlagsHasDefault) != 0) {
3930 router = &routes->list[0].gateway;
3931 if (*ifindex_p == 0) {
3932 *ifindex_p = routes->list[0].ifindex;
3933 }
3934 if (*ifname_p == NULL) {
3935 *ifname_p = ipdict_get_ifname(service);
3936 }
3937 }
3938 return (router);
3939 }
3940
3941 static void
3942 ipv6_service_update_router(CFStringRef serviceID, CFDictionaryRef new_service)
3943 {
3944 IFIndex ifindex = 0;
3945 CFStringRef ifname = NULL;
3946 char ntopbuf[INET6_ADDRSTRLEN];
3947 CFDictionaryRef old_service;
3948 struct in6_addr * old_router;
3949 struct in6_addr * new_router;
3950 int s = -1;
3951
3952 old_service = service_dict_get(serviceID, kSCEntNetIPv6);
3953 old_router = ipv6_service_get_router(old_service, &ifindex, &ifname);
3954 new_router = ipv6_service_get_router(new_service, &ifindex, &ifname);
3955 if (ifname == NULL || ifindex == 0) {
3956 return;
3957 }
3958 s = inet6_dgram_socket();
3959 if (s < 0) {
3960 my_log(LOG_ERR,
3961 "IPMonitor: ipv6_service_update_router: socket failed, %s",
3962 strerror(errno));
3963 goto done;
3964 }
3965 /* remove the old router if it was defined */
3966 if (old_router != NULL
3967 && (new_router == NULL
3968 || !IN6_ARE_ADDR_EQUAL(old_router, new_router))) {
3969 if (siocdrdel_in6(s, ifindex, old_router) < 0) {
3970 if (errno != EINVAL
3971 || (S_IPMonitor_debug & kDebugFlag1) != 0) {
3972 my_log((errno == EINVAL) ? LOG_DEBUG : LOG_ERR,
3973 "IPMonitor: siocdrdel_in6(%@, %s) failed, %s",
3974 ifname,
3975 inet_ntop(AF_INET6, old_router,
3976 ntopbuf, sizeof(ntopbuf)),
3977 strerror(errno));
3978 }
3979 }
3980 else if (S_IPMonitor_debug & kDebugFlag1) {
3981 my_log(LOG_DEBUG,
3982 "IPMonitor: %@ removed default route %s",
3983 ifname,
3984 inet_ntop(AF_INET6, old_router, ntopbuf, sizeof(ntopbuf)));
3985 }
3986 }
3987 /* add the new router if it is defined */
3988 if (new_router != NULL
3989 && (old_router == NULL
3990 || !IN6_ARE_ADDR_EQUAL(old_router, new_router))) {
3991 if (siocdradd_in6(s, ifindex, new_router, 0) < 0) {
3992 if (errno != EINVAL
3993 || (S_IPMonitor_debug & kDebugFlag1) != 0) {
3994 my_log((errno == EINVAL) ? LOG_DEBUG : LOG_ERR,
3995 "IPMonitor: siocdradd_in6(%@, %s) failed, %s",
3996 ifname,
3997 inet_ntop(AF_INET6, new_router,
3998 ntopbuf, sizeof(ntopbuf)),
3999 strerror(errno));
4000 }
4001 }
4002 else if (S_IPMonitor_debug & kDebugFlag1) {
4003 my_log(LOG_DEBUG,
4004 "IPMonitor: %@ added default route %s",
4005 ifname,
4006 inet_ntop(AF_INET6, new_router, ntopbuf, sizeof(ntopbuf)));
4007 }
4008 }
4009 close(s);
4010
4011 done:
4012 return;
4013 }
4014 #endif /* !TARGET_IPHONE_SIMULATOR */
4015
4016 #define ALLOW_EMPTY_STRING 0x1
4017
4018 static CF_RETURNS_RETAINED CFTypeRef
4019 sanitize_prop(CFTypeRef val, uint32_t flags)
4020 {
4021 if (val != NULL) {
4022 if (isA_CFString(val)) {
4023 CFMutableStringRef str;
4024
4025 str = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)val);
4026 CFStringTrimWhitespace(str);
4027 if (!(flags & ALLOW_EMPTY_STRING) && (CFStringGetLength(str) == 0)) {
4028 CFRelease(str);
4029 str = NULL;
4030 }
4031 val = str;
4032 } else {
4033 CFRetain(val);
4034 }
4035 }
4036
4037 return val;
4038 }
4039
4040 static void
4041 merge_array_prop(CFMutableDictionaryRef dict,
4042 CFStringRef key,
4043 CFDictionaryRef state_dict,
4044 CFDictionaryRef setup_dict,
4045 uint32_t flags,
4046 Boolean append)
4047 {
4048 CFMutableArrayRef merge_prop;
4049 CFArrayRef setup_prop = NULL;
4050 CFArrayRef state_prop = NULL;
4051
4052 if (setup_dict != NULL) {
4053 setup_prop = isA_CFArray(CFDictionaryGetValue(setup_dict, key));
4054 }
4055 if (state_dict != NULL) {
4056 state_prop = isA_CFArray(CFDictionaryGetValue(state_dict, key));
4057 }
4058
4059 if ((setup_prop == NULL) && (state_prop == NULL)) {
4060 return;
4061 }
4062
4063 merge_prop = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
4064 if (setup_prop != NULL) {
4065 CFIndex i;
4066 CFIndex n;
4067
4068 n = CFArrayGetCount(setup_prop);
4069 for (i = 0; i < n; i++) {
4070 CFTypeRef val;
4071
4072 val = CFArrayGetValueAtIndex(setup_prop, i);
4073 val = sanitize_prop(val, flags);
4074 if (val != NULL) {
4075 CFArrayAppendValue(merge_prop, val);
4076 CFRelease(val);
4077 }
4078 }
4079 }
4080 if (state_prop != NULL
4081 && (setup_prop == NULL || S_append_state)) {
4082 CFIndex i;
4083 CFIndex n;
4084 CFRange setup_range = CFRangeMake(0, CFArrayGetCount(merge_prop));
4085
4086 n = CFArrayGetCount(state_prop);
4087 for (i = 0; i < n; i++) {
4088 CFTypeRef val;
4089
4090 val = CFArrayGetValueAtIndex(state_prop, i);
4091 val = sanitize_prop(val, flags);
4092 if (val != NULL) {
4093 if (append || !CFArrayContainsValue(merge_prop, setup_range, val)) {
4094 CFArrayAppendValue(merge_prop, val);
4095 }
4096 CFRelease(val);
4097 }
4098 }
4099 }
4100 if (CFArrayGetCount(merge_prop) > 0) {
4101 CFDictionarySetValue(dict, key, merge_prop);
4102 }
4103 CFRelease(merge_prop);
4104 return;
4105 }
4106
4107 static void
4108 pick_prop(CFMutableDictionaryRef dict,
4109 CFStringRef key,
4110 CFDictionaryRef state_dict,
4111 CFDictionaryRef setup_dict,
4112 uint32_t flags)
4113 {
4114 CFTypeRef val = NULL;
4115
4116 if (setup_dict != NULL) {
4117 val = CFDictionaryGetValue(setup_dict, key);
4118 val = sanitize_prop(val, flags);
4119 }
4120 if (val == NULL && state_dict != NULL) {
4121 val = CFDictionaryGetValue(state_dict, key);
4122 val = sanitize_prop(val, flags);
4123 }
4124 if (val != NULL) {
4125 CFDictionarySetValue(dict, key, val);
4126 CFRelease(val);
4127 }
4128
4129 return;
4130 }
4131
4132 /**
4133 ** GetEntityChangesFunc functions
4134 **/
4135 #define IPV4_ROUTES_N_STATIC 5
4136 #define IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4137 (roundup(IPv4RouteListComputeSize(IPV4_ROUTES_N_STATIC), \
4138 sizeof(uint32_t)) \
4139 / sizeof(uint32_t))
4140
4141 #define IPV4_ROUTES_BUF_DECL(routes) \
4142 IPv4RouteListRef routes; \
4143 uint32_t routes_buf[IPV4_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4144 \
4145 routes = (IPv4RouteListRef)(void *)routes_buf; \
4146 routes->size = IPV4_ROUTES_N_STATIC; \
4147 routes->count = 0; \
4148 routes->flags = 0;
4149
4150 static CFDataRef
4151 IPv4RouteListDataCreate(CFDictionaryRef dict, CFNumberRef rank_assertion)
4152 {
4153 IPv4RouteListRef r;
4154 CFDataRef routes_data;
4155 IPV4_ROUTES_BUF_DECL(routes);
4156
4157 r = IPv4RouteListCreateWithDictionary(routes, dict, rank_assertion);
4158 if (r != NULL) {
4159 routes_data = CFDataCreate(NULL,
4160 (const void *)r,
4161 IPv4RouteListComputeSize(r->count));
4162 if (r != routes) {
4163 free(r);
4164 }
4165 }
4166 else {
4167 routes_data = NULL;
4168 }
4169 return (routes_data);
4170 }
4171 #define IPV6_ROUTES_N_STATIC 3
4172 #define IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32 \
4173 (roundup(IPv6RouteListComputeSize(IPV6_ROUTES_N_STATIC), \
4174 sizeof(uint32_t)) \
4175 / sizeof(uint32_t))
4176
4177 #define IPV6_ROUTES_BUF_DECL(routes) \
4178 IPv6RouteListRef routes; \
4179 uint32_t routes_buf[IPV6_ROUTES_ALIGN_BUF_SIZE_UINT32]; \
4180 \
4181 routes = (IPv6RouteListRef)(void *)routes_buf; \
4182 routes->size = IPV6_ROUTES_N_STATIC; \
4183 routes->count = 0; \
4184 routes->flags = 0;
4185
4186 static CFDataRef
4187 IPv6RouteListDataCreate(CFDictionaryRef dict, CFNumberRef rank_assertion)
4188 {
4189 IPv6RouteListRef r;
4190 CFDataRef routes_data;
4191 IPV6_ROUTES_BUF_DECL(routes);
4192
4193 r = IPv6RouteListCreateWithDictionary(routes, dict, rank_assertion);
4194 if (r != NULL) {
4195 routes_data = CFDataCreate(NULL,
4196 (const void *)r,
4197 IPv6RouteListComputeSize(r->count));
4198 if (r != routes) {
4199 free(r);
4200 }
4201 }
4202 else {
4203 routes_data = NULL;
4204 }
4205 return (routes_data);
4206 }
4207
4208 static CFDictionaryRef
4209 IPDictCreate(int af, CFDictionaryRef state_dict, CFDictionaryRef setup_dict,
4210 CFNumberRef rank_assertion)
4211 {
4212 CFDictionaryRef aggregated_dict = NULL;
4213 CFDictionaryRef dict;
4214 CFMutableDictionaryRef modified_dict = NULL;
4215 CFDataRef routes_data;
4216
4217 dict = state_dict;
4218 if (dict != NULL && setup_dict != NULL) {
4219 /* look for keys in Setup: that override/merge with State: */
4220 CFArrayRef additional_routes;
4221 CFStringRef router;
4222 in_addr router_ip;
4223 CFStringRef router_prop;
4224 CFStringRef route_list_prop;
4225
4226 /* Router */
4227 switch (af) {
4228 case AF_INET:
4229 router_prop = kSCPropNetIPv4Router;
4230 route_list_prop = kSCPropNetIPv4AdditionalRoutes;
4231 break;
4232 default:
4233 case AF_INET6:
4234 router_prop = kSCPropNetIPv6Router;
4235 route_list_prop = kSCPropNetIPv6AdditionalRoutes;
4236 break;
4237 }
4238 router = CFDictionaryGetValue(setup_dict, router_prop);
4239 if (router != NULL
4240 && !cfstring_to_ipvx(af, router, &router_ip, sizeof(router_ip))) {
4241 router = NULL;
4242 }
4243
4244 /* AdditionalRoutes */
4245 additional_routes
4246 = CFDictionaryGetValue(setup_dict, route_list_prop);
4247 additional_routes = isA_CFArray(additional_routes);
4248
4249 if (router != NULL || additional_routes != NULL) {
4250 modified_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
4251 if (router != NULL) {
4252 CFDictionarySetValue(modified_dict,
4253 router_prop,
4254 router);
4255 }
4256 if (additional_routes != NULL) {
4257 CFArrayRef combined_routes = NULL;
4258 CFArrayRef state_routes;
4259
4260 state_routes
4261 = CFDictionaryGetValue(state_dict,
4262 route_list_prop);
4263 if (isA_CFArray(state_routes) != NULL) {
4264 combined_routes
4265 = my_CFArrayCreateCombinedArray(additional_routes,
4266 state_routes);
4267 additional_routes = combined_routes;
4268 }
4269 CFDictionarySetValue(modified_dict,
4270 route_list_prop,
4271 additional_routes);
4272 if (combined_routes != NULL) {
4273 CFRelease(combined_routes);
4274 }
4275 }
4276 dict = modified_dict;
4277 }
4278 }
4279 switch (af) {
4280 case AF_INET:
4281 routes_data = IPv4RouteListDataCreate(dict, rank_assertion);
4282 break;
4283 default:
4284 case AF_INET6:
4285 routes_data = IPv6RouteListDataCreate(dict, rank_assertion);
4286 break;
4287 }
4288 if (routes_data != NULL) {
4289 aggregated_dict = ipdict_create(dict, routes_data);
4290 CFRelease(routes_data);
4291 }
4292 if (modified_dict != NULL) {
4293 CFRelease(modified_dict);
4294 }
4295 return (aggregated_dict);
4296 }
4297
4298 static boolean_t
4299 get_ipv4_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
4300 CFDictionaryRef setup_dict, CFDictionaryRef info)
4301 {
4302 CFDictionaryRef dict = NULL;
4303 boolean_t changed = FALSE;
4304 CFNumberRef rank_assertion = NULL;
4305 CFDictionaryRef service_options;
4306
4307 if (state_dict == NULL) {
4308 goto done;
4309 }
4310 service_options = service_dict_get(serviceID, kSCEntNetService);
4311 if (service_options != NULL) {
4312 rank_assertion
4313 = CFDictionaryGetValue(service_options,
4314 kServiceOptionRankAssertion);
4315 }
4316 dict = IPDictCreate(AF_INET, state_dict, setup_dict, rank_assertion);
4317
4318 done:
4319 changed = service_dict_set(serviceID, kSCEntNetIPv4, dict);
4320 if (dict == NULL) {
4321 /* clean up the rank too */
4322 CFDictionaryRemoveValue(S_ipv4_service_rank_dict, serviceID);
4323 }
4324 my_CFRelease(&dict);
4325 return (changed);
4326 }
4327
4328 static boolean_t
4329 get_ipv6_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
4330 CFDictionaryRef setup_dict, CFDictionaryRef info)
4331 {
4332 CFDictionaryRef dict = NULL;
4333 boolean_t changed = FALSE;
4334 CFNumberRef rank_assertion = NULL;
4335 CFDictionaryRef service_options;
4336
4337 if (state_dict == NULL) {
4338 goto done;
4339 }
4340 service_options = service_dict_get(serviceID, kSCEntNetService);
4341 if (service_options != NULL) {
4342 rank_assertion
4343 = CFDictionaryGetValue(service_options,
4344 kServiceOptionRankAssertion);
4345 }
4346 dict = IPDictCreate(AF_INET6, state_dict, setup_dict, rank_assertion);
4347
4348 done:
4349 #if !TARGET_IPHONE_SIMULATOR
4350 ipv6_service_update_router(serviceID, dict);
4351 #endif /* !TARGET_IPHONE_SIMULATOR */
4352 changed = service_dict_set(serviceID, kSCEntNetIPv6, dict);
4353 if (dict == NULL) {
4354 /* clean up the rank too */
4355 CFDictionaryRemoveValue(S_ipv6_service_rank_dict, serviceID);
4356 }
4357 my_CFRelease(&dict);
4358 return (changed);
4359 }
4360
4361
4362 #ifdef TEST_DNS
4363 __private_extern__ CFDictionaryRef
4364 ipv4_dict_create(CFDictionaryRef state_dict)
4365 {
4366 return (IPDictCreate(AF_INET, state_dict, NULL, NULL));
4367 }
4368
4369 __private_extern__ CFDictionaryRef
4370 ipv6_dict_create(CFDictionaryRef state_dict)
4371 {
4372 return (IPDictCreate(AF_INET6, state_dict, NULL, NULL));
4373 }
4374
4375 #endif /* TEST_DNS */
4376
4377 static void
4378 accumulate_dns_servers(CFArrayRef in_servers, ProtocolFlags active_protos,
4379 CFMutableArrayRef out_servers, CFStringRef interface)
4380 {
4381 CFIndex count;
4382 CFIndex i;
4383
4384 count = CFArrayGetCount(in_servers);
4385 for (i = 0; i < count; i++) {
4386 CFStringRef addr;
4387 struct in6_addr ipv6_addr;
4388 struct in_addr ip_addr;
4389
4390 addr = CFArrayGetValueAtIndex(in_servers, i);
4391 assert(addr != NULL);
4392
4393 if (cfstring_to_ip(addr, &ip_addr)) {
4394 /* IPv4 address */
4395 if ((active_protos & kProtocolFlagsIPv4) == 0
4396 && ntohl(ip_addr.s_addr) != INADDR_LOOPBACK) {
4397 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4398 my_log(LOG_DEBUG,
4399 "IPMonitor: no IPv4 connectivity, "
4400 "ignoring DNS server address " IP_FORMAT,
4401 IP_LIST(&ip_addr));
4402 }
4403 continue;
4404 }
4405
4406 CFRetain(addr);
4407 }
4408 else if (cfstring_to_ip6(addr, &ipv6_addr)) {
4409 /* IPv6 address */
4410 if ((active_protos & kProtocolFlagsIPv6) == 0
4411 && !IN6_IS_ADDR_LOOPBACK(&ipv6_addr)) {
4412 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4413 char ntopbuf[INET6_ADDRSTRLEN];
4414
4415 my_log(LOG_DEBUG,
4416 "IPMonitor: no IPv6 connectivity, "
4417 "ignoring DNS server address %s",
4418 inet_ntop(AF_INET6, &ipv6_addr,
4419 ntopbuf, sizeof(ntopbuf)));
4420 }
4421 continue;
4422 }
4423
4424 if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr) ||
4425 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr))
4426 && (interface != NULL)
4427 && (CFStringFind(addr, CFSTR("%"), 0).location == kCFNotFound)) {
4428 // append interface name to IPv6 link local address
4429 addr = CFStringCreateWithFormat(NULL, NULL,
4430 CFSTR("%@%%%@"),
4431 addr,
4432 interface);
4433 } else {
4434 CFRetain(addr);
4435 }
4436 }
4437 else {
4438 /* bad IP address */
4439 my_log(LOG_NOTICE,
4440 "IPMonitor: ignoring bad DNS server address '%@'",
4441 addr);
4442 continue;
4443 }
4444
4445 /* DNS server is valid and one we want */
4446 CFArrayAppendValue(out_servers, addr);
4447 CFRelease(addr);
4448 }
4449 return;
4450 }
4451
4452 static void
4453 merge_dns_servers(CFMutableDictionaryRef new_dict,
4454 CFArrayRef state_servers,
4455 CFArrayRef setup_servers,
4456 Boolean have_setup,
4457 ProtocolFlags active_protos,
4458 CFStringRef interface)
4459 {
4460 CFMutableArrayRef dns_servers;
4461 Boolean have_dns_setup = FALSE;
4462
4463 if (state_servers == NULL && setup_servers == NULL) {
4464 /* no DNS servers */
4465 return;
4466 }
4467 dns_servers = CFArrayCreateMutable(NULL, 0,
4468 &kCFTypeArrayCallBacks);
4469 if (setup_servers != NULL) {
4470 accumulate_dns_servers(setup_servers, active_protos,
4471 dns_servers, interface);
4472 if (CFArrayGetCount(dns_servers) > 0) {
4473 have_dns_setup = TRUE;
4474 }
4475 }
4476 if ((CFArrayGetCount(dns_servers) == 0 || S_append_state)
4477 && state_servers != NULL) {
4478 accumulate_dns_servers(state_servers, active_protos,
4479 dns_servers, NULL);
4480 }
4481
4482 /*
4483 * Here, we determine whether or not we want all queries for this DNS
4484 * configuration to be bound to the associated network interface.
4485 *
4486 * For dynamically derived network configurations (i.e. from State:)
4487 * this would be the preferred option using the argument "Hey, the
4488 * server told us to use these servers on this network so let's not
4489 * argue".
4490 *
4491 * But, when a DNS configuration has been provided by the user/admin
4492 * via the Network pref pane (i.e. from Setup:) we opt to not force
4493 * binding of the outbound queries. The simplest example why we take
4494 * this stance is with a multi-homing configuration. Consider a system
4495 * with one network service associated with "en0" and a second service
4496 * associated with "en1". The "en0" service has been set higher in
4497 * the network service order so it would be primary but the user/admin
4498 * wants the DNS queries to go to a server only accessible via "en1".
4499 * Without this exception we would take the DNS server addresses from
4500 * the Network pref pane (for "en0") and have the queries bound to
4501 * "en0" where they'd never reach their intended destination (via
4502 * "en1"). So, our exception to the rule is that we will not bind
4503 * user/admin configurations to any specific network interface.
4504 *
4505 * We also add an exception to the "follow the dynamically derived
4506 * network configuration" path for on-the-fly (no Setup: content)
4507 * network services.
4508 */
4509 if (CFArrayGetCount(dns_servers) != 0) {
4510 CFDictionarySetValue(new_dict,
4511 kSCPropNetDNSServerAddresses, dns_servers);
4512 if (have_setup && !have_dns_setup) {
4513 CFDictionarySetValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue);
4514 }
4515 }
4516
4517 my_CFRelease(&dns_servers);
4518 return;
4519 }
4520
4521
4522 static boolean_t
4523 get_dns_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
4524 CFDictionaryRef setup_dict, CFDictionaryRef info)
4525 {
4526 ProtocolFlags active_protos = kProtocolFlagsNone;
4527 boolean_t changed = FALSE;
4528 CFStringRef domain;
4529 Boolean have_setup = FALSE;
4530 CFStringRef interface = NULL;
4531 CFDictionaryRef ipv4;
4532 CFDictionaryRef ipv6;
4533 int i;
4534 const struct {
4535 CFStringRef key;
4536 uint32_t flags;
4537 Boolean append;
4538 } merge_list[] = {
4539 { kSCPropNetDNSSearchDomains, 0, FALSE },
4540 { kSCPropNetDNSSortList, 0, FALSE },
4541 { kSCPropNetDNSSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE },
4542 { kSCPropNetDNSSupplementalMatchOrders, 0, TRUE },
4543 };
4544 CFMutableDictionaryRef new_dict = NULL;
4545 const CFStringRef pick_list[] = {
4546 kSCPropNetDNSDomainName,
4547 kSCPropNetDNSOptions,
4548 kSCPropNetDNSSearchOrder,
4549 kSCPropNetDNSServerPort,
4550 kSCPropNetDNSServerTimeout,
4551 kSCPropNetDNSServiceIdentifier,
4552 kSCPropNetDNSSupplementalMatchDomainsNoSearch,
4553 };
4554
4555 if ((state_dict == NULL) && (setup_dict == NULL)) {
4556 /* there is no DNS content */
4557 goto done;
4558 }
4559
4560 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
4561 if (ipv4 != NULL) {
4562 if (get_service_setup_entity(info, serviceID, kSCEntNetIPv4) != NULL) {
4563 have_setup = TRUE;
4564 }
4565 active_protos |= kProtocolFlagsIPv4;
4566 interface = ipdict_get_ifname(ipv4);
4567 }
4568
4569 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
4570 if (ipv6 != NULL) {
4571 if (!have_setup
4572 && (get_service_setup_entity(info, serviceID, kSCEntNetIPv6)
4573 != NULL)) {
4574 have_setup = TRUE;
4575 }
4576 active_protos |= kProtocolFlagsIPv6;
4577 if (interface == NULL) {
4578 interface = ipdict_get_ifname(ipv6);
4579 }
4580 }
4581
4582
4583 if (active_protos == kProtocolFlagsNone) {
4584 /* there is no IPv4 nor IPv6 */
4585 if (state_dict == NULL) {
4586 /* ... and no DNS content that we care about */
4587 goto done;
4588 }
4589 setup_dict = NULL;
4590 }
4591
4592 /* merge DNS configuration */
4593 new_dict = CFDictionaryCreateMutable(NULL, 0,
4594 &kCFTypeDictionaryKeyCallBacks,
4595 &kCFTypeDictionaryValueCallBacks);
4596
4597 if (active_protos == kProtocolFlagsNone) {
4598 merge_dns_servers(new_dict,
4599 my_CFDictionaryGetArray(state_dict,
4600 kSCPropNetDNSServerAddresses),
4601 NULL,
4602 FALSE,
4603 kProtocolFlagsIPv4 | kProtocolFlagsIPv6,
4604 NULL);
4605 }
4606 else {
4607 merge_dns_servers(new_dict,
4608 my_CFDictionaryGetArray(state_dict,
4609 kSCPropNetDNSServerAddresses),
4610 my_CFDictionaryGetArray(setup_dict,
4611 kSCPropNetDNSServerAddresses),
4612 have_setup,
4613 active_protos,
4614 interface);
4615 }
4616
4617 for (i = 0; i < countof(merge_list); i++) {
4618 merge_array_prop(new_dict,
4619 merge_list[i].key,
4620 state_dict,
4621 setup_dict,
4622 merge_list[i].flags,
4623 merge_list[i].append);
4624 }
4625
4626 for (i = 0; i < countof(pick_list); i++) {
4627 pick_prop(new_dict,
4628 pick_list[i],
4629 state_dict,
4630 setup_dict,
4631 0);
4632 }
4633
4634 if (active_protos == kProtocolFlagsNone) {
4635 /* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
4636 if (CFDictionaryContainsKey(new_dict,
4637 kSCPropNetDNSSupplementalMatchDomains)) {
4638 /* only keep State: supplemental */
4639 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSDomainName);
4640 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchDomains);
4641 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchOrder);
4642 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSortList);
4643
4644 if ((interface == NULL) && (setup_dict == NULL) && (state_dict != NULL)) {
4645 /*
4646 * for supplemental-only configurations, add any scoped (or
4647 * wild-card "*") interface
4648 */
4649 interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName);
4650 }
4651 } else if (CFDictionaryContainsKey(new_dict, kSCPropNetDNSServiceIdentifier) &&
4652 (interface == NULL) &&
4653 (state_dict != NULL)) {
4654 interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName);
4655 } else {
4656 goto done;
4657 }
4658 }
4659
4660 if (CFDictionaryGetCount(new_dict) == 0) {
4661 my_CFRelease(&new_dict);
4662 goto done;
4663 }
4664
4665 if (interface != NULL) {
4666 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
4667 }
4668
4669 if (S_append_state) {
4670 /*
4671 * ensure any specified domain name (e.g. the domain returned by
4672 * a DHCP server) is in the search list.
4673 */
4674 domain = CFDictionaryGetValue(new_dict, kSCPropNetDNSDomainName);
4675 if (isA_CFString(domain)) {
4676 CFArrayRef search;
4677
4678 search = CFDictionaryGetValue(new_dict, kSCPropNetDNSSearchDomains);
4679 if (isA_CFArray(search) &&
4680 !CFArrayContainsValue(search, CFRangeMake(0, CFArrayGetCount(search)), domain)) {
4681 CFMutableArrayRef new_search;
4682
4683 new_search = CFArrayCreateMutableCopy(NULL, 0, search);
4684 CFArrayAppendValue(new_search, domain);
4685 CFDictionarySetValue(new_dict, kSCPropNetDNSSearchDomains, new_search);
4686 my_CFRelease(&new_search);
4687 }
4688 }
4689 }
4690
4691 done:
4692 changed = service_dict_set(serviceID, kSCEntNetDNS, new_dict);
4693 my_CFRelease(&new_dict);
4694 return (changed);
4695 }
4696
4697 static void
4698 merge_dict(const void *key, const void *value, void *context)
4699 {
4700 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context;
4701
4702 CFDictionarySetValue(dict, key, value);
4703 return;
4704 }
4705
4706 #define PROXY_AUTO_DISCOVERY_URL 252
4707
4708 static CF_RETURNS_RETAINED CFStringRef
4709 wpadURL_dhcp(CFDictionaryRef dhcp_options)
4710 {
4711 CFStringRef urlString = NULL;
4712
4713 if (dhcp_options != NULL) {
4714 CFDataRef data;
4715
4716 data = DHCPInfoGetOptionData(dhcp_options, PROXY_AUTO_DISCOVERY_URL);
4717 if (data != NULL) {
4718 CFURLRef url;
4719 const UInt8 *urlBytes;
4720 CFIndex urlLen;
4721
4722 urlBytes = CFDataGetBytePtr(data);
4723 urlLen = CFDataGetLength(data);
4724 while ((urlLen > 0) && (urlBytes[urlLen - 1] == 0)) {
4725 // remove trailing NUL
4726 urlLen--;
4727 }
4728
4729 if (urlLen <= 0) {
4730 return NULL;
4731 }
4732
4733 url = CFURLCreateWithBytes(NULL, urlBytes, urlLen, kCFStringEncodingUTF8, NULL);
4734 if (url != NULL) {
4735 urlString = CFURLGetString(url);
4736 if (urlString != NULL) {
4737 CFRetain(urlString);
4738 }
4739 CFRelease(url);
4740 }
4741 }
4742 }
4743
4744 return urlString;
4745 }
4746
4747 static CF_RETURNS_RETAINED CFStringRef
4748 wpadURL_dns(void)
4749 {
4750 CFURLRef url;
4751 CFStringRef urlString = NULL;
4752
4753 url = CFURLCreateWithString(NULL, CFSTR("http://wpad/wpad.dat"), NULL);
4754 if (url != NULL) {
4755 urlString = CFURLGetString(url);
4756 if (urlString != NULL) {
4757 CFRetain(urlString);
4758 }
4759 CFRelease(url);
4760 }
4761
4762 return urlString;
4763 }
4764
4765 static boolean_t
4766 get_proxies_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
4767 CFDictionaryRef setup_dict, CFDictionaryRef info)
4768 {
4769 ProtocolFlags active_protos = kProtocolFlagsNone;
4770 boolean_t changed = FALSE;
4771 CFStringRef interface = NULL;
4772 CFDictionaryRef ipv4;
4773 CFDictionaryRef ipv6;
4774 CFMutableDictionaryRef new_dict = NULL;
4775 const struct {
4776 CFStringRef key;
4777 uint32_t flags;
4778 Boolean append;
4779 } merge_list[] = {
4780 { kSCPropNetProxiesSupplementalMatchDomains, ALLOW_EMPTY_STRING, TRUE },
4781 { kSCPropNetProxiesSupplementalMatchOrders, 0, TRUE },
4782 };
4783 const struct {
4784 CFStringRef key1; /* an "enable" key */
4785 CFStringRef key2;
4786 CFStringRef key3;
4787 } pick_list[] = {
4788 { kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort },
4789 { kSCPropNetProxiesGopherEnable, kSCPropNetProxiesGopherProxy, kSCPropNetProxiesGopherPort },
4790 { kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort },
4791 { kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort },
4792 { kSCPropNetProxiesRTSPEnable, kSCPropNetProxiesRTSPProxy, kSCPropNetProxiesRTSPPort },
4793 { kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort },
4794 { kSCPropNetProxiesProxyAutoConfigEnable,
4795 kSCPropNetProxiesProxyAutoConfigURLString,
4796 kSCPropNetProxiesProxyAutoConfigJavaScript, },
4797 { kSCPropNetProxiesProxyAutoDiscoveryEnable,
4798 NULL,
4799 NULL, }
4800 };
4801
4802 if ((state_dict == NULL) && (setup_dict == NULL)) {
4803 /* there is no proxy content */
4804 goto done;
4805 }
4806 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
4807 if (ipdict_get_routelist(ipv4) != NULL) {
4808 active_protos |= kProtocolFlagsIPv4;
4809 interface = ipdict_get_ifname(ipv4);
4810 }
4811 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
4812 if (ipdict_get_routelist(ipv6) != NULL) {
4813 active_protos |= kProtocolFlagsIPv6;
4814 if (interface == NULL) {
4815 interface = ipdict_get_ifname(ipv6);
4816 }
4817 }
4818 if (active_protos == kProtocolFlagsNone) {
4819 /* there is no IPv4 nor IPv6 */
4820 if (state_dict == NULL) {
4821 /* ... and no proxy content that we care about */
4822 goto done;
4823 }
4824 setup_dict = NULL;
4825 }
4826
4827 if ((setup_dict != NULL) && (state_dict != NULL)) {
4828 CFIndex i;
4829 CFMutableDictionaryRef setup_copy;
4830
4831 /*
4832 * Merge the per-service "Setup:" and "State:" proxy information with
4833 * the "Setup:" information always taking precedence. Additionally,
4834 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
4835 * Port) is defined than all of the values for that group will be
4836 * used. That is, we don't allow mixing some of the values from
4837 * the "Setup:" keys and others from the "State:" keys.
4838 */
4839 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
4840 for (i = 0; i < countof(merge_list); i++) {
4841 merge_array_prop(new_dict,
4842 merge_list[i].key,
4843 state_dict,
4844 setup_dict,
4845 merge_list[i].flags,
4846 merge_list[i].append);
4847 }
4848
4849 setup_copy = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
4850 for (i = 0; i < countof(pick_list); i++) {
4851 if (CFDictionaryContainsKey(setup_copy, pick_list[i].key1)) {
4852 /*
4853 * if a "Setup:" enabled key has been provided than we want to
4854 * ignore all of the "State:" keys
4855 */
4856 CFDictionaryRemoveValue(new_dict, pick_list[i].key1);
4857 if (pick_list[i].key2 != NULL) {
4858 CFDictionaryRemoveValue(new_dict, pick_list[i].key2);
4859 }
4860 if (pick_list[i].key3 != NULL) {
4861 CFDictionaryRemoveValue(new_dict, pick_list[i].key3);
4862 }
4863 } else if (CFDictionaryContainsKey(state_dict, pick_list[i].key1) ||
4864 ((pick_list[i].key2 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key2)) ||
4865 ((pick_list[i].key3 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key3))) {
4866 /*
4867 * if a "Setup:" enabled key has not been provided and we have
4868 * some" "State:" keys than we remove all of of "Setup:" keys
4869 */
4870 CFDictionaryRemoveValue(setup_copy, pick_list[i].key1);
4871 if (pick_list[i].key2 != NULL) {
4872 CFDictionaryRemoveValue(setup_copy, pick_list[i].key2);
4873 }
4874 if (pick_list[i].key3 != NULL) {
4875 CFDictionaryRemoveValue(setup_copy, pick_list[i].key3);
4876 }
4877 }
4878 }
4879
4880 /* merge the "Setup:" keys */
4881 CFDictionaryApplyFunction(setup_copy, merge_dict, new_dict);
4882 CFRelease(setup_copy);
4883 }
4884 else if (setup_dict != NULL) {
4885 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
4886 }
4887 else if (state_dict != NULL) {
4888 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
4889 }
4890
4891 if ((new_dict != NULL) && (CFDictionaryGetCount(new_dict) == 0)) {
4892 CFRelease(new_dict);
4893 new_dict = NULL;
4894 }
4895
4896 if ((new_dict != NULL) && (interface != NULL)) {
4897 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
4898 }
4899
4900 /* process WPAD */
4901 if (new_dict != NULL) {
4902 CFDictionaryRef dhcp_options;
4903 CFNumberRef num;
4904 CFNumberRef wpad = NULL;
4905 int wpadEnabled = 0;
4906 CFStringRef wpadURL = NULL;
4907
4908 if (CFDictionaryGetValueIfPresent(new_dict,
4909 kSCPropNetProxiesProxyAutoDiscoveryEnable,
4910 (const void **)&num) &&
4911 isA_CFNumber(num)) {
4912 /* if we have a WPAD key */
4913 wpad = num;
4914 if (!CFNumberGetValue(num, kCFNumberIntType, &wpadEnabled)) {
4915 /* if we don't like the enabled key/value */
4916 wpadEnabled = 0;
4917 }
4918 }
4919
4920 if (wpadEnabled) {
4921 int pacEnabled = 0;
4922
4923 num = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigEnable);
4924 if (!isA_CFNumber(num) ||
4925 !CFNumberGetValue(num, kCFNumberIntType, &pacEnabled)) {
4926 /* if we don't like the enabled key/value */
4927 pacEnabled = 0;
4928 }
4929
4930 if (pacEnabled) {
4931 CFStringRef pacURL;
4932
4933 pacURL = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigURLString);
4934 if (pacURL != NULL) {
4935 if (!isA_CFString(pacURL)) {
4936 /* if we don't like the PAC URL */
4937 pacEnabled = 0;
4938 }
4939 } else {
4940 CFStringRef pacJS;
4941
4942 pacJS = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigJavaScript);
4943 if (!isA_CFString(pacJS)) {
4944 /* if we don't have (or like) the PAC JavaScript */
4945 pacEnabled = 0;
4946 }
4947 }
4948 }
4949
4950 if (pacEnabled) {
4951 /*
4952 * we already have a PAC URL so disable WPAD.
4953 */
4954 wpadEnabled = 0;
4955 goto setWPAD;
4956 }
4957
4958 /*
4959 * if WPAD is enabled and we don't already have a PAC URL then
4960 * we check for a DHCP provided URL. If not available, we use
4961 * a PAC URL pointing to a well-known file (wpad.dat) on a
4962 * well-known host (wpad.<domain>).
4963 */
4964 dhcp_options = get_service_state_entity(info, serviceID, kSCEntNetDHCP);
4965 wpadURL = wpadURL_dhcp(dhcp_options);
4966 if (wpadURL == NULL) {
4967 wpadURL = wpadURL_dns();
4968 }
4969 if (wpadURL == NULL) {
4970 wpadEnabled = 0; /* if we don't have a WPAD URL */
4971 goto setWPAD;
4972 }
4973
4974 pacEnabled = 1;
4975 num = CFNumberCreate(NULL, kCFNumberIntType, &pacEnabled);
4976 CFDictionarySetValue(new_dict,
4977 kSCPropNetProxiesProxyAutoConfigEnable,
4978 num);
4979 CFRelease(num);
4980 CFDictionarySetValue(new_dict,
4981 kSCPropNetProxiesProxyAutoConfigURLString,
4982 wpadURL);
4983 CFRelease(wpadURL);
4984 }
4985
4986 setWPAD:
4987 if (wpad != NULL) {
4988 num = CFNumberCreate(NULL, kCFNumberIntType, &wpadEnabled);
4989 CFDictionarySetValue(new_dict,
4990 kSCPropNetProxiesProxyAutoDiscoveryEnable,
4991 num);
4992 CFRelease(num);
4993 }
4994 }
4995
4996 done:
4997 changed = service_dict_set(serviceID, kSCEntNetProxies, new_dict);
4998 my_CFRelease(&new_dict);
4999 return (changed);
5000 }
5001
5002 #if !TARGET_OS_IPHONE
5003 static boolean_t
5004 get_smb_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
5005 CFDictionaryRef setup_dict, CFDictionaryRef info)
5006 {
5007 boolean_t changed = FALSE;
5008 int i;
5009 CFMutableDictionaryRef new_dict = NULL;
5010 const CFStringRef pick_list[] = {
5011 kSCPropNetSMBNetBIOSName,
5012 kSCPropNetSMBNetBIOSNodeType,
5013 #ifdef ADD_NETBIOS_SCOPE
5014 kSCPropNetSMBNetBIOSScope,
5015 #endif // ADD_NETBIOS_SCOPE
5016 kSCPropNetSMBWorkgroup,
5017 };
5018
5019 if (state_dict == NULL && setup_dict == NULL) {
5020 /* there is no SMB */
5021 goto done;
5022 }
5023 if (service_dict_get(serviceID, kSCEntNetIPv4) == NULL
5024 && service_dict_get(serviceID, kSCEntNetIPv6) == NULL) {
5025 /* there is no IPv4 or IPv6 */
5026 goto done;
5027 }
5028
5029 /* merge SMB configuration */
5030 new_dict = CFDictionaryCreateMutable(NULL, 0,
5031 &kCFTypeDictionaryKeyCallBacks,
5032 &kCFTypeDictionaryValueCallBacks);
5033 merge_array_prop(new_dict,
5034 kSCPropNetSMBWINSAddresses,
5035 state_dict,
5036 setup_dict,
5037 0,
5038 FALSE);
5039 for (i = 0; i < countof(pick_list); i++) {
5040 pick_prop(new_dict,
5041 pick_list[i],
5042 state_dict,
5043 setup_dict,
5044 0);
5045 }
5046
5047 if (CFDictionaryGetCount(new_dict) == 0) {
5048 my_CFRelease(&new_dict);
5049 goto done;
5050 }
5051
5052 done:
5053 changed = service_dict_set(serviceID, kSCEntNetSMB, new_dict);
5054 my_CFRelease(&new_dict);
5055 return (changed);
5056 }
5057 #endif /* !TARGET_OS_IPHONE */
5058
5059 static CFStringRef
5060 services_info_get_interface(CFDictionaryRef services_info,
5061 CFStringRef serviceID)
5062 {
5063 CFStringRef interface = NULL;
5064 CFDictionaryRef ipv4_dict;
5065
5066 ipv4_dict = get_service_state_entity(services_info, serviceID,
5067 kSCEntNetIPv4);
5068 if (ipv4_dict != NULL) {
5069 interface = CFDictionaryGetValue(ipv4_dict, kSCPropInterfaceName);
5070 }
5071 else {
5072 CFDictionaryRef ipv6_dict;
5073
5074 ipv6_dict = get_service_state_entity(services_info, serviceID,
5075 kSCEntNetIPv6);
5076 if (ipv6_dict != NULL) {
5077 interface = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
5078 }
5079 }
5080 return (interface);
5081 }
5082
5083
5084 static const struct {
5085 const CFStringRef * entityName;
5086 const CFStringRef * statusKey;
5087 } transientServiceInfo[] = {
5088 { &kSCEntNetIPSec, &kSCPropNetIPSecStatus },
5089 { &kSCEntNetPPP, &kSCPropNetPPPStatus },
5090 { &kSCEntNetVPN, &kSCPropNetVPNStatus },
5091 };
5092
5093 static Boolean
5094 get_transient_status_changes(CFStringRef serviceID,
5095 CFDictionaryRef services_info)
5096 {
5097 boolean_t changed = FALSE;
5098 int i;
5099
5100 for (i = 0; i < countof(transientServiceInfo); i++) {
5101 CFDictionaryRef dict;
5102 CFNumberRef status = NULL;
5103 CFMutableDictionaryRef ts_dict = NULL;
5104
5105 dict = get_service_state_entity(services_info, serviceID,
5106 *transientServiceInfo[i].entityName);
5107
5108 if (dict != NULL) {
5109 status = CFDictionaryGetValue(dict,
5110 *transientServiceInfo[i].statusKey);
5111 }
5112
5113 if (isA_CFNumber(status) != NULL) {
5114 ts_dict = CFDictionaryCreateMutable(NULL,
5115 0,
5116 &kCFTypeDictionaryKeyCallBacks,
5117 &kCFTypeDictionaryValueCallBacks);
5118 CFDictionaryAddValue(ts_dict,
5119 *transientServiceInfo[i].statusKey,
5120 status);
5121 }
5122
5123 if (service_dict_set(serviceID, *transientServiceInfo[i].entityName,
5124 ts_dict)) {
5125 changed = TRUE;
5126 }
5127
5128 if (ts_dict != NULL) {
5129 CFRelease(ts_dict);
5130 }
5131 }
5132 return (changed);
5133 }
5134
5135 static boolean_t
5136 service_is_expensive(CFStringRef serviceID, CFDictionaryRef services_info)
5137 {
5138 CFStringRef ifname;
5139 boolean_t is_expensive = FALSE;
5140
5141 ifname = services_info_get_interface(services_info, serviceID);
5142 if (ifname != NULL) {
5143 CFDictionaryRef if_dict;
5144 CFStringRef key;
5145
5146 key = interface_entity_key_copy(ifname, kSCEntNetLink);
5147 if_dict = CFDictionaryGetValue(services_info, key);
5148 CFRelease(key);
5149 if (isA_CFDictionary(if_dict) != NULL) {
5150 CFBooleanRef expensive;
5151
5152 expensive = CFDictionaryGetValue(if_dict, kSCPropNetLinkExpensive);
5153 if (isA_CFBoolean(expensive) != NULL
5154 && CFBooleanGetValue(expensive)) {
5155 is_expensive = TRUE;
5156 }
5157 }
5158 }
5159 return (is_expensive);
5160 }
5161
5162 static boolean_t
5163 get_rank_changes(CFStringRef serviceID, CFDictionaryRef state_options,
5164 CFDictionaryRef setup_options, CFDictionaryRef services_info)
5165 {
5166 boolean_t changed = FALSE;
5167 boolean_t ip_is_coupled = FALSE;
5168 CFMutableDictionaryRef new_dict = NULL;
5169 Rank rank_assertion = kRankAssertionDefault;
5170 Boolean rank_assertion_is_set = FALSE;
5171 CFStringRef setup_rank = NULL;
5172 CFStringRef state_rank = NULL;
5173
5174
5175 if (state_options != NULL) {
5176 CFBooleanRef coupled;
5177
5178 state_rank
5179 = CFDictionaryGetValue(state_options, kSCPropNetServicePrimaryRank);
5180 state_rank = isA_CFString(state_rank);
5181 coupled = CFDictionaryGetValue(state_options, kIPIsCoupled);
5182 if (isA_CFBoolean(coupled) != NULL && CFBooleanGetValue(coupled)) {
5183 ip_is_coupled = TRUE;
5184 }
5185 }
5186 if (setup_options != NULL) {
5187 CFBooleanRef coupled;
5188
5189 setup_rank
5190 = CFDictionaryGetValue(setup_options, kSCPropNetServicePrimaryRank);
5191 setup_rank = isA_CFString(setup_rank);
5192 coupled = CFDictionaryGetValue(setup_options, kIPIsCoupled);
5193 if (isA_CFBoolean(coupled) != NULL && CFBooleanGetValue(coupled)) {
5194 ip_is_coupled = TRUE;
5195 }
5196 }
5197
5198 if (ip_is_coupled == FALSE) {
5199 ip_is_coupled = service_is_expensive(serviceID, services_info);
5200 }
5201 if (setup_rank != NULL || state_rank != NULL) {
5202 /* rank assertion is set on the service */
5203 Rank setup_assertion;
5204 Rank state_assertion;
5205 Boolean state_assertion_is_set = FALSE;
5206
5207 setup_assertion = PrimaryRankGetRankAssertion(setup_rank, NULL);
5208 state_assertion = PrimaryRankGetRankAssertion(state_rank,
5209 &state_assertion_is_set);
5210 if (setup_assertion > state_assertion) {
5211 rank_assertion = setup_assertion;
5212 rank_assertion_is_set = TRUE;
5213 }
5214 else if (state_assertion_is_set) {
5215 rank_assertion = state_assertion;
5216 rank_assertion_is_set = TRUE;
5217 }
5218 }
5219
5220 if (rank_assertion_is_set == FALSE) {
5221 /* check for a rank assertion on the interface */
5222 CFStringRef interface;
5223
5224 interface = services_info_get_interface(services_info, serviceID);
5225 if (interface != NULL) {
5226 CFNumberRef if_rank = NULL;
5227
5228 if (S_if_rank_dict != NULL) {
5229 if_rank = CFDictionaryGetValue(S_if_rank_dict, interface);
5230 }
5231 rank_assertion
5232 = InterfaceRankGetRankAssertion(if_rank,
5233 &rank_assertion_is_set);
5234 if (S_IPMonitor_debug & kDebugFlag1) {
5235 my_log(LOG_DEBUG,
5236 "serviceID %@ interface %@ rank = %@",
5237 serviceID, interface, if_rank);
5238 }
5239 }
5240 }
5241
5242
5243 if (rank_assertion_is_set || ip_is_coupled) {
5244 new_dict = CFDictionaryCreateMutable(NULL, 0,
5245 &kCFTypeDictionaryKeyCallBacks,
5246 &kCFTypeDictionaryValueCallBacks);
5247 if (rank_assertion_is_set) {
5248 CFNumberRef new_rank;
5249
5250 new_rank = CFNumberCreate(NULL, kCFNumberSInt32Type,
5251 (const void *)&rank_assertion);
5252 CFDictionarySetValue(new_dict, kServiceOptionRankAssertion,
5253 new_rank);
5254 CFRelease(new_rank);
5255 }
5256 if (ip_is_coupled) {
5257 CFDictionarySetValue(new_dict, kIPIsCoupled, kCFBooleanTrue);
5258 }
5259 }
5260 changed = service_dict_set(serviceID, kSCEntNetService, new_dict);
5261 my_CFRelease(&new_dict);
5262 return (changed);
5263 }
5264
5265 static void
5266 add_service_keys(CFStringRef serviceID,
5267 CFMutableArrayRef keys, CFMutableArrayRef patterns)
5268 {
5269 int i;
5270 CFStringRef key;
5271
5272 if (CFEqual(serviceID, kSCCompAnyRegex)) {
5273 keys = patterns;
5274 }
5275
5276 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
5277 key = setup_service_key(serviceID, *entityTypeNames[i]);
5278 CFArrayAppendValue(keys, key);
5279 CFRelease(key);
5280 key = state_service_key(serviceID, *entityTypeNames[i]);
5281 CFArrayAppendValue(keys, key);
5282 CFRelease(key);
5283 }
5284
5285 key = state_service_key(serviceID, kSCEntNetDHCP);
5286 CFArrayAppendValue(patterns, key);
5287 CFRelease(key);
5288
5289 key = setup_service_key(serviceID, NULL);
5290 CFArrayAppendValue(patterns, key);
5291 CFRelease(key);
5292 key = state_service_key(serviceID, NULL);
5293 CFArrayAppendValue(patterns, key);
5294 CFRelease(key);
5295
5296 return;
5297 }
5298
5299 static void
5300 add_transient_status_keys(CFStringRef service_id, CFMutableArrayRef patterns)
5301 {
5302 int i;
5303
5304 for (i = 0; i < countof(transientServiceInfo); i++) {
5305 CFStringRef pattern;
5306
5307 pattern = state_service_key(service_id,
5308 *transientServiceInfo[i].entityName);
5309 CFArrayAppendValue(patterns, pattern);
5310 CFRelease(pattern);
5311 }
5312
5313 return;
5314 }
5315
5316 static const CFStringRef *reachabilitySetupKeys[] = {
5317 &kSCEntNetPPP,
5318 &kSCEntNetInterface,
5319 &kSCEntNetIPv4,
5320 &kSCEntNetIPv6,
5321 };
5322
5323
5324 static void
5325 add_reachability_patterns(CFMutableArrayRef patterns)
5326 {
5327 int i;
5328
5329 for (i = 0; i < countof(reachabilitySetupKeys); i++) {
5330 CFStringRef pattern;
5331 pattern = setup_service_key(kSCCompAnyRegex, *reachabilitySetupKeys[i]);
5332 CFArrayAppendValue(patterns, pattern);
5333 CFRelease(pattern);
5334 }
5335 }
5336
5337
5338 static void
5339 add_vpn_pattern(CFMutableArrayRef patterns)
5340 {
5341 CFStringRef pattern;
5342
5343 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
5344 CFArrayAppendValue(patterns, pattern);
5345 CFRelease(pattern);
5346 }
5347
5348 static void
5349 add_interface_link_pattern(CFMutableArrayRef patterns)
5350 {
5351 CFStringRef pattern;
5352
5353 pattern = interface_entity_key_copy(kSCCompAnyRegex, kSCEntNetLink);
5354 CFArrayAppendValue(patterns, pattern);
5355 CFRelease(pattern);
5356 }
5357
5358 static CFDictionaryRef
5359 services_info_copy(SCDynamicStoreRef session, CFArrayRef service_list)
5360 {
5361 CFIndex count;
5362 CFMutableArrayRef get_keys;
5363 CFMutableArrayRef get_patterns;
5364 CFDictionaryRef info;
5365 CFIndex s;
5366
5367 count = CFArrayGetCount(service_list);
5368 get_keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5369 get_patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5370
5371 CFArrayAppendValue(get_keys, S_setup_global_ipv4);
5372 CFArrayAppendValue(get_keys, S_multicast_resolvers);
5373 CFArrayAppendValue(get_keys, S_private_resolvers);
5374
5375 for (s = 0; s < count; s++) {
5376 CFStringRef serviceID = CFArrayGetValueAtIndex(service_list, s);
5377
5378 add_service_keys(serviceID, get_keys, get_patterns);
5379 add_transient_status_keys(serviceID, get_keys);
5380 }
5381
5382 add_reachability_patterns(get_patterns);
5383
5384 add_vpn_pattern(get_patterns);
5385
5386 add_interface_link_pattern(get_patterns);
5387
5388 info = SCDynamicStoreCopyMultiple(session, get_keys, get_patterns);
5389 my_CFRelease(&get_keys);
5390 my_CFRelease(&get_patterns);
5391 return (info);
5392 }
5393
5394 #if !TARGET_IPHONE_SIMULATOR
5395
5396 static int
5397 multicast_route(int sockfd, int cmd)
5398 {
5399 IPv4Route route;
5400
5401 bzero(&route, sizeof(route));
5402 route.dest.s_addr = htonl(INADDR_UNSPEC_GROUP);
5403 route.mask.s_addr = htonl(IN_CLASSD_NET);
5404 route.ifindex = lo0_ifindex();
5405 return (IPv4RouteApply((RouteRef)&route, cmd, sockfd));
5406 }
5407
5408 #endif /* !TARGET_IPHONE_SIMULATOR */
5409
5410 #if !TARGET_IPHONE_SIMULATOR
5411
5412 static boolean_t
5413 set_ipv6_default_interface(IFIndex ifindex)
5414 {
5415 struct in6_ndifreq ndifreq;
5416 int sock;
5417 boolean_t success = FALSE;
5418
5419 bzero((char *)&ndifreq, sizeof(ndifreq));
5420 strlcpy(ndifreq.ifname, kLoopbackInterface, sizeof(ndifreq.ifname));
5421 if (ifindex != 0) {
5422 ndifreq.ifindex = ifindex;
5423 }
5424 else {
5425 ndifreq.ifindex = lo0_ifindex();
5426 }
5427 sock = inet6_dgram_socket();
5428 if (sock == -1) {
5429 my_log(LOG_ERR,
5430 "IPMonitor: set_ipv6_default_interface: socket failed, %s",
5431 strerror(errno));
5432 }
5433 else {
5434 if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) == -1) {
5435 my_log(LOG_ERR,
5436 "IPMonitor: ioctl(SIOCSDEFIFACE_IN6) failed, %s",
5437 strerror(errno));
5438 }
5439 else {
5440 success = TRUE;
5441 }
5442 close(sock);
5443 }
5444 return (success);
5445 }
5446
5447 #endif /* !TARGET_IPHONE_SIMULATOR */
5448
5449 #if !TARGET_OS_IPHONE
5450 static __inline__ void
5451 empty_dns()
5452 {
5453 (void)unlink(VAR_RUN_RESOLV_CONF);
5454 }
5455
5456 static void
5457 set_dns(CFArrayRef val_search_domains,
5458 CFStringRef val_domain_name,
5459 CFArrayRef val_servers,
5460 CFArrayRef val_sortlist)
5461 {
5462 FILE * f = fopen(VAR_RUN_RESOLV_CONF "-", "w");
5463
5464 /* publish new resolv.conf */
5465 if (f) {
5466 CFIndex i;
5467 CFIndex n;
5468
5469 SCPrint(TRUE, f, CFSTR("#\n"));
5470 SCPrint(TRUE, f, CFSTR("# Mac OS X Notice\n"));
5471 SCPrint(TRUE, f, CFSTR("#\n"));
5472 SCPrint(TRUE, f, CFSTR("# This file is not used by the host name and address resolution\n"));
5473 SCPrint(TRUE, f, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
5474 SCPrint(TRUE, f, CFSTR("# this Mac OS X system.\n"));
5475 SCPrint(TRUE, f, CFSTR("#\n"));
5476 SCPrint(TRUE, f, CFSTR("# This file is automatically generated.\n"));
5477 SCPrint(TRUE, f, CFSTR("#\n"));
5478
5479 if (isA_CFArray(val_search_domains)) {
5480 SCPrint(TRUE, f, CFSTR("search"));
5481 n = CFArrayGetCount(val_search_domains);
5482 for (i = 0; i < n; i++) {
5483 CFStringRef domain;
5484
5485 domain = CFArrayGetValueAtIndex(val_search_domains, i);
5486 if (isA_CFString(domain)) {
5487 SCPrint(TRUE, f, CFSTR(" %@"), domain);
5488 }
5489 }
5490 SCPrint(TRUE, f, CFSTR("\n"));
5491 }
5492 else if (isA_CFString(val_domain_name)) {
5493 SCPrint(TRUE, f, CFSTR("domain %@\n"), val_domain_name);
5494 }
5495
5496 if (isA_CFArray(val_servers)) {
5497 n = CFArrayGetCount(val_servers);
5498 for (i = 0; i < n; i++) {
5499 CFStringRef nameserver;
5500
5501 nameserver = CFArrayGetValueAtIndex(val_servers, i);
5502 if (isA_CFString(nameserver)) {
5503 SCPrint(TRUE, f, CFSTR("nameserver %@\n"), nameserver);
5504 }
5505 }
5506 }
5507
5508 if (isA_CFArray(val_sortlist)) {
5509 SCPrint(TRUE, f, CFSTR("sortlist"));
5510 n = CFArrayGetCount(val_sortlist);
5511 for (i = 0; i < n; i++) {
5512 CFStringRef address;
5513
5514 address = CFArrayGetValueAtIndex(val_sortlist, i);
5515 if (isA_CFString(address)) {
5516 SCPrint(TRUE, f, CFSTR(" %@"), address);
5517 }
5518 }
5519 SCPrint(TRUE, f, CFSTR("\n"));
5520 }
5521
5522 fclose(f);
5523 rename(VAR_RUN_RESOLV_CONF "-", VAR_RUN_RESOLV_CONF);
5524 }
5525 return;
5526 }
5527 #endif /* !TARGET_OS_IPHONE */
5528
5529 static boolean_t
5530 service_get_ip_is_coupled(CFStringRef serviceID)
5531 {
5532 CFDictionaryRef dict;
5533 boolean_t ip_is_coupled = FALSE;
5534
5535 dict = service_dict_get(serviceID, kSCEntNetService);
5536 if (dict != NULL) {
5537 if (CFDictionaryContainsKey(dict, kIPIsCoupled)) {
5538 ip_is_coupled = TRUE;
5539 }
5540 }
5541 return (ip_is_coupled);
5542 }
5543
5544 static CFStringRef
5545 my_CFStringCreateWithInAddr(struct in_addr ip)
5546 {
5547 CFStringRef str;
5548
5549 str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT), IP_LIST(&ip));
5550 return (str);
5551 }
5552
5553 static CFStringRef
5554 my_CFStringCreateWithIn6Addr(const struct in6_addr * ip)
5555 {
5556 char ntopbuf[INET6_ADDRSTRLEN];
5557
5558 (void)inet_ntop(AF_INET6, ip, ntopbuf, sizeof(ntopbuf));
5559 return (CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), ntopbuf));
5560 }
5561
5562 /*
5563 * Function: update_ipv4
5564 * Purpose:
5565 * Update the IPv4 configuration based on the latest information.
5566 * Publish the State:/Network/Global/IPv4 information, and update the
5567 * IPv4 routing table.
5568 */
5569 static void
5570 update_ipv4(CFStringRef primary,
5571 IPv4RouteListRef new_routelist,
5572 keyChangeListRef keys)
5573 {
5574 #if !TARGET_IPHONE_SIMULATOR
5575 int sockfd;
5576 #endif /* !TARGET_IPHONE_SIMULATOR */
5577
5578 if (keys != NULL) {
5579 if (new_routelist != NULL && primary != NULL) {
5580 const char * ifn_p = NULL;
5581 char ifname[IFNAMSIZ];
5582 IPv4RouteRef r;
5583 CFMutableDictionaryRef dict = NULL;
5584
5585 dict = CFDictionaryCreateMutable(NULL, 0,
5586 &kCFTypeDictionaryKeyCallBacks,
5587 &kCFTypeDictionaryValueCallBacks);
5588 /* the first entry is the default route */
5589 r = new_routelist->list;
5590 if (r->gateway.s_addr != 0) {
5591 CFStringRef str;
5592
5593 str = my_CFStringCreateWithInAddr(r->gateway);
5594 CFDictionarySetValue(dict, kSCPropNetIPv4Router, str);
5595 CFRelease(str);
5596 }
5597 ifn_p = my_if_indextoname(r->ifindex, ifname);
5598 if (ifn_p != NULL) {
5599 CFStringRef ifname_cf;
5600
5601 ifname_cf = CFStringCreateWithCString(NULL,
5602 ifn_p,
5603 kCFStringEncodingASCII);
5604 if (ifname_cf != NULL) {
5605 CFDictionarySetValue(dict,
5606 kSCDynamicStorePropNetPrimaryInterface,
5607 ifname_cf);
5608 CFRelease(ifname_cf);
5609 }
5610 }
5611 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
5612 primary);
5613 keyChangeListSetValue(keys, S_state_global_ipv4, dict);
5614 CFRelease(dict);
5615 }
5616 else {
5617 keyChangeListRemoveValue(keys, S_state_global_ipv4);
5618 }
5619 }
5620
5621 #if !TARGET_IPHONE_SIMULATOR
5622 sockfd = open_routing_socket();
5623 if (sockfd != -1) {
5624 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5625 if (S_ipv4_routelist == NULL) {
5626 my_log(LOG_DEBUG, "Old Routes = <none>");
5627 }
5628 else {
5629 my_log(LOG_DEBUG, "Old Routes = ");
5630 IPv4RouteListLog(LOG_DEBUG, S_ipv4_routelist);
5631 }
5632 if (new_routelist == NULL) {
5633 my_log(LOG_DEBUG, "New Routes = <none>");
5634 }
5635 else {
5636 my_log(LOG_DEBUG, "New Routes = ");
5637 IPv4RouteListLog(LOG_DEBUG, new_routelist);
5638 }
5639 }
5640 /* go through routelist and bind any unbound routes */
5641 IPv4RouteListFinalize(new_routelist);
5642 IPv4RouteListApply(S_ipv4_routelist, new_routelist, sockfd);
5643 if (new_routelist != NULL) {
5644 (void)multicast_route(sockfd, RTM_DELETE);
5645 }
5646 else {
5647 (void)multicast_route(sockfd, RTM_ADD);
5648 }
5649 close(sockfd);
5650 }
5651 if (S_ipv4_routelist != NULL) {
5652 free(S_ipv4_routelist);
5653 }
5654 S_ipv4_routelist = new_routelist;
5655 #else /* !TARGET_IPHONE_SIMULATOR */
5656 if (new_routelist != NULL) {
5657 free(new_routelist);
5658 }
5659 #endif /* !TARGET_IPHONE_SIMULATOR */
5660
5661 return;
5662 }
5663
5664 /*
5665 * Function: update_ipv6
5666 * Purpose:
5667 * Update the IPv6 configuration based on the latest information.
5668 * Publish the State:/Network/Global/IPv6 information, and update the
5669 * IPv6 routing table.
5670 */
5671 static void
5672 update_ipv6(CFStringRef primary,
5673 IPv6RouteListRef new_routelist,
5674 keyChangeListRef keys)
5675 {
5676 #if !TARGET_IPHONE_SIMULATOR
5677 int sockfd;
5678 #endif /* !TARGET_IPHONE_SIMULATOR */
5679
5680 if (keys != NULL) {
5681 if (new_routelist != NULL && primary != NULL) {
5682 const char * ifn_p = NULL;
5683 char ifname[IFNAMSIZ];
5684 IPv6RouteRef r;
5685 CFMutableDictionaryRef dict = NULL;
5686
5687 dict = CFDictionaryCreateMutable(NULL, 0,
5688 &kCFTypeDictionaryKeyCallBacks,
5689 &kCFTypeDictionaryValueCallBacks);
5690 /* the first entry is the default route */
5691 r = new_routelist->list;
5692 if ((r->flags & kRouteFlagsHasGateway) != 0) {
5693 CFStringRef router;
5694
5695 router = my_CFStringCreateWithIn6Addr(&r->gateway);
5696 CFDictionarySetValue(dict, kSCPropNetIPv6Router, router);
5697 CFRelease(router);
5698 }
5699 ifn_p = my_if_indextoname(r->ifindex, ifname);
5700 if (ifn_p != NULL) {
5701 CFStringRef ifname_cf;
5702
5703 ifname_cf = CFStringCreateWithCString(NULL,
5704 ifn_p,
5705 kCFStringEncodingASCII);
5706 if (ifname_cf != NULL) {
5707 CFDictionarySetValue(dict,
5708 kSCDynamicStorePropNetPrimaryInterface,
5709 ifname_cf);
5710 CFRelease(ifname_cf);
5711 }
5712 }
5713 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
5714 primary);
5715 keyChangeListSetValue(keys, S_state_global_ipv6, dict);
5716 CFRelease(dict);
5717 #if !TARGET_IPHONE_SIMULATOR
5718 if (S_scopedroute_v6) {
5719 set_ipv6_default_interface(r->ifindex);
5720 }
5721 #endif /* !TARGET_IPHONE_SIMULATOR */
5722 }
5723 else {
5724 #if !TARGET_IPHONE_SIMULATOR
5725 if (S_scopedroute_v6) {
5726 set_ipv6_default_interface(0);
5727 }
5728 #endif /* !TARGET_IPHONE_SIMULATOR */
5729 keyChangeListRemoveValue(keys, S_state_global_ipv6);
5730 }
5731 }
5732
5733 #if !TARGET_IPHONE_SIMULATOR
5734 sockfd = open_routing_socket();
5735 if (sockfd != -1) {
5736 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5737 if (S_ipv6_routelist == NULL) {
5738 my_log(LOG_DEBUG, "Old Routes = <none>");
5739 }
5740 else {
5741 my_log(LOG_DEBUG, "Old Routes = ");
5742 IPv6RouteListLog(LOG_DEBUG, S_ipv6_routelist);
5743 }
5744 if (new_routelist == NULL) {
5745 my_log(LOG_DEBUG, "New Routes = <none>");
5746 }
5747 else {
5748 my_log(LOG_DEBUG, "New Routes = ");
5749 IPv6RouteListLog(LOG_DEBUG, new_routelist);
5750 }
5751 }
5752 /* go through routelist and bind any unbound routes */
5753 IPv6RouteListFinalize(new_routelist);
5754 IPv6RouteListApply(S_ipv6_routelist, new_routelist, sockfd);
5755 close(sockfd);
5756 }
5757 if (S_ipv6_routelist != NULL) {
5758 free(S_ipv6_routelist);
5759 }
5760 S_ipv6_routelist = new_routelist;
5761 #else /* !TARGET_IPHONE_SIMULATOR */
5762 if (new_routelist != NULL) {
5763 free(new_routelist);
5764 }
5765 #endif /* !TARGET_IPHONE_SIMULATOR */
5766
5767 return;
5768 }
5769
5770 static Boolean
5771 update_dns(CFDictionaryRef services_info,
5772 CFStringRef primary,
5773 keyChangeListRef keys)
5774 {
5775 Boolean changed = FALSE;
5776 CFDictionaryRef dict = NULL;
5777
5778 if (primary != NULL) {
5779 CFDictionaryRef service_dict;
5780
5781 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
5782 if (service_dict != NULL) {
5783 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
5784 }
5785 }
5786
5787 if (!_SC_CFEqual(S_dns_dict, dict)) {
5788 if (dict == NULL) {
5789 #if !TARGET_OS_IPHONE
5790 empty_dns();
5791 #endif /* !TARGET_OS_IPHONE */
5792 keyChangeListRemoveValue(keys, S_state_global_dns);
5793 } else {
5794 CFMutableDictionaryRef new_dict;
5795
5796 #if !TARGET_OS_IPHONE
5797 set_dns(CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains),
5798 CFDictionaryGetValue(dict, kSCPropNetDNSDomainName),
5799 CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses),
5800 CFDictionaryGetValue(dict, kSCPropNetDNSSortList));
5801 #endif /* !TARGET_OS_IPHONE */
5802 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
5803 CFDictionaryRemoveValue(new_dict, kSCPropInterfaceName);
5804 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchDomains);
5805 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchOrders);
5806 CFDictionaryRemoveValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY);
5807 keyChangeListSetValue(keys, S_state_global_dns, new_dict);
5808 CFRelease(new_dict);
5809 }
5810 changed = TRUE;
5811 }
5812
5813 if (dict != NULL) CFRetain(dict);
5814 if (S_dns_dict != NULL) CFRelease(S_dns_dict);
5815 S_dns_dict = dict;
5816
5817 return changed;
5818 }
5819
5820 static Boolean
5821 update_dnsinfo(CFDictionaryRef services_info,
5822 CFStringRef primary,
5823 keyChangeListRef keys,
5824 CFArrayRef service_order)
5825 {
5826 Boolean changed;
5827 CFDictionaryRef dict = NULL;
5828 CFArrayRef multicastResolvers;
5829 CFArrayRef privateResolvers;
5830
5831 multicastResolvers = CFDictionaryGetValue(services_info, S_multicast_resolvers);
5832 privateResolvers = CFDictionaryGetValue(services_info, S_private_resolvers);
5833
5834 if (primary != NULL) {
5835 CFDictionaryRef service_dict;
5836
5837 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
5838 if (service_dict != NULL) {
5839 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
5840 }
5841 }
5842
5843 changed = dns_configuration_set(dict,
5844 S_service_state_dict,
5845 service_order,
5846 multicastResolvers,
5847 privateResolvers);
5848 if (changed) {
5849 keyChangeListNotifyKey(keys, S_state_global_dns);
5850 }
5851 return changed;
5852 }
5853
5854 static Boolean
5855 update_nwi(nwi_state_t state)
5856 {
5857 unsigned char signature[CC_SHA1_DIGEST_LENGTH];
5858 static unsigned char signature_last[CC_SHA1_DIGEST_LENGTH];
5859
5860 _nwi_state_signature(state, signature, sizeof(signature));
5861 if (bcmp(signature, signature_last, sizeof(signature)) == 0) {
5862 return FALSE;
5863 }
5864
5865 // save [new] signature
5866 bcopy(signature, signature_last, sizeof(signature));
5867
5868 // save [new] configuration
5869 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5870 my_log(LOG_DEBUG, "Updating network information");
5871 S_nwi_state_dump(state);
5872 }
5873 if (_nwi_state_store(state) == FALSE) {
5874 my_log(LOG_ERR, "Notifying nwi_state_store failed");
5875 }
5876
5877 return TRUE;
5878 }
5879
5880 static Boolean
5881 update_proxies(CFDictionaryRef services_info,
5882 CFStringRef primary,
5883 keyChangeListRef keys,
5884 CFArrayRef service_order)
5885 {
5886 Boolean changed = FALSE;
5887 CFDictionaryRef dict = NULL;
5888 CFDictionaryRef new_dict;
5889
5890 if (primary != NULL) {
5891 CFDictionaryRef service_dict;
5892
5893 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
5894 if (service_dict != NULL) {
5895 dict = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
5896 }
5897 }
5898
5899 new_dict = proxy_configuration_update(dict,
5900 S_service_state_dict,
5901 service_order,
5902 services_info);
5903 if (!_SC_CFEqual(S_proxies_dict, new_dict)) {
5904 if (new_dict == NULL) {
5905 keyChangeListRemoveValue(keys, S_state_global_proxies);
5906 } else {
5907 keyChangeListSetValue(keys, S_state_global_proxies, new_dict);
5908 }
5909 changed = TRUE;
5910 }
5911
5912 if (S_proxies_dict != NULL) CFRelease(S_proxies_dict);
5913 S_proxies_dict = new_dict;
5914
5915 return changed;
5916 }
5917
5918 #if !TARGET_OS_IPHONE
5919 static Boolean
5920 update_smb(CFDictionaryRef services_info,
5921 CFStringRef primary,
5922 keyChangeListRef keys)
5923 {
5924 Boolean changed = FALSE;
5925 CFDictionaryRef dict = NULL;
5926
5927 if (primary != NULL) {
5928 CFDictionaryRef service_dict;
5929
5930 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
5931 if (service_dict != NULL) {
5932 dict = CFDictionaryGetValue(service_dict, kSCEntNetSMB);
5933 }
5934 }
5935
5936 if (!_SC_CFEqual(S_smb_dict, dict)) {
5937 if (dict == NULL) {
5938 keyChangeListRemoveValue(keys, S_state_global_smb);
5939 } else {
5940 keyChangeListSetValue(keys, S_state_global_smb, dict);
5941 }
5942 changed = TRUE;
5943 }
5944
5945 if (dict != NULL) CFRetain(dict);
5946 if (S_smb_dict != NULL) CFRelease(S_smb_dict);
5947 S_smb_dict = dict;
5948
5949 return changed;
5950 }
5951 #endif /* !TARGET_OS_IPHONE */
5952
5953 static Rank
5954 get_service_rank(CFArrayRef order, int n_order, CFStringRef serviceID)
5955 {
5956 CFIndex i;
5957 Rank rank = kRankIndexMask;
5958
5959 if (serviceID != NULL && order != NULL && n_order > 0) {
5960 for (i = 0; i < n_order; i++) {
5961 CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(order, i));
5962
5963 if (s == NULL) {
5964 continue;
5965 }
5966 if (CFEqual(serviceID, s)) {
5967 rank = (Rank)i + 1;
5968 break;
5969 }
5970 }
5971 }
5972 return (rank);
5973 }
5974
5975 /**
5976 ** Service election:
5977 **/
5978 /*
5979 * Function: rank_dict_get_service_rank
5980 * Purpose:
5981 * Retrieve the service rank in the given dictionary.
5982 */
5983 static Rank
5984 rank_dict_get_service_rank(CFDictionaryRef rank_dict, CFStringRef serviceID)
5985 {
5986 CFNumberRef rank;
5987 Rank rank_val;
5988
5989 rank_val = RankMake(kRankIndexMask, kRankAssertionDefault);
5990 rank = CFDictionaryGetValue(rank_dict, serviceID);
5991 if (rank != NULL) {
5992 CFNumberGetValue(rank, kCFNumberSInt32Type, &rank_val);
5993 }
5994 return (rank_val);
5995 }
5996
5997 /*
5998 * Function: rank_dict_set_service_rank
5999 * Purpose:
6000 * Save the results of ranking the service so we can look it up later without
6001 * repeating all of the ranking code.
6002 */
6003 static void
6004 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict,
6005 CFStringRef serviceID, Rank rank_val)
6006 {
6007 CFNumberRef rank;
6008
6009 rank = CFNumberCreate(NULL, kCFNumberSInt32Type, (const void *)&rank_val);
6010 if (rank != NULL) {
6011 CFDictionarySetValue(rank_dict, serviceID, rank);
6012 CFRelease(rank);
6013 }
6014 return;
6015 }
6016
6017 static const CFStringRef *transientInterfaceEntityNames[] = {
6018 &kSCEntNetPPP,
6019 };
6020
6021
6022 static void
6023 CollectTransientServices(const void * key,
6024 const void * value,
6025 void * context)
6026 {
6027 int i;
6028 CFStringRef service = key;
6029 CFMutableArrayRef vif_setup_keys = context;
6030
6031 /* This service is either a vpn type service or a comm center service */
6032 if (!CFStringHasPrefix(service, kSCDynamicStoreDomainSetup)) {
6033 return;
6034 }
6035
6036 for (i = 0; i < countof(transientInterfaceEntityNames); i++) {
6037 if (CFStringHasSuffix(service, *transientInterfaceEntityNames[i])) {
6038 CFArrayAppendValue(vif_setup_keys, service);
6039 break;
6040 }
6041 }
6042
6043 return;
6044 }
6045
6046
6047 static SCNetworkReachabilityFlags
6048 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info,
6049 CFStringRef service_id,
6050 CFStringRef entity,
6051 CFStringRef vpn_setup_key)
6052 {
6053 CFStringRef key;
6054 CFDictionaryRef dict;
6055 SCNetworkReachabilityFlags flags = 0;
6056
6057
6058 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6059 kSCDynamicStoreDomainSetup,
6060 service_id,
6061 kSCEntNetInterface);
6062 dict = CFDictionaryGetValue(services_info, key);
6063 CFRelease(key);
6064
6065 if (isA_CFDictionary(dict)
6066 && CFDictionaryContainsKey(dict, kSCPropNetInterfaceDeviceName)) {
6067
6068 flags = (kSCNetworkReachabilityFlagsReachable
6069 | kSCNetworkReachabilityFlagsTransientConnection
6070 | kSCNetworkReachabilityFlagsConnectionRequired);
6071
6072 if (CFEqual(entity, kSCEntNetPPP)) {
6073 CFNumberRef num;
6074 CFDictionaryRef p_dict = CFDictionaryGetValue(services_info, vpn_setup_key);
6075
6076 if (!isA_CFDictionary(p_dict)) {
6077 return (flags);
6078 }
6079
6080 // get PPP dial-on-traffic status
6081 num = CFDictionaryGetValue(p_dict, kSCPropNetPPPDialOnDemand);
6082 if (isA_CFNumber(num)) {
6083 int32_t ppp_demand;
6084
6085 if (CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand)) {
6086 if (ppp_demand) {
6087 flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
6088 }
6089 }
6090 }
6091 }
6092 }
6093 return (flags);
6094 }
6095
6096 static Boolean
6097 S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value)
6098 {
6099 Boolean ret = def_value;
6100
6101 if (dict != NULL) {
6102 CFBooleanRef val;
6103
6104 val = CFDictionaryGetValue(dict, key);
6105 if (isA_CFBoolean(val) != NULL) {
6106 ret = CFBooleanGetValue(val);
6107 }
6108 }
6109 return (ret);
6110 }
6111
6112
6113 static void
6114 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info,
6115 SCNetworkReachabilityFlags *reach_flags_v4,
6116 SCNetworkReachabilityFlags *reach_flags_v6)
6117 {
6118 CFIndex i;
6119 CFIndex count;
6120 CFMutableArrayRef vif_setup_keys;
6121
6122 vif_setup_keys = CFArrayCreateMutable(NULL,
6123 0,
6124 &kCFTypeArrayCallBacks);
6125 CFDictionaryApplyFunction(services_info, CollectTransientServices,
6126 vif_setup_keys);
6127 count = CFArrayGetCount(vif_setup_keys);
6128 for (i = 0; i < count; i++) {
6129 CFArrayRef components = NULL;
6130 CFStringRef entity;
6131 CFStringRef service_id;
6132 CFStringRef vif_setup_key;
6133
6134 vif_setup_key = CFArrayGetValueAtIndex(vif_setup_keys, i);
6135
6136 /*
6137 * setup key in the following format:
6138 * Setup:/Network/Service/<Service ID>/<Entity>
6139 */
6140 components = CFStringCreateArrayBySeparatingStrings(NULL, vif_setup_key, CFSTR("/"));
6141
6142 if (CFArrayGetCount(components) != 5) {
6143 // invalid Setup key encountered
6144 goto skip;
6145 }
6146
6147 /* service id is the 3rd element */
6148 service_id = CFArrayGetValueAtIndex(components, 3);
6149
6150 /* entity id is the 4th element */
6151 entity = CFArrayGetValueAtIndex(components, 4);
6152
6153
6154 if (CFEqual(entity, kSCEntNetPPP)) {
6155 SCNetworkReachabilityFlags flags;
6156 CFStringRef key;
6157
6158 flags = GetReachabilityFlagsFromVPN(services_info,
6159 service_id,
6160 entity,
6161 vif_setup_key);
6162
6163 /* Check for the v4 reachability flags */
6164 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6165 kSCDynamicStoreDomainSetup,
6166 service_id,
6167 kSCEntNetIPv4);
6168
6169 if (CFDictionaryContainsKey(services_info, key)) {
6170 *reach_flags_v4 |= flags;
6171 my_log(LOG_DEBUG, "Service %@ setting ipv4 reach flags: %d", service_id, *reach_flags_v4);
6172 }
6173
6174 CFRelease(key);
6175
6176 /* Check for the v6 reachability flags */
6177 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6178 kSCDynamicStoreDomainSetup,
6179 service_id,
6180 kSCEntNetIPv6);
6181
6182 if (CFDictionaryContainsKey(services_info, key)) {
6183 *reach_flags_v6 |= flags;
6184 my_log(LOG_DEBUG, "Service %@ setting ipv6 reach flags: %d", service_id, *reach_flags_v6);
6185 }
6186 CFRelease(key);
6187
6188 if (flags != 0) {
6189 if (components != NULL) {
6190 CFRelease(components);
6191 }
6192 goto done;
6193 }
6194 }
6195 skip:
6196 if (components != NULL) {
6197 CFRelease(components);
6198 }
6199 }
6200 done:
6201 CFRelease(vif_setup_keys);
6202 return;
6203 }
6204
6205 static SCNetworkReachabilityFlags
6206 GetReachFlagsFromStatus(CFStringRef entity, int status)
6207 {
6208 SCNetworkReachabilityFlags flags = 0;
6209
6210 if (CFEqual(entity, kSCEntNetPPP)) {
6211 switch (status) {
6212 case PPP_RUNNING :
6213 /* if we're really UP and RUNNING */
6214 break;
6215 case PPP_ONHOLD :
6216 /* if we're effectively UP and RUNNING */
6217 break;
6218 case PPP_IDLE :
6219 /* if we're not connected at all */
6220 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6221 break;
6222 case PPP_STATERESERVED :
6223 // if we're not connected at all
6224 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6225 break;
6226 default :
6227 /* if we're in the process of [dis]connecting */
6228 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6229 break;
6230 }
6231 }
6232 else if (CFEqual(entity, kSCEntNetIPSec)) {
6233 switch (status) {
6234 case IPSEC_RUNNING :
6235 /* if we're really UP and RUNNING */
6236 break;
6237 case IPSEC_IDLE :
6238 /* if we're not connected at all */
6239 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6240 break;
6241 default :
6242 /* if we're in the process of [dis]connecting */
6243 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6244 break;
6245 }
6246 }
6247 else if (CFEqual(entity, kSCEntNetVPN)) {
6248 switch (status) {
6249 case VPN_RUNNING :
6250 /* if we're really UP and RUNNING */
6251 break;
6252 case VPN_IDLE :
6253 case VPN_LOADING :
6254 case VPN_LOADED :
6255 case VPN_UNLOADING :
6256 /* if we're not connected at all */
6257 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6258 break;
6259 default :
6260 /* if we're in the process of [dis]connecting */
6261 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6262 break;
6263 }
6264 }
6265 return (flags);
6266 }
6267
6268 static void
6269 VPNAttributesGet(CFStringRef service_id,
6270 CFDictionaryRef services_info,
6271 SCNetworkReachabilityFlags *flags,
6272 CFStringRef *server_address,
6273 int af)
6274 {
6275 int i;
6276 CFDictionaryRef entity_dict;
6277 CFNumberRef num;
6278 CFDictionaryRef p_state = NULL;
6279 int status = 0;
6280 CFStringRef transient_entity = NULL;
6281
6282 if (af == AF_INET) {
6283 entity_dict = service_dict_get(service_id, kSCEntNetIPv4);
6284 }
6285 else {
6286 entity_dict = service_dict_get(service_id, kSCEntNetIPv6);
6287 }
6288 entity_dict = ipdict_get_service(entity_dict);
6289 if (entity_dict == NULL) {
6290 return;
6291 }
6292
6293 for (i = 0; i < countof(transientServiceInfo); i++) {
6294 CFStringRef entity = *transientServiceInfo[i].entityName;
6295
6296 p_state = service_dict_get(service_id, entity);
6297
6298 /* ensure that this is a VPN Type service */
6299 if (isA_CFDictionary(p_state)) {
6300 transient_entity = entity;
6301 break;
6302 }
6303 }
6304
6305 /* Did we find a vpn type service? If not, we are done.*/
6306 if (transient_entity == NULL) {
6307 return;
6308 }
6309
6310 *flags |= (kSCNetworkReachabilityFlagsReachable
6311 | kSCNetworkReachabilityFlagsTransientConnection);
6312
6313 /* Get the Server Address */
6314 if (server_address != NULL) {
6315 *server_address = CFDictionaryGetValue(entity_dict,
6316 CFSTR("ServerAddress"));
6317 *server_address = isA_CFString(*server_address);
6318 if (*server_address != NULL) {
6319 CFRetain(*server_address);
6320 }
6321 }
6322
6323 /* get status */
6324 if (!CFDictionaryGetValueIfPresent(p_state,
6325 kSCPropNetVPNStatus, // IPSecStatus, PPPStatus, VPNStatus
6326 (const void **)&num) ||
6327 !isA_CFNumber(num) ||
6328 !CFNumberGetValue(num, kCFNumberSInt32Type, &status)) {
6329 return;
6330 }
6331
6332 *flags |= GetReachFlagsFromStatus(transient_entity, status);
6333 if (CFEqual(transient_entity, kSCEntNetPPP)) {
6334 CFStringRef key;
6335 CFDictionaryRef p_setup;
6336 int ppp_demand;
6337
6338 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6339 kSCDynamicStoreDomainSetup,
6340 service_id,
6341 kSCEntNetPPP);
6342 p_setup = CFDictionaryGetValue(services_info, key);
6343 CFRelease(key);
6344
6345 /* get dial-on-traffic status */
6346 if (isA_CFDictionary(p_setup) &&
6347 CFDictionaryGetValueIfPresent(p_setup,
6348 kSCPropNetPPPDialOnDemand,
6349 (const void **)&num) &&
6350 isA_CFNumber(num) &&
6351 CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand) &&
6352 (ppp_demand != 0)) {
6353 *flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
6354 if (status == PPP_IDLE) {
6355 *flags |= kSCNetworkReachabilityFlagsInterventionRequired;
6356 }
6357 }
6358 }
6359 return;
6360 }
6361
6362
6363 typedef struct ElectionInfo {
6364 int af;
6365 CFStringRef entity;
6366 int n_services;
6367 CFArrayRef order;
6368 int n_order;
6369 ElectionResultsRef results;
6370 CFMutableDictionaryRef rank_dict;
6371 } ElectionInfo, * ElectionInfoRef;
6372
6373 typedef CFDictionaryApplierFunction ElectionFuncRef;
6374
6375 static void
6376 CandidateRelease(CandidateRef candidate)
6377 {
6378 my_CFRelease(&candidate->serviceID);
6379 my_CFRelease(&candidate->if_name);
6380 my_CFRelease(&candidate->signature);
6381 return;
6382 }
6383
6384 static void
6385 CandidateCopy(CandidateRef dest, CandidateRef src)
6386 {
6387 *dest = *src;
6388 if (dest->serviceID) {
6389 CFRetain(dest->serviceID);
6390 }
6391 if (dest->if_name) {
6392 CFRetain(dest->if_name);
6393 }
6394 if(dest->signature) {
6395 CFRetain(dest->signature);
6396 }
6397 return;
6398 }
6399
6400 static ElectionResultsRef
6401 ElectionResultsAlloc(int af, int size)
6402 {
6403 ElectionResultsRef results;
6404
6405 results = (ElectionResultsRef)malloc(ElectionResultsComputeSize(size));
6406 results->af = af;
6407 results->count = 0;
6408 results->size = size;
6409 return (results);
6410 }
6411
6412 static void
6413 ElectionResultsRelease(ElectionResultsRef results)
6414 {
6415 int i;
6416 CandidateRef scan;
6417
6418 for (i = 0, scan = results->candidates;
6419 i < results->count;
6420 i++, scan++) {
6421 CandidateRelease(scan);
6422 }
6423 free(results);
6424 return;
6425 }
6426
6427 static void
6428 ElectionResultsLog(int level, ElectionResultsRef results, const char * prefix)
6429 {
6430 int i;
6431 CandidateRef scan;
6432
6433 if (results == NULL) {
6434 my_log(level, "%s: no candidates", prefix);
6435 return;
6436 }
6437 my_log(level, "%s: %d candidates", prefix, results->count);
6438 for (i = 0, scan = results->candidates;
6439 i < results->count;
6440 i++, scan++) {
6441 char ntopbuf[INET6_ADDRSTRLEN];
6442
6443 (void)inet_ntop(results->af, &scan->addr, ntopbuf, sizeof(ntopbuf));
6444 my_log(level, "%d. %@ serviceID=%@ addr=%s rank=0x%x",
6445 i, scan->if_name, scan->serviceID, ntopbuf, scan->rank);
6446 }
6447 return;
6448 }
6449
6450 /*
6451 * Function: ElectionResultsAddCandidate
6452 * Purpose:
6453 * Add the candidate into the election results. Find the insertion point
6454 * by comparing the rank of the candidate with existing entries.
6455 */
6456 static void
6457 ElectionResultsAddCandidate(ElectionResultsRef results, CandidateRef candidate)
6458 {
6459 CFIndex i;
6460 CFIndex where;
6461
6462 if (results->count == results->size) {
6463 /* this should not happen */
6464 my_log(LOG_NOTICE, "can't fit another candidate");
6465 return;
6466 }
6467
6468 /* find the insertion point */
6469 where = kCFNotFound;
6470 for (i = 0; i < results->count; i++) {
6471 CandidateRef this_candidate = results->candidates + i;
6472
6473 if (candidate->rank < this_candidate->rank) {
6474 where = i;
6475 break;
6476 }
6477 }
6478 /* add it to the end */
6479 if (where == kCFNotFound) {
6480 CandidateCopy(results->candidates + results->count, candidate);
6481 results->count++;
6482 return;
6483 }
6484 /* slide existing entries over */
6485 for (i = results->count; i > where; i--) {
6486 results->candidates[i] = results->candidates[i - 1];
6487 }
6488 /* insert element */
6489 CandidateCopy(results->candidates + where, candidate);
6490 results->count++;
6491 return;
6492 }
6493
6494 static void
6495 elect_ip(const void * key, const void * value, void * context);
6496
6497 /*
6498 * Function: ElectionResultsCopy
6499 * Purpose:
6500 * Visit all of the services and invoke the protocol-specific election
6501 * function. Return the results of the election.
6502 */
6503 static ElectionResultsRef
6504 ElectionResultsCopy(int af, CFArrayRef order, int n_order)
6505 {
6506 int count;
6507 ElectionInfo info;
6508
6509 count = (int)CFDictionaryGetCount(S_service_state_dict);
6510 if (count == 0) {
6511 return (NULL);
6512 }
6513 info.af = af;
6514 if (af == AF_INET) {
6515 info.entity = kSCEntNetIPv4;
6516 info.rank_dict = S_ipv4_service_rank_dict;
6517 }
6518 else {
6519 info.entity = kSCEntNetIPv6;
6520 info.rank_dict = S_ipv6_service_rank_dict;
6521 }
6522 info.results = ElectionResultsAlloc(af, count);
6523 info.n_services = count;
6524 info.order = order;
6525 info.n_order = n_order;
6526 CFDictionaryApplyFunction(S_service_state_dict, elect_ip, (void *)&info);
6527 if (info.results->count == 0) {
6528 ElectionResultsRelease(info.results);
6529 info.results = NULL;
6530 }
6531 return (info.results);
6532 }
6533
6534 /*
6535 * Function: ElectionResultsCandidateNeedsDemotion
6536 * Purpose:
6537 * Check whether the given candidate requires demotion. A candidate
6538 * might need to be demoted if its IPv4 and IPv6 services must be coupled
6539 * but a higher ranked service has IPv4 or IPv6.
6540 */
6541 static Boolean
6542 ElectionResultsCandidateNeedsDemotion(ElectionResultsRef other_results,
6543 CandidateRef candidate)
6544 {
6545 CandidateRef other_candidate;
6546 Boolean ret = FALSE;
6547
6548 if (other_results == NULL
6549 || candidate->ip_is_coupled == FALSE
6550 || RANK_ASSERTION_MASK(candidate->rank) == kRankAssertionNever) {
6551 goto done;
6552 }
6553 other_candidate = other_results->candidates;
6554 if (CFEqual(other_candidate->if_name, candidate->if_name)) {
6555 /* they are over the same interface, no need to demote */
6556 goto done;
6557 }
6558 if (CFStringHasPrefix(other_candidate->if_name, CFSTR("stf"))) {
6559 /* avoid creating a feedback loop */
6560 goto done;
6561 }
6562 if (RANK_ASSERTION_MASK(other_candidate->rank) == kRankAssertionNever) {
6563 /* the other candidate isn't eligible to become primary, ignore */
6564 goto done;
6565 }
6566 if (candidate->rank < other_candidate->rank) {
6567 /* we're higher ranked than the other candidate, ignore */
6568 goto done;
6569 }
6570 ret = TRUE;
6571
6572 done:
6573 return (ret);
6574
6575 }
6576
6577
6578 static void
6579 get_signature_sha1(CFStringRef signature,
6580 unsigned char * sha1)
6581 {
6582 CC_SHA1_CTX ctx;
6583 CFDataRef signature_data;
6584
6585 signature_data = CFStringCreateExternalRepresentation(NULL,
6586 signature,
6587 kCFStringEncodingUTF8,
6588 0);
6589
6590 CC_SHA1_Init(&ctx);
6591 CC_SHA1_Update(&ctx,
6592 signature_data,
6593 (CC_LONG)CFDataGetLength(signature_data));
6594 CC_SHA1_Final(sha1, &ctx);
6595
6596 CFRelease(signature_data);
6597
6598 return;
6599 }
6600
6601
6602 static void
6603 add_candidate_to_nwi_state(nwi_state_t nwi_state, int af,
6604 CandidateRef candidate, Rank rank)
6605 {
6606 uint64_t flags = 0;
6607 char ifname[IFNAMSIZ];
6608 nwi_ifstate_t ifstate;
6609
6610 if (nwi_state == NULL) {
6611 /* can't happen */
6612 return;
6613 }
6614 if (RANK_ASSERTION_MASK(rank) == kRankAssertionNever) {
6615 flags |= NWI_IFSTATE_FLAGS_NOT_IN_LIST;
6616 }
6617 if (service_dict_get(candidate->serviceID, kSCEntNetDNS) != NULL) {
6618 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
6619 }
6620 CFStringGetCString(candidate->if_name, ifname, sizeof(ifname),
6621 kCFStringEncodingASCII);
6622 if ((S_IPMonitor_debug & kDebugFlag2) != 0) {
6623 char ntopbuf[INET6_ADDRSTRLEN];
6624
6625 (void)inet_ntop(af, &candidate->addr, ntopbuf, sizeof(ntopbuf));
6626 my_log(LOG_DEBUG,
6627 "Inserting IPv%c [%s] %s "
6628 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
6629 ipvx_char(af), ifname, ntopbuf,
6630 flags, rank, candidate->reachability_flags);
6631 }
6632 ifstate = nwi_insert_ifstate(nwi_state, ifname, af, flags, rank,
6633 (void *)&candidate->addr,
6634 (void *)&candidate->vpn_server_addr,
6635 candidate->reachability_flags);
6636 if (ifstate != NULL && candidate->signature) {
6637 uint8_t hash[CC_SHA1_DIGEST_LENGTH];
6638
6639 get_signature_sha1(candidate->signature, hash);
6640 nwi_ifstate_set_signature(ifstate, hash);
6641 }
6642 return;
6643 }
6644
6645
6646 static void
6647 add_reachability_flags_to_candidate(CandidateRef candidate, CFDictionaryRef services_info, int af)
6648 {
6649 SCNetworkReachabilityFlags flags = kSCNetworkReachabilityFlagsReachable;
6650 CFStringRef vpn_server_address = NULL;
6651
6652 VPNAttributesGet(candidate->serviceID,
6653 services_info,
6654 &flags,
6655 &vpn_server_address,
6656 af);
6657
6658 candidate->reachability_flags = flags;
6659
6660 if (vpn_server_address == NULL) {
6661 bzero(&candidate->vpn_server_addr, sizeof(candidate->vpn_server_addr));
6662 } else {
6663 char buf[128];
6664
6665 CFStringGetCString(vpn_server_address, buf, sizeof(buf),
6666 kCFStringEncodingASCII);
6667 _SC_string_to_sockaddr(buf,
6668 AF_UNSPEC,
6669 (void *)&candidate->vpn_server_addr,
6670 sizeof(candidate->vpn_server_addr));
6671
6672 CFRelease(vpn_server_address);
6673 }
6674 return;
6675 }
6676 /*
6677 * Function: ElectionResultsCopyPrimary
6678 * Purpose:
6679 * Use the results of the current protocol and the other protocol to
6680 * determine which service should become primary.
6681 *
6682 * At the same time, generate the IPv4/IPv6 routing table and
6683 * the nwi_state for the protocol.
6684 */
6685 static CFStringRef
6686 ElectionResultsCopyPrimary(ElectionResultsRef results,
6687 ElectionResultsRef other_results,
6688 nwi_state_t nwi_state, int af,
6689 RouteListRef * ret_routes,
6690 CFDictionaryRef services_info)
6691 {
6692 CFStringRef primary = NULL;
6693 Boolean primary_is_null = FALSE;
6694 RouteListRef routes = NULL;
6695
6696 if (nwi_state != NULL) {
6697 nwi_state_clear(nwi_state, af);
6698 }
6699 if (results != NULL) {
6700 CandidateRef deferred[results->count];
6701 int deferred_count;
6702 CFStringRef entity_name;
6703 int i;
6704 int initial_size;
6705 RouteListInfoRef info;
6706 CandidateRef scan;
6707
6708 switch (af) {
6709 case AF_INET:
6710 entity_name = kSCEntNetIPv4;
6711 info = &IPv4RouteListInfo;
6712 initial_size = results->count * IPV4_ROUTES_N_STATIC;
6713 break;
6714 default:
6715 case AF_INET6:
6716 entity_name = kSCEntNetIPv6;
6717 info = &IPv6RouteListInfo;
6718 initial_size = results->count * IPV6_ROUTES_N_STATIC;
6719 break;
6720 }
6721 deferred_count = 0;
6722 for (i = 0, scan = results->candidates;
6723 i < results->count;
6724 i++, scan++) {
6725 Boolean is_primary = FALSE;
6726 Rank rank = scan->rank;
6727 CFDictionaryRef service_dict;
6728 RouteListRef service_routes;
6729 Boolean skip = FALSE;
6730
6731 if (primary == NULL
6732 && RANK_ASSERTION_MASK(rank) != kRankAssertionNever) {
6733 if (ElectionResultsCandidateNeedsDemotion(other_results,
6734 scan)) {
6735 /* demote to RankNever */
6736 my_log(LOG_NOTICE,
6737 "IPv%c over %@ demoted: not primary for IPv%c",
6738 ipvx_char(af), scan->if_name, ipvx_other_char(af));
6739 rank = RankMake(rank, kRankAssertionNever);
6740 deferred[deferred_count++] = scan;
6741 skip = TRUE;
6742 }
6743 else {
6744 primary = CFRetain(scan->serviceID);
6745 is_primary = TRUE;
6746 }
6747 }
6748 /* contribute to the routing table */
6749 service_dict = service_dict_get(scan->serviceID, entity_name);
6750 service_routes = ipdict_get_routelist(service_dict);
6751 if (service_routes != NULL) {
6752 routes = RouteListAddRouteList(info, routes, initial_size,
6753 service_routes, rank);
6754 if ((service_routes->flags & kRouteListFlagsExcludeNWI) != 0) {
6755 skip = TRUE;
6756 }
6757 }
6758 else {
6759 skip = TRUE;
6760 }
6761 if (skip) {
6762 /* if we're skipping the primary, it's NULL */
6763 if (is_primary) {
6764 primary_is_null = TRUE;
6765 }
6766 }
6767 else {
6768 if (primary_is_null) {
6769 /* everything after the primary must be Never */
6770 rank = RankMake(rank, kRankAssertionNever);
6771 }
6772 add_reachability_flags_to_candidate(scan, services_info, af);
6773 add_candidate_to_nwi_state(nwi_state, af, scan, rank);
6774 }
6775 }
6776 for (i = 0; i < deferred_count; i++) {
6777 CandidateRef candidate = deferred[i];
6778 Rank rank;
6779
6780 /* demote to RankNever */
6781 rank = RankMake(candidate->rank, kRankAssertionNever);
6782 add_reachability_flags_to_candidate(candidate, services_info, af);
6783 add_candidate_to_nwi_state(nwi_state, af, candidate, rank);
6784 }
6785 }
6786 if (nwi_state != NULL) {
6787 nwi_state_set_last(nwi_state, af);
6788 }
6789 if (ret_routes != NULL) {
6790 *ret_routes = routes;
6791 }
6792 else if (routes != NULL) {
6793 free(routes);
6794 }
6795 if (primary_is_null) {
6796 my_CFRelease(&primary);
6797 }
6798 return (primary);
6799 }
6800
6801
6802 static inline
6803 CFStringRef
6804 service_dict_get_signature(CFDictionaryRef service_dict)
6805 {
6806 CFStringRef ifname;
6807
6808 ifname = CFDictionaryGetValue(service_dict, kSCPropInterfaceName);
6809 if (isA_CFString(ifname) == NULL
6810 || confirm_interface_name(service_dict, ifname) == FALSE) {
6811 return (NULL);
6812 }
6813 return (CFDictionaryGetValue(service_dict, kStoreKeyNetworkSignature));
6814 }
6815
6816 /*
6817 * Function: elect_ip
6818 * Purpose:
6819 * Evaluate the service and determine what rank the service should have.
6820 * If it's a suitable candidate, add it to the election results.
6821 */
6822 static void
6823 elect_ip(const void * key, const void * value, void * context)
6824 {
6825 CFDictionaryRef all_entities_dict = (CFDictionaryRef)value;
6826 Candidate candidate;
6827 Rank default_rank;
6828 ElectionInfoRef elect_info;
6829 CFStringRef if_name;
6830 CFDictionaryRef ipdict;
6831 Rank primary_rank;
6832 RouteListUnion routelist;
6833 CFDictionaryRef service_dict;
6834
6835 elect_info = (ElectionInfoRef)context;
6836 ipdict = CFDictionaryGetValue(all_entities_dict, elect_info->entity);
6837 if (ipdict != NULL) {
6838 routelist.ptr = ipdict_get_routelist(ipdict);
6839 service_dict = ipdict_get_service(ipdict);
6840 }
6841 else {
6842 routelist.ptr = NULL;
6843 }
6844 if (routelist.ptr == NULL || service_dict == NULL) {
6845 /* no connectivity */
6846 return;
6847 }
6848 if ((routelist.common->flags & kRouteListFlagsHasDefault) == 0) {
6849 /* no default route, not a candidate for being primary */
6850 return;
6851 }
6852 if_name = CFDictionaryGetValue(service_dict, kSCPropInterfaceName);
6853 if (if_name == NULL) {
6854 /* need an interface name */
6855 return;
6856 }
6857 if (CFEqual(if_name, CFSTR(kLoopbackInterface))) {
6858 /* don't process loopback */
6859 return;
6860 }
6861 bzero(&candidate, sizeof(candidate));
6862 candidate.serviceID = (CFStringRef)key;
6863 candidate.rank = get_service_rank(elect_info->order, elect_info->n_order,
6864 candidate.serviceID);
6865 if (elect_info->af == AF_INET) {
6866 default_rank = routelist.v4->list->rank;
6867 candidate.addr.v4 = routelist.v4->list->ifa;
6868 }
6869 else {
6870 default_rank = routelist.v6->list->rank;
6871 candidate.addr.v6 = routelist.v6->list->ifa;
6872 }
6873 primary_rank = RANK_ASSERTION_MASK(default_rank);
6874 if (S_ppp_override_primary) {
6875 char ifn[IFNAMSIZ];
6876
6877 if (CFStringGetCString(if_name, ifn, sizeof(ifn),
6878 kCFStringEncodingASCII)
6879 && (strncmp(PPP_PREFIX, ifn, sizeof(PPP_PREFIX) - 1) == 0)) {
6880 /* PPP override: make ppp* look the best */
6881 primary_rank = kRankAssertionFirst;
6882 }
6883 }
6884 candidate.rank = RankMake(candidate.rank, primary_rank);
6885 candidate.ip_is_coupled = service_get_ip_is_coupled(candidate.serviceID);
6886 candidate.if_name = if_name;
6887 rank_dict_set_service_rank(elect_info->rank_dict,
6888 candidate.serviceID, candidate.rank);
6889 candidate.signature = service_dict_get_signature(service_dict);
6890 ElectionResultsAddCandidate(elect_info->results, &candidate);
6891 return;
6892 }
6893
6894
6895 static uint32_t
6896 service_changed(CFDictionaryRef services_info, CFStringRef serviceID)
6897 {
6898 uint32_t changed = 0;
6899 int i;
6900
6901 /* update service options first (e.g. rank) */
6902 if (get_rank_changes(serviceID,
6903 get_service_state_entity(services_info, serviceID,
6904 NULL),
6905 get_service_setup_entity(services_info, serviceID,
6906 NULL),
6907 services_info)) {
6908 changed |= (1 << kEntityTypeServiceOptions);
6909 }
6910 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
6911 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
6912 GetEntityChangesFuncRef func = entityChangeFunc[i];
6913 if ((*func)(serviceID,
6914 get_service_state_entity(services_info, serviceID,
6915 *entityTypeNames[i]),
6916 get_service_setup_entity(services_info, serviceID,
6917 *entityTypeNames[i]),
6918 services_info)) {
6919 changed |= (1 << i);
6920 }
6921 }
6922
6923 if (get_transient_status_changes(serviceID, services_info)) {
6924 changed |= (1 << kEntityTypeTransientStatus);
6925 }
6926
6927 return (changed);
6928 }
6929
6930 static CFArrayRef
6931 service_order_get(CFDictionaryRef services_info)
6932 {
6933 CFArrayRef order = NULL;
6934 CFDictionaryRef ipv4_dict;
6935
6936 ipv4_dict = my_CFDictionaryGetDictionary(services_info,
6937 S_setup_global_ipv4);
6938 if (ipv4_dict != NULL) {
6939 CFNumberRef ppp_override;
6940 int ppp_val = 0;
6941
6942 order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
6943 order = isA_CFArray(order);
6944
6945 /* get ppp override primary */
6946 ppp_override = CFDictionaryGetValue(ipv4_dict,
6947 kSCPropNetPPPOverridePrimary);
6948 ppp_override = isA_CFNumber(ppp_override);
6949 if (ppp_override != NULL) {
6950 CFNumberGetValue(ppp_override, kCFNumberIntType, &ppp_val);
6951 }
6952 S_ppp_override_primary = (ppp_val != 0) ? TRUE : FALSE;
6953 }
6954 else {
6955 S_ppp_override_primary = FALSE;
6956 }
6957 return (order);
6958 }
6959
6960 static boolean_t
6961 set_new_primary(CFStringRef * primary_p, CFStringRef new_primary,
6962 const char * entity)
6963 {
6964 boolean_t changed = FALSE;
6965 CFStringRef primary = *primary_p;
6966
6967 if (new_primary != NULL) {
6968 if (primary != NULL && CFEqual(new_primary, primary)) {
6969 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
6970 my_log(LOG_DEBUG,
6971 "IPMonitor: %@ is still primary %s",
6972 new_primary, entity);
6973 }
6974 }
6975 else {
6976 my_CFRelease(primary_p);
6977 *primary_p = CFRetain(new_primary);
6978 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
6979 my_log(LOG_DEBUG,
6980 "IPMonitor: %@ is the new primary %s",
6981 new_primary, entity);
6982 }
6983 changed = TRUE;
6984 }
6985 }
6986 else if (primary != NULL) {
6987 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
6988 my_log(LOG_DEBUG,
6989 "IPMonitor: %@ is no longer primary %s",
6990 primary, entity);
6991 }
6992 my_CFRelease(primary_p);
6993 changed = TRUE;
6994 }
6995 return (changed);
6996 }
6997
6998 static Rank
6999 rank_service_entity(CFDictionaryRef rank_dict, CFStringRef serviceID,
7000 CFStringRef entity)
7001 {
7002 if (service_dict_get(serviceID, entity) == NULL) {
7003 return (RankMake(kRankIndexMask, kRankAssertionDefault));
7004 }
7005 return (rank_dict_get_service_rank(rank_dict, serviceID));
7006 }
7007
7008 static void
7009 append_serviceIDs_for_interface(CFMutableArrayRef services_changed,
7010 CFStringRef ifname)
7011 {
7012 CFIndex count;
7013 CFIndex i;
7014 void * * keys;
7015 #define N_KEYS_VALUES_STATIC 10
7016 void * keys_values_buf[N_KEYS_VALUES_STATIC * 2];
7017 void * * values;
7018
7019 count = CFDictionaryGetCount(S_service_state_dict);
7020 if (count <= N_KEYS_VALUES_STATIC) {
7021 keys = keys_values_buf;
7022 } else {
7023 keys = (void * *)malloc(sizeof(*keys) * count * 2);
7024 }
7025 values = keys + count;
7026 CFDictionaryGetKeysAndValues(S_service_state_dict,
7027 (const void * *)keys,
7028 (const void * *)values);
7029
7030 for (i = 0; i < count; i++) {
7031 CFDictionaryRef ipdict = NULL;
7032 CFStringRef interface = NULL;
7033 CFStringRef serviceID;
7034 CFDictionaryRef service_dict;
7035
7036 serviceID = (CFStringRef)keys[i];
7037 service_dict = (CFDictionaryRef)values[i];
7038
7039 /* check whether service has IPv4 or IPv6 */
7040 ipdict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
7041 if (ipdict == NULL) {
7042 ipdict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
7043 if (ipdict == NULL) {
7044 continue;
7045 }
7046 }
7047 interface = ipdict_get_ifname(ipdict);
7048 if (interface != NULL && CFEqual(interface, ifname)) {
7049 if (S_IPMonitor_debug & kDebugFlag1) {
7050 my_log(LOG_DEBUG,
7051 "Found IP service %@ on interface %@.",
7052 serviceID, ifname);
7053 }
7054 my_CFArrayAppendUniqueValue(services_changed, serviceID);
7055 }
7056 }
7057 if (keys != keys_values_buf) {
7058 free(keys);
7059 }
7060 return;
7061 }
7062
7063 static __inline__ const char *
7064 get_changed_str(CFStringRef serviceID, CFStringRef entity,
7065 CFDictionaryRef old_dict)
7066 {
7067 CFDictionaryRef new_dict = NULL;
7068
7069 if (serviceID != NULL) {
7070 new_dict = service_dict_get(serviceID, entity);
7071 }
7072
7073 if (old_dict == NULL) {
7074 if (new_dict != NULL) {
7075 return "+";
7076 }
7077 } else {
7078 if (new_dict == NULL) {
7079 return "-";
7080 } else if (!CFEqual(old_dict, new_dict)) {
7081 return "!";
7082 }
7083 }
7084 return "";
7085 }
7086
7087 static CF_RETURNS_RETAINED CFStringRef
7088 generate_log_changes(nwi_state_t changes_state,
7089 boolean_t dns_changed,
7090 boolean_t dnsinfo_changed,
7091 CFDictionaryRef old_primary_dns,
7092 boolean_t proxy_changed,
7093 CFDictionaryRef old_primary_proxy,
7094 boolean_t smb_changed,
7095 CFDictionaryRef old_primary_smb
7096 )
7097 {
7098 int idx;
7099 CFMutableStringRef log_output;
7100 nwi_ifstate_t scan;
7101
7102 log_output = CFStringCreateMutable(NULL, 0);
7103
7104 if (changes_state != NULL) {
7105 for (idx = 0; idx < countof(nwi_af_list); idx++) {
7106 CFMutableStringRef changes = NULL;
7107 CFMutableStringRef primary_str = NULL;
7108
7109 scan = nwi_state_get_first_ifstate(changes_state, nwi_af_list[idx]);
7110
7111 while (scan != NULL) {
7112 const char * changed_str;
7113
7114 changed_str = nwi_ifstate_get_diff_str(scan);
7115 if (changed_str != NULL) {
7116 void * address;
7117 const char * addr_str;
7118 char ntopbuf[INET6_ADDRSTRLEN];
7119
7120 address = (void *)nwi_ifstate_get_address(scan);
7121 addr_str = inet_ntop(scan->af, address, ntopbuf,
7122 sizeof(ntopbuf));
7123 if (primary_str == NULL) {
7124 primary_str = CFStringCreateMutable(NULL, 0);
7125 CFStringAppendFormat(primary_str, NULL,
7126 CFSTR("%s%s:%s"),
7127 nwi_ifstate_get_ifname(scan),
7128 changed_str, addr_str);
7129 } else {
7130 if (changes == NULL) {
7131 changes = CFStringCreateMutable(NULL, 0);
7132 }
7133 CFStringAppendFormat(changes, NULL, CFSTR(", %s"),
7134 nwi_ifstate_get_ifname(scan));
7135 if (strcmp(changed_str, "") != 0) {
7136 CFStringAppendFormat(changes, NULL, CFSTR("%s:%s"),
7137 changed_str, addr_str);
7138 }
7139 }
7140 }
7141 scan = nwi_ifstate_get_next(scan, scan->af);
7142 }
7143
7144 if (primary_str != NULL) {
7145 CFStringAppendFormat(log_output, NULL, CFSTR(" %s(%@"),
7146 nwi_af_list[idx] == AF_INET ? "v4" : "v6",
7147 primary_str);
7148
7149 if (changes != NULL && CFStringGetLength(changes) != 0) {
7150 CFStringAppendFormat(log_output, NULL, CFSTR("%@"),
7151 changes);
7152 }
7153 CFStringAppend(log_output, CFSTR(")"));
7154
7155 my_CFRelease(&primary_str);
7156 my_CFRelease(&changes);
7157 }
7158 }
7159 }
7160
7161 if (dns_changed || dnsinfo_changed) {
7162 const char *str;
7163
7164 str = get_changed_str(S_primary_dns, kSCEntNetDNS, old_primary_dns);
7165 if ((strcmp(str, "") == 0) && dnsinfo_changed) {
7166 str = "*"; // dnsinfo change w/no change to primary
7167 }
7168 CFStringAppendFormat(log_output, NULL, CFSTR(" DNS%s"), str);
7169 } else if (S_primary_dns != NULL) {
7170 CFStringAppend(log_output, CFSTR(" DNS"));
7171 }
7172
7173 if (proxy_changed) {
7174 const char *str;
7175
7176 str = get_changed_str(S_primary_proxies, kSCEntNetProxies, old_primary_proxy);
7177 CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy%s"), str);
7178 } else if (S_primary_proxies != NULL) {
7179 CFStringAppend(log_output, CFSTR(" Proxy"));
7180 }
7181
7182 #if !TARGET_OS_IPHONE
7183 if (smb_changed) {
7184 const char *str;
7185
7186 str = get_changed_str(S_primary_smb, kSCEntNetSMB, old_primary_smb);
7187 CFStringAppendFormat(log_output, NULL, CFSTR(" SMB%s"), str);
7188 } else if (S_primary_smb != NULL) {
7189 CFStringAppend(log_output, CFSTR(" SMB"));
7190 }
7191 #endif // !TARGET_OS_IPHONE
7192
7193 return log_output;
7194 }
7195
7196 #pragma mark -
7197 #pragma mark Network changed notification
7198
7199 static dispatch_queue_t
7200 __network_change_queue()
7201 {
7202 static dispatch_once_t once;
7203 static dispatch_queue_t q;
7204
7205 dispatch_once(&once, ^{
7206 q = dispatch_queue_create("network change queue", NULL);
7207 });
7208
7209 return q;
7210 }
7211
7212 // Note: must run on __network_change_queue()
7213 static void
7214 post_network_change_when_ready()
7215 {
7216 int status;
7217
7218 if (S_network_change_needed == 0) {
7219 return;
7220 }
7221
7222 if (!S_network_change_timeout &&
7223 (!S_dnsinfo_synced || !S_nwi_synced)) {
7224 // if we [still] need to wait for the DNS configuration
7225 // or network information changes to be ack'd
7226
7227 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
7228 my_log(LOG_DEBUG,
7229 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s, %s)",
7230 S_dnsinfo_synced ? "DNS" : "!DNS",
7231 S_nwi_synced ? "nwi" : "!nwi");
7232 }
7233 return;
7234 }
7235
7236 // cancel any running timer
7237 if (S_network_change_timer != NULL) {
7238 dispatch_source_cancel(S_network_change_timer);
7239 dispatch_release(S_network_change_timer);
7240 S_network_change_timer = NULL;
7241 S_network_change_timeout = FALSE;
7242 }
7243
7244 // set (and log?) the post time
7245 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
7246 struct timeval elapsed;
7247 struct timeval end;
7248
7249 (void) gettimeofday(&end, NULL);
7250 timersub(&end, &S_network_change_start, &elapsed);
7251
7252 #define QUERY_TIME__FMT "%ld.%6.6d"
7253 #define QUERY_TIME__DIV 1
7254
7255 my_log(LOG_DEBUG,
7256 "Post \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s: " QUERY_TIME__FMT ": 0x%x)",
7257 S_network_change_timeout ? "timeout" : "delayed",
7258 elapsed.tv_sec,
7259 elapsed.tv_usec / QUERY_TIME__DIV,
7260 S_network_change_needed);
7261 }
7262
7263 if ((S_network_change_needed & NETWORK_CHANGE_NET) != 0) {
7264 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI);
7265 if (status != NOTIFY_STATUS_OK) {
7266 my_log(LOG_ERR,
7267 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI ") failed: error=%d", status);
7268 }
7269 }
7270
7271 if ((S_network_change_needed & NETWORK_CHANGE_DNS) != 0) {
7272 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS);
7273 if (status != NOTIFY_STATUS_OK) {
7274 my_log(LOG_ERR,
7275 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS ") failed: error=%d", status);
7276 }
7277 }
7278
7279 if ((S_network_change_needed & NETWORK_CHANGE_PROXY) != 0) {
7280 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
7281 if (status != NOTIFY_STATUS_OK) {
7282 my_log(LOG_ERR,
7283 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY ") failed: error=%d", status);
7284 }
7285 }
7286
7287 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE);
7288 if (status != NOTIFY_STATUS_OK) {
7289 my_log(LOG_ERR,
7290 "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE ") failed: error=%d", status);
7291 }
7292
7293 S_network_change_needed = 0;
7294 return;
7295 }
7296
7297 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
7298
7299 // Note: must run on __network_change_queue()
7300 static void
7301 post_network_change(uint32_t change)
7302 {
7303 if (S_network_change_needed == 0) {
7304 // set the start time
7305 (void) gettimeofday(&S_network_change_start, NULL);
7306 }
7307
7308 // indicate that we need to post a change for ...
7309 S_network_change_needed |= change;
7310
7311 // cancel any running timer
7312 if (S_network_change_timer != NULL) {
7313 dispatch_source_cancel(S_network_change_timer);
7314 dispatch_release(S_network_change_timer);
7315 S_network_change_timer = NULL;
7316 S_network_change_timeout = FALSE;
7317 }
7318
7319 // if needed, start new timer
7320 if (!S_dnsinfo_synced || !S_nwi_synced) {
7321 S_network_change_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
7322 0,
7323 0,
7324 __network_change_queue());
7325 dispatch_source_set_event_handler(S_network_change_timer, ^{
7326 S_network_change_timeout = TRUE;
7327 post_network_change_when_ready();
7328 });
7329 dispatch_source_set_timer(S_network_change_timer,
7330 dispatch_time(DISPATCH_TIME_NOW,
7331 TRAILING_EDGE_TIMEOUT_NSEC), // start
7332 DISPATCH_TIME_FOREVER, // interval
7333 10 * NSEC_PER_MSEC); // leeway
7334 dispatch_resume(S_network_change_timer);
7335 }
7336
7337 post_network_change_when_ready();
7338
7339 return;
7340 }
7341
7342 #pragma mark -
7343 #pragma mark Process network (SCDynamicStore) changes
7344
7345 static void
7346 IPMonitorProcessChanges(SCDynamicStoreRef session, CFArrayRef changed_keys,
7347 CFArrayRef if_rank_changes)
7348 {
7349 CFIndex count = 0;
7350 uint32_t changes = 0;
7351 nwi_state_t changes_state = NULL;
7352 boolean_t dns_changed = FALSE;
7353 boolean_t dnsinfo_changed = FALSE;
7354 boolean_t global_ipv4_changed = FALSE;
7355 boolean_t global_ipv6_changed = FALSE;
7356 CFIndex i;
7357 keyChangeList keys;
7358 CFIndex n;
7359 CFStringRef network_change_msg = NULL;
7360 int n_services;
7361 int n_service_order = 0;
7362 nwi_state_t old_nwi_state = NULL;
7363 CFDictionaryRef old_primary_dns = NULL;
7364 CFDictionaryRef old_primary_proxy = NULL;
7365 #if !TARGET_OS_IPHONE
7366 CFDictionaryRef old_primary_smb = NULL;
7367 #endif // !TARGET_OS_IPHONE
7368 boolean_t proxies_changed = FALSE;
7369 boolean_t reachability_changed = FALSE;
7370 CFArrayRef service_order;
7371 CFMutableArrayRef service_changes = NULL;
7372 CFDictionaryRef services_info = NULL;
7373 #if !TARGET_OS_IPHONE
7374 boolean_t smb_changed = FALSE;
7375 #endif // !TARGET_OS_IPHONE
7376
7377 /* populate name/index cache */
7378 my_if_nameindex();
7379
7380 if (changed_keys != NULL) {
7381 count = CFArrayGetCount(changed_keys);
7382 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
7383 my_log(LOG_DEBUG,
7384 "IPMonitor: changed keys %@ (%ld)", changed_keys, count);
7385 }
7386 }
7387 if (if_rank_changes == NULL && count == 0) {
7388 return;
7389 }
7390
7391 if (S_primary_dns != NULL) {
7392 old_primary_dns = service_dict_get(S_primary_dns, kSCEntNetDNS);
7393 if (old_primary_dns != NULL) {
7394 old_primary_dns = CFDictionaryCreateCopy(NULL, old_primary_dns);
7395 }
7396 }
7397
7398 if (S_primary_proxies != NULL) {
7399 old_primary_proxy
7400 = service_dict_get(S_primary_proxies, kSCEntNetProxies);
7401 if (old_primary_proxy != NULL) {
7402 old_primary_proxy = CFDictionaryCreateCopy(NULL, old_primary_proxy);
7403 }
7404 }
7405
7406 #if !TARGET_OS_IPHONE
7407 if (S_primary_smb != NULL) {
7408 old_primary_smb = service_dict_get(S_primary_smb, kSCEntNetSMB);
7409 if (old_primary_smb != NULL) {
7410 old_primary_smb = CFDictionaryCreateCopy(NULL, old_primary_smb);
7411 }
7412 }
7413 #endif // !TARGET_OS_IPHONE
7414
7415 keyChangeListInit(&keys);
7416 service_changes = CFArrayCreateMutable(NULL, 0,
7417 &kCFTypeArrayCallBacks);
7418
7419 for (i = 0; i < count; i++) {
7420 CFStringRef change = CFArrayGetValueAtIndex(changed_keys, i);
7421 if (CFEqual(change, S_setup_global_ipv4)) {
7422 global_ipv4_changed = TRUE;
7423 global_ipv6_changed = TRUE;
7424 }
7425 else if (CFEqual(change, S_multicast_resolvers)) {
7426 dnsinfo_changed = TRUE;
7427 }
7428 else if (CFEqual(change, S_private_resolvers)) {
7429 dnsinfo_changed = TRUE;
7430 }
7431 #if !TARGET_OS_IPHONE
7432 else if (CFEqual(change, CFSTR(_PATH_RESOLVER_DIR))) {
7433 dnsinfo_changed = TRUE;
7434 }
7435 #endif /* !TARGET_OS_IPHONE */
7436 else if (CFStringHasPrefix(change, S_state_service_prefix)) {
7437 CFStringRef serviceID;
7438
7439 serviceID = parse_component(change, S_state_service_prefix);
7440 if (serviceID) {
7441 my_CFArrayAppendUniqueValue(service_changes, serviceID);
7442 CFRelease(serviceID);
7443 }
7444 }
7445 else if (CFStringHasPrefix(change, S_setup_service_prefix)) {
7446 int j;
7447
7448 CFStringRef serviceID = parse_component(change,
7449 S_setup_service_prefix);
7450 if (serviceID) {
7451 my_CFArrayAppendUniqueValue(service_changes, serviceID);
7452 CFRelease(serviceID);
7453 }
7454
7455 for (j = 0; j < countof(transientInterfaceEntityNames); j++) {
7456 if (CFStringHasSuffix(change,
7457 *transientInterfaceEntityNames[j])) {
7458 reachability_changed = TRUE;
7459 break;
7460 }
7461 }
7462
7463 if (CFStringHasSuffix(change, kSCEntNetInterface)) {
7464 reachability_changed = TRUE;
7465 }
7466 }
7467 }
7468
7469 /* determine which serviceIDs are impacted by the interface rank changes */
7470 if (if_rank_changes != NULL) {
7471 n = CFArrayGetCount(if_rank_changes);
7472 for (i = 0; i < n; i++) {
7473 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i);
7474
7475 if (S_IPMonitor_debug & kDebugFlag1) {
7476 my_log(LOG_DEBUG, "Interface rank changed %@",
7477 ifname);
7478 }
7479 append_serviceIDs_for_interface(service_changes, ifname);
7480 }
7481 }
7482
7483 /* grab a snapshot of everything we need */
7484 services_info = services_info_copy(session, service_changes);
7485 service_order = service_order_get(services_info);
7486 if (service_order != NULL) {
7487 n_service_order = (int)CFArrayGetCount(service_order);
7488 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
7489 my_log(LOG_DEBUG,
7490 "IPMonitor: service_order %@ ", service_order);
7491 }
7492 }
7493
7494 n = CFArrayGetCount(service_changes);
7495 for (i = 0; i < n; i++) {
7496 uint32_t changes;
7497 CFStringRef serviceID;
7498
7499 serviceID = CFArrayGetValueAtIndex(service_changes, i);
7500 changes = service_changed(services_info, serviceID);
7501 if ((changes & (1 << kEntityTypeServiceOptions)) != 0) {
7502 /* if __Service__ (e.g. PrimaryRank) changed */
7503 global_ipv4_changed = TRUE;
7504 global_ipv6_changed = TRUE;
7505 }
7506 else {
7507 if ((changes & (1 << kEntityTypeIPv4)) != 0) {
7508 global_ipv4_changed = TRUE;
7509 dnsinfo_changed = TRUE;
7510 proxies_changed = TRUE;
7511 }
7512 if ((changes & (1 << kEntityTypeIPv6)) != 0) {
7513 global_ipv6_changed = TRUE;
7514 dnsinfo_changed = TRUE;
7515 proxies_changed = TRUE;
7516 }
7517 }
7518 if ((changes & (1 << kEntityTypeDNS)) != 0) {
7519 if (S_primary_dns != NULL && CFEqual(S_primary_dns, serviceID)) {
7520 dns_changed = TRUE;
7521 }
7522 dnsinfo_changed = TRUE;
7523 }
7524 if ((changes & (1 << kEntityTypeProxies)) != 0) {
7525 proxies_changed = TRUE;
7526 }
7527 #if !TARGET_OS_IPHONE
7528 if ((changes & (1 << kEntityTypeSMB)) != 0) {
7529 if (S_primary_smb != NULL && CFEqual(S_primary_smb, serviceID)) {
7530 smb_changed = TRUE;
7531 }
7532 }
7533 #endif
7534 if ((changes & (1 << kEntityTypeTransientStatus)) != 0
7535 && (service_dict_get(serviceID, kSCEntNetIPv4) != NULL
7536 || service_dict_get(serviceID, kSCEntNetIPv6) != NULL)) {
7537 dnsinfo_changed = TRUE;
7538 }
7539 }
7540
7541 /* ensure S_nwi_state can hold as many services as we have currently */
7542 n_services = (int)CFDictionaryGetCount(S_service_state_dict);
7543 old_nwi_state = nwi_state_copy_priv(S_nwi_state);
7544 S_nwi_state = nwi_state_new(S_nwi_state, n_services);
7545
7546 if (global_ipv4_changed) {
7547 if (S_ipv4_results != NULL) {
7548 ElectionResultsRelease(S_ipv4_results);
7549 }
7550 S_ipv4_results
7551 = ElectionResultsCopy(AF_INET, service_order, n_service_order);
7552 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
7553 ElectionResultsLog(LOG_DEBUG, S_ipv4_results, "IPv4");
7554 }
7555 }
7556 if (global_ipv6_changed) {
7557 if (S_ipv6_results != NULL) {
7558 ElectionResultsRelease(S_ipv6_results);
7559 }
7560 S_ipv6_results
7561 = ElectionResultsCopy(AF_INET6, service_order, n_service_order);
7562 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
7563 ElectionResultsLog(LOG_DEBUG, S_ipv6_results, "IPv6");
7564 }
7565 }
7566 if (global_ipv4_changed || global_ipv6_changed || dnsinfo_changed) {
7567 CFStringRef new_primary;
7568 RouteListUnion new_routelist;
7569
7570 /* IPv4 */
7571 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
7572 my_log(LOG_DEBUG,
7573 "IPMonitor: electing IPv4 primary");
7574 }
7575 new_routelist.ptr = NULL;
7576 new_primary = ElectionResultsCopyPrimary(S_ipv4_results,
7577 S_ipv6_results,
7578 S_nwi_state, AF_INET,
7579 &new_routelist.common,
7580 services_info);
7581 (void)set_new_primary(&S_primary_ipv4, new_primary, "IPv4");
7582 update_ipv4(S_primary_ipv4, new_routelist.v4, &keys);
7583 my_CFRelease(&new_primary);
7584
7585 /* IPv6 */
7586 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
7587 my_log(LOG_DEBUG,
7588 "IPMonitor: electing IPv6 primary");
7589 }
7590 new_routelist.ptr = NULL;
7591 new_primary = ElectionResultsCopyPrimary(S_ipv6_results,
7592 S_ipv4_results,
7593 S_nwi_state, AF_INET6,
7594 &new_routelist.common,
7595 services_info);
7596 (void)set_new_primary(&S_primary_ipv6, new_primary, "IPv6");
7597 update_ipv6(S_primary_ipv6, new_routelist.v6, &keys);
7598 my_CFRelease(&new_primary);
7599 }
7600
7601 if (global_ipv4_changed || global_ipv6_changed) {
7602 CFStringRef new_primary_dns = NULL;
7603 CFStringRef new_primary_proxies = NULL;
7604 #if !TARGET_OS_IPHONE
7605 CFStringRef new_primary_smb = NULL;
7606 #endif /* !TARGET_OS_IPHONE */
7607
7608 if (S_primary_ipv4 != NULL && S_primary_ipv6 != NULL) {
7609 /* decide between IPv4 and IPv6 */
7610 if (rank_service_entity(S_ipv4_service_rank_dict,
7611 S_primary_ipv4, kSCEntNetDNS)
7612 <= rank_service_entity(S_ipv6_service_rank_dict,
7613 S_primary_ipv6, kSCEntNetDNS)) {
7614 new_primary_dns = S_primary_ipv4;
7615 }
7616 else {
7617 new_primary_dns = S_primary_ipv6;
7618 }
7619 if (rank_service_entity(S_ipv4_service_rank_dict,
7620 S_primary_ipv4, kSCEntNetProxies)
7621 <= rank_service_entity(S_ipv6_service_rank_dict,
7622 S_primary_ipv6, kSCEntNetProxies)) {
7623 new_primary_proxies = S_primary_ipv4;
7624 }
7625 else {
7626 new_primary_proxies = S_primary_ipv6;
7627 }
7628 #if !TARGET_OS_IPHONE
7629 if (rank_service_entity(S_ipv4_service_rank_dict,
7630 S_primary_ipv4, kSCEntNetSMB)
7631 <= rank_service_entity(S_ipv6_service_rank_dict,
7632 S_primary_ipv6, kSCEntNetSMB)) {
7633 new_primary_smb = S_primary_ipv4;
7634 }
7635 else {
7636 new_primary_smb = S_primary_ipv6;
7637 }
7638 #endif /* !TARGET_OS_IPHONE */
7639
7640 }
7641 else if (S_primary_ipv6 != NULL) {
7642 new_primary_dns = S_primary_ipv6;
7643 new_primary_proxies = S_primary_ipv6;
7644 #if !TARGET_OS_IPHONE
7645 new_primary_smb = S_primary_ipv6;
7646 #endif /* !TARGET_OS_IPHONE */
7647 }
7648 else if (S_primary_ipv4 != NULL) {
7649 new_primary_dns = S_primary_ipv4;
7650 new_primary_proxies = S_primary_ipv4;
7651 #if !TARGET_OS_IPHONE
7652 new_primary_smb = S_primary_ipv4;
7653 #endif /* !TARGET_OS_IPHONE */
7654 }
7655
7656 if (set_new_primary(&S_primary_dns, new_primary_dns, "DNS")) {
7657 dns_changed = TRUE;
7658 dnsinfo_changed = TRUE;
7659 }
7660 if (set_new_primary(&S_primary_proxies, new_primary_proxies,
7661 "Proxies")) {
7662 proxies_changed = TRUE;
7663 }
7664 #if !TARGET_OS_IPHONE
7665 if (set_new_primary(&S_primary_smb, new_primary_smb, "SMB")) {
7666 smb_changed = TRUE;
7667 }
7668 #endif /* !TARGET_OS_IPHONE */
7669 }
7670
7671 if (!proxies_changed && dnsinfo_changed
7672 && ((G_supplemental_proxies_follow_dns != NULL)
7673 && CFBooleanGetValue(G_supplemental_proxies_follow_dns))) {
7674 proxies_changed = TRUE;
7675 }
7676
7677 changes_state = nwi_state_diff(old_nwi_state, S_nwi_state);
7678
7679 if (global_ipv4_changed || global_ipv6_changed
7680 || dnsinfo_changed || reachability_changed) {
7681 if (S_nwi_state != NULL) {
7682 S_nwi_state->generation_count = mach_absolute_time();
7683 if (global_ipv4_changed || global_ipv6_changed
7684 || reachability_changed) {
7685 SCNetworkReachabilityFlags reach_flags_v4 = 0;
7686 SCNetworkReachabilityFlags reach_flags_v6 = 0;
7687
7688 GetReachabilityFlagsFromTransientServices(services_info,
7689 &reach_flags_v4,
7690 &reach_flags_v6);
7691
7692 _nwi_state_set_reachability_flags(S_nwi_state, reach_flags_v4,
7693 reach_flags_v6);
7694 }
7695
7696 /* Update the per-interface generation count */
7697 _nwi_state_update_interface_generations(old_nwi_state, S_nwi_state,
7698 changes_state);
7699 }
7700
7701 if (update_nwi(S_nwi_state)) {
7702 changes |= NETWORK_CHANGE_NET;
7703
7704 /*
7705 * the DNS configuration includes per-resolver configuration
7706 * reachability flags that are based on the nwi state. Let's
7707 * make sure that we check for changes
7708 */
7709 dnsinfo_changed = TRUE;
7710 }
7711 }
7712 if (dns_changed) {
7713 if (update_dns(services_info, S_primary_dns, &keys)) {
7714 changes |= NETWORK_CHANGE_DNS;
7715 dnsinfo_changed = TRUE;
7716 } else {
7717 dns_changed = FALSE;
7718 }
7719 }
7720 if (dnsinfo_changed) {
7721 if (update_dnsinfo(services_info, S_primary_dns,
7722 &keys, service_order)) {
7723 changes |= NETWORK_CHANGE_DNS;
7724 } else {
7725 dnsinfo_changed = FALSE;
7726 }
7727 }
7728 if (proxies_changed) {
7729 // if proxy change OR supplemental Proxies follow supplemental DNS
7730 if (update_proxies(services_info, S_primary_proxies,
7731 &keys, service_order)) {
7732 changes |= NETWORK_CHANGE_PROXY;
7733 } else {
7734 proxies_changed = FALSE;
7735 }
7736 }
7737 #if !TARGET_OS_IPHONE
7738 if (smb_changed) {
7739 if (update_smb(services_info, S_primary_smb, &keys)) {
7740 changes |= NETWORK_CHANGE_SMB;
7741 } else {
7742 smb_changed = FALSE;
7743 }
7744 }
7745 #endif /* !TARGET_OS_IPHONE */
7746 my_CFRelease(&service_changes);
7747 my_CFRelease(&services_info);
7748
7749 if (changes != 0) {
7750 network_change_msg =
7751 generate_log_changes(changes_state,
7752 dns_changed,
7753 dnsinfo_changed,
7754 old_primary_dns,
7755 proxies_changed,
7756 old_primary_proxy,
7757 #if !TARGET_OS_IPHONE
7758 smb_changed,
7759 old_primary_smb
7760 #else // !TARGET_OS_IPHONE
7761 FALSE, // smb_changed
7762 NULL // old_primary_smb
7763 #endif // !TARGET_OS_IPHONE
7764 );
7765 }
7766
7767 keyChangeListApplyToStore(&keys, session);
7768 my_CFRelease(&old_primary_dns);
7769 my_CFRelease(&old_primary_proxy);
7770 #if !TARGET_OS_IPHONE
7771 my_CFRelease(&old_primary_smb);
7772 #endif // !TARGET_OS_IPHONE
7773
7774 if (changes != 0) {
7775 dispatch_async(__network_change_queue(), ^{
7776 post_network_change(changes);
7777 });
7778 }
7779
7780 if ((network_change_msg != NULL)
7781 && (CFStringGetLength(network_change_msg) != 0)) {
7782 my_log(LOG_NOTICE, "network changed:%@", network_change_msg);
7783 } else if (keyChangeListActive(&keys)) {
7784 my_log(LOG_NOTICE, "network changed.");
7785 } else {
7786 my_log(LOG_DEBUG, "network event w/no changes");
7787 }
7788
7789 my_CFRelease(&network_change_msg);
7790
7791 if (changes_state != NULL) {
7792 nwi_state_release(changes_state);
7793 }
7794 if (old_nwi_state != NULL) {
7795 nwi_state_release(old_nwi_state);
7796 }
7797 keyChangeListFree(&keys);
7798
7799 /* release the name/index cache */
7800 my_if_freenameindex();
7801
7802 return;
7803 }
7804
7805 static void
7806 IPMonitorNotify(SCDynamicStoreRef session, CFArrayRef changed_keys,
7807 void * not_used)
7808 {
7809 IPMonitorProcessChanges(session, changed_keys, NULL);
7810 return;
7811 }
7812
7813 static void
7814 watch_proxies()
7815 {
7816 #if !TARGET_OS_IPHONE
7817 const _scprefs_observer_type type = scprefs_observer_type_mcx;
7818 #else
7819 const _scprefs_observer_type type = scprefs_observer_type_global;
7820 #endif
7821 static dispatch_queue_t proxy_cb_queue;
7822
7823 proxy_cb_queue = dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL);
7824 _scprefs_observer_watch(type,
7825 "com.apple.SystemConfiguration.plist",
7826 proxy_cb_queue,
7827 ^{
7828 SCDynamicStoreNotifyValue(NULL, S_state_global_proxies);
7829 notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
7830 my_log(LOG_DEBUG, "IPMonitor: Notifying:\n%@",
7831 S_state_global_proxies);
7832 });
7833 return;
7834 }
7835
7836 #include "IPMonitorControlPrefs.h"
7837
7838 __private_extern__ SCLoggerRef
7839 my_log_get_logger()
7840 {
7841 return (S_IPMonitor_logger);
7842 }
7843
7844 static void
7845 prefs_changed(__unused SCPreferencesRef prefs)
7846 {
7847 if (S_bundle_logging_verbose || IPMonitorControlPrefsIsVerbose()) {
7848 S_IPMonitor_debug = kDebugFlagDefault;
7849 S_IPMonitor_verbose = TRUE;
7850 SCLoggerSetFlags(S_IPMonitor_logger, kSCLoggerFlagsFile | kSCLoggerFlagsDefault);
7851 my_log(LOG_DEBUG, "IPMonitor: Setting logging verbose mode on.");
7852 } else {
7853 my_log(LOG_DEBUG, "IPMonitor: Setting logging verbose mode off.");
7854 S_IPMonitor_debug = 0;
7855 S_IPMonitor_verbose = FALSE;
7856 SCLoggerSetFlags(S_IPMonitor_logger, kSCLoggerFlagsDefault);
7857 }
7858 return;
7859 }
7860
7861 #define LOGGER_ID CFSTR("com.apple.networking.IPMonitor")
7862 static void
7863 my_log_init()
7864 {
7865 if (S_IPMonitor_logger != NULL) {
7866 return;
7867 }
7868 S_IPMonitor_logger = SCLoggerCreate(LOGGER_ID);
7869 return;
7870
7871 }
7872
7873
7874 #if !TARGET_IPHONE_SIMULATOR
7875 static int
7876 flush_routes(int s)
7877 {
7878 char * buf = NULL;
7879 int i;
7880 char * lim;
7881 #define N_MIB 6
7882 int mib[N_MIB];
7883 size_t needed;
7884 char * next;
7885 struct rt_msghdr * rtm;
7886 struct sockaddr_in *sin;
7887
7888 mib[0] = CTL_NET;
7889 mib[1] = PF_ROUTE;
7890 mib[2] = 0;
7891 mib[3] = AF_INET;
7892 mib[4] = NET_RT_FLAGS;
7893 mib[5] = RTF_STATIC | RTF_DYNAMIC;
7894 for (i = 0; i < 3; i++) {
7895 if (sysctl(mib, N_MIB, NULL, &needed, NULL, 0) < 0) {
7896 break;
7897 }
7898 if ((buf = malloc(needed)) == NULL) {
7899 break;
7900 }
7901 if (sysctl(mib, N_MIB, buf, &needed, NULL, 0) >= 0) {
7902 break;
7903 }
7904 free(buf);
7905 buf = NULL;
7906 }
7907 if (buf == NULL) {
7908 return (-1);
7909 }
7910 lim = buf + needed;
7911 for (next = buf; next < lim; next += rtm->rtm_msglen) {
7912 uint32_t addr;
7913
7914 /* ALIGN: assume kernel provides necessary alignment */
7915 rtm = (struct rt_msghdr *)(void *)next;
7916 sin = (struct sockaddr_in *)(rtm + 1);
7917
7918 addr = ntohl(sin->sin_addr.s_addr);
7919 if (IN_LOOPBACK(addr)) {
7920 my_log(LOG_DEBUG,
7921 "IPMonitor: flush_routes: ignoring loopback route");
7922 continue;
7923 }
7924 if (IN_LOCAL_GROUP(addr)) {
7925 my_log(LOG_DEBUG,
7926 "IPMonitor: flush_routes: ignoring multicast route");
7927 continue;
7928 }
7929 rtm->rtm_type = RTM_DELETE;
7930 rtm->rtm_seq = ++rtm_seq;
7931 if (write(s, rtm, rtm->rtm_msglen) < 0) {
7932 my_log(LOG_DEBUG,
7933 "IPMonitor: flush_routes: removing route for "
7934 IP_FORMAT " failed, %s",
7935 IP_LIST(&sin->sin_addr),
7936 strerror(errno));
7937 }
7938 else {
7939 my_log(LOG_DEBUG,
7940 "IPMonitor: flush_routes: removed route for " IP_FORMAT,
7941 IP_LIST(&sin->sin_addr));
7942 }
7943 }
7944 free(buf);
7945 return (0);
7946 }
7947
7948 static void
7949 flush_inet_routes(void)
7950 {
7951 int s;
7952
7953 s = open_routing_socket();
7954 if (s != -1) {
7955 flush_routes(s);
7956 close(s);
7957 }
7958 }
7959
7960 #else /* !TARGET_IPHONE_SIMULATOR */
7961
7962 static void
7963 flush_inet_routes(void)
7964 {
7965 }
7966
7967 #endif /* !TARGET_IPHONE_SIMULATOR */
7968
7969
7970
7971 static void
7972 ip_plugin_init()
7973 {
7974 CFMutableArrayRef keys = NULL;
7975 CFStringRef pattern;
7976 CFMutableArrayRef patterns = NULL;
7977 CFRunLoopSourceRef rls = NULL;
7978
7979 if (S_is_network_boot() != 0) {
7980 S_netboot = TRUE;
7981 }
7982 else {
7983 /* flush routes */
7984 flush_inet_routes();
7985 }
7986
7987 if (S_is_scoped_routing_enabled() != 0) {
7988 S_scopedroute = TRUE;
7989 }
7990
7991 if (S_is_scoped_v6_routing_enabled() != 0) {
7992 S_scopedroute_v6 = TRUE;
7993 }
7994
7995 S_session = SCDynamicStoreCreate(NULL, CFSTR("IPMonitor"),
7996 IPMonitorNotify, NULL);
7997 if (S_session == NULL) {
7998 my_log(LOG_ERR,
7999 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8000 SCErrorString(SCError()));
8001 return;
8002 }
8003 S_state_global_ipv4
8004 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8005 kSCDynamicStoreDomainState,
8006 kSCEntNetIPv4);
8007 S_state_global_ipv6
8008 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8009 kSCDynamicStoreDomainState,
8010 kSCEntNetIPv6);
8011 S_state_global_dns
8012 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8013 kSCDynamicStoreDomainState,
8014 kSCEntNetDNS);
8015 S_state_global_proxies
8016 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8017 kSCDynamicStoreDomainState,
8018 kSCEntNetProxies);
8019 #if !TARGET_OS_IPHONE
8020 S_state_global_smb
8021 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8022 kSCDynamicStoreDomainState,
8023 kSCEntNetSMB);
8024 #endif /* !TARGET_OS_IPHONE */
8025 S_setup_global_ipv4
8026 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8027 kSCDynamicStoreDomainSetup,
8028 kSCEntNetIPv4);
8029 S_state_service_prefix
8030 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
8031 kSCDynamicStoreDomainState,
8032 CFSTR(""),
8033 NULL);
8034 S_setup_service_prefix
8035 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
8036 kSCDynamicStoreDomainSetup,
8037 CFSTR(""),
8038 NULL);
8039 S_service_state_dict
8040 = CFDictionaryCreateMutable(NULL, 0,
8041 &kCFTypeDictionaryKeyCallBacks,
8042 &kCFTypeDictionaryValueCallBacks);
8043
8044 S_ipv4_service_rank_dict
8045 = CFDictionaryCreateMutable(NULL, 0,
8046 &kCFTypeDictionaryKeyCallBacks,
8047 &kCFTypeDictionaryValueCallBacks);
8048
8049 S_ipv6_service_rank_dict
8050 = CFDictionaryCreateMutable(NULL, 0,
8051 &kCFTypeDictionaryKeyCallBacks,
8052 &kCFTypeDictionaryValueCallBacks);
8053
8054 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
8055 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
8056
8057 /* register for State: and Setup: per-service notifications */
8058 add_service_keys(kSCCompAnyRegex, keys, patterns);
8059
8060 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetPPP);
8061 CFArrayAppendValue(patterns, pattern);
8062 CFRelease(pattern);
8063
8064 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
8065 CFArrayAppendValue(patterns, pattern);
8066 CFRelease(pattern);
8067
8068 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetInterface);
8069 CFArrayAppendValue(patterns, pattern);
8070 CFRelease(pattern);
8071
8072 /* register for State: per-service PPP/VPN/IPSec status notifications */
8073 add_transient_status_keys(kSCCompAnyRegex, patterns);
8074
8075 /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
8076 CFArrayAppendValue(keys, S_setup_global_ipv4);
8077
8078 /* add notifier for multicast DNS configuration (Bonjour/.local) */
8079 S_multicast_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
8080 kSCDynamicStoreDomainState,
8081 kSCCompNetwork,
8082 CFSTR(kDNSServiceCompMulticastDNS));
8083 CFArrayAppendValue(keys, S_multicast_resolvers);
8084
8085 /* add notifier for private DNS configuration (Back to My Mac) */
8086 S_private_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
8087 kSCDynamicStoreDomainState,
8088 kSCCompNetwork,
8089 CFSTR(kDNSServiceCompPrivateDNS));
8090 CFArrayAppendValue(keys, S_private_resolvers);
8091
8092 if (!SCDynamicStoreSetNotificationKeys(S_session, keys, patterns)) {
8093 my_log(LOG_ERR,
8094 "IPMonitor ip_plugin_init "
8095 "SCDynamicStoreSetNotificationKeys failed: %s",
8096 SCErrorString(SCError()));
8097 goto done;
8098 }
8099
8100 rls = SCDynamicStoreCreateRunLoopSource(NULL, S_session, 0);
8101 if (rls == NULL) {
8102 my_log(LOG_ERR,
8103 "IPMonitor ip_plugin_init "
8104 "SCDynamicStoreCreateRunLoopSource failed: %s",
8105 SCErrorString(SCError()));
8106 goto done;
8107 }
8108
8109 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
8110 CFRelease(rls);
8111
8112 /* initialize dns configuration */
8113 (void)dns_configuration_set(NULL, NULL, NULL, NULL, NULL);
8114 #if !TARGET_OS_IPHONE
8115 empty_dns();
8116 #endif /* !TARGET_OS_IPHONE */
8117 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_dns);
8118
8119 #if !TARGET_OS_IPHONE
8120 /* initialize SMB configuration */
8121 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_smb);
8122 #endif /* !TARGET_OS_IPHONE */
8123
8124 watch_proxies();
8125
8126 done:
8127 my_CFRelease(&keys);
8128 my_CFRelease(&patterns);
8129 return;
8130 }
8131
8132 __private_extern__
8133 void
8134 prime_IPMonitor()
8135 {
8136 /* initialize multicast route */
8137 update_ipv4(NULL, NULL, NULL);
8138 return;
8139 }
8140
8141 static boolean_t
8142 S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
8143 boolean_t def)
8144 {
8145 CFBooleanRef b;
8146 boolean_t ret = def;
8147
8148 b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
8149 if (b != NULL) {
8150 ret = CFBooleanGetValue(b);
8151 }
8152 return (ret);
8153 }
8154
8155 #if !TARGET_IPHONE_SIMULATOR
8156 #include "IPMonitorControlServer.h"
8157
8158 static void
8159 InterfaceRankChanged(void * info)
8160 {
8161 CFDictionaryRef assertions = NULL;
8162 CFArrayRef changes;
8163
8164 changes = IPMonitorControlServerCopyInterfaceRankInformation(&assertions);
8165 if (S_if_rank_dict != NULL) {
8166 CFRelease(S_if_rank_dict);
8167 }
8168 S_if_rank_dict = assertions;
8169 if (changes != NULL) {
8170 IPMonitorProcessChanges(S_session, NULL, changes);
8171 CFRelease(changes);
8172 }
8173 return;
8174 }
8175
8176 static void
8177 StartIPMonitorControlServer(void)
8178 {
8179 CFRunLoopSourceContext context;
8180 CFRunLoopSourceRef rls;
8181
8182 bzero(&context, sizeof(context));
8183 context.perform = InterfaceRankChanged;
8184 rls = CFRunLoopSourceCreate(NULL, 0, &context);
8185 if (IPMonitorControlServerStart(CFRunLoopGetCurrent(), rls,
8186 &S_bundle_logging_verbose) == FALSE) {
8187 my_log(LOG_ERR, "IPMonitorControlServerStart failed");
8188 }
8189 else {
8190 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls,
8191 kCFRunLoopDefaultMode);
8192 }
8193 CFRelease(rls);
8194 return;
8195 }
8196
8197 #endif /* !TARGET_IPHONE_SIMULATOR */
8198
8199 __private_extern__
8200 void
8201 load_IPMonitor(CFBundleRef bundle, Boolean bundleVerbose)
8202 {
8203 CFDictionaryRef info_dict;
8204
8205 info_dict = CFBundleGetInfoDictionary(bundle);
8206
8207 if (info_dict != NULL) {
8208 S_append_state
8209 = S_get_plist_boolean(info_dict,
8210 CFSTR("AppendStateArrayToSetupArray"),
8211 FALSE);
8212 }
8213 if (bundleVerbose) {
8214 S_IPMonitor_debug = kDebugFlagDefault;
8215 S_bundle_logging_verbose = bundleVerbose;
8216 S_IPMonitor_verbose = TRUE;
8217 }
8218
8219 my_log_init();
8220
8221 /* register to receive changes to verbose and read the initial setting */
8222 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed);
8223 prefs_changed(NULL);
8224
8225
8226 load_DNSConfiguration(bundle, // bundle
8227 S_IPMonitor_logger, // SCLogger
8228 &S_bundle_logging_verbose, // bundleVerbose
8229 ^(Boolean inSync) { // syncHandler
8230 dispatch_async(__network_change_queue(), ^{
8231 S_dnsinfo_synced = inSync;
8232
8233 if (inSync &&
8234 ((S_network_change_needed & NETWORK_CHANGE_DNS) == 0)) {
8235 // all of the mDNSResponder ack's should result
8236 // in a [new] network change being posted
8237 post_network_change(NETWORK_CHANGE_DNS);
8238 } else {
8239 post_network_change_when_ready();
8240 }
8241 });
8242 });
8243
8244 load_NetworkInformation(bundle, // bundle
8245 S_IPMonitor_logger, // SCLogger
8246 &S_bundle_logging_verbose, // bundleVerbose
8247 ^(Boolean inSync) { // syncHandler
8248 dispatch_async(__network_change_queue(), ^{
8249 S_nwi_synced = inSync;
8250 post_network_change_when_ready();
8251 });
8252 });
8253 #if !TARGET_IPHONE_SIMULATOR
8254 StartIPMonitorControlServer();
8255 #endif /* !TARGET_OS_IPHONE */
8256
8257 dns_configuration_init(bundle);
8258
8259 proxy_configuration_init(bundle);
8260
8261 ip_plugin_init();
8262
8263 #if !TARGET_OS_IPHONE
8264 if (S_session != NULL) {
8265 dns_configuration_monitor(S_session, IPMonitorNotify);
8266 }
8267 #endif /* !TARGET_OS_IPHONE */
8268
8269 #if !TARGET_IPHONE_SIMULATOR
8270 load_hostname((S_IPMonitor_debug & kDebugFlag1) != 0);
8271 #endif /* !TARGET_IPHONE_SIMULATOR */
8272
8273 #if !TARGET_OS_IPHONE
8274 load_smb_configuration((S_IPMonitor_debug & kDebugFlag1) != 0);
8275 #endif /* !TARGET_OS_IPHONE */
8276
8277 return;
8278 }
8279
8280
8281 #pragma mark -
8282 #pragma mark Standalone test code
8283
8284
8285 #ifdef TEST_IPMONITOR
8286
8287 #include "dns-configuration.c"
8288
8289 #if !TARGET_IPHONE_SIMULATOR
8290 #include "set-hostname.c"
8291 #endif /* !TARGET_IPHONE_SIMULATOR */
8292
8293 int
8294 main(int argc, char **argv)
8295 {
8296 _sc_log = FALSE;
8297
8298 S_IPMonitor_debug = kDebugFlag1;
8299 if (argc > 1) {
8300 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
8301 }
8302
8303 load_IPMonitor(CFBundleGetMainBundle(), FALSE);
8304 prime_IPMonitor();
8305 S_IPMonitor_debug = kDebugFlag1;
8306 CFRunLoopRun();
8307 /* not reached */
8308 exit(0);
8309 return 0;
8310 }
8311 #endif /* TEST_IPMONITOR */
8312
8313 #ifdef TEST_ROUTELIST
8314 #include "dns-configuration.c"
8315 #include "set-hostname.c"
8316
8317 struct route {
8318 const char * dest;
8319 int prefix_length;
8320 const char * gateway;
8321 const char * ifname;
8322 };
8323
8324 #endif
8325
8326 #ifdef TEST_IPV4_ROUTELIST
8327
8328 typedef struct {
8329 const char * addr;
8330 int prefix_length;
8331 const char * dest;
8332 const char * router;
8333 const char * ifname;
8334 Rank rank;
8335 const CFStringRef * primary_rank;
8336 struct route * additional_routes;
8337 int additional_routes_count;
8338 struct route * excluded_routes;
8339 int excluded_routes_count;
8340 } IPv4ServiceContents;
8341
8342 typedef const IPv4ServiceContents * IPv4ServiceContentsRef;
8343
8344 struct route loop_routelist[] = {
8345 { "1.1.1.1", 32, "1.1.1.2", NULL },
8346 { "1.1.1.2", 32, "1.1.1.3", NULL },
8347 { "1.1.1.3", 32, "1.1.1.4", NULL },
8348 { "1.1.1.4", 32, "1.1.1.5", NULL },
8349 { "1.1.1.5", 32, "1.1.1.6", NULL },
8350 { "1.1.1.6", 32, "1.1.1.7", NULL },
8351 { "1.1.1.7", 32, "1.1.1.8", NULL },
8352 { "1.1.1.8", 32, "1.1.1.9", NULL },
8353 { "1.1.1.9", 32, "1.1.1.10", NULL },
8354 { "1.1.1.10", 32, "1.1.1.11", NULL },
8355 { "1.1.1.11", 32, "1.1.1.1", NULL },
8356 };
8357
8358 struct route vpn_routelist[] = {
8359 { "10.1.3.0", 24, "17.153.46.24", NULL },
8360 { "10.1.4.0", 24, "17.153.46.24", NULL },
8361 { "10.1.5.0", 24, "17.153.46.24", NULL },
8362 { "10.1.6.0", 24, "17.153.46.24", NULL },
8363 { "10.1.7.0", 24, "17.153.46.24", NULL },
8364 { "10.16.0.0", 12, "17.153.46.24", NULL },
8365 { "10.45.0.0", 16, "17.153.46.24", NULL },
8366 { "10.53.0.0", 16, "17.153.46.24", NULL },
8367 { "10.70.0.0", 15, "17.153.46.24", NULL },
8368 { "10.74.0.0", 15, "17.153.46.24", NULL },
8369 { "10.90.0.0", 15, "17.153.46.24", NULL },
8370 { "10.91.0.0", 16, "17.153.46.24", NULL },
8371 { "10.100.0.0", 16, "17.153.46.24", NULL },
8372 { "10.113.0.0", 16, "17.153.46.24", NULL },
8373 { "10.128.0.0", 9, "17.153.46.24", NULL },
8374 { "17.0.0.0", 9, "17.153.46.24", NULL },
8375 { "17.34.0.0", 16, "17.153.46.24", NULL },
8376 { "17.112.156.53", 32, "17.153.46.24", NULL },
8377 { "17.128.0.0", 10, "17.153.46.24", NULL },
8378 { "17.149.0.121", 32, "17.153.46.24", NULL },
8379 { "17.149.7.200", 32, "17.153.46.24", NULL },
8380 { "17.153.46.24", 32, "17.153.46.24", NULL },
8381 { "17.192.0.0", 12, "17.153.46.24", NULL },
8382 { "17.208.0.0", 15, "17.153.46.24", NULL },
8383 { "17.211.0.0", 16, "17.153.46.24", NULL },
8384 { "17.212.0.0", 14, "17.153.46.24", NULL },
8385 { "17.216.0.0", 13, "17.153.46.24", NULL },
8386 { "17.224.0.0", 12, "17.153.46.24", NULL },
8387 { "17.240.0.0", 16, "17.153.46.24", NULL },
8388 { "17.241.0.0", 16, "17.153.46.24", NULL },
8389 { "17.248.0.0", 14, "17.153.46.24", NULL },
8390 { "17.251.104.200", 32, "17.153.46.24", NULL },
8391 { "17.252.0.0", 16, "17.153.46.24", NULL },
8392 { "17.253.0.0", 16, "17.153.46.24", NULL },
8393 { "17.254.0.0", 16, "17.153.46.24", NULL },
8394 { "17.255.0.0", 16, "17.153.46.24", NULL },
8395 { "151.193.141.0", 27, "17.153.46.24", NULL },
8396 { "172.16.2.0", 24, "17.153.46.24", NULL },
8397 { "192.35.50.0", 24, "17.153.46.24", NULL },
8398 { "204.179.20.0", 24, "17.153.46.24", NULL },
8399 { "206.112.116.0", 24, "17.153.46.24", NULL },
8400 };
8401
8402 struct route vpn_routelist_ext[] = {
8403 { "17.151.63.82", 32, "10.0.0.1", "en0" },
8404 { "17.151.63.81", 32, "17.151.63.81", "en0" },
8405 { "17.151.63.80", 32, NULL, NULL },
8406 { "17.1.0.0", 16, NULL, NULL },
8407 { "17.2.0.0", 24, NULL, NULL },
8408 { "10.0.0.0", 24, NULL, NULL },
8409 };
8410
8411 /*
8412 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
8413 */
8414 const IPv4ServiceContents en0_10 = {
8415 "10.0.0.10", 24, NULL, "10.0.0.1", "en0", 10, NULL, NULL, 0, NULL, 0
8416 };
8417
8418 const IPv4ServiceContents en0_15 = {
8419 "10.0.0.19", 24, NULL, "10.0.0.1", "en0", 15, NULL, NULL, 0, NULL, 0
8420 };
8421
8422 const IPv4ServiceContents en0_30 = {
8423 "10.0.0.11", 24, NULL, "10.0.0.1", "en0", 30, NULL, NULL, 0, NULL, 0
8424 };
8425
8426 const IPv4ServiceContents en0_40 = {
8427 "10.0.0.12", 24, NULL, "10.0.0.1", "en0", 40, NULL, NULL, 0, NULL, 0
8428 };
8429
8430 const IPv4ServiceContents en0_50 = {
8431 "10.0.0.13", 24, NULL, "10.0.0.1", "en0", 50, NULL, NULL, 0, NULL, 0
8432 };
8433
8434 const IPv4ServiceContents en0_110 = {
8435 "192.168.2.10", 24, NULL, "192.168.2.1", "en0", 110, NULL, NULL, 0, NULL, 0
8436 };
8437
8438 const IPv4ServiceContents en0_1 = {
8439 "17.202.40.191", 22, NULL, "17.202.20.1", "en0", 1, NULL, NULL, 0, NULL, 0
8440 };
8441
8442 const IPv4ServiceContents en1_20 = {
8443 "10.0.0.20", 24, NULL, "10.0.0.1", "en1", 20, NULL, NULL, 0, NULL, 0
8444 };
8445
8446 const IPv4ServiceContents en1_2 = {
8447 "17.202.42.24", 22, NULL, "17.202.20.1", "en1", 2, NULL, NULL, 0, NULL, 0
8448 };
8449
8450 const IPv4ServiceContents en1_125 = {
8451 "192.168.2.20", 24, NULL, "192.168.2.1", "en1", 125, NULL, NULL, 0, NULL, 0
8452 };
8453
8454 const IPv4ServiceContents fw0_25 = {
8455 "192.168.2.30", 24, NULL, "192.168.2.1", "fw0", 25, NULL, NULL, 0, NULL, 0
8456 };
8457
8458 const IPv4ServiceContents fw0_21 = {
8459 "192.168.3.30", 24, NULL, "192.168.3.1", "fw0", 21, NULL, NULL, 0, NULL, 0
8460 };
8461
8462 const IPv4ServiceContents ppp0_0_1 = {
8463 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL, NULL, 0, NULL, 0
8464 };
8465
8466 const IPv4ServiceContents utun0 = {
8467 "17.153.46.24", -1, "17.153.46.24", "17.153.46.24", "utun0", 20, NULL, vpn_routelist, countof(vpn_routelist), vpn_routelist_ext, countof(vpn_routelist_ext)
8468 };
8469
8470 const IPv4ServiceContents en0_test6 = {
8471 "17.202.42.113", 22, NULL, "17.202.40.1", "en0", 2, NULL, NULL, 0, NULL, 0
8472 };
8473
8474 const IPv4ServiceContents en1_test6 = {
8475 "17.202.42.111", 22, NULL, "17.202.40.1", "en1", 3, NULL, NULL, 0, NULL, 0
8476 };
8477
8478 const IPv4ServiceContents en2_test6 = {
8479 "17.255.98.164", 20, NULL, "17.255.96.1", "en2", 1, NULL, NULL, 0, NULL, 0
8480 };
8481
8482 const IPv4ServiceContents en0_test7 = {
8483 "17.202.42.113", 22, NULL, "17.202.40.1", "en0", 3, NULL, NULL, 0, NULL, 0
8484 };
8485
8486 const IPv4ServiceContents en1_test7 = {
8487 "17.202.42.111", 22, NULL, "17.202.40.1", "en1", 2, NULL, NULL, 0, NULL, 0
8488 };
8489
8490 const IPv4ServiceContents en2_test7 = {
8491 "17.255.98.164", 20, NULL, "17.255.96.1", "en2", 1, NULL, NULL, 0, NULL, 0
8492 };
8493
8494 const IPv4ServiceContents fw0_test6_and_7 = {
8495 "169.254.11.33", 16, NULL, NULL, "fw0", 0x0ffffff, NULL, NULL, 0, NULL, 0
8496 };
8497
8498 const IPv4ServiceContents en0_10_last = {
8499 "10.0.0.10", 24, NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast, NULL, 0, NULL, 0
8500 };
8501
8502 const IPv4ServiceContents en0_10_never = {
8503 "10.0.0.10", 24, NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever, NULL, 0, NULL, 0
8504 };
8505
8506 const IPv4ServiceContents en1_20_first = {
8507 "10.0.0.20", 24, NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst, NULL, 0, NULL, 0
8508 };
8509
8510 const IPv4ServiceContents en1_20_never = {
8511 "10.0.0.20", 24, NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever, NULL, 0, NULL, 0
8512 };
8513
8514 const IPv4ServiceContents en1_20_other_never = {
8515 "192.168.2.50", 24, NULL, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever, NULL, 0, NULL, 0
8516 };
8517
8518 const IPv4ServiceContents en0_linklocal = {
8519 "169.254.22.44", 16, NULL, NULL, "en0", 0xfffff, NULL, NULL, 0, NULL, 0
8520 };
8521
8522 const IPv4ServiceContents en0_route_loop = {
8523 "192.168.130.16", 24, NULL, "192.168.130.1", "en0", 2, NULL, loop_routelist, countof(loop_routelist), NULL, 0
8524 };
8525
8526 typedef struct {
8527 const char * name;
8528 IPv4ServiceContentsRef test[];
8529 } IPv4RouteTest, * IPv4RouteTestRef;
8530
8531 static IPv4RouteTest test1 = {
8532 "test1",
8533 {
8534 &en0_40,
8535 &en0_15,
8536 &fw0_25,
8537 &en0_30,
8538 &en1_20,
8539 &en0_50,
8540 &en0_10,
8541 NULL
8542 }
8543 };
8544
8545 static IPv4RouteTest test2 = {
8546 "test2",
8547 {
8548 &en0_40,
8549 &fw0_25,
8550 &en0_30,
8551 &en1_20,
8552 &en0_50,
8553 &en0_10,
8554 NULL
8555 }
8556 };
8557
8558 static IPv4RouteTest test3 = {
8559 "test3",
8560 {
8561 &en0_40,
8562 &en1_20,
8563 &en0_50,
8564 &en0_10,
8565 &en0_110,
8566 &en1_125,
8567 &fw0_25,
8568 &fw0_21,
8569 &en0_40,
8570 &en0_30,
8571 NULL
8572 }
8573 };
8574
8575 static IPv4RouteTest test4 = {
8576 "test4",
8577 {
8578 &en0_1,
8579 &en0_40,
8580 &en0_30,
8581 &en1_20,
8582 &en1_2,
8583 NULL
8584 }
8585 };
8586
8587 static IPv4RouteTest test5 = {
8588 "test5",
8589 {
8590 &ppp0_0_1,
8591 &en0_1,
8592 &en0_40,
8593 &en0_30,
8594 &en1_20,
8595 &en1_2,
8596 NULL
8597 }
8598 };
8599
8600 static IPv4RouteTest test6 = {
8601 "test6",
8602 {
8603 &en0_test6,
8604 &en1_test6,
8605 &en2_test6,
8606 &fw0_test6_and_7,
8607 NULL
8608 }
8609 };
8610
8611 static IPv4RouteTest test7 = {
8612 "test7",
8613 {
8614 &en0_test7,
8615 &en1_test7,
8616 &en2_test7,
8617 &fw0_test6_and_7,
8618 NULL
8619 }
8620 };
8621
8622 static IPv4RouteTest test8 = {
8623 "test8",
8624 {
8625 &en0_10,
8626 &en1_20,
8627 NULL
8628 }
8629 };
8630
8631 static IPv4RouteTest test9 = {
8632 "test9",
8633 {
8634 &en0_10,
8635 &en1_20_first,
8636 &fw0_25,
8637 NULL
8638 }
8639 };
8640
8641 static IPv4RouteTest test10 = {
8642 "test10",
8643 {
8644 &en0_10_last,
8645 &en1_20,
8646 &fw0_25,
8647 NULL
8648 }
8649 };
8650
8651 static IPv4RouteTest test11 = {
8652 "test11",
8653 {
8654 &en0_10_never,
8655 &en1_20,
8656 &fw0_25,
8657 NULL
8658 }
8659 };
8660
8661 static IPv4RouteTest test12 = {
8662 "test12",
8663 {
8664 &en0_10,
8665 &en1_20,
8666 NULL
8667 }
8668 };
8669
8670 static IPv4RouteTest test13 = {
8671 "test13",
8672 {
8673 &en0_10,
8674 &en1_20_never,
8675 NULL
8676 }
8677 };
8678
8679 static IPv4RouteTest test14 = {
8680 "test14",
8681 {
8682 &en1_20_never,
8683 NULL
8684 }
8685 };
8686
8687 static IPv4RouteTest test15 = {
8688 "test15",
8689 {
8690 &en0_linklocal,
8691 NULL
8692 }
8693 };
8694
8695 static IPv4RouteTest test16 = {
8696 "test16",
8697 {
8698 &en0_10,
8699 &utun0,
8700 NULL
8701 }
8702 };
8703
8704 static IPv4RouteTest test17 = {
8705 "test17",
8706 {
8707 &en0_10,
8708 &en1_20_other_never,
8709 NULL
8710 }
8711 };
8712
8713 static IPv4RouteTest test18 = {
8714 "test18",
8715 {
8716 &en0_route_loop,
8717 NULL
8718 }
8719 };
8720
8721 static IPv4RouteTestRef ipv4_tests[] = {
8722 &test1,
8723 &test2,
8724 &test3,
8725 &test4,
8726 &test5,
8727 &test6,
8728 &test7,
8729 &test8,
8730 &test9,
8731 &test10,
8732 &test11,
8733 &test12,
8734 &test13,
8735 &test14,
8736 &test15,
8737 &test16,
8738 &test17,
8739 &test18,
8740 NULL
8741 };
8742
8743 static boolean_t
8744 ipv4_prefix_length_is_valid(int prefix_length)
8745 {
8746 if (prefix_length < 0 || prefix_length > IPV4_ROUTE_ALL_BITS_SET) {
8747 return (FALSE);
8748 }
8749 return (TRUE);
8750 }
8751
8752 static void
8753 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
8754 const char * str)
8755 {
8756 CFStringRef prop_val;
8757
8758 if (str == NULL) {
8759 return;
8760 }
8761 prop_val = CFStringCreateWithCString(NULL,
8762 str,
8763 kCFStringEncodingASCII);
8764 CFDictionarySetValue(dict, prop_name, prop_val);
8765 CFRelease(prop_val);
8766 return;
8767 }
8768
8769 static void
8770 dict_add_string_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
8771 const char * str)
8772 {
8773 CFArrayRef array;
8774 CFStringRef prop_val;
8775
8776 if (str == NULL) {
8777 return;
8778 }
8779 prop_val = CFStringCreateWithCString(NULL,
8780 str,
8781 kCFStringEncodingASCII);
8782 array = CFArrayCreate(NULL,
8783 (const void **)&prop_val, 1,
8784 &kCFTypeArrayCallBacks);
8785 CFRelease(prop_val);
8786 CFDictionarySetValue(dict, prop_name, array);
8787 CFRelease(array);
8788 return;
8789 }
8790
8791 static void
8792 dict_add_ip(CFMutableDictionaryRef dict, CFStringRef prop_name,
8793 struct in_addr ip)
8794 {
8795 CFStringRef str;
8796
8797 str = my_CFStringCreateWithInAddr(ip);
8798 CFDictionarySetValue(dict, prop_name, str);
8799 CFRelease(str);
8800 return;
8801 }
8802
8803 static void
8804 dict_add_ip_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
8805 struct in_addr ip)
8806 {
8807 CFArrayRef array;
8808 CFStringRef str;
8809
8810 str = my_CFStringCreateWithInAddr(ip);
8811 array = CFArrayCreate(NULL,
8812 (const void **)&str, 1,
8813 &kCFTypeArrayCallBacks);
8814 CFRelease(str);
8815 CFDictionarySetValue(dict, prop_name, array);
8816 CFRelease(array);
8817 return;
8818 }
8819
8820 static void
8821 dict_insert_routes(CFMutableDictionaryRef dict, CFStringRef prop_name,
8822 struct route * routes, int routes_count)
8823 {
8824 int i;
8825 CFMutableArrayRef route_list;
8826 struct route * scan;
8827
8828 if (routes == NULL || routes_count == 0) {
8829 return;
8830 }
8831 route_list = CFArrayCreateMutable(NULL, routes_count,
8832 &kCFTypeArrayCallBacks);
8833 for (i = 0, scan = routes; i < routes_count; i++, scan++) {
8834 struct in_addr mask;
8835 CFMutableDictionaryRef route_dict;
8836
8837 route_dict
8838 = CFDictionaryCreateMutable(NULL, 0,
8839 &kCFTypeDictionaryKeyCallBacks,
8840 &kCFTypeDictionaryValueCallBacks);
8841 dict_add_string(route_dict, kSCPropNetIPv4RouteDestinationAddress,
8842 scan->dest);
8843 if (ipv4_prefix_length_is_valid(scan->prefix_length)) {
8844 mask.s_addr = htonl(prefix_to_mask32(scan->prefix_length));
8845 dict_add_ip(route_dict, kSCPropNetIPv4RouteSubnetMask, mask);
8846 }
8847 dict_add_string(route_dict, kSCPropNetIPv4RouteGatewayAddress,
8848 scan->gateway);
8849 dict_add_string(route_dict, kSCPropNetIPv4RouteInterfaceName,
8850 scan->ifname);
8851 CFArrayAppendValue(route_list, route_dict);
8852 CFRelease(route_dict);
8853 }
8854 CFDictionarySetValue(dict, prop_name, route_list);
8855 CFRelease(route_list);
8856 return;
8857 }
8858
8859 static CFDictionaryRef
8860 make_IPv4_dict(IPv4ServiceContentsRef t)
8861 {
8862 CFMutableDictionaryRef dict;
8863
8864 dict = CFDictionaryCreateMutable(NULL, 0,
8865 &kCFTypeDictionaryKeyCallBacks,
8866 &kCFTypeDictionaryValueCallBacks);
8867 dict_add_string_as_array(dict, kSCPropNetIPv4Addresses, t->addr);
8868 if (ipv4_prefix_length_is_valid(t->prefix_length)) {
8869 struct in_addr mask;
8870
8871 mask.s_addr = htonl(prefix_to_mask32(t->prefix_length));
8872 dict_add_ip_as_array(dict, kSCPropNetIPv4SubnetMasks, mask);
8873 }
8874 dict_add_string_as_array(dict, kSCPropNetIPv4DestAddresses, t->dest);
8875 dict_add_string(dict, kSCPropNetIPv4Router, t->router);
8876 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
8877 dict_add_string(dict, kSCPropConfirmedInterfaceName, t->ifname);
8878 dict_insert_routes(dict, kSCPropNetIPv4AdditionalRoutes,
8879 t->additional_routes, t->additional_routes_count);
8880 dict_insert_routes(dict, kSCPropNetIPv4ExcludedRoutes,
8881 t->excluded_routes, t->excluded_routes_count);
8882 return (dict);
8883 }
8884
8885 typedef enum {
8886 kDirectionForwards = 0,
8887 kDirectionBackwards = 1
8888 } Direction;
8889
8890 typedef enum {
8891 kLogRouteDisabled = 0,
8892 kLogRouteEnabled = 1
8893 } LogRoute;
8894
8895 static IPv4RouteListRef
8896 make_IPv4RouteList_for_test(IPv4RouteListRef list,
8897 IPv4ServiceContentsRef test,
8898 LogRoute log_it)
8899 {
8900 CFDictionaryRef dict;
8901 IPv4RouteListRef r;
8902 Rank rank;
8903 Rank rank_assertion = kRankAssertionDefault;
8904 CFNumberRef rank_assertion_cf = NULL;
8905 Boolean rank_assertion_is_set = FALSE;
8906 IPv4RouteListRef ret = NULL;
8907 IPV4_ROUTES_BUF_DECL(routes);
8908
8909 dict = make_IPv4_dict(test);
8910 if (dict == NULL) {
8911 fprintf(stderr, "make_IPv4_dict failed\n");
8912 exit(1);
8913 }
8914 if (test->primary_rank != NULL) {
8915 rank_assertion
8916 = PrimaryRankGetRankAssertion(*test->primary_rank,
8917 &rank_assertion_is_set);
8918 if (rank_assertion_is_set) {
8919 rank_assertion_cf
8920 = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank_assertion);
8921 }
8922 }
8923 r = IPv4RouteListCreateWithDictionary(routes, dict,
8924 rank_assertion_cf);
8925 my_CFRelease(&rank_assertion_cf);
8926 if (r == NULL) {
8927 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
8928 exit(1);
8929 }
8930
8931 if (rank_assertion == kRankAssertionScoped) {
8932 rank_assertion = kRankAssertionNever;
8933 }
8934 rank = RankMake(test->rank, rank_assertion);
8935 if (log_it == kLogRouteEnabled
8936 && (S_IPMonitor_debug & kDebugFlag4) != 0) {
8937 CFStringRef descr;
8938
8939 descr = IPv4RouteListCopyDescription(r);
8940 SCLog(TRUE, LOG_NOTICE, CFSTR("Adding %@"), descr);
8941 CFRelease(descr);
8942 }
8943 ret = IPv4RouteListAddRouteList(list, 1, r, rank);
8944 if (r != routes) {
8945 free(r);
8946 }
8947 CFRelease(dict);
8948 return (ret);
8949 }
8950
8951 static IPv4RouteListRef
8952 make_IPv4RouteList(IPv4ServiceContentsRef * test, Direction direction,
8953 LogRoute log_it)
8954 {
8955 IPv4RouteListRef ret = NULL;
8956 IPv4ServiceContentsRef * scan;
8957
8958 switch (direction) {
8959 case kDirectionBackwards:
8960 for (scan = test; *scan != NULL; scan++) {
8961 /* find the end of the list */
8962 }
8963 for (scan--; scan >= test; scan--) {
8964 ret = make_IPv4RouteList_for_test(ret, *scan, log_it);
8965 }
8966 break;
8967 default:
8968 case kDirectionForwards:
8969 for (scan = test; *scan != NULL; scan++) {
8970 ret = make_IPv4RouteList_for_test(ret, *scan, log_it);
8971 }
8972 break;
8973 }
8974 IPv4RouteListFinalize(ret);
8975 return (ret);
8976 }
8977
8978 #define EMPHASIS_CHARS "================="
8979
8980 /*
8981 * Function: routelist_build_test
8982 * Purpose:
8983 * Runs through the given set of routes first in the forward direction,
8984 * then again backwards. We should end up with exactly the same set of
8985 * routes at the end.
8986 */
8987 static boolean_t
8988 routelist_build_test(IPv4RouteTestRef test)
8989 {
8990 CFStringRef descr;
8991 boolean_t ret = FALSE;
8992 IPv4RouteListRef routes1;
8993 IPv4RouteListRef routes2;
8994
8995 printf("\n" EMPHASIS_CHARS "> RouteList Build '%s' <"
8996 EMPHASIS_CHARS "\n",
8997 test->name);
8998
8999 routes1 = make_IPv4RouteList(test->test, kDirectionForwards,
9000 kLogRouteEnabled);
9001 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
9002 if (routes1 != NULL) {
9003 descr = IPv4RouteListCopyDescription(routes1);
9004 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
9005 CFRelease(descr);
9006 }
9007 }
9008 routes2 = make_IPv4RouteList(test->test, kDirectionBackwards,
9009 kLogRouteEnabled);
9010 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
9011 if (routes2 != NULL) {
9012 descr = IPv4RouteListCopyDescription(routes2);
9013 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
9014 CFRelease(descr);
9015 }
9016 }
9017 if ((routes1 != NULL && routes2 == NULL)
9018 || (routes1 == NULL && routes2 != NULL)) {
9019 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
9020 (routes1 != NULL) ? "not " : "",
9021 (routes2 != NULL) ? "not " : "");
9022 }
9023 else if (routes1 != NULL && routes2 != NULL) {
9024 /* check if they are different */
9025 if (routes1->count != routes2->count) {
9026 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
9027 routes1->count, routes2->count);
9028 }
9029 else if (bcmp(routes1, routes2,
9030 IPv4RouteListComputeSize(routes1->count)) != 0) {
9031 fprintf(stderr, "routes1 and routes2 are different\n");
9032 }
9033 else {
9034 printf("routes1 and routes2 are the same\n");
9035 ret = TRUE;
9036 }
9037 }
9038 if (routes1 != NULL) {
9039 free(routes1);
9040 }
9041 if (routes2 != NULL) {
9042 free(routes2);
9043 }
9044 printf(EMPHASIS_CHARS "> RouteList Build '%s': %s <"
9045 EMPHASIS_CHARS "\n",
9046 test->name, ret ? "PASSED" : "FAILED");
9047 return (ret);
9048 }
9049
9050 static void
9051 apply_test(IPv4RouteTestRef old_test, IPv4RouteTestRef new_test)
9052 {
9053 IPv4RouteListRef new_routes;
9054 IPv4RouteListRef old_routes;
9055
9056 printf("\n" EMPHASIS_CHARS "> Apply '%s', '%s' Begin <"
9057 EMPHASIS_CHARS "\n",
9058 old_test->name, new_test->name);
9059
9060 old_routes = make_IPv4RouteList(old_test->test, kDirectionForwards,
9061 kLogRouteDisabled);
9062 new_routes = make_IPv4RouteList(new_test->test, kDirectionForwards,
9063 kLogRouteDisabled);
9064 if (old_routes == NULL) {
9065 printf("No Old Routes\n");
9066 }
9067 else {
9068 printf("Old routes ('%s') = ", old_test->name);
9069 IPv4RouteListPrint(old_routes);
9070 }
9071
9072 /* apply the old routes */
9073 IPv4RouteListApply(NULL, old_routes, -1);
9074
9075 if (new_routes == NULL) {
9076 printf("No New Routes\n");
9077 }
9078 else {
9079 printf("New Routes ('%s') = ", new_test->name);
9080 IPv4RouteListPrint(new_routes);
9081 }
9082
9083 /* apply the new routes */
9084 IPv4RouteListApply(old_routes, new_routes, -1);
9085
9086 if (old_routes != NULL) {
9087 free(old_routes);
9088 }
9089 if (new_routes != NULL) {
9090 free(new_routes);
9091 }
9092 printf(EMPHASIS_CHARS "> Apply '%s', '%s' End <"
9093 EMPHASIS_CHARS "\n",
9094 old_test->name, new_test->name);
9095 return;
9096 }
9097
9098 int
9099 main(int argc, char **argv)
9100 {
9101 IPv4RouteTestRef * test;
9102
9103 _sc_log = FALSE;
9104 _sc_verbose = (argc > 1) ? TRUE : FALSE;
9105 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
9106 if (argc > 1) {
9107 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
9108 }
9109 S_scopedroute = (argc < 3);
9110 for (test = ipv4_tests; *test != NULL; test++) {
9111 if (routelist_build_test(*test) == FALSE) {
9112 fprintf(stderr, "%s failed\n", (*test)->name);
9113 exit(1);
9114 }
9115 }
9116 for (test = ipv4_tests; *test != NULL; test++) {
9117 IPv4RouteTestRef * test2;
9118
9119 for (test2 = test + 1; *test2 != NULL; test2++) {
9120 apply_test(*test, *test2);
9121 apply_test(*test2, *test);
9122 }
9123 }
9124
9125 {
9126 char cmd[128];
9127
9128 printf("\nChecking for leaks\n");
9129 sprintf(cmd, "leaks %d 2>&1", getpid());
9130 fflush(stdout);
9131 (void)system(cmd);
9132 }
9133 exit(0);
9134 return (0);
9135 }
9136
9137 #endif /* TEST_IPV4_ROUTELIST */
9138
9139 #ifdef TEST_IPV6_ROUTELIST
9140
9141 typedef struct {
9142 const char * addr;
9143 int prefix_length;
9144 const char * dest;
9145 } IPv6Address;
9146
9147 typedef const IPv6Address * IPv6AddressRef;
9148
9149 typedef struct {
9150 IPv6AddressRef addr;
9151 int addr_count;
9152 const char * router;
9153 const char * ifname;
9154 Rank rank;
9155 const CFStringRef * primary_rank;
9156 struct route * additional_routes;
9157 int additional_routes_count;
9158 struct route * excluded_routes;
9159 int excluded_routes_count;
9160 } IPv6ServiceContents;
9161
9162 typedef const IPv6ServiceContents * IPv6ServiceContentsRef;
9163
9164 struct route loop_routelist[] = {
9165 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
9166 "2620:149:4:f01:225:ff:fecc:89a2", NULL },
9167 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
9168 "2620:149:4:f01:225:ff:fecc:89a3", NULL },
9169 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
9170 "2620:149:4:f01:225:ff:fecc:89a4", NULL },
9171 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
9172 "2620:149:4:f01:225:ff:fecc:89a5", NULL },
9173 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
9174 "2620:149:4:f01:225:ff:fecc:89a6", NULL },
9175 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
9176 "2620:149:4:f01:225:ff:fecc:89a7", NULL },
9177 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
9178 "2620:149:4:f01:225:ff:fecc:89a8", NULL },
9179 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
9180 "2620:149:4:f01:225:ff:fecc:89a9", NULL },
9181 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
9182 "2620:149:4:f01:225:ff:fecc:89aa", NULL },
9183 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
9184 "2620:149:4:f01:225:ff:fecc:89ab", NULL },
9185 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
9186 "2620:149:4:f01:225:ff:fecc:89a1", NULL },
9187 };
9188
9189 struct route vpn_routelist[] = {
9190 { "2010:470:1f05:3cb::", 64,
9191 "fe80::2d0:bcff:fe3d:8c00", NULL },
9192 { "2010:222:3fa5:acb::", 48,
9193 "fe80::2d0:bcff:fe3d:8c00", NULL },
9194 { "2010:222:3fa5:1234::", 40,
9195 "fe80::2d0:bcff:fe3d:8c00", NULL },
9196 { "2010:222:3fa5:5678::", 40,
9197 NULL, NULL },
9198 };
9199
9200 struct route vpn_routelist_ext[] = {
9201 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL, NULL },
9202 };
9203
9204 struct route en1_routelist_ext[] = {
9205 { "2020:299:abcd:ef12::", 64, NULL, NULL },
9206 };
9207
9208
9209 static const IPv6Address en0_addr1[] = {
9210 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL },
9211 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL }
9212 };
9213
9214 static const IPv6Address en0_addr2[] = {
9215 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL },
9216 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL }
9217 };
9218
9219 static const IPv6Address en0_addr3[] = {
9220 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL },
9221 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL }
9222 };
9223
9224 static const IPv6Address en0_addr4[] = {
9225 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL },
9226 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL }
9227 };
9228
9229 static const IPv6Address en0_addr5[] = {
9230 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL },
9231 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL }
9232 };
9233
9234 static const IPv6Address en0_addr6[] = {
9235 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL },
9236 };
9237
9238 static const IPv6Address en0_lladdr[] = {
9239 { "fe80::cabc:c8ff:fe96:96af", 64, NULL }
9240 };
9241
9242 static const IPv6Address en1_addr[] = {
9243 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL },
9244 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL }
9245 };
9246
9247 static const IPv6Address utun0_addr[] = {
9248 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL },
9249 };
9250
9251 static const IPv6Address fw0_addr1[] = {
9252 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL },
9253 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL }
9254 };
9255
9256 /*
9257 * address+address-count
9258 * router ifname pri rank additional-routes+count excluded-routes+count
9259 */
9260
9261 static const IPv6ServiceContents en0_10 = {
9262 en0_addr1, countof(en0_addr1),
9263 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL, NULL, 0, NULL, 0
9264 };
9265
9266 static const IPv6ServiceContents en0_15 = {
9267 en0_addr2, countof(en0_addr2),
9268 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL, NULL, 0, NULL, 0
9269 };
9270
9271 static const IPv6ServiceContents en0_30 = {
9272 en0_addr3, countof(en0_addr3),
9273 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL, NULL, 0, NULL, 0
9274 };
9275
9276 static const IPv6ServiceContents en0_40 = {
9277 en0_addr4, countof(en0_addr4),
9278 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL, NULL, 0, NULL, 0
9279 };
9280
9281 static const IPv6ServiceContents en0_50 = {
9282 en0_addr5, countof(en0_addr5),
9283 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL, NULL, 0, NULL, 0
9284 };
9285
9286 static const IPv6ServiceContents en0_10_a = {
9287 en0_addr6, countof(en0_addr6),
9288 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL, NULL, 0, NULL, 0
9289 };
9290
9291 static const IPv6ServiceContents fw0_25 = {
9292 fw0_addr1, countof(fw0_addr1),
9293 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL, NULL, 0, NULL, 0
9294 };
9295
9296 static const IPv6ServiceContents en1_20 = {
9297 en1_addr, countof(en1_addr),
9298 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL, NULL, 0, NULL, 0
9299 };
9300
9301 static const IPv6ServiceContents en1_10_ext = {
9302 en1_addr, countof(en1_addr),
9303 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL, NULL, 0,
9304 en1_routelist_ext, countof(en1_routelist_ext)
9305 };
9306
9307 static const IPv6ServiceContents en0_0_lladdr = {
9308 en0_lladdr, countof(en0_lladdr),
9309 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL, NULL, 0, NULL, 0
9310 };
9311
9312 static const IPv6ServiceContents en0_loop = {
9313 en0_addr1, countof(en0_addr1),
9314 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL,
9315 loop_routelist, countof(loop_routelist), NULL, 0
9316 };
9317
9318 static const IPv6ServiceContents utun0 = {
9319 utun0_addr, countof(utun0_addr),
9320 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL,
9321 vpn_routelist, countof(vpn_routelist),
9322 vpn_routelist_ext, countof(vpn_routelist_ext),
9323 };
9324
9325 typedef struct {
9326 const char * name;
9327 IPv6ServiceContentsRef test[];
9328 } IPv6RouteTest, * IPv6RouteTestRef;
9329
9330 static IPv6RouteTest test1 = {
9331 "test1",
9332 {
9333 &en0_40,
9334 &en0_15,
9335 &fw0_25,
9336 &en0_30,
9337 &en1_20,
9338 &en0_50,
9339 &en0_10,
9340 NULL
9341 }
9342 };
9343
9344 static IPv6RouteTest test2 = {
9345 "test2",
9346 {
9347 &en0_40,
9348 &fw0_25,
9349 &en0_30,
9350 &en1_20,
9351 &en0_50,
9352 &en0_10,
9353 NULL
9354 }
9355 };
9356
9357 static IPv6RouteTest test3 = {
9358 "test3",
9359 {
9360 &en0_10_a,
9361 &en1_10_ext,
9362 NULL
9363 }
9364 };
9365
9366 static IPv6RouteTest test4 = {
9367 "test4",
9368 {
9369 &en0_loop,
9370 &en1_20,
9371 NULL
9372 }
9373 };
9374
9375 static IPv6RouteTest test5 = {
9376 "test5",
9377 {
9378 &en0_10,
9379 &utun0,
9380 &en0_0_lladdr,
9381 &en1_20,
9382 NULL
9383 }
9384 };
9385
9386
9387 static IPv6RouteTestRef ipv6_tests[] = {
9388 &test1,
9389 &test2,
9390 &test3,
9391 &test4,
9392 &test5,
9393 NULL
9394 };
9395
9396
9397 static void
9398 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
9399 const char * str)
9400 {
9401 CFStringRef prop_val;
9402
9403 if (str == NULL) {
9404 return;
9405 }
9406 prop_val = CFStringCreateWithCString(NULL,
9407 str,
9408 kCFStringEncodingASCII);
9409 CFDictionarySetValue(dict, prop_name, prop_val);
9410 CFRelease(prop_val);
9411 return;
9412 }
9413
9414 static void
9415 dict_add_int(CFMutableDictionaryRef dict, CFStringRef prop_name,
9416 int int_val)
9417 {
9418 CFNumberRef num;
9419
9420 num = CFNumberCreate(NULL, kCFNumberIntType, &int_val);
9421 CFDictionarySetValue(dict, prop_name, num);
9422 CFRelease(num);
9423 return;
9424 }
9425
9426 static void
9427 dict_insert_v6_routes(CFMutableDictionaryRef dict, CFStringRef prop_name,
9428 struct route * routes, int routes_count)
9429 {
9430 int i;
9431 CFMutableArrayRef route_list;
9432 struct route * scan;
9433
9434 if (routes == NULL || routes_count == 0) {
9435 return;
9436 }
9437 route_list = CFArrayCreateMutable(NULL, routes_count,
9438 &kCFTypeArrayCallBacks);
9439 for (i = 0, scan = routes; i < routes_count; i++, scan++) {
9440 CFMutableDictionaryRef route_dict;
9441
9442 route_dict = CFDictionaryCreateMutable(NULL, 0,
9443 &kCFTypeDictionaryKeyCallBacks,
9444 &kCFTypeDictionaryValueCallBacks);
9445 dict_add_string(route_dict, kSCPropNetIPv6RouteDestinationAddress,
9446 scan->dest);
9447 dict_add_int(route_dict, kSCPropNetIPv6PrefixLength,
9448 scan->prefix_length);
9449 dict_add_string(route_dict, kSCPropNetIPv6RouteGatewayAddress,
9450 scan->gateway);
9451 dict_add_string(route_dict, kSCPropNetIPv6RouteInterfaceName,
9452 scan->ifname);
9453 CFArrayAppendValue(route_list, route_dict);
9454 CFRelease(route_dict);
9455 }
9456 CFDictionarySetValue(dict, prop_name, route_list);
9457 CFRelease(route_list);
9458 return;
9459 }
9460
9461 static void
9462 array_add_string(CFMutableArrayRef array, const char * c_str)
9463 {
9464 CFStringRef str;
9465
9466 str = CFStringCreateWithCString(NULL,
9467 c_str,
9468 kCFStringEncodingUTF8);
9469 CFArrayAppendValue(array, str);
9470 CFRelease(str);
9471 return;
9472 }
9473
9474 static void
9475 array_add_int(CFMutableArrayRef array, int int_val)
9476 {
9477 CFNumberRef num;
9478
9479 num = CFNumberCreate(NULL, kCFNumberIntType, &int_val);
9480 CFArrayAppendValue(array, num);
9481 CFRelease(num);
9482 return;
9483 }
9484
9485 static void
9486 dict_add_ipv6_addressing(CFMutableDictionaryRef dict,
9487 IPv6AddressRef list, int list_count)
9488 {
9489 CFMutableArrayRef addr = NULL;
9490 CFMutableArrayRef dest = NULL;
9491 int i;
9492 CFMutableArrayRef prefix = NULL;
9493 IPv6AddressRef scan;
9494
9495 if (list == NULL || list_count == 0) {
9496 return;
9497 }
9498 for (i = 0, scan = list; i < list_count; i++, scan++) {
9499 if (scan->addr != NULL) {
9500 if (addr == NULL) {
9501 addr = CFArrayCreateMutable(NULL, list_count,
9502 &kCFTypeArrayCallBacks);
9503 }
9504 array_add_string(addr, scan->addr);
9505 }
9506 if (scan->prefix_length >= 0) {
9507 if (prefix == NULL) {
9508 prefix = CFArrayCreateMutable(NULL, list_count,
9509 &kCFTypeArrayCallBacks);
9510 }
9511 array_add_int(prefix, scan->prefix_length);
9512 }
9513 if (scan->dest != NULL) {
9514 if (dest == NULL) {
9515 dest = CFArrayCreateMutable(NULL, list_count,
9516 &kCFTypeArrayCallBacks);
9517 }
9518 array_add_string(dest, scan->dest);
9519 }
9520 }
9521 if (addr != NULL) {
9522 CFDictionarySetValue(dict, kSCPropNetIPv6Addresses, addr);
9523 CFRelease(addr);
9524 }
9525 if (dest != NULL) {
9526 CFDictionarySetValue(dict, kSCPropNetIPv6DestAddresses, dest);
9527 CFRelease(dest);
9528 }
9529 if (prefix != NULL) {
9530 CFDictionarySetValue(dict, kSCPropNetIPv6PrefixLength, prefix);
9531 CFRelease(prefix);
9532 }
9533 return;
9534 }
9535
9536 static CFDictionaryRef
9537 make_IPv6_dict(IPv6ServiceContentsRef t)
9538 {
9539 CFMutableDictionaryRef dict;
9540
9541 dict = CFDictionaryCreateMutable(NULL, 0,
9542 &kCFTypeDictionaryKeyCallBacks,
9543 &kCFTypeDictionaryValueCallBacks);
9544 dict_add_ipv6_addressing(dict, t->addr, t->addr_count);
9545 dict_add_string(dict, kSCPropNetIPv6Router, t->router);
9546 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
9547 dict_insert_v6_routes(dict, kSCPropNetIPv6AdditionalRoutes,
9548 t->additional_routes, t->additional_routes_count);
9549 dict_insert_v6_routes(dict, kSCPropNetIPv6ExcludedRoutes,
9550 t->excluded_routes, t->excluded_routes_count);
9551 return (dict);
9552 }
9553
9554 typedef enum {
9555 kDirectionForwards = 0,
9556 kDirectionBackwards = 1
9557 } Direction;
9558
9559 typedef enum {
9560 kLogRouteDisabled = 0,
9561 kLogRouteEnabled = 1
9562 } LogRoute;
9563
9564 static IPv6RouteListRef
9565 make_IPv6RouteList_for_test(IPv6RouteListRef list,
9566 IPv6ServiceContentsRef test,
9567 LogRoute log_it)
9568 {
9569 CFDictionaryRef dict;
9570 IPv6RouteListRef r;
9571 Rank rank;
9572 Rank rank_assertion = kRankAssertionDefault;
9573 CFNumberRef rank_assertion_cf = NULL;
9574 Boolean rank_assertion_is_set = FALSE;
9575 IPv6RouteListRef ret = NULL;
9576 IPV6_ROUTES_BUF_DECL(routes);
9577
9578 dict = make_IPv6_dict(test);
9579 if (dict == NULL) {
9580 fprintf(stderr, "make_IPv6_dict failed\n");
9581 exit(1);
9582 }
9583 if (test->primary_rank != NULL) {
9584 rank_assertion
9585 = PrimaryRankGetRankAssertion(*test->primary_rank,
9586 &rank_assertion_is_set);
9587 if (rank_assertion_is_set) {
9588 rank_assertion_cf
9589 = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank_assertion);
9590 }
9591 }
9592 r = IPv6RouteListCreateWithDictionary(routes, dict,
9593 rank_assertion_cf);
9594 my_CFRelease(&rank_assertion_cf);
9595 if (r == NULL) {
9596 fprintf(stderr, "IPv6RouteListCreateWithDictionary failed\n");
9597 exit(1);
9598 }
9599
9600 if (rank_assertion == kRankAssertionScoped) {
9601 rank_assertion = kRankAssertionNever;
9602 }
9603 rank = RankMake(test->rank, rank_assertion);
9604 if (log_it == kLogRouteEnabled
9605 && (S_IPMonitor_debug & kDebugFlag4) != 0) {
9606 CFStringRef descr;
9607
9608 descr = IPv6RouteListCopyDescription(r);
9609 SCLog(TRUE, LOG_NOTICE, CFSTR("Adding %@"), descr);
9610 CFRelease(descr);
9611 }
9612 ret = IPv6RouteListAddRouteList(list, 1, r, rank);
9613 if (r != routes) {
9614 free(r);
9615 }
9616 CFRelease(dict);
9617 return (ret);
9618 }
9619
9620 static IPv6RouteListRef
9621 make_IPv6RouteList(IPv6ServiceContentsRef * test, Direction direction,
9622 LogRoute log_it)
9623 {
9624 IPv6RouteListRef ret = NULL;
9625 IPv6ServiceContentsRef * scan;
9626
9627 switch (direction) {
9628 case kDirectionBackwards:
9629 for (scan = test; *scan != NULL; scan++) {
9630 /* find the end of the list */
9631 }
9632 for (scan--; scan >= test; scan--) {
9633 ret = make_IPv6RouteList_for_test(ret, *scan, log_it);
9634 }
9635 break;
9636 default:
9637 case kDirectionForwards:
9638 for (scan = test; *scan != NULL; scan++) {
9639 ret = make_IPv6RouteList_for_test(ret, *scan, log_it);
9640 }
9641 break;
9642 }
9643 IPv6RouteListFinalize(ret);
9644 return (ret);
9645 }
9646
9647 #define EMPHASIS_CHARS "================="
9648
9649 /*
9650 * Function: routelist_build_test
9651 * Purpose:
9652 * Runs through the given set of routes first in the forward direction,
9653 * then again backwards. We should end up with exactly the same set of
9654 * routes at the end.
9655 */
9656 static boolean_t
9657 routelist_build_test(IPv6RouteTestRef test)
9658 {
9659 CFStringRef descr;
9660 boolean_t ret = FALSE;
9661 IPv6RouteListRef routes1;
9662 IPv6RouteListRef routes2;
9663
9664 printf("\n" EMPHASIS_CHARS "> RouteList Build '%s' <"
9665 EMPHASIS_CHARS "\n",
9666 test->name);
9667
9668 routes1 = make_IPv6RouteList(test->test, kDirectionForwards,
9669 kLogRouteEnabled);
9670 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
9671 if (routes1 != NULL) {
9672 descr = IPv6RouteListCopyDescription(routes1);
9673 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
9674 CFRelease(descr);
9675 }
9676 }
9677 routes2 = make_IPv6RouteList(test->test, kDirectionBackwards,
9678 kLogRouteEnabled);
9679 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
9680 if (routes2 != NULL) {
9681 descr = IPv6RouteListCopyDescription(routes2);
9682 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
9683 CFRelease(descr);
9684 }
9685 }
9686 if ((routes1 != NULL && routes2 == NULL)
9687 || (routes1 == NULL && routes2 != NULL)) {
9688 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
9689 (routes1 != NULL) ? "not " : "",
9690 (routes2 != NULL) ? "not " : "");
9691 }
9692 else if (routes1 != NULL && routes2 != NULL) {
9693 /* check if they are different */
9694 if (routes1->count != routes2->count) {
9695 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
9696 routes1->count, routes2->count);
9697 }
9698 else if (bcmp(routes1, routes2,
9699 IPv6RouteListComputeSize(routes1->count)) != 0) {
9700 fprintf(stderr, "routes1 and routes2 are different\n");
9701 }
9702 else {
9703 printf("routes1 and routes2 are the same\n");
9704 ret = TRUE;
9705 }
9706 }
9707 if (routes1 != NULL) {
9708 free(routes1);
9709 }
9710 if (routes2 != NULL) {
9711 free(routes2);
9712 }
9713 printf(EMPHASIS_CHARS "> RouteList Build '%s': %s <"
9714 EMPHASIS_CHARS "\n",
9715 test->name, ret ? "PASSED" : "FAILED");
9716 return (ret);
9717 }
9718
9719 static void
9720 apply_test(IPv6RouteTestRef old_test, IPv6RouteTestRef new_test)
9721 {
9722 IPv6RouteListRef new_routes;
9723 IPv6RouteListRef old_routes;
9724
9725 printf("\n" EMPHASIS_CHARS "> Apply '%s', '%s' Begin <"
9726 EMPHASIS_CHARS "\n",
9727 old_test->name, new_test->name);
9728
9729 old_routes = make_IPv6RouteList(old_test->test, kDirectionForwards,
9730 kLogRouteDisabled);
9731 new_routes = make_IPv6RouteList(new_test->test, kDirectionForwards,
9732 kLogRouteDisabled);
9733 if (old_routes == NULL) {
9734 printf("No Old Routes\n");
9735 }
9736 else {
9737 printf("Old routes ('%s') = ", old_test->name);
9738 IPv6RouteListPrint(old_routes);
9739 }
9740
9741 /* apply the old routes */
9742 IPv6RouteListApply(NULL, old_routes, -1);
9743 if (new_routes == NULL) {
9744 printf("No New Routes\n");
9745 }
9746 else {
9747 printf("New Routes ('%s') = ", new_test->name);
9748 IPv6RouteListPrint(new_routes);
9749 }
9750
9751 /* apply the new routes */
9752 IPv6RouteListApply(old_routes, new_routes, -1);
9753 if (old_routes != NULL) {
9754 free(old_routes);
9755 }
9756 if (new_routes != NULL) {
9757 free(new_routes);
9758 }
9759 printf(EMPHASIS_CHARS "> Apply '%s', '%s' End <"
9760 EMPHASIS_CHARS "\n",
9761 old_test->name, new_test->name);
9762 return;
9763 }
9764
9765 int
9766 main(int argc, char **argv)
9767 {
9768 IPv6RouteTestRef * test;
9769
9770 _sc_log = FALSE;
9771 _sc_verbose = (argc > 1) ? TRUE : FALSE;
9772 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
9773 if (argc > 1) {
9774 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
9775 }
9776 S_scopedroute_v6 = (argc < 3);
9777 for (test = ipv6_tests; *test != NULL; test++) {
9778 if (routelist_build_test(*test) == FALSE) {
9779 fprintf(stderr, "%s failed\n", (*test)->name);
9780 exit(1);
9781 }
9782 }
9783 for (test = ipv6_tests; *test != NULL; test++) {
9784 IPv6RouteTestRef * test2;
9785
9786 for (test2 = test + 1; *test2 != NULL; test2++) {
9787 apply_test(*test, *test2);
9788 apply_test(*test2, *test);
9789 }
9790 }
9791
9792 {
9793 char cmd[128];
9794
9795 printf("\nChecking for leaks\n");
9796 sprintf(cmd, "leaks %d 2>&1", getpid());
9797 fflush(stdout);
9798 (void)system(cmd);
9799 }
9800 exit(0);
9801 return (0);
9802 }
9803
9804 #endif /* TEST_IPV6_ROUTELIST */
9805