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