]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/ip_plugin.c
configd-1109.40.9.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, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort },
5177 { kSCPropNetProxiesGopherEnable, kSCPropNetProxiesGopherProxy, kSCPropNetProxiesGopherPort },
5178 { kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort },
5179 { kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort },
5180 { kSCPropNetProxiesRTSPEnable, kSCPropNetProxiesRTSPProxy, kSCPropNetProxiesRTSPPort },
5181 { kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort },
5182 { kSCPropNetProxiesProxyAutoConfigEnable,
5183 kSCPropNetProxiesProxyAutoConfigURLString,
5184 kSCPropNetProxiesProxyAutoConfigJavaScript, },
5185 { kSCPropNetProxiesProxyAutoDiscoveryEnable,
5186 NULL,
5187 NULL, }
5188 };
5189
5190 if ((state_dict == NULL) && (setup_dict == NULL)) {
5191 /* there is no proxy content */
5192 goto done;
5193 }
5194 ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
5195 if (ipdict_is_routable(ipv4)) {
5196 active_protos |= kProtocolFlagsIPv4;
5197 interface = ipdict_get_ifname(ipv4);
5198 }
5199 ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
5200 if (ipdict_is_routable(ipv6)) {
5201 active_protos |= kProtocolFlagsIPv6;
5202 if (interface == NULL) {
5203 interface = ipdict_get_ifname(ipv6);
5204 }
5205 }
5206 if (active_protos == kProtocolFlagsNone) {
5207 /* there is no IPv4 nor IPv6 */
5208 if (state_dict == NULL) {
5209 /* ... and no proxy content that we care about */
5210 goto done;
5211 }
5212 setup_dict = NULL;
5213 }
5214
5215 if ((setup_dict != NULL) && (state_dict != NULL)) {
5216 CFMutableDictionaryRef setup_copy;
5217
5218 /*
5219 * Merge the per-service "Setup:" and "State:" proxy information with
5220 * the "Setup:" information always taking precedence. Additionally,
5221 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
5222 * Port) is defined than all of the values for that group will be
5223 * used. That is, we don't allow mixing some of the values from
5224 * the "Setup:" keys and others from the "State:" keys.
5225 */
5226 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
5227 for (size_t i = 0; i < countof(merge_list); i++) {
5228 merge_array_prop(new_dict,
5229 merge_list[i].key,
5230 state_dict,
5231 setup_dict,
5232 merge_list[i].flags,
5233 merge_list[i].append);
5234 }
5235
5236 setup_copy = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
5237 for (size_t i = 0; i < countof(pick_list); i++) {
5238 if (CFDictionaryContainsKey(setup_copy, pick_list[i].key1)) {
5239 /*
5240 * if a "Setup:" enabled key has been provided than we want to
5241 * ignore all of the "State:" keys
5242 */
5243 CFDictionaryRemoveValue(new_dict, pick_list[i].key1);
5244 if (pick_list[i].key2 != NULL) {
5245 CFDictionaryRemoveValue(new_dict, pick_list[i].key2);
5246 }
5247 if (pick_list[i].key3 != NULL) {
5248 CFDictionaryRemoveValue(new_dict, pick_list[i].key3);
5249 }
5250 } else if (CFDictionaryContainsKey(state_dict, pick_list[i].key1) ||
5251 ((pick_list[i].key2 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key2)) ||
5252 ((pick_list[i].key3 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key3))) {
5253 /*
5254 * if a "Setup:" enabled key has not been provided and we have
5255 * some" "State:" keys than we remove all of of "Setup:" keys
5256 */
5257 CFDictionaryRemoveValue(setup_copy, pick_list[i].key1);
5258 if (pick_list[i].key2 != NULL) {
5259 CFDictionaryRemoveValue(setup_copy, pick_list[i].key2);
5260 }
5261 if (pick_list[i].key3 != NULL) {
5262 CFDictionaryRemoveValue(setup_copy, pick_list[i].key3);
5263 }
5264 }
5265 }
5266
5267 /* merge the "Setup:" keys */
5268 CFDictionaryApplyFunction(setup_copy, merge_dict, new_dict);
5269 CFRelease(setup_copy);
5270 }
5271 else if (setup_dict != NULL) {
5272 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
5273 }
5274 else if (state_dict != NULL) {
5275 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
5276 }
5277
5278 if ((new_dict != NULL) && (CFDictionaryGetCount(new_dict) == 0)) {
5279 CFRelease(new_dict);
5280 new_dict = NULL;
5281 }
5282
5283 if ((new_dict != NULL) && (interface != NULL)) {
5284 CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
5285 }
5286
5287 /* process WPAD */
5288 if (new_dict != NULL) {
5289 CFDictionaryRef dhcp_options;
5290 CFNumberRef num;
5291 CFNumberRef wpad = NULL;
5292 int wpadEnabled = 0;
5293 CFStringRef wpadURL = NULL;
5294
5295 if (CFDictionaryGetValueIfPresent(new_dict,
5296 kSCPropNetProxiesProxyAutoDiscoveryEnable,
5297 (const void **)&num) &&
5298 isA_CFNumber(num)) {
5299 /* if we have a WPAD key */
5300 wpad = num;
5301 if (!CFNumberGetValue(num, kCFNumberIntType, &wpadEnabled)) {
5302 /* if we don't like the enabled key/value */
5303 wpadEnabled = 0;
5304 }
5305 }
5306
5307 if (wpadEnabled) {
5308 int pacEnabled = 0;
5309
5310 num = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigEnable);
5311 if (!isA_CFNumber(num) ||
5312 !CFNumberGetValue(num, kCFNumberIntType, &pacEnabled)) {
5313 /* if we don't like the enabled key/value */
5314 pacEnabled = 0;
5315 }
5316
5317 if (pacEnabled) {
5318 CFStringRef pacURL;
5319
5320 pacURL = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigURLString);
5321 if (pacURL != NULL) {
5322 if (!isA_CFString(pacURL) || (CFStringGetLength(pacURL) == 0)) {
5323 /* if we don't like the PAC URL */
5324 pacEnabled = 0;
5325 }
5326 } else {
5327 CFStringRef pacJS;
5328
5329 pacJS = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigJavaScript);
5330 if (!isA_CFString(pacJS) || (CFStringGetLength(pacJS) == 0)) {
5331 /* if we don't have (or like) the PAC JavaScript */
5332 pacEnabled = 0;
5333 }
5334 }
5335 }
5336
5337 if (pacEnabled) {
5338 /*
5339 * we already have a PAC URL so disable WPAD.
5340 */
5341 wpadEnabled = 0;
5342 goto setWPAD;
5343 }
5344
5345 /*
5346 * if WPAD is enabled and we don't already have a PAC URL then
5347 * we check for a DHCP provided URL. If not available, we use
5348 * a PAC URL pointing to a well-known file (wpad.dat) on a
5349 * well-known host (wpad.<domain>).
5350 */
5351 dhcp_options = get_service_state_entity(info, serviceID, kSCEntNetDHCP);
5352 wpadURL = wpadURL_dhcp(dhcp_options);
5353 if (wpadURL == NULL) {
5354 wpadURL = wpadURL_dns();
5355 }
5356 if (wpadURL == NULL) {
5357 wpadEnabled = 0; /* if we don't have a WPAD URL */
5358 goto setWPAD;
5359 }
5360
5361 pacEnabled = 1;
5362 num = CFNumberCreate(NULL, kCFNumberIntType, &pacEnabled);
5363 CFDictionarySetValue(new_dict,
5364 kSCPropNetProxiesProxyAutoConfigEnable,
5365 num);
5366 CFRelease(num);
5367 CFDictionarySetValue(new_dict,
5368 kSCPropNetProxiesProxyAutoConfigURLString,
5369 wpadURL);
5370 CFRelease(wpadURL);
5371 }
5372
5373 setWPAD:
5374 if (wpad != NULL) {
5375 num = CFNumberCreate(NULL, kCFNumberIntType, &wpadEnabled);
5376 CFDictionarySetValue(new_dict,
5377 kSCPropNetProxiesProxyAutoDiscoveryEnable,
5378 num);
5379 CFRelease(num);
5380 }
5381 }
5382
5383 done:
5384 changed = service_dict_set(serviceID, kSCEntNetProxies, new_dict);
5385 my_CFRelease(&new_dict);
5386 return (changed);
5387 }
5388
5389 #if !TARGET_OS_IPHONE
5390 static boolean_t
5391 get_smb_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
5392 CFDictionaryRef setup_dict, CFDictionaryRef info)
5393 {
5394 #pragma unused(info)
5395 boolean_t changed = FALSE;
5396 CFMutableDictionaryRef new_dict = NULL;
5397 const CFStringRef pick_list[] = {
5398 kSCPropNetSMBNetBIOSName,
5399 kSCPropNetSMBNetBIOSNodeType,
5400 #ifdef ADD_NETBIOS_SCOPE
5401 kSCPropNetSMBNetBIOSScope,
5402 #endif // ADD_NETBIOS_SCOPE
5403 kSCPropNetSMBWorkgroup,
5404 };
5405
5406 if (state_dict == NULL && setup_dict == NULL) {
5407 /* there is no SMB */
5408 goto done;
5409 }
5410 if (service_dict_get(serviceID, kSCEntNetIPv4) == NULL
5411 && service_dict_get(serviceID, kSCEntNetIPv6) == NULL) {
5412 /* there is no IPv4 or IPv6 */
5413 goto done;
5414 }
5415
5416 /* merge SMB configuration */
5417 new_dict = CFDictionaryCreateMutable(NULL, 0,
5418 &kCFTypeDictionaryKeyCallBacks,
5419 &kCFTypeDictionaryValueCallBacks);
5420 merge_array_prop(new_dict,
5421 kSCPropNetSMBWINSAddresses,
5422 state_dict,
5423 setup_dict,
5424 0,
5425 FALSE);
5426 for (size_t i = 0; i < countof(pick_list); i++) {
5427 pick_prop(new_dict,
5428 pick_list[i],
5429 state_dict,
5430 setup_dict,
5431 0);
5432 }
5433
5434 if (CFDictionaryGetCount(new_dict) == 0) {
5435 my_CFRelease(&new_dict);
5436 goto done;
5437 }
5438
5439 done:
5440 changed = service_dict_set(serviceID, kSCEntNetSMB, new_dict);
5441 my_CFRelease(&new_dict);
5442 return (changed);
5443 }
5444 #endif /* !TARGET_OS_IPHONE */
5445
5446 static CFStringRef
5447 services_info_get_interface(CFDictionaryRef services_info,
5448 CFStringRef serviceID)
5449 {
5450 CFStringRef interface = NULL;
5451 CFDictionaryRef ipv4_dict;
5452
5453 ipv4_dict = get_service_state_entity(services_info, serviceID,
5454 kSCEntNetIPv4);
5455 if (ipv4_dict != NULL) {
5456 interface = CFDictionaryGetValue(ipv4_dict, kSCPropInterfaceName);
5457 }
5458 else {
5459 CFDictionaryRef ipv6_dict;
5460
5461 ipv6_dict = get_service_state_entity(services_info, serviceID,
5462 kSCEntNetIPv6);
5463 if (ipv6_dict != NULL) {
5464 interface = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
5465 }
5466 }
5467 return (interface);
5468 }
5469
5470
5471 static const struct {
5472 const CFStringRef * entityName;
5473 const CFStringRef * statusKey;
5474 } transientServiceInfo[] = {
5475 { &kSCEntNetIPSec, &kSCPropNetIPSecStatus },
5476 { &kSCEntNetPPP, &kSCPropNetPPPStatus },
5477 { &kSCEntNetVPN, &kSCPropNetVPNStatus },
5478 };
5479
5480 static Boolean
5481 get_transient_status_changes(CFStringRef serviceID,
5482 CFDictionaryRef services_info)
5483 {
5484 boolean_t changed = FALSE;
5485
5486 for (size_t i = 0; i < countof(transientServiceInfo); i++) {
5487 CFDictionaryRef dict;
5488 CFNumberRef status = NULL;
5489 CFMutableDictionaryRef ts_dict = NULL;
5490
5491 dict = get_service_state_entity(services_info, serviceID,
5492 *transientServiceInfo[i].entityName);
5493
5494 if (dict != NULL) {
5495 status = CFDictionaryGetValue(dict,
5496 *transientServiceInfo[i].statusKey);
5497 }
5498
5499 if (isA_CFNumber(status) != NULL) {
5500 ts_dict = CFDictionaryCreateMutable(NULL,
5501 0,
5502 &kCFTypeDictionaryKeyCallBacks,
5503 &kCFTypeDictionaryValueCallBacks);
5504 CFDictionaryAddValue(ts_dict,
5505 *transientServiceInfo[i].statusKey,
5506 status);
5507 }
5508
5509 if (service_dict_set(serviceID, *transientServiceInfo[i].entityName,
5510 ts_dict)) {
5511 changed = TRUE;
5512 }
5513
5514 if (ts_dict != NULL) {
5515 CFRelease(ts_dict);
5516 }
5517 }
5518 return (changed);
5519 }
5520
5521 static boolean_t
5522 if_dict_is_expensive(CFDictionaryRef if_dict)
5523 {
5524 boolean_t is_expensive = FALSE;
5525
5526 if (isA_CFDictionary(if_dict) != NULL) {
5527 CFBooleanRef expensive;
5528 expensive = CFDictionaryGetValue(if_dict, kSCPropNetLinkExpensive);
5529 if (isA_CFBoolean(expensive) != NULL
5530 && CFBooleanGetValue(expensive)) {
5531 is_expensive = TRUE;
5532 }
5533 }
5534 return is_expensive;
5535 }
5536
5537 static boolean_t
5538 service_is_expensive(CFStringRef serviceID, CFDictionaryRef services_info)
5539 {
5540 CFStringRef ifname;
5541 boolean_t is_expensive = FALSE;
5542
5543 ifname = services_info_get_interface(services_info, serviceID);
5544 if (ifname != NULL) {
5545 CFDictionaryRef if_dict;
5546 CFStringRef key;
5547
5548 key = interface_entity_key_copy(ifname, kSCEntNetLink);
5549 if_dict = CFDictionaryGetValue(services_info, key);
5550 CFRelease(key);
5551 is_expensive = if_dict_is_expensive(if_dict);
5552 }
5553 return (is_expensive);
5554 }
5555
5556 static boolean_t
5557 interface_is_expensive(CFStringRef ifname)
5558 {
5559 boolean_t is_expensive = FALSE;
5560
5561 if (ifname != NULL) {
5562 CFDictionaryRef if_dict;
5563 CFStringRef key;
5564
5565 key = interface_entity_key_copy(ifname, kSCEntNetLink);
5566 if_dict = SCDynamicStoreCopyValue(S_session, key);
5567 CFRelease(key);
5568 if (if_dict != NULL) {
5569 is_expensive = if_dict_is_expensive(if_dict);
5570 CFRelease(if_dict);
5571 }
5572 }
5573 return (is_expensive);
5574 }
5575
5576 static CFNumberRef
5577 service_rank_entity_get_index(CFDictionaryRef dict, CFStringRef serviceID,
5578 CFStringRef which, uint32_t * ret_val)
5579 {
5580 CFNumberRef service_index = NULL;
5581
5582 if (dict != NULL) {
5583 service_index = CFDictionaryGetValue(dict,
5584 kSCPropNetServiceServiceIndex);
5585 service_index = isA_CFNumber(service_index);
5586 }
5587 if (service_index != NULL) {
5588 SInt32 index_val;
5589
5590 if (!CFNumberGetValue(service_index, kCFNumberSInt32Type,
5591 &index_val)
5592 || index_val <= 0) {
5593 /* ServiceIndex must be >= 1 */
5594 my_log(LOG_NOTICE,
5595 "%@%@ ServiceIndex %@ is invalid, ignoring",
5596 which, serviceID, service_index);
5597 service_index = NULL;
5598 }
5599 else if (ret_val != NULL) {
5600 *ret_val = (uint32_t)index_val;
5601 }
5602 }
5603 return (service_index);
5604 }
5605
5606 static boolean_t
5607 get_rank_changes(CFStringRef serviceID, CFDictionaryRef state_options,
5608 CFDictionaryRef setup_options, CFDictionaryRef services_info)
5609 {
5610 boolean_t changed = FALSE;
5611 CFStringRef interface;
5612 boolean_t ip_is_coupled = FALSE;
5613 CFMutableDictionaryRef new_dict = NULL;
5614 Rank rank_assertion = kRankAssertionDefault;
5615 Boolean rank_assertion_is_set = FALSE;
5616 CFStringRef setup_rank = NULL;
5617 CFStringRef state_rank = NULL;
5618 CFNumberRef service_index = NULL;
5619 boolean_t use_setup_rank = TRUE;
5620
5621
5622 if (setup_options != NULL) {
5623 CFBooleanRef coupled;
5624
5625 setup_rank
5626 = CFDictionaryGetValue(setup_options, kSCPropNetServicePrimaryRank);
5627 setup_rank = isA_CFString(setup_rank);
5628 if (setup_rank != NULL && !use_setup_rank) {
5629 my_log(LOG_DEBUG, "%@ ignoring Setup PrimaryRank = %@",
5630 serviceID, setup_rank);
5631 setup_rank = NULL;
5632 }
5633 coupled = CFDictionaryGetValue(setup_options, kIPIsCoupled);
5634 if (isA_CFBoolean(coupled) != NULL && CFBooleanGetValue(coupled)) {
5635 ip_is_coupled = TRUE;
5636 }
5637 service_index
5638 = service_rank_entity_get_index(setup_options,
5639 serviceID,
5640 kSCDynamicStoreDomainSetup,
5641 NULL);
5642 }
5643 if (state_options != NULL) {
5644 CFBooleanRef coupled;
5645
5646 state_rank
5647 = CFDictionaryGetValue(state_options, kSCPropNetServicePrimaryRank);
5648 state_rank = isA_CFString(state_rank);
5649 coupled = CFDictionaryGetValue(state_options, kIPIsCoupled);
5650 if (isA_CFBoolean(coupled) != NULL && CFBooleanGetValue(coupled)) {
5651 ip_is_coupled = TRUE;
5652 }
5653 if (service_index == NULL) {
5654 service_index
5655 = service_rank_entity_get_index(state_options,
5656 serviceID,
5657 kSCDynamicStoreDomainState,
5658 NULL);
5659 }
5660 }
5661
5662 if (!ip_is_coupled) {
5663 ip_is_coupled = service_is_expensive(serviceID, services_info);
5664 }
5665 if (setup_rank != NULL || state_rank != NULL) {
5666 /* rank assertion is set on the service */
5667 Rank setup_assertion;
5668 Boolean setup_assertion_is_set = FALSE;
5669 Rank state_assertion;
5670 Boolean state_assertion_is_set = FALSE;
5671
5672 setup_assertion = PrimaryRankGetRankAssertion(setup_rank,
5673 &setup_assertion_is_set);
5674 state_assertion = PrimaryRankGetRankAssertion(state_rank,
5675 &state_assertion_is_set);
5676 if (setup_assertion_is_set && state_assertion_is_set) {
5677 if (setup_assertion > state_assertion) {
5678 rank_assertion = setup_assertion;
5679 }
5680 else {
5681 rank_assertion = state_assertion;
5682 }
5683 rank_assertion_is_set = TRUE;
5684 }
5685 else if (setup_assertion_is_set) {
5686 rank_assertion = setup_assertion;
5687 rank_assertion_is_set = TRUE;
5688 }
5689 else if (state_assertion_is_set) {
5690 rank_assertion = state_assertion;
5691 rank_assertion_is_set = TRUE;
5692 }
5693 }
5694
5695 interface = services_info_get_interface(services_info, serviceID);
5696 if (interface != NULL) {
5697 if (!rank_assertion_is_set) {
5698 /* check for a rank assertion on the interface */
5699 CFNumberRef if_rank = NULL;
5700
5701 if (S_if_rank_dict != NULL) {
5702 if_rank = CFDictionaryGetValue(S_if_rank_dict, interface);
5703 }
5704 rank_assertion
5705 = InterfaceRankGetRankAssertion(if_rank,
5706 &rank_assertion_is_set);
5707 #define kNotSetString ((CFTypeRef)CFSTR("not set"))
5708 my_log(LOG_INFO,
5709 "serviceID %@ interface %@ rank = 0x%x (%@)%s",
5710 serviceID,
5711 interface,
5712 rank_assertion,
5713 (if_rank != NULL) ? (CFTypeRef)if_rank : kNotSetString,
5714 ip_is_coupled ? " [coupled]" : "");
5715 }
5716 else {
5717 my_log(LOG_INFO,
5718 "serviceID %@ interface %@ rank = 0x%x%s",
5719 serviceID, interface, rank_assertion,
5720 ip_is_coupled ? " [coupled]" : "");
5721 }
5722 }
5723
5724
5725 if (service_index != NULL || rank_assertion_is_set || ip_is_coupled) {
5726 new_dict = CFDictionaryCreateMutable(NULL, 0,
5727 &kCFTypeDictionaryKeyCallBacks,
5728 &kCFTypeDictionaryValueCallBacks);
5729 if (rank_assertion_is_set) {
5730 CFNumberRef new_rank;
5731
5732 new_rank = CFNumberCreate(NULL, kCFNumberSInt32Type,
5733 (const void *)&rank_assertion);
5734 CFDictionarySetValue(new_dict, kServiceOptionRankAssertion,
5735 new_rank);
5736 CFRelease(new_rank);
5737 }
5738 if (ip_is_coupled) {
5739 CFDictionarySetValue(new_dict, kIPIsCoupled, kCFBooleanTrue);
5740 }
5741 if (service_index != NULL) {
5742 CFDictionarySetValue(new_dict, kSCPropNetServiceServiceIndex,
5743 service_index);
5744 }
5745 }
5746 changed = service_dict_set(serviceID, kSCEntNetService, new_dict);
5747 my_CFRelease(&new_dict);
5748 return (changed);
5749 }
5750
5751 static void
5752 add_service_keys(CFStringRef serviceID,
5753 CFMutableArrayRef keys)
5754 {
5755 int i;
5756 CFStringRef key;
5757
5758 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
5759 CFStringRef name = *entityTypeNames[i];
5760
5761 key = setup_service_key(serviceID, name);
5762 my_CFArrayAppendUniqueValue(keys, key);
5763 CFRelease(key);
5764 key = state_service_key(serviceID, name);
5765 my_CFArrayAppendUniqueValue(keys, key);
5766 CFRelease(key);
5767 }
5768
5769 key = state_service_key(serviceID, kSCEntNetDHCP);
5770 my_CFArrayAppendUniqueValue(keys, key);
5771 CFRelease(key);
5772
5773 key = setup_service_key(serviceID, NULL);
5774 my_CFArrayAppendUniqueValue(keys, key);
5775 CFRelease(key);
5776 key = state_service_key(serviceID, NULL);
5777 my_CFArrayAppendUniqueValue(keys, key);
5778 CFRelease(key);
5779
5780 return;
5781 }
5782
5783 static void
5784 add_transient_status_keys(CFStringRef serviceID,
5785 CFMutableArrayRef keys)
5786 {
5787 for (size_t i = 0; i < countof(transientServiceInfo); i++) {
5788 CFStringRef key;
5789
5790 key = state_service_key(serviceID,
5791 *transientServiceInfo[i].entityName);
5792 my_CFArrayAppendUniqueValue(keys, key);
5793 CFRelease(key);
5794 }
5795
5796 return;
5797 }
5798
5799 static const CFStringRef *reachabilitySetupKeys[] = {
5800 &kSCEntNetPPP,
5801 &kSCEntNetInterface,
5802 &kSCEntNetIPv4,
5803 &kSCEntNetIPv6,
5804 };
5805
5806
5807 static void
5808 add_reachability_patterns(CFMutableArrayRef patterns)
5809 {
5810 for (size_t i = 0; i < countof(reachabilitySetupKeys); i++) {
5811 CFStringRef pattern;
5812 pattern = setup_service_key(kSCCompAnyRegex, *reachabilitySetupKeys[i]);
5813 my_CFArrayAppendUniqueValue(patterns, pattern);
5814 CFRelease(pattern);
5815 }
5816 }
5817
5818
5819 static void
5820 add_vpn_pattern(CFMutableArrayRef patterns)
5821 {
5822 CFStringRef pattern;
5823
5824 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
5825 my_CFArrayAppendUniqueValue(patterns, pattern);
5826 CFRelease(pattern);
5827 }
5828
5829 static void
5830 add_interface_link_pattern(CFMutableArrayRef patterns)
5831 {
5832 CFStringRef pattern;
5833
5834 pattern = interface_entity_key_copy(kSCCompAnyRegex, kSCEntNetLink);
5835 my_CFArrayAppendUniqueValue(patterns, pattern);
5836 CFRelease(pattern);
5837 }
5838
5839 static CFDictionaryRef
5840 services_info_copy(SCDynamicStoreRef session, CFArrayRef service_list)
5841 {
5842 CFIndex count;
5843 CFMutableArrayRef keys;
5844 CFDictionaryRef info;
5845 CFMutableArrayRef patterns;
5846
5847 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5848 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5849
5850 CFArrayAppendValue(keys, S_setup_global_ipv4);
5851 CFArrayAppendValue(keys, S_multicast_resolvers);
5852 CFArrayAppendValue(keys, S_private_resolvers);
5853
5854 count = CFArrayGetCount(service_list);
5855 for (CFIndex s = 0; s < count; s++) {
5856 CFStringRef serviceID = CFArrayGetValueAtIndex(service_list, s);
5857
5858 add_service_keys(serviceID, keys);
5859 add_transient_status_keys(serviceID, keys);
5860 }
5861
5862 add_reachability_patterns(patterns);
5863
5864 add_vpn_pattern(patterns);
5865
5866 add_interface_link_pattern(patterns);
5867
5868 info = SCDynamicStoreCopyMultiple(session, keys, patterns);
5869 my_CFRelease(&keys);
5870 my_CFRelease(&patterns);
5871 return (info);
5872 }
5873
5874 #if !TARGET_OS_SIMULATOR
5875
5876 static boolean_t
5877 set_ipv6_default_interface(IFIndex ifindex)
5878 {
5879 struct in6_ndifreq ndifreq;
5880 int sock;
5881 boolean_t success = FALSE;
5882
5883 memset((char *)&ndifreq, 0, sizeof(ndifreq));
5884 strlcpy(ndifreq.ifname, kLoopbackInterface, sizeof(ndifreq.ifname));
5885 if (ifindex != 0) {
5886 ndifreq.ifindex = ifindex;
5887 }
5888 else {
5889 ndifreq.ifindex = lo0_ifindex();
5890 }
5891 sock = inet6_dgram_socket();
5892 if (sock < 0) {
5893 goto done;
5894 }
5895 if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) == -1) {
5896 my_log(LOG_ERR,
5897 "ioctl(SIOCSDEFIFACE_IN6) failed: %s",
5898 strerror(errno));
5899 }
5900 else {
5901 success = TRUE;
5902 }
5903 close(sock);
5904 done:
5905 return (success);
5906 }
5907
5908 #endif /* !TARGET_OS_SIMULATOR */
5909
5910 #if !TARGET_OS_IPHONE
5911 static __inline__ void
5912 empty_dns()
5913 {
5914 (void)unlink(VAR_RUN_RESOLV_CONF);
5915 }
5916
5917 static void
5918 set_dns(CFArrayRef val_search_domains,
5919 CFStringRef val_domain_name,
5920 CFArrayRef val_servers,
5921 CFArrayRef val_sortlist)
5922 {
5923 FILE * f = fopen(VAR_RUN_RESOLV_CONF "-", "w");
5924
5925 /* publish new resolv.conf */
5926 if (f) {
5927 CFIndex i;
5928 CFIndex n;
5929
5930 SCPrint(TRUE, f, CFSTR("#\n"));
5931 SCPrint(TRUE, f, CFSTR("# macOS Notice\n"));
5932 SCPrint(TRUE, f, CFSTR("#\n"));
5933 SCPrint(TRUE, f, CFSTR("# This file is not consulted for DNS hostname resolution, address\n"));
5934 SCPrint(TRUE, f, CFSTR("# resolution, or the DNS query routing mechanism used by most\n"));
5935 SCPrint(TRUE, f, CFSTR("# processes on this system.\n"));
5936 SCPrint(TRUE, f, CFSTR("#\n"));
5937 SCPrint(TRUE, f, CFSTR("# To view the DNS configuration used by this system, use:\n"));
5938 SCPrint(TRUE, f, CFSTR("# scutil --dns\n"));
5939 SCPrint(TRUE, f, CFSTR("#\n"));
5940 SCPrint(TRUE, f, CFSTR("# SEE ALSO\n"));
5941 SCPrint(TRUE, f, CFSTR("# dns-sd(1), scutil(8)\n"));
5942 SCPrint(TRUE, f, CFSTR("#\n"));
5943 SCPrint(TRUE, f, CFSTR("# This file is automatically generated.\n"));
5944 SCPrint(TRUE, f, CFSTR("#\n"));
5945
5946 if (isA_CFArray(val_search_domains)) {
5947 SCPrint(TRUE, f, CFSTR("search"));
5948 n = CFArrayGetCount(val_search_domains);
5949 for (i = 0; i < n; i++) {
5950 CFStringRef domain;
5951
5952 domain = CFArrayGetValueAtIndex(val_search_domains, i);
5953 if (isA_CFString(domain)) {
5954 SCPrint(TRUE, f, CFSTR(" %@"), domain);
5955 }
5956 }
5957 SCPrint(TRUE, f, CFSTR("\n"));
5958 }
5959 else if (isA_CFString(val_domain_name)) {
5960 SCPrint(TRUE, f, CFSTR("domain %@\n"), val_domain_name);
5961 }
5962
5963 if (isA_CFArray(val_servers)) {
5964 n = CFArrayGetCount(val_servers);
5965 for (i = 0; i < n; i++) {
5966 CFStringRef nameserver;
5967
5968 nameserver = CFArrayGetValueAtIndex(val_servers, i);
5969 if (isA_CFString(nameserver)) {
5970 SCPrint(TRUE, f, CFSTR("nameserver %@\n"), nameserver);
5971 }
5972 }
5973 }
5974
5975 if (isA_CFArray(val_sortlist)) {
5976 SCPrint(TRUE, f, CFSTR("sortlist"));
5977 n = CFArrayGetCount(val_sortlist);
5978 for (i = 0; i < n; i++) {
5979 CFStringRef address;
5980
5981 address = CFArrayGetValueAtIndex(val_sortlist, i);
5982 if (isA_CFString(address)) {
5983 SCPrint(TRUE, f, CFSTR(" %@"), address);
5984 }
5985 }
5986 SCPrint(TRUE, f, CFSTR("\n"));
5987 }
5988
5989 fclose(f);
5990 (void)rename(VAR_RUN_RESOLV_CONF "-", VAR_RUN_RESOLV_CONF);
5991 }
5992 return;
5993 }
5994 #endif /* !TARGET_OS_IPHONE */
5995
5996 static boolean_t
5997 service_get_ip_is_coupled(CFStringRef serviceID)
5998 {
5999 CFDictionaryRef dict;
6000 boolean_t ip_is_coupled = FALSE;
6001
6002 dict = service_dict_get(serviceID, kSCEntNetService);
6003 if (dict != NULL) {
6004 if (CFDictionaryContainsKey(dict, kIPIsCoupled)) {
6005 ip_is_coupled = TRUE;
6006 }
6007 }
6008 return (ip_is_coupled);
6009 }
6010
6011 static CFStringRef
6012 my_CFStringCreateWithInAddr(struct in_addr ip)
6013 {
6014 CFStringRef str;
6015
6016 str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT), IP_LIST(&ip));
6017 return (str);
6018 }
6019
6020 static CFStringRef
6021 my_CFStringCreateWithIn6Addr(const struct in6_addr * ip)
6022 {
6023 char ntopbuf[INET6_ADDRSTRLEN];
6024
6025 (void)inet_ntop(AF_INET6, ip, ntopbuf, sizeof(ntopbuf));
6026 return (CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), ntopbuf));
6027 }
6028
6029 /*
6030 * Function: update_ipv4
6031 * Purpose:
6032 * Update the IPv4 configuration based on the latest information.
6033 * Publish the State:/Network/Global/IPv4 information, and update the
6034 * IPv4 routing table.
6035 */
6036 static void
6037 update_ipv4(CFStringRef primary,
6038 IPv4RouteListRef new_routelist,
6039 keyChangeListRef keys)
6040 {
6041 #if !TARGET_OS_SIMULATOR
6042 int sockfd;
6043 #endif /* !TARGET_OS_SIMULATOR */
6044
6045 if (keys != NULL) {
6046 if (new_routelist != NULL && primary != NULL) {
6047 const char * ifn_p = NULL;
6048 char ifname[IFNAMSIZ];
6049 IPv4RouteRef r;
6050 CFMutableDictionaryRef dict = NULL;
6051
6052 dict = CFDictionaryCreateMutable(NULL, 0,
6053 &kCFTypeDictionaryKeyCallBacks,
6054 &kCFTypeDictionaryValueCallBacks);
6055 /* the first entry is the default route */
6056 r = new_routelist->list;
6057 if (r->gateway.s_addr != 0) {
6058 CFStringRef str;
6059
6060 str = my_CFStringCreateWithInAddr(r->gateway);
6061 CFDictionarySetValue(dict, kSCPropNetIPv4Router, str);
6062 CFRelease(str);
6063 }
6064 ifn_p = my_if_indextoname(r->ifindex, ifname);
6065 if (ifn_p != NULL) {
6066 CFStringRef ifname_cf;
6067
6068 ifname_cf = CFStringCreateWithCString(NULL,
6069 ifn_p,
6070 kCFStringEncodingASCII);
6071 if (ifname_cf != NULL) {
6072 CFDictionarySetValue(dict,
6073 kSCDynamicStorePropNetPrimaryInterface,
6074 ifname_cf);
6075 CFRelease(ifname_cf);
6076 }
6077 }
6078 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
6079 primary);
6080 keyChangeListSetValue(keys, S_state_global_ipv4, dict);
6081 CFRelease(dict);
6082 }
6083 else {
6084 keyChangeListRemoveValue(keys, S_state_global_ipv4);
6085 }
6086 }
6087
6088 #if !TARGET_OS_SIMULATOR
6089 sockfd = open_routing_socket();
6090 if (sockfd != -1) {
6091 /* go through routelist and bind any unbound routes */
6092 if (new_routelist != NULL) {
6093 IPv4RouteListFinalize(new_routelist);
6094 }
6095 else {
6096 /* provide a routelist with just loopback multicast */
6097 new_routelist = IPv4RouteListCopyMulticastLoopback();
6098 }
6099 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
6100 if (S_ipv4_routelist == NULL) {
6101 my_log(LOG_DEBUG, "Old Routes = <none>");
6102 }
6103 else {
6104 my_log(LOG_DEBUG, "Old Routes = ");
6105 IPv4RouteListLog(LOG_DEBUG, S_ipv4_routelist);
6106 }
6107 if (new_routelist == NULL) {
6108 my_log(LOG_DEBUG, "New Routes = <none>");
6109 }
6110 else {
6111 my_log(LOG_DEBUG, "New Routes = ");
6112 IPv4RouteListLog(LOG_DEBUG, new_routelist);
6113 }
6114 }
6115 IPv4RouteListApply(S_ipv4_routelist, new_routelist, sockfd);
6116 close(sockfd);
6117 }
6118 if (S_ipv4_routelist != NULL) {
6119 free(S_ipv4_routelist);
6120 }
6121 S_ipv4_routelist = new_routelist;
6122 #else /* !TARGET_OS_SIMULATOR */
6123 if (new_routelist != NULL) {
6124 free(new_routelist);
6125 }
6126 #endif /* !TARGET_OS_SIMULATOR */
6127
6128 return;
6129 }
6130
6131 /*
6132 * Function: update_ipv6
6133 * Purpose:
6134 * Update the IPv6 configuration based on the latest information.
6135 * Publish the State:/Network/Global/IPv6 information, and update the
6136 * IPv6 routing table.
6137 */
6138 static void
6139 update_ipv6(CFStringRef primary,
6140 IPv6RouteListRef new_routelist,
6141 keyChangeListRef keys)
6142 {
6143 #if !TARGET_OS_SIMULATOR
6144 int sockfd;
6145 #endif /* !TARGET_OS_SIMULATOR */
6146
6147 if (keys != NULL) {
6148 if (new_routelist != NULL && primary != NULL) {
6149 const char * ifn_p = NULL;
6150 char ifname[IFNAMSIZ];
6151 IPv6RouteRef r;
6152 CFMutableDictionaryRef dict = NULL;
6153
6154 dict = CFDictionaryCreateMutable(NULL, 0,
6155 &kCFTypeDictionaryKeyCallBacks,
6156 &kCFTypeDictionaryValueCallBacks);
6157 /* the first entry is the default route */
6158 r = new_routelist->list;
6159 if ((r->flags & kRouteFlagsHasGateway) != 0) {
6160 CFStringRef router;
6161
6162 router = my_CFStringCreateWithIn6Addr(&r->gateway);
6163 CFDictionarySetValue(dict, kSCPropNetIPv6Router, router);
6164 CFRelease(router);
6165 }
6166 ifn_p = my_if_indextoname(r->ifindex, ifname);
6167 if (ifn_p != NULL) {
6168 CFStringRef ifname_cf;
6169
6170 ifname_cf = CFStringCreateWithCString(NULL,
6171 ifn_p,
6172 kCFStringEncodingASCII);
6173 if (ifname_cf != NULL) {
6174 CFDictionarySetValue(dict,
6175 kSCDynamicStorePropNetPrimaryInterface,
6176 ifname_cf);
6177 CFRelease(ifname_cf);
6178 }
6179 }
6180 CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
6181 primary);
6182 keyChangeListSetValue(keys, S_state_global_ipv6, dict);
6183 CFRelease(dict);
6184 #if !TARGET_OS_SIMULATOR
6185 set_ipv6_default_interface(r->ifindex);
6186 #endif /* !TARGET_OS_SIMULATOR */
6187 }
6188 else {
6189 #if !TARGET_OS_SIMULATOR
6190 set_ipv6_default_interface(0);
6191 #endif /* !TARGET_OS_SIMULATOR */
6192 keyChangeListRemoveValue(keys, S_state_global_ipv6);
6193 }
6194 }
6195
6196 #if !TARGET_OS_SIMULATOR
6197 sockfd = open_routing_socket();
6198 if (sockfd != -1) {
6199 /* go through routelist and bind any unbound routes */
6200 IPv6RouteListFinalize(new_routelist);
6201 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
6202 if (S_ipv6_routelist == NULL) {
6203 my_log(LOG_DEBUG, "Old Routes = <none>");
6204 }
6205 else {
6206 my_log(LOG_DEBUG, "Old Routes = ");
6207 IPv6RouteListLog(LOG_DEBUG, S_ipv6_routelist);
6208 }
6209 if (new_routelist == NULL) {
6210 my_log(LOG_DEBUG, "New Routes = <none>");
6211 }
6212 else {
6213 my_log(LOG_DEBUG, "New Routes = ");
6214 IPv6RouteListLog(LOG_DEBUG, new_routelist);
6215 }
6216 }
6217 IPv6RouteListApply(S_ipv6_routelist, new_routelist, sockfd);
6218 close(sockfd);
6219 }
6220 if (S_ipv6_routelist != NULL) {
6221 free(S_ipv6_routelist);
6222 }
6223 S_ipv6_routelist = new_routelist;
6224 #else /* !TARGET_OS_SIMULATOR */
6225 if (new_routelist != NULL) {
6226 free(new_routelist);
6227 }
6228 #endif /* !TARGET_OS_SIMULATOR */
6229
6230 return;
6231 }
6232
6233 static Boolean
6234 update_dns(CFDictionaryRef services_info,
6235 CFStringRef primary,
6236 keyChangeListRef keys)
6237 {
6238 #pragma unused(services_info)
6239 #pragma unused(keys)
6240 Boolean changed = FALSE;
6241 CFDictionaryRef dict = NULL;
6242
6243 if (primary != NULL) {
6244 CFDictionaryRef service_dict;
6245
6246 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
6247 if (service_dict != NULL) {
6248 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
6249 }
6250 }
6251
6252 if (!_SC_CFEqual(S_dns_primary_dict, dict)) {
6253 changed = TRUE;
6254 }
6255
6256 if (dict != NULL) CFRetain(dict);
6257 if (S_dns_primary_dict != NULL) CFRelease(S_dns_primary_dict);
6258 S_dns_primary_dict = dict;
6259
6260 return changed;
6261 }
6262
6263 static Boolean
6264 update_dns_global_resolver(CFDictionaryRef dict,
6265 keyChangeListRef keys)
6266 {
6267 if (_SC_CFEqual(S_dns_global_dict, dict)) {
6268 // if no changes
6269 return FALSE;
6270 }
6271
6272 if (dict != NULL) CFRetain(dict);
6273 if (S_dns_global_dict != NULL) CFRelease(S_dns_global_dict);
6274 S_dns_global_dict = dict;
6275
6276 if (dict == NULL) {
6277 #if !TARGET_OS_IPHONE
6278 /*
6279 * remove /etc/resolv.conf
6280 */
6281 empty_dns();
6282 #endif /* !TARGET_OS_IPHONE */
6283 /*
6284 * remove State:/Network/Global/DNS
6285 */
6286 keyChangeListRemoveValue(keys, S_state_global_dns);
6287 } else {
6288 CFMutableDictionaryRef new_dict;
6289
6290 #if !TARGET_OS_IPHONE
6291 /*
6292 * update /etc/resolv.conf
6293 */
6294 set_dns(CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains),
6295 CFDictionaryGetValue(dict, kSCPropNetDNSDomainName),
6296 CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses),
6297 CFDictionaryGetValue(dict, kSCPropNetDNSSortList));
6298 #endif /* !TARGET_OS_IPHONE */
6299
6300 /*
6301 * update State:/Network/Global/DNS
6302 */
6303 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
6304 CFDictionaryRemoveValue(new_dict, kSCPropInterfaceName);
6305 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchDomains);
6306 CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchOrders);
6307 CFDictionaryRemoveValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY);
6308 keyChangeListSetValue(keys, S_state_global_dns, new_dict);
6309 CFRelease(new_dict);
6310 }
6311
6312 return TRUE;
6313 }
6314
6315 static Boolean
6316 update_dnsinfo(CFDictionaryRef services_info,
6317 CFStringRef primary,
6318 keyChangeListRef keys,
6319 CFArrayRef service_order)
6320 {
6321 Boolean changed;
6322 CFDictionaryRef dict = NULL;
6323 CFDictionaryRef globalResolver = NULL;
6324 CFArrayRef multicastResolvers;
6325 CFArrayRef privateResolvers;
6326
6327 multicastResolvers = CFDictionaryGetValue(services_info, S_multicast_resolvers);
6328 privateResolvers = CFDictionaryGetValue(services_info, S_private_resolvers);
6329
6330 if (primary != NULL) {
6331 CFDictionaryRef service_dict;
6332
6333 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
6334 if (service_dict != NULL) {
6335 dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
6336 }
6337 }
6338
6339 changed = dns_configuration_set(dict,
6340 S_service_state_dict,
6341 service_order,
6342 multicastResolvers,
6343 privateResolvers,
6344 &globalResolver);
6345 if (changed) {
6346 if (!update_dns_global_resolver(globalResolver, keys)) {
6347 /*
6348 * There was no change to the default/global resolver
6349 * configuration. Even so, we still want to strobe
6350 * the State:/Network/Global/DNS key to indicate that
6351 * "a" change had occured.
6352 */
6353 keyChangeListNotifyKey(keys, S_state_global_dns);
6354 }
6355 }
6356 if (globalResolver != NULL) CFRelease(globalResolver);
6357
6358 return changed;
6359 }
6360
6361 static Boolean
6362 update_nwi(nwi_state_t state)
6363 {
6364 unsigned char signature[CC_SHA256_DIGEST_LENGTH];
6365 static unsigned char signature_last[CC_SHA256_DIGEST_LENGTH];
6366
6367 _nwi_state_compute_sha256_hash(state, signature);
6368 if (bcmp(signature, signature_last, sizeof(signature)) == 0) {
6369 my_log(LOG_DEBUG, "Not updating network information");
6370 return FALSE;
6371 }
6372
6373 // save [new] signature
6374 memcpy(signature_last, signature, sizeof(signature));
6375
6376 // save [new] configuration
6377 my_log(LOG_INFO, "Updating network information");
6378 _nwi_state_log(state, TRUE, NULL);
6379
6380 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
6381 if (!_nwi_state_store(state)) {
6382 my_log(LOG_ERR, "Notifying nwi_state_store failed");
6383 }
6384 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
6385
6386 return TRUE;
6387 }
6388
6389 static Boolean
6390 update_proxies(CFDictionaryRef services_info,
6391 CFStringRef primary,
6392 keyChangeListRef keys,
6393 CFArrayRef service_order)
6394 {
6395 Boolean changed = FALSE;
6396 CFDictionaryRef dict = NULL;
6397 CFDictionaryRef new_dict;
6398
6399 if (primary != NULL) {
6400 CFDictionaryRef service_dict;
6401
6402 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
6403 if (service_dict != NULL) {
6404 dict = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
6405 }
6406 }
6407
6408 new_dict = proxy_configuration_update(dict,
6409 S_service_state_dict,
6410 service_order,
6411 services_info);
6412 if (!_SC_CFEqual(S_proxies_dict, new_dict)) {
6413 if (new_dict == NULL) {
6414 keyChangeListRemoveValue(keys, S_state_global_proxies);
6415 } else {
6416 keyChangeListSetValue(keys, S_state_global_proxies, new_dict);
6417 }
6418 changed = TRUE;
6419 }
6420
6421 if (S_proxies_dict != NULL) CFRelease(S_proxies_dict);
6422 S_proxies_dict = new_dict;
6423
6424 return changed;
6425 }
6426
6427 #if !TARGET_OS_IPHONE
6428 static Boolean
6429 update_smb(CFDictionaryRef services_info,
6430 CFStringRef primary,
6431 keyChangeListRef keys)
6432 {
6433 #pragma unused(services_info)
6434 Boolean changed = FALSE;
6435 CFDictionaryRef dict = NULL;
6436
6437 if (primary != NULL) {
6438 CFDictionaryRef service_dict;
6439
6440 service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
6441 if (service_dict != NULL) {
6442 dict = CFDictionaryGetValue(service_dict, kSCEntNetSMB);
6443 }
6444 }
6445
6446 if (!_SC_CFEqual(S_smb_dict, dict)) {
6447 if (dict == NULL) {
6448 keyChangeListRemoveValue(keys, S_state_global_smb);
6449 } else {
6450 keyChangeListSetValue(keys, S_state_global_smb, dict);
6451 }
6452 changed = TRUE;
6453 }
6454
6455 if (dict != NULL) CFRetain(dict);
6456 if (S_smb_dict != NULL) CFRelease(S_smb_dict);
6457 S_smb_dict = dict;
6458
6459 return changed;
6460 }
6461 #endif /* !TARGET_OS_IPHONE */
6462
6463 static Rank
6464 get_service_index(CFDictionaryRef rank_entity,
6465 CFArrayRef order, CFIndex n_order, CFStringRef serviceID)
6466 {
6467 CFIndex i;
6468 Rank rank = kRankIndexMask;
6469 CFNumberRef service_index;
6470
6471 service_index
6472 = service_rank_entity_get_index(rank_entity,
6473 serviceID,
6474 CFSTR(""),
6475 &rank);
6476 if (service_index != NULL) {
6477 /* ServiceIndex specified in service entity */
6478 rank += n_order;
6479 my_log(LOG_INFO,
6480 "%@ specifies ServiceIndex %@, effective index is %d",
6481 serviceID, service_index, rank);
6482 }
6483 else if (serviceID != NULL && order != NULL && n_order > 0) {
6484 for (i = 0; i < n_order; i++) {
6485 CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(order, i));
6486
6487 if (s == NULL) {
6488 continue;
6489 }
6490 if (CFEqual(serviceID, s)) {
6491 rank = (Rank)i + 1;
6492 break;
6493 }
6494 }
6495 }
6496 return (rank);
6497 }
6498
6499 /**
6500 ** Service election:
6501 **/
6502 /*
6503 * Function: rank_dict_get_service_rank
6504 * Purpose:
6505 * Retrieve the service rank in the given dictionary.
6506 */
6507 static Rank
6508 rank_dict_get_service_rank(CFDictionaryRef rank_dict, CFStringRef serviceID)
6509 {
6510 CFNumberRef rank;
6511 Rank rank_val = kRankAssertionDefault;
6512
6513 rank_val = RankMake(kRankIndexMask, kRankAssertionDefault);
6514 rank = CFDictionaryGetValue(rank_dict, serviceID);
6515 if (rank != NULL) {
6516 if (!CFNumberGetValue(rank, kCFNumberSInt32Type, &rank_val)) {
6517 /* if we don't like the rank value */
6518 rank_val = kRankAssertionDefault;
6519 }
6520
6521 }
6522 return (rank_val);
6523 }
6524
6525 /*
6526 * Function: rank_dict_set_service_rank
6527 * Purpose:
6528 * Save the results of ranking the service so we can look it up later without
6529 * repeating all of the ranking code.
6530 */
6531 static void
6532 rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict,
6533 CFStringRef serviceID, Rank rank_val)
6534 {
6535 CFNumberRef rank;
6536
6537 rank = CFNumberCreate(NULL, kCFNumberSInt32Type, (const void *)&rank_val);
6538 if (rank != NULL) {
6539 CFDictionarySetValue(rank_dict, serviceID, rank);
6540 CFRelease(rank);
6541 }
6542 return;
6543 }
6544
6545 static const CFStringRef *transientInterfaceEntityNames[] = {
6546 &kSCEntNetPPP,
6547 };
6548
6549
6550 static void
6551 CollectTransientServices(const void * key,
6552 const void * value,
6553 void * context)
6554 {
6555 #pragma unused(value)
6556 CFStringRef service = key;
6557 CFMutableArrayRef vif_setup_keys = context;
6558
6559 /* This service is either a vpn type service or a comm center service */
6560 if (!CFStringHasPrefix(service, kSCDynamicStoreDomainSetup)) {
6561 return;
6562 }
6563
6564 for (size_t i = 0; i < countof(transientInterfaceEntityNames); i++) {
6565 if (CFStringHasSuffix(service, *transientInterfaceEntityNames[i])) {
6566 my_CFArrayAppendUniqueValue(vif_setup_keys, service);
6567 break;
6568 }
6569 }
6570
6571 return;
6572 }
6573
6574
6575 static SCNetworkReachabilityFlags
6576 GetReachabilityFlagsFromVPN(CFDictionaryRef services_info,
6577 CFStringRef service_id,
6578 CFStringRef entity,
6579 CFStringRef vpn_setup_key)
6580 {
6581 CFStringRef key;
6582 CFDictionaryRef dict;
6583 SCNetworkReachabilityFlags flags = 0;
6584
6585
6586 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6587 kSCDynamicStoreDomainSetup,
6588 service_id,
6589 kSCEntNetInterface);
6590 dict = CFDictionaryGetValue(services_info, key);
6591 CFRelease(key);
6592
6593 if (isA_CFDictionary(dict)
6594 && CFDictionaryContainsKey(dict, kSCPropNetInterfaceDeviceName)) {
6595
6596 flags = (kSCNetworkReachabilityFlagsReachable
6597 | kSCNetworkReachabilityFlagsTransientConnection
6598 | kSCNetworkReachabilityFlagsConnectionRequired);
6599
6600 if (CFEqual(entity, kSCEntNetPPP)) {
6601 CFNumberRef num;
6602 CFDictionaryRef p_dict = CFDictionaryGetValue(services_info, vpn_setup_key);
6603
6604 if (!isA_CFDictionary(p_dict)) {
6605 return (flags);
6606 }
6607
6608 // get PPP dial-on-traffic status
6609 num = CFDictionaryGetValue(p_dict, kSCPropNetPPPDialOnDemand);
6610 if (isA_CFNumber(num)) {
6611 int32_t ppp_demand;
6612
6613 if (CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand)) {
6614 if (ppp_demand) {
6615 flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
6616 }
6617 }
6618 }
6619 }
6620 }
6621 return (flags);
6622 }
6623
6624 static Boolean
6625 S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value)
6626 {
6627 Boolean ret = def_value;
6628
6629 if (dict != NULL) {
6630 CFBooleanRef val;
6631
6632 val = CFDictionaryGetValue(dict, key);
6633 if (isA_CFBoolean(val) != NULL) {
6634 ret = CFBooleanGetValue(val);
6635 }
6636 }
6637 return (ret);
6638 }
6639
6640
6641 static void
6642 GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info,
6643 SCNetworkReachabilityFlags *reach_flags_v4,
6644 SCNetworkReachabilityFlags *reach_flags_v6)
6645 {
6646 CFIndex i;
6647 CFIndex count;
6648 CFMutableArrayRef vif_setup_keys;
6649
6650 vif_setup_keys = CFArrayCreateMutable(NULL,
6651 0,
6652 &kCFTypeArrayCallBacks);
6653 CFDictionaryApplyFunction(services_info, CollectTransientServices,
6654 vif_setup_keys);
6655 count = CFArrayGetCount(vif_setup_keys);
6656 for (i = 0; i < count; i++) {
6657 CFArrayRef components = NULL;
6658 CFStringRef entity;
6659 CFStringRef service_id;
6660 CFStringRef vif_setup_key;
6661
6662 vif_setup_key = CFArrayGetValueAtIndex(vif_setup_keys, i);
6663
6664 /*
6665 * setup key in the following format:
6666 * Setup:/Network/Service/<Service ID>/<Entity>
6667 */
6668 components = CFStringCreateArrayBySeparatingStrings(NULL, vif_setup_key, CFSTR("/"));
6669
6670 if (CFArrayGetCount(components) != 5) {
6671 // invalid Setup key encountered
6672 goto skip;
6673 }
6674
6675 /* service id is the 3rd element */
6676 service_id = CFArrayGetValueAtIndex(components, 3);
6677
6678 /* entity id is the 4th element */
6679 entity = CFArrayGetValueAtIndex(components, 4);
6680
6681
6682 if (CFEqual(entity, kSCEntNetPPP)) {
6683 SCNetworkReachabilityFlags flags;
6684 CFStringRef key;
6685
6686 flags = GetReachabilityFlagsFromVPN(services_info,
6687 service_id,
6688 entity,
6689 vif_setup_key);
6690
6691 /* Check for the v4 reachability flags */
6692 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6693 kSCDynamicStoreDomainSetup,
6694 service_id,
6695 kSCEntNetIPv4);
6696
6697 if (CFDictionaryContainsKey(services_info, key)) {
6698 *reach_flags_v4 |= flags;
6699 my_log(LOG_DEBUG, "Service %@ setting ipv4 reach flags: %d", service_id, *reach_flags_v4);
6700 }
6701
6702 CFRelease(key);
6703
6704 /* Check for the v6 reachability flags */
6705 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6706 kSCDynamicStoreDomainSetup,
6707 service_id,
6708 kSCEntNetIPv6);
6709
6710 if (CFDictionaryContainsKey(services_info, key)) {
6711 *reach_flags_v6 |= flags;
6712 my_log(LOG_DEBUG, "Service %@ setting ipv6 reach flags: %d", service_id, *reach_flags_v6);
6713 }
6714 CFRelease(key);
6715
6716 if (flags != 0) {
6717 if (components != NULL) {
6718 CFRelease(components);
6719 }
6720 goto done;
6721 }
6722 }
6723 skip:
6724 if (components != NULL) {
6725 CFRelease(components);
6726 }
6727 }
6728 done:
6729 CFRelease(vif_setup_keys);
6730 return;
6731 }
6732
6733 static SCNetworkReachabilityFlags
6734 GetReachFlagsFromStatus(CFStringRef entity, int status)
6735 {
6736 SCNetworkReachabilityFlags flags = 0;
6737
6738 if (CFEqual(entity, kSCEntNetPPP)) {
6739 switch (status) {
6740 case PPP_RUNNING :
6741 /* if we're really UP and RUNNING */
6742 break;
6743 case PPP_ONHOLD :
6744 /* if we're effectively UP and RUNNING */
6745 break;
6746 case PPP_IDLE :
6747 /* if we're not connected at all */
6748 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6749 break;
6750 case PPP_STATERESERVED :
6751 // if we're not connected at all
6752 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6753 break;
6754 default :
6755 /* if we're in the process of [dis]connecting */
6756 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6757 break;
6758 }
6759 }
6760 else if (CFEqual(entity, kSCEntNetIPSec)) {
6761 switch (status) {
6762 case IPSEC_RUNNING :
6763 /* if we're really UP and RUNNING */
6764 break;
6765 case IPSEC_IDLE :
6766 /* if we're not connected at all */
6767 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6768 break;
6769 default :
6770 /* if we're in the process of [dis]connecting */
6771 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6772 break;
6773 }
6774 }
6775 else if (CFEqual(entity, kSCEntNetVPN)) {
6776 switch (status) {
6777 case VPN_RUNNING :
6778 /* if we're really UP and RUNNING */
6779 break;
6780 case VPN_IDLE :
6781 case VPN_LOADING :
6782 case VPN_LOADED :
6783 case VPN_UNLOADING :
6784 /* if we're not connected at all */
6785 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6786 break;
6787 default :
6788 /* if we're in the process of [dis]connecting */
6789 flags |= kSCNetworkReachabilityFlagsConnectionRequired;
6790 break;
6791 }
6792 }
6793 return (flags);
6794 }
6795
6796 static void
6797 VPNAttributesGet(CFStringRef service_id,
6798 CFDictionaryRef services_info,
6799 SCNetworkReachabilityFlags *flags,
6800 CFStringRef *server_address,
6801 int af)
6802 {
6803 CFDictionaryRef entity_dict;
6804 CFNumberRef num;
6805 CFDictionaryRef p_state = NULL;
6806 int status = 0;
6807 CFStringRef transient_entity = NULL;
6808
6809 if (af == AF_INET) {
6810 entity_dict = service_dict_get(service_id, kSCEntNetIPv4);
6811 } else {
6812 entity_dict = service_dict_get(service_id, kSCEntNetIPv6);
6813 }
6814 entity_dict = ipdict_get_service(entity_dict);
6815 if (entity_dict == NULL) {
6816 return;
6817 }
6818
6819 for (size_t i = 0; i < countof(transientServiceInfo); i++) {
6820 CFStringRef entity = *transientServiceInfo[i].entityName;
6821
6822 p_state = service_dict_get(service_id, entity);
6823
6824 /* ensure that this is a VPN Type service */
6825 if (isA_CFDictionary(p_state)) {
6826 transient_entity = entity;
6827 break;
6828 }
6829 }
6830
6831 /* Did we find a vpn type service? If not, we are done.*/
6832 if (transient_entity == NULL) {
6833 return;
6834 }
6835
6836 *flags |= (kSCNetworkReachabilityFlagsReachable
6837 | kSCNetworkReachabilityFlagsTransientConnection);
6838
6839 /* Get the Server Address */
6840 if (server_address != NULL) {
6841 *server_address = CFDictionaryGetValue(entity_dict,
6842 CFSTR("ServerAddress"));
6843 *server_address = isA_CFString(*server_address);
6844 if (*server_address != NULL) {
6845 CFRetain(*server_address);
6846 }
6847 }
6848
6849 /* get status */
6850 if (!CFDictionaryGetValueIfPresent(p_state,
6851 kSCPropNetVPNStatus, // IPSecStatus, PPPStatus, VPNStatus
6852 (const void **)&num) ||
6853 !isA_CFNumber(num) ||
6854 !CFNumberGetValue(num, kCFNumberIntType, &status)) {
6855 return;
6856 }
6857
6858 *flags |= GetReachFlagsFromStatus(transient_entity, status);
6859 if (CFEqual(transient_entity, kSCEntNetPPP)) {
6860 CFStringRef key;
6861 CFDictionaryRef p_setup;
6862 int ppp_demand;
6863
6864 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6865 kSCDynamicStoreDomainSetup,
6866 service_id,
6867 kSCEntNetPPP);
6868 p_setup = CFDictionaryGetValue(services_info, key);
6869 CFRelease(key);
6870
6871 /* get dial-on-traffic status */
6872 if (isA_CFDictionary(p_setup) &&
6873 CFDictionaryGetValueIfPresent(p_setup,
6874 kSCPropNetPPPDialOnDemand,
6875 (const void **)&num) &&
6876 isA_CFNumber(num) &&
6877 CFNumberGetValue(num, kCFNumberIntType, &ppp_demand) &&
6878 (ppp_demand != 0)) {
6879 *flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
6880 if (status == PPP_IDLE) {
6881 *flags |= kSCNetworkReachabilityFlagsInterventionRequired;
6882 }
6883 }
6884 }
6885 return;
6886 }
6887
6888
6889 typedef struct ElectionInfo {
6890 int af;
6891 CFStringRef entity;
6892 int n_services;
6893 CFArrayRef order;
6894 CFIndex n_order;
6895 ElectionResultsRef results;
6896 CFMutableDictionaryRef rank_dict;
6897 } ElectionInfo, * ElectionInfoRef;
6898
6899 typedef CFDictionaryApplierFunction ElectionFuncRef;
6900
6901 static void
6902 CandidateRelease(CandidateRef candidate)
6903 {
6904 my_CFRelease(&candidate->serviceID);
6905 my_CFRelease(&candidate->if_name);
6906 my_CFRelease(&candidate->signature);
6907 return;
6908 }
6909
6910 static void
6911 CandidateCopy(CandidateRef dest, CandidateRef src)
6912 {
6913 *dest = *src;
6914 if (dest->serviceID) {
6915 CFRetain(dest->serviceID);
6916 }
6917 if (dest->if_name) {
6918 CFRetain(dest->if_name);
6919 }
6920 if(dest->signature) {
6921 CFRetain(dest->signature);
6922 }
6923 return;
6924 }
6925
6926 static ElectionResultsRef
6927 ElectionResultsAlloc(int af, int size)
6928 {
6929 ElectionResultsRef results;
6930
6931 results = (ElectionResultsRef)malloc(ElectionResultsComputeSize(size));
6932 results->af = af;
6933 results->count = 0;
6934 results->size = size;
6935 return (results);
6936 }
6937
6938 static void
6939 ElectionResultsRelease(ElectionResultsRef results)
6940 {
6941 int i;
6942 CandidateRef scan;
6943
6944 for (i = 0, scan = results->candidates;
6945 i < results->count;
6946 i++, scan++) {
6947 CandidateRelease(scan);
6948 }
6949 free(results);
6950 return;
6951 }
6952
6953 static void
6954 ElectionResultsLog(int level, ElectionResultsRef results, const char * prefix)
6955 {
6956 int i;
6957 CandidateRef scan;
6958
6959 if (results == NULL) {
6960 my_log(level, "%s: no candidates", prefix);
6961 return;
6962 }
6963 my_log(level, "%s: %d candidates", prefix, results->count);
6964 for (i = 0, scan = results->candidates;
6965 i < results->count;
6966 i++, scan++) {
6967 char ntopbuf[INET6_ADDRSTRLEN];
6968
6969 (void)inet_ntop(results->af, &scan->addr, ntopbuf, sizeof(ntopbuf));
6970 my_log(level, "%d. %@ serviceID=%@ addr=%s rank=0x%x%s",
6971 i, scan->if_name, scan->serviceID, ntopbuf, scan->rank,
6972 scan->ineligible ? " [ineligible]" : "");
6973 }
6974 return;
6975 }
6976
6977 /*
6978 * Function: ElectionResultsAddCandidate
6979 * Purpose:
6980 * Add the candidate into the election results. Find the insertion point
6981 * by comparing the rank of the candidate with existing entries.
6982 */
6983 static void
6984 ElectionResultsAddCandidate(ElectionResultsRef results, CandidateRef candidate)
6985 {
6986 CFIndex i;
6987 CFIndex where;
6988
6989 if (results->count == results->size) {
6990 /* this should not happen */
6991 my_log(LOG_NOTICE, "can't fit another candidate");
6992 return;
6993 }
6994
6995 /* find the insertion point */
6996 where = kCFNotFound;
6997 for (i = 0; i < results->count; i++) {
6998 CandidateRef this_candidate = results->candidates + i;
6999
7000 if (candidate->rank < this_candidate->rank) {
7001 where = i;
7002 break;
7003 }
7004 }
7005 /* add it to the end */
7006 if (where == kCFNotFound) {
7007 CandidateCopy(results->candidates + results->count, candidate);
7008 results->count++;
7009 return;
7010 }
7011 /* slide existing entries over */
7012 for (i = results->count; i > where; i--) {
7013 results->candidates[i] = results->candidates[i - 1];
7014 }
7015 /* insert element */
7016 CandidateCopy(results->candidates + where, candidate);
7017 results->count++;
7018 return;
7019 }
7020
7021 static void
7022 elect_ip(const void * key, const void * value, void * context);
7023
7024 /*
7025 * Function: ElectionResultsCopy
7026 * Purpose:
7027 * Visit all of the services and invoke the protocol-specific election
7028 * function. Return the results of the election.
7029 */
7030 static ElectionResultsRef
7031 ElectionResultsCopy(int af, CFArrayRef order)
7032 {
7033 int count;
7034 ElectionInfo info;
7035
7036 count = (int)CFDictionaryGetCount(S_service_state_dict);
7037 if (count == 0) {
7038 return (NULL);
7039 }
7040 info.af = af;
7041 if (af == AF_INET) {
7042 info.entity = kSCEntNetIPv4;
7043 info.rank_dict = S_ipv4_service_rank_dict;
7044 }
7045 else {
7046 info.entity = kSCEntNetIPv6;
7047 info.rank_dict = S_ipv6_service_rank_dict;
7048 }
7049 info.results = ElectionResultsAlloc(af, count);
7050 info.n_services = count;
7051 info.order = order;
7052 if (order != NULL) {
7053 info.n_order = CFArrayGetCount(order);
7054 }
7055 else {
7056 info.order = 0;
7057 }
7058 CFDictionaryApplyFunction(S_service_state_dict, elect_ip, (void *)&info);
7059 if (info.results->count == 0) {
7060 ElectionResultsRelease(info.results);
7061 info.results = NULL;
7062 }
7063 return (info.results);
7064 }
7065
7066 /*
7067 * Function: ElectionResultsCandidateNeedsDemotion
7068 * Purpose:
7069 * Check whether the given candidate requires demotion. A candidate
7070 * might need to be demoted if its IPv4 and IPv6 services must be coupled
7071 * but a higher ranked service has IPv4 or IPv6.
7072 *
7073 * The converse is also true: if the given candidate has lower rank than
7074 * the other candidate and the other candidate is coupled, this candidate
7075 * needs to be demoted.
7076 */
7077 static Boolean
7078 ElectionResultsCandidateNeedsDemotion(CandidateRef other_candidate,
7079 CandidateRef candidate)
7080 {
7081 Boolean ret = FALSE;
7082
7083 if (other_candidate == NULL) {
7084 /* no other candidate */
7085 goto done;
7086 }
7087 if (other_candidate->ineligible) {
7088 /* other candidate can't become primary */
7089 goto done;
7090 }
7091 if (RANK_ASSERTION_MASK(other_candidate->rank) == kRankAssertionNever) {
7092 /* the other candidate can't become primary */
7093 goto done;
7094 }
7095 if (!candidate->ip_is_coupled && !other_candidate->ip_is_coupled) {
7096 /* neither candidate is coupled */
7097 goto done;
7098 }
7099 if (CFEqual(other_candidate->if_name, candidate->if_name)) {
7100 /* they are over the same interface, no need to demote */
7101 goto done;
7102 }
7103 if (CFStringHasPrefix(other_candidate->if_name, CFSTR("stf"))) {
7104 /* avoid creating a feedback loop */
7105 goto done;
7106 }
7107 if (candidate->rank < other_candidate->rank) {
7108 /* we're higher ranked than the other candidate, ignore */
7109 goto done;
7110 }
7111 if (candidate->ip_is_coupled) {
7112 if (other_candidate->ip_is_coupled
7113 && candidate->rank == other_candidate->rank) {
7114 /* same rank as another service that is coupled, ignore */
7115 goto done;
7116 }
7117 }
7118 else if (other_candidate->ip_is_coupled) { /* must be true */
7119 if (candidate->rank == other_candidate->rank) {
7120 /* other candidate will be demoted, so we don't need to */
7121 goto done;
7122 }
7123 /* we're lower rank and need to be demoted */
7124 }
7125 else { /* can't happen, we already tested for this above */
7126 /* neither candidate is coupled */
7127 goto done;
7128 }
7129 ret = TRUE;
7130
7131 done:
7132 return (ret);
7133
7134 }
7135
7136
7137 static void
7138 get_signature_sha256(CFStringRef signature,
7139 unsigned char * sha256)
7140 {
7141 CC_SHA256_CTX ctx;
7142 CFDataRef signature_data;
7143
7144 signature_data = CFStringCreateExternalRepresentation(NULL,
7145 signature,
7146 kCFStringEncodingUTF8,
7147 0);
7148
7149 CC_SHA256_Init(&ctx);
7150 CC_SHA256_Update(&ctx,
7151 CFDataGetBytePtr(signature_data),
7152 (CC_LONG)CFDataGetLength(signature_data));
7153 CC_SHA256_Final(sha256, &ctx);
7154
7155 CFRelease(signature_data);
7156
7157 return;
7158 }
7159
7160
7161 static void
7162 add_candidate_to_nwi_state(nwi_state_t nwi_state, int af,
7163 CandidateRef candidate, Boolean not_in_list,
7164 Boolean not_in_iflist)
7165 {
7166 uint64_t flags = 0;
7167 char ifname[IFNAMSIZ];
7168 nwi_ifstate_t ifstate;
7169
7170 if (nwi_state == NULL) {
7171 /* can't happen */
7172 return;
7173 }
7174 if (not_in_list
7175 || RANK_ASSERTION_MASK(candidate->rank) == kRankAssertionNever) {
7176 flags |= NWI_IFSTATE_FLAGS_NOT_IN_LIST;
7177 }
7178 if (not_in_iflist) {
7179 flags |= NWI_IFSTATE_FLAGS_NOT_IN_IFLIST;
7180 }
7181 if (service_dict_get(candidate->serviceID, kSCEntNetDNS) != NULL) {
7182 flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
7183 }
7184 if ((af == AF_INET) && service_has_clat46_address(candidate->serviceID)) {
7185 flags |= NWI_IFSTATE_FLAGS_HAS_CLAT46;
7186 }
7187 CFStringGetCString(candidate->if_name, ifname, sizeof(ifname),
7188 kCFStringEncodingASCII);
7189 if ((S_IPMonitor_debug & kDebugFlag2) != 0) {
7190 char ntopbuf[INET6_ADDRSTRLEN];
7191
7192 (void)inet_ntop(af, &candidate->addr, ntopbuf, sizeof(ntopbuf));
7193 my_log(LOG_DEBUG,
7194 "Adding IPv%c [%s] %s "
7195 "with flags 0x%llx rank 0x%x reach_flags 0x%x",
7196 ipvx_char(af), ifname, ntopbuf,
7197 flags, candidate->rank, candidate->reachability_flags);
7198 }
7199 ifstate = nwi_state_add_ifstate(nwi_state, ifname, af, flags,
7200 candidate->rank,
7201 (void *)&candidate->addr,
7202 (void *)&candidate->vpn_server_addr,
7203 candidate->reachability_flags);
7204 if (ifstate != NULL && candidate->signature) {
7205 uint8_t hash[CC_SHA256_DIGEST_LENGTH];
7206
7207 get_signature_sha256(candidate->signature, hash);
7208 nwi_ifstate_set_signature(ifstate, hash);
7209 }
7210 return;
7211 }
7212
7213
7214 static void
7215 add_reachability_flags_to_candidate(CandidateRef candidate, CFDictionaryRef services_info, int af)
7216 {
7217 SCNetworkReachabilityFlags flags = kSCNetworkReachabilityFlagsReachable;
7218 CFStringRef vpn_server_address = NULL;
7219
7220 assert(candidate != NULL);
7221 assert(services_info != NULL);
7222
7223 VPNAttributesGet(candidate->serviceID,
7224 services_info,
7225 &flags,
7226 &vpn_server_address,
7227 af);
7228
7229 candidate->reachability_flags = flags;
7230
7231 if (vpn_server_address == NULL) {
7232 memset(&candidate->vpn_server_addr, 0, sizeof(candidate->vpn_server_addr));
7233 } else {
7234 char buf[128];
7235
7236 CFStringGetCString(vpn_server_address, buf, sizeof(buf),
7237 kCFStringEncodingASCII);
7238 _SC_string_to_sockaddr(buf,
7239 AF_UNSPEC,
7240 (void *)&candidate->vpn_server_addr,
7241 sizeof(candidate->vpn_server_addr));
7242
7243 CFRelease(vpn_server_address);
7244 }
7245 return;
7246 }
7247 /*
7248 * Function: ElectionResultsGetPrimary
7249 * Purpose:
7250 * Use the results of the current protocol and the other protocol to
7251 * determine which service should become primary.
7252 *
7253 * At the same time, generate the IPv4/IPv6 routing table and
7254 * the nwi_state for the protocol.
7255 */
7256 static CandidateRef
7257 ElectionResultsGetPrimary(ElectionResultsRef results,
7258 CandidateRef other_candidate,
7259 nwi_state_t nwi_state, int af,
7260 RouteListRef * ret_routes,
7261 CFDictionaryRef services_info,
7262 CFSetRef ip_service_changes)
7263 {
7264 CandidateRef primary = NULL;
7265 Boolean primary_is_null = FALSE;
7266 RouteListRef routes = NULL;
7267
7268 assert(services_info != NULL);
7269
7270 if (results != NULL) {
7271 CandidateRef deferred[results->count];
7272 int deferred_count;
7273 CFStringRef entity_name;
7274 int i;
7275 int initial_size;
7276 RouteListInfoRef info;
7277 CandidateRef scan;
7278
7279 switch (af) {
7280 case AF_INET:
7281 entity_name = kSCEntNetIPv4;
7282 info = &IPv4RouteListInfo;
7283 initial_size = results->count * IPV4_ROUTES_N_STATIC;
7284 break;
7285 default:
7286 case AF_INET6:
7287 entity_name = kSCEntNetIPv6;
7288 info = &IPv6RouteListInfo;
7289 initial_size = results->count * IPV6_ROUTES_N_STATIC;
7290 break;
7291 }
7292 deferred_count = 0;
7293 for (i = 0, scan = results->candidates;
7294 i < results->count;
7295 i++, scan++) {
7296 Boolean is_primary = FALSE;
7297 CFDictionaryRef service_dict;
7298 RouteListRef service_routes;
7299 Boolean skip = FALSE;
7300
7301 if (!scan->ineligible
7302 && primary == NULL
7303 && RANK_ASSERTION_MASK(scan->rank) != kRankAssertionNever) {
7304 if (ElectionResultsCandidateNeedsDemotion(other_candidate,
7305 scan)) {
7306 /* demote the service */
7307 my_log(LOG_NOTICE,
7308 "IPv%c over %@ (rank 0x%x) demoted: "
7309 "primary IPv%c %@ (rank 0x%x)",
7310 ipvx_char(af), scan->if_name, scan->rank,
7311 ipvx_other_char(af), other_candidate->if_name,
7312 other_candidate->rank);
7313 deferred[deferred_count++] = scan;
7314 skip = TRUE;
7315 }
7316 else {
7317 primary = scan;
7318 is_primary = TRUE;
7319 }
7320 }
7321 /* contribute to the routing table */
7322 service_dict = service_dict_get(scan->serviceID, entity_name);
7323 service_routes = ipdict_get_routelist(service_dict);
7324 if (service_routes != NULL) {
7325 boolean_t force;
7326 Rank rank = scan->rank;
7327
7328 if (skip) {
7329 /* routes are RankNever to prevent becoming primary */
7330 rank = RankMake(rank, kRankAssertionNever);
7331 }
7332 force = my_CFSetContainsValue(ip_service_changes,
7333 scan->serviceID);
7334 routes = RouteListAddRouteList(info, routes, initial_size,
7335 service_routes, rank, force);
7336 if ((service_routes->flags & kRouteListFlagsExcludeNWI) != 0) {
7337 skip = TRUE;
7338 }
7339 }
7340 else {
7341 skip = TRUE;
7342 }
7343 if (skip) {
7344 /* if we're skipping the primary, it's NULL */
7345 if (is_primary) {
7346 primary_is_null = TRUE;
7347 }
7348 }
7349 else if (!scan->ineligible) {
7350 Boolean not_in_iflist;
7351
7352 add_reachability_flags_to_candidate(scan, services_info, af);
7353 not_in_iflist
7354 = (service_routes->flags & kRouteListFlagsScopedOnly) != 0;
7355 add_candidate_to_nwi_state(nwi_state, af, scan,
7356 primary_is_null,
7357 not_in_iflist);
7358 }
7359 }
7360 for (i = 0; i < deferred_count; i++) {
7361 CandidateRef candidate = deferred[i];
7362
7363 add_reachability_flags_to_candidate(candidate, services_info, af);
7364 add_candidate_to_nwi_state(nwi_state, af, candidate, TRUE, FALSE);
7365 }
7366 }
7367 if (ret_routes != NULL) {
7368 *ret_routes = routes;
7369 }
7370 else if (routes != NULL) {
7371 free(routes);
7372 }
7373 if (primary_is_null) {
7374 primary = NULL;
7375 }
7376 return (primary);
7377 }
7378
7379
7380 static inline
7381 CFStringRef
7382 service_dict_get_signature(CFDictionaryRef service_dict)
7383 {
7384 CFStringRef ifname;
7385
7386 ifname = CFDictionaryGetValue(service_dict, kSCPropInterfaceName);
7387 if (isA_CFString(ifname) == NULL
7388 || !confirm_interface_name(service_dict, ifname)) {
7389 return (NULL);
7390 }
7391 return (CFDictionaryGetValue(service_dict, kStoreKeyNetworkSignature));
7392 }
7393
7394 /*
7395 * Function: elect_ip
7396 * Purpose:
7397 * Evaluate the service and determine what rank the service should have.
7398 * If it's a suitable candidate, add it to the election results.
7399 */
7400 static void
7401 elect_ip(const void * key, const void * value, void * context)
7402 {
7403 CFDictionaryRef all_entities_dict = (CFDictionaryRef)value;
7404 Candidate candidate;
7405 Rank default_rank;
7406 ElectionInfoRef elect_info;
7407 CFStringRef if_name;
7408 CFDictionaryRef ipdict;
7409 Rank primary_rank;
7410 CFDictionaryRef rank_entity;
7411 RouteListUnion routelist;
7412 CFDictionaryRef service_dict;
7413
7414 elect_info = (ElectionInfoRef)context;
7415 ipdict = CFDictionaryGetValue(all_entities_dict, elect_info->entity);
7416 if (ipdict != NULL) {
7417 routelist.ptr = ipdict_get_routelist(ipdict);
7418 service_dict = ipdict_get_service(ipdict);
7419 }
7420 else {
7421 routelist.ptr = NULL;
7422 }
7423 if (routelist.ptr == NULL || service_dict == NULL) {
7424 /* no connectivity */
7425 return;
7426 }
7427 if_name = CFDictionaryGetValue(service_dict, kSCPropInterfaceName);
7428 if (if_name == NULL) {
7429 /* need an interface name */
7430 return;
7431 }
7432 if (CFEqual(if_name, CFSTR(kLoopbackInterface))) {
7433 /* don't process loopback */
7434 return;
7435 }
7436 memset(&candidate, 0, sizeof(candidate));
7437 candidate.serviceID = (CFStringRef)key;
7438 if ((routelist.common->flags & kRouteListFlagsHasDefault) == 0) {
7439 /* no default route means it's ineligible to become primary */
7440 candidate.ineligible = TRUE;
7441 }
7442 rank_entity = CFDictionaryGetValue(all_entities_dict, kSCEntNetService);
7443 candidate.rank = get_service_index(rank_entity,
7444 elect_info->order, elect_info->n_order,
7445 candidate.serviceID);
7446 if (elect_info->af == AF_INET) {
7447 default_rank = routelist.v4->list->rank;
7448 candidate.addr.v4 = routelist.v4->list->ifa;
7449 }
7450 else {
7451 default_rank = routelist.v6->list->rank;
7452 candidate.addr.v6 = routelist.v6->list->ifa;
7453 }
7454 primary_rank = RANK_ASSERTION_MASK(default_rank);
7455 if (S_ppp_override_primary) {
7456 char ifn[IFNAMSIZ];
7457
7458 if (CFStringGetCString(if_name, ifn, sizeof(ifn),
7459 kCFStringEncodingASCII)
7460 && (strncmp(PPP_PREFIX, ifn, sizeof(PPP_PREFIX) - 1) == 0)) {
7461 /* PPP override: make ppp* look the best */
7462 primary_rank = kRankAssertionFirst;
7463 }
7464 }
7465 candidate.rank = RankMake(candidate.rank, primary_rank);
7466 candidate.ip_is_coupled = service_get_ip_is_coupled(candidate.serviceID);
7467 candidate.if_name = if_name;
7468 rank_dict_set_service_rank(elect_info->rank_dict,
7469 candidate.serviceID, candidate.rank);
7470 candidate.signature = service_dict_get_signature(service_dict);
7471 ElectionResultsAddCandidate(elect_info->results, &candidate);
7472 return;
7473 }
7474
7475
7476 static uint32_t
7477 service_changed(CFDictionaryRef services_info, CFStringRef serviceID)
7478 {
7479 uint32_t changed = 0;
7480 int i;
7481
7482 /* update service options first (e.g. rank) */
7483 if (get_rank_changes(serviceID,
7484 get_service_state_entity(services_info, serviceID,
7485 NULL),
7486 get_service_setup_entity(services_info, serviceID,
7487 NULL),
7488 services_info)) {
7489 changed |= (1 << kEntityTypeServiceOptions);
7490 }
7491
7492 /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
7493 for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
7494 GetEntityChangesFuncRef func;
7495 CFStringRef name;
7496
7497 func = entityChangeFunc[i];
7498 name = *entityTypeNames[i];
7499 if ((*func)(serviceID,
7500 get_service_state_entity(services_info, serviceID, name),
7501 get_service_setup_entity(services_info, serviceID, name),
7502 services_info)) {
7503 changed |= (1 << i);
7504 }
7505 }
7506
7507 /* update transient service status */
7508 if (get_transient_status_changes(serviceID, services_info)) {
7509 changed |= (1 << kEntityTypeTransientStatus);
7510 }
7511
7512 return (changed);
7513 }
7514
7515 static CFStringRef
7516 serviceID_get_ifname(CFStringRef serviceID)
7517 {
7518 CFDictionaryRef entity_dict;
7519 CFStringRef ifname = NULL;
7520
7521 entity_dict = service_dict_get(serviceID, kSCEntNetIPv4);
7522 if (entity_dict == NULL) {
7523 entity_dict = service_dict_get(serviceID, kSCEntNetIPv6);
7524 }
7525 if (entity_dict != NULL) {
7526 ifname = ipdict_get_ifname(entity_dict);
7527 }
7528 return (ifname);
7529 }
7530
7531 __private_extern__ boolean_t
7532 check_if_service_expensive(CFStringRef serviceID)
7533 {
7534 CFStringRef ifname;
7535 ifname = serviceID_get_ifname(serviceID);
7536
7537 return interface_is_expensive(ifname);
7538 }
7539
7540 static CFArrayRef
7541 service_order_get(CFDictionaryRef services_info)
7542 {
7543 CFArrayRef order = NULL;
7544 CFDictionaryRef ipv4_dict;
7545
7546 ipv4_dict = my_CFDictionaryGetDictionary(services_info,
7547 S_setup_global_ipv4);
7548 if (ipv4_dict != NULL) {
7549 CFNumberRef ppp_override;
7550 int ppp_val = 0;
7551
7552 order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
7553 order = isA_CFArray(order);
7554
7555 /* get ppp override primary */
7556 ppp_override = CFDictionaryGetValue(ipv4_dict,
7557 kSCPropNetPPPOverridePrimary);
7558 ppp_override = isA_CFNumber(ppp_override);
7559 if (ppp_override != NULL) {
7560 CFNumberGetValue(ppp_override, kCFNumberIntType, &ppp_val);
7561 }
7562 S_ppp_override_primary = (ppp_val != 0) ? TRUE : FALSE;
7563 }
7564 else {
7565 S_ppp_override_primary = FALSE;
7566 }
7567 return (order);
7568 }
7569
7570 static boolean_t
7571 set_new_primary(CFStringRef * primary_p, CFStringRef new_primary,
7572 const char * entity)
7573 {
7574 boolean_t changed = FALSE;
7575 CFStringRef primary = *primary_p;
7576
7577 if (new_primary != NULL) {
7578 if (primary != NULL && CFEqual(new_primary, primary)) {
7579 my_log(LOG_INFO, "%@ is still primary %s", new_primary, entity);
7580 }
7581 else {
7582 my_CFRelease(primary_p);
7583 *primary_p = CFRetain(new_primary);
7584 my_log(LOG_INFO, "%@ is the new primary %s", new_primary, entity);
7585 changed = TRUE;
7586 }
7587 }
7588 else if (primary != NULL) {
7589 my_log(LOG_INFO, "%@ is no longer primary %s", primary, entity);
7590 my_CFRelease(primary_p);
7591 changed = TRUE;
7592 }
7593 return (changed);
7594 }
7595
7596 static Rank
7597 rank_service_entity(CFDictionaryRef rank_dict, CFStringRef serviceID,
7598 CFStringRef entity)
7599 {
7600 if (service_dict_get(serviceID, entity) == NULL) {
7601 return (RankMake(kRankIndexMask, kRankAssertionDefault));
7602 }
7603 return (rank_dict_get_service_rank(rank_dict, serviceID));
7604 }
7605
7606 static void
7607 append_serviceIDs_for_interface(CFMutableArrayRef services_changed,
7608 CFStringRef ifname)
7609 {
7610 CFIndex count;
7611 CFIndex i;
7612 void * * keys;
7613 #define N_KEYS_VALUES_STATIC 10
7614 void * keys_values_buf[N_KEYS_VALUES_STATIC * 2];
7615 void * * values;
7616
7617 count = CFDictionaryGetCount(S_service_state_dict);
7618 if (count <= N_KEYS_VALUES_STATIC) {
7619 keys = keys_values_buf;
7620 } else {
7621 keys = (void * *)malloc(sizeof(*keys) * count * 2);
7622 }
7623 values = keys + count;
7624 CFDictionaryGetKeysAndValues(S_service_state_dict,
7625 (const void * *)keys,
7626 (const void * *)values);
7627
7628 for (i = 0; i < count; i++) {
7629 CFDictionaryRef ipdict = NULL;
7630 CFStringRef interface = NULL;
7631 CFStringRef serviceID;
7632 CFDictionaryRef service_dict;
7633
7634 serviceID = (CFStringRef)keys[i];
7635 service_dict = (CFDictionaryRef)values[i];
7636
7637 /* check whether service has IPv4 or IPv6 */
7638 ipdict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
7639 if (ipdict == NULL) {
7640 ipdict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
7641 if (ipdict == NULL) {
7642 continue;
7643 }
7644 }
7645 interface = ipdict_get_ifname(ipdict);
7646 if (interface != NULL && CFEqual(interface, ifname)) {
7647 my_log(LOG_DEBUG,
7648 "Found IP service %@ on interface %@",
7649 serviceID, ifname);
7650 my_CFArrayAppendUniqueValue(services_changed, serviceID);
7651 }
7652 }
7653 if (keys != keys_values_buf) {
7654 free(keys);
7655 }
7656 return;
7657 }
7658
7659 static __inline__ const char *
7660 get_changed_str(CFStringRef serviceID, CFStringRef entity,
7661 CFDictionaryRef old_dict)
7662 {
7663 CFDictionaryRef new_dict = NULL;
7664
7665 if (serviceID != NULL) {
7666 new_dict = service_dict_get(serviceID, entity);
7667 }
7668
7669 if (old_dict == NULL) {
7670 if (new_dict != NULL) {
7671 return "+";
7672 }
7673 } else {
7674 if (new_dict == NULL) {
7675 return "-";
7676 } else if (!CFEqual(old_dict, new_dict)) {
7677 return "!";
7678 }
7679 }
7680 return "";
7681 }
7682
7683 #if !TARGET_OS_SIMULATOR
7684
7685 #ifdef SIOCSIFORDER
7686 #define MANAGE_IF_ORDER
7687 #define MANAGE_IF_IOCTL
7688 #endif /* SIOCSIFORDER */
7689
7690 #ifdef SIOCSIFNETSIGNATURE
7691 #define MANAGE_IF_SIGNATURE
7692 #define MANAGE_IF_IOCTL
7693 #endif /* SIOCSIFNETSIGNATURE */
7694
7695 #ifdef MANAGE_IF_IOCTL
7696 static int
7697 inet_dgram_socket(void)
7698 {
7699 int sockfd;
7700
7701 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
7702 if (sockfd == -1) {
7703 my_log(LOG_ERR, "socket() failed: %s", strerror(errno));
7704 }
7705
7706 return sockfd;
7707 }
7708 #endif /* MANAGE_IF_IOCTL */
7709
7710 #ifdef MANAGE_IF_ORDER
7711 static Boolean
7712 interface_order_changed(nwi_state_t old_state, nwi_state_t new_state)
7713 {
7714 if (old_state == NULL && new_state == NULL) {
7715 // Both are NULL, nothing changed
7716 return FALSE;
7717 }
7718
7719 if (old_state == NULL || new_state == NULL) {
7720 // One is NULL, something changed
7721 return TRUE;
7722 }
7723
7724 if (old_state->if_list_count != new_state->if_list_count) {
7725 // Count is different, something changed
7726 return TRUE;
7727 }
7728
7729 if (new_state->if_list_count == 0) {
7730 // Count is same and 0, nothing changed
7731 return FALSE;
7732 }
7733
7734 int i;
7735 nwi_ifindex_t *old_scan;
7736 nwi_ifindex_t *new_scan;
7737 for (i = 0, old_scan = nwi_state_if_list(old_state), new_scan = nwi_state_if_list(new_state);
7738 i < new_state->if_list_count; i++, old_scan++, new_scan++) {
7739 if (strcmp(old_state->ifstate_list[*old_scan].ifname, new_state->ifstate_list[*new_scan].ifname) != 0) {
7740 // Some interface in the list is different, something changed
7741 return TRUE;
7742 }
7743 }
7744
7745 // Count and contents are the same, nothing changed
7746 return FALSE;
7747 }
7748
7749 static Boolean
7750 update_interface_order(nwi_state_t state, int sockfd)
7751 {
7752 Boolean success = FALSE;
7753
7754 // Set interface order into the kernel
7755 struct if_order interface_order;
7756 interface_order.ifo_count = (uint32_t)state->if_list_count;
7757 interface_order.ifo_ordered_indices = (mach_vm_address_t)calloc((size_t)interface_order.ifo_count, sizeof(uint32_t));
7758 if (((uint32_t *)interface_order.ifo_ordered_indices) != NULL) {
7759 int i;
7760 nwi_ifindex_t *scan;
7761 for (i = 0, scan = nwi_state_if_list(state);
7762 i < state->if_list_count; i++, scan++) {
7763 const char *ifname = state->ifstate_list[*scan].ifname;
7764 ((uint32_t *)interface_order.ifo_ordered_indices)[i] = my_if_nametoindex(ifname);
7765 }
7766 }
7767 if (ioctl(sockfd, SIOCSIFORDER, &interface_order) != 0) {
7768 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));
7769 } else {
7770 my_log(LOG_INFO, "Set kernel interface order for %u interfaces", interface_order.ifo_count);
7771 success = TRUE;
7772 }
7773 if (((uint32_t *)interface_order.ifo_ordered_indices) != NULL) {
7774 free((void *)interface_order.ifo_ordered_indices);
7775 interface_order.ifo_ordered_indices = (mach_vm_address_t)NULL;
7776 }
7777
7778 return success;
7779 }
7780 #endif /* MANAGE_IF_ORDER */
7781
7782 #ifdef MANAGE_IF_SIGNATURE
7783 static int
7784 siocsifnetsignature(int s, const char * ifname, int af,
7785 const uint8_t * signature, size_t signature_length)
7786 {
7787 struct if_nsreq nsreq;
7788
7789 memset(&nsreq, 0, sizeof(nsreq));
7790 strlcpy(nsreq.ifnsr_name, ifname, sizeof(nsreq.ifnsr_name));
7791 nsreq.ifnsr_family = af;
7792 if (signature_length > 0) {
7793 if (signature_length > sizeof(nsreq.ifnsr_data)) {
7794 signature_length = sizeof(nsreq.ifnsr_data);
7795 }
7796 nsreq.ifnsr_len = signature_length;
7797 memcpy(nsreq.ifnsr_data, signature, signature_length);
7798 }
7799 return (ioctl(s, SIOCSIFNETSIGNATURE, &nsreq));
7800 }
7801
7802 static void
7803 process_ifstate_difference(nwi_ifstate_t ifstate, int af, int sockfd)
7804 {
7805 nwi_ifstate_difference_t diff;
7806 boolean_t set_signature = FALSE;
7807 int signature_length = 0;
7808
7809 diff = nwi_ifstate_get_difference(ifstate);
7810 switch (diff) {
7811 case knwi_ifstate_difference_changed:
7812 /* set signature for this interface */
7813 set_signature = TRUE;
7814 if ((ifstate->flags & NWI_IFSTATE_FLAGS_HAS_SIGNATURE) != 0) {
7815 signature_length = sizeof(ifstate->signature);
7816 }
7817 break;
7818 case knwi_ifstate_difference_removed:
7819 /* remove signature for this interface */
7820 set_signature = TRUE;
7821 break;
7822 default:
7823 break;
7824 }
7825 if (set_signature) {
7826 if (siocsifnetsignature(sockfd, ifstate->ifname, af,
7827 ifstate->signature,
7828 signature_length) < 0) {
7829 my_log(LOG_ERR,
7830 "siocsifnetsignature(%s, IPv%c, %d) failed: %s",
7831 ifstate->ifname, ipvx_char(af),
7832 signature_length,
7833 strerror(errno));
7834 }
7835 else {
7836 my_log(LOG_DEBUG, "IPv%c Network Signature %s %s",
7837 ipvx_char(af),
7838 (signature_length > 0) ? "Set" : "Cleared",
7839 ifstate->ifname);
7840 if (signature_length > 0
7841 && (S_IPMonitor_debug & kDebugFlag1) != 0) {
7842 int i;
7843 char sig_buf[signature_length * 3 + 1];
7844
7845 sig_buf[0] = '\0';
7846 for (i = 0; i < signature_length; i++) {
7847 char byte_buf[4];
7848
7849 snprintf(byte_buf, sizeof(byte_buf),
7850 "%02x ", ifstate->signature[i]);
7851 strlcat(sig_buf, byte_buf, sizeof(sig_buf));
7852 }
7853 my_log(LOG_DEBUG, "Signature Bytes: %s", sig_buf);
7854 }
7855 }
7856 }
7857 return;
7858 }
7859
7860 static void
7861 process_state_differences(nwi_state_t state, int af, int sockfd)
7862 {
7863 int count;
7864 int i;
7865 nwi_ifstate_t scan;
7866
7867 if (af == AF_INET) {
7868 count = state->ipv4_count;
7869 }
7870 else {
7871 count = state->ipv6_count;
7872 }
7873 for (i = 0, scan = nwi_state_ifstate_list(state, af);
7874 i < count; i++, scan++) {
7875 process_ifstate_difference(scan, af, sockfd);
7876 }
7877 return;
7878 }
7879 #endif /* MANAGE_IF_SIGNATURE */
7880
7881 #endif /* !TARGET_OS_SIMULATOR */
7882
7883 static void
7884 process_nwi_changes(CFMutableStringRef log_output,
7885 nwi_state_t changes_state,
7886 nwi_state_t new_state,
7887 nwi_state_t old_state,
7888 boolean_t dns_changed,
7889 boolean_t dnsinfo_changed,
7890 CFDictionaryRef old_primary_dns,
7891 boolean_t proxy_changed,
7892 CFDictionaryRef old_primary_proxy,
7893 boolean_t smb_changed,
7894 CFDictionaryRef old_primary_smb)
7895 {
7896 #ifndef MANAGE_IF_ORDER
7897 #pragma unused(new_state)
7898 #pragma unused(old_state)
7899 #endif // !MANAGE_IF_ORDER
7900 #if TARGET_OS_IPHONE
7901 #pragma unused(smb_changed)
7902 #pragma unused(old_primary_smb)
7903 #endif // TARGET_OS_IPHONE
7904
7905 if (changes_state != NULL) {
7906 const sa_family_t af_list[] = {AF_INET, AF_INET6};
7907 nwi_ifstate_t scan;
7908 #ifdef MANAGE_IF_IOCTL
7909 int sockfd = inet_dgram_socket();
7910 #endif /* MANAGE_IF_IOCTL */
7911
7912 #ifdef MANAGE_IF_ORDER
7913 if (interface_order_changed(new_state, old_state)) {
7914 update_interface_order(new_state, sockfd);
7915 }
7916 #endif /* MANAGE_IF_ORDER */
7917
7918 for (size_t idx = 0; idx < countof(af_list); idx++) {
7919 int af = af_list[idx];
7920 CFMutableStringRef changes = NULL;
7921 CFMutableStringRef primary_str = NULL;
7922
7923 #ifdef MANAGE_IF_SIGNATURE
7924 process_state_differences(changes_state, af, sockfd);
7925 #endif /* MANAGE_IF_SIGNATURE */
7926 scan = nwi_state_get_first_ifstate(changes_state, af);
7927 while (scan != NULL) {
7928 const char * changed_str;
7929
7930 changed_str = nwi_ifstate_get_diff_str(scan);
7931 if (changed_str != NULL) {
7932 void * address;
7933 const char * addr_str;
7934 char ntopbuf[INET6_ADDRSTRLEN];
7935
7936 address = (void *)nwi_ifstate_get_address(scan);
7937 addr_str = inet_ntop(scan->af, address, ntopbuf,
7938 sizeof(ntopbuf));
7939 if (primary_str == NULL) {
7940 primary_str = CFStringCreateMutable(NULL, 0);
7941 CFStringAppendFormat(primary_str, NULL,
7942 CFSTR("%s%s:%s"),
7943 nwi_ifstate_get_ifname(scan),
7944 changed_str, addr_str);
7945 } else {
7946 if (changes == NULL) {
7947 changes = CFStringCreateMutable(NULL, 0);
7948 }
7949 CFStringAppendFormat(changes, NULL, CFSTR(", %s"),
7950 nwi_ifstate_get_ifname(scan));
7951 if (strcmp(changed_str, "") != 0) {
7952 CFStringAppendFormat(changes, NULL, CFSTR("%s:%s"),
7953 changed_str, addr_str);
7954 }
7955 }
7956 }
7957 scan = nwi_ifstate_get_next(scan, scan->af);
7958 }
7959
7960 if (primary_str != NULL) {
7961 CFStringAppendFormat(log_output, NULL, CFSTR(" %s(%@"),
7962 af == AF_INET ? "v4" : "v6",
7963 primary_str);
7964
7965 if (changes != NULL && CFStringGetLength(changes) != 0) {
7966 CFStringAppendFormat(log_output, NULL, CFSTR("%@"),
7967 changes);
7968 }
7969 CFStringAppend(log_output, CFSTR(")"));
7970
7971 my_CFRelease(&primary_str);
7972 my_CFRelease(&changes);
7973 }
7974 }
7975 #ifdef MANAGE_IF_IOCTL
7976 if (sockfd >= 0) {
7977 close(sockfd);
7978 }
7979 #endif /* MANAGE_IF_IOCTL */
7980 }
7981
7982 if (dns_changed || dnsinfo_changed) {
7983 const char *str;
7984
7985 str = get_changed_str(S_primary_dns, kSCEntNetDNS, old_primary_dns);
7986 if ((strcmp(str, "") == 0) && dnsinfo_changed) {
7987 str = "*"; // dnsinfo change w/no change to primary
7988 }
7989 CFStringAppendFormat(log_output, NULL, CFSTR(" DNS%s"), str);
7990 } else if (S_primary_dns != NULL) {
7991 CFStringAppend(log_output, CFSTR(" DNS"));
7992 }
7993
7994 if (proxy_changed) {
7995 const char *str;
7996
7997 str = get_changed_str(S_primary_proxies, kSCEntNetProxies, old_primary_proxy);
7998 CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy%s"), str);
7999 } else if (S_primary_proxies != NULL) {
8000 CFStringAppend(log_output, CFSTR(" Proxy"));
8001 }
8002
8003 #if !TARGET_OS_IPHONE
8004 if (smb_changed) {
8005 const char *str;
8006
8007 str = get_changed_str(S_primary_smb, kSCEntNetSMB, old_primary_smb);
8008 CFStringAppendFormat(log_output, NULL, CFSTR(" SMB%s"), str);
8009 } else if (S_primary_smb != NULL) {
8010 CFStringAppend(log_output, CFSTR(" SMB"));
8011 }
8012 #endif // !TARGET_OS_IPHONE
8013
8014 return;
8015 }
8016
8017 #pragma mark -
8018 #pragma mark Network changed notification
8019
8020 static dispatch_queue_t
8021 __network_change_queue()
8022 {
8023 static dispatch_once_t once;
8024 static dispatch_queue_t q;
8025
8026 dispatch_once(&once, ^{
8027 q = dispatch_queue_create("network change queue", NULL);
8028 });
8029
8030 return q;
8031 }
8032
8033 // Note: must run on __network_change_queue()
8034 static void
8035 post_network_change_when_ready()
8036 {
8037 int status;
8038
8039 dispatch_assert_queue(__network_change_queue());
8040
8041 if (S_network_change_needed == 0) {
8042 return;
8043 }
8044
8045 if (!S_network_change_timeout &&
8046 (!S_dnsinfo_synced || !S_nwi_synced)) {
8047 // if we [still] need to wait for the DNS configuration
8048 // or network information changes to be ack'd
8049 my_log(LOG_DEBUG,
8050 "Defer \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s, %s)",
8051 S_dnsinfo_synced ? "DNS" : "!DNS",
8052 S_nwi_synced ? "nwi" : "!nwi");
8053 return;
8054 }
8055
8056 // cancel any running timer
8057 if (S_network_change_timer != NULL) {
8058 dispatch_source_cancel(S_network_change_timer);
8059 dispatch_release(S_network_change_timer);
8060 S_network_change_timer = NULL;
8061 S_network_change_timeout = FALSE;
8062 }
8063
8064 // set (and log?) the post time
8065 {
8066 struct timeval elapsed;
8067 struct timeval end;
8068
8069 (void) gettimeofday(&end, NULL);
8070 timersub(&end, &S_network_change_start, &elapsed);
8071
8072 #define QUERY_TIME__FMT "%ld.%6.6d"
8073 #define QUERY_TIME__DIV 1
8074
8075 my_log(LOG_INFO,
8076 "Post \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s: " QUERY_TIME__FMT ": 0x%x)",
8077 S_network_change_timeout ? "timeout" : "delayed",
8078 elapsed.tv_sec,
8079 elapsed.tv_usec / QUERY_TIME__DIV,
8080 S_network_change_needed);
8081 }
8082
8083
8084 /* We are about to post a network change to everyone, get the agents up to date */
8085 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8086 if ((S_network_change_needed & NETWORK_CHANGE_DNS) != 0) {
8087 /* Setup or Update config agents */
8088 process_AgentMonitor_DNS();
8089 }
8090 #endif //!TARGET_OS_SIMULATOR
8091
8092 if ((S_network_change_needed & NETWORK_CHANGE_NET) != 0) {
8093 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI);
8094 if (status != NOTIFY_STATUS_OK) {
8095 my_log(LOG_ERR,
8096 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI ") failed: error=%d", status);
8097 }
8098 }
8099
8100 if ((S_network_change_needed & NETWORK_CHANGE_DNS) != 0) {
8101 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS);
8102 if (status != NOTIFY_STATUS_OK) {
8103 my_log(LOG_ERR,
8104 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS ") failed: error=%d", status);
8105 }
8106 }
8107
8108 if ((S_network_change_needed & NETWORK_CHANGE_PROXY) != 0) {
8109 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8110 /* Setup or Update config agents */
8111 process_AgentMonitor_Proxy();
8112 #endif //!TARGET_OS_SIMULATOR
8113 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
8114 if (status != NOTIFY_STATUS_OK) {
8115 my_log(LOG_ERR,
8116 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY ") failed: error=%d", status);
8117 }
8118 }
8119
8120 if ((S_network_change_needed & NETWORK_CHANGE_NAT64) != 0) {
8121 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8122 // process any NAT64 prefix update requests (and refresh existing prefixes on change)
8123 if (S_nat64_prefix_requests != NULL || S_nat64_prefix_updates != NULL
8124 || S_nat64_cancel_prefix_requests != NULL) {
8125 nat64_configuration_update(S_nat64_prefix_requests,
8126 S_nat64_prefix_updates,
8127 S_nat64_cancel_prefix_requests);
8128 my_CFRelease(&S_nat64_prefix_requests);
8129 my_CFRelease(&S_nat64_prefix_updates);
8130 my_CFRelease(&S_nat64_cancel_prefix_requests);
8131 }
8132 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8133
8134 S_network_change_needed &= ~(NETWORK_CHANGE_NAT64);
8135 }
8136
8137 if (S_network_change_needed != 0) {
8138 // if more than just a NAT64 prefix change
8139 status = notify_post(_SC_NOTIFY_NETWORK_CHANGE);
8140 if (status != NOTIFY_STATUS_OK) {
8141 my_log(LOG_ERR,
8142 "notify_post(" _SC_NOTIFY_NETWORK_CHANGE ") failed: error=%d", status);
8143 }
8144 }
8145
8146 S_network_change_needed = 0;
8147 return;
8148 }
8149
8150 #define TRAILING_EDGE_TIMEOUT_NSEC 5 * NSEC_PER_SEC // 5s
8151
8152 // Note: must run on __network_change_queue()
8153 static void
8154 post_network_change(uint32_t change)
8155 {
8156 dispatch_assert_queue(__network_change_queue());
8157
8158 if (S_network_change_needed == 0) {
8159 // set the start time
8160 (void) gettimeofday(&S_network_change_start, NULL);
8161 }
8162
8163 // indicate that we need to post a change for ...
8164 S_network_change_needed |= change;
8165
8166 // cancel any running timer
8167 if (S_network_change_timer != NULL) {
8168 dispatch_source_cancel(S_network_change_timer);
8169 dispatch_release(S_network_change_timer);
8170 S_network_change_timer = NULL;
8171 S_network_change_timeout = FALSE;
8172 }
8173
8174 // if needed, start new timer
8175 if (!S_dnsinfo_synced || !S_nwi_synced) {
8176 S_network_change_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
8177 0,
8178 0,
8179 __network_change_queue());
8180 dispatch_source_set_event_handler(S_network_change_timer, ^{
8181 S_network_change_timeout = TRUE;
8182 post_network_change_when_ready();
8183 });
8184 dispatch_source_set_timer(S_network_change_timer,
8185 dispatch_time(DISPATCH_TIME_NOW,
8186 TRAILING_EDGE_TIMEOUT_NSEC), // start
8187 DISPATCH_TIME_FOREVER, // interval
8188 10 * NSEC_PER_MSEC); // leeway
8189 dispatch_resume(S_network_change_timer);
8190 }
8191
8192 post_network_change_when_ready();
8193
8194 return;
8195 }
8196
8197 #pragma mark -
8198 #pragma mark Process network (SCDynamicStore) changes
8199
8200 static void
8201 IPMonitorProcessChanges(SCDynamicStoreRef session, CFArrayRef changed_keys,
8202 CFArrayRef if_rank_changes)
8203 {
8204 CFIndex count = 0;
8205 uint32_t changes = 0;
8206 nwi_state_t changes_state = NULL;
8207 boolean_t dns_changed = FALSE;
8208 boolean_t dnsinfo_changed = FALSE;
8209 boolean_t global_ipv4_changed = FALSE;
8210 boolean_t global_ipv6_changed = FALSE;
8211 CFMutableSetRef ipv4_service_changes = NULL;
8212 CFMutableSetRef ipv6_service_changes = NULL;
8213 keyChangeList keys;
8214 CFIndex n;
8215 boolean_t nat64_changed = FALSE;
8216 CFMutableStringRef network_change_msg = NULL;
8217 int n_services;
8218 nwi_state_t old_nwi_state = NULL;
8219 CFDictionaryRef old_primary_dns = NULL;
8220 CFDictionaryRef old_primary_proxy = NULL;
8221 #if !TARGET_OS_IPHONE
8222 CFDictionaryRef old_primary_smb = NULL;
8223 #endif // !TARGET_OS_IPHONE
8224 boolean_t proxies_changed = FALSE;
8225 boolean_t reachability_changed = FALSE;
8226 CFArrayRef service_order;
8227 CFMutableArrayRef service_changes = NULL;
8228 CFDictionaryRef services_info = NULL;
8229 #if !TARGET_OS_IPHONE
8230 boolean_t smb_changed = FALSE;
8231 #endif // !TARGET_OS_IPHONE
8232
8233 /* populate name/index cache */
8234 my_if_nameindex();
8235
8236 if (changed_keys != NULL) {
8237 count = CFArrayGetCount(changed_keys);
8238 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
8239 my_log(LOG_DEBUG,
8240 "changed keys %@ (%ld)", changed_keys, count);
8241 }
8242 }
8243 if (if_rank_changes == NULL && count == 0) {
8244 return;
8245 }
8246
8247 if (S_primary_dns != NULL) {
8248 old_primary_dns = service_dict_get(S_primary_dns, kSCEntNetDNS);
8249 if (old_primary_dns != NULL) {
8250 old_primary_dns = CFDictionaryCreateCopy(NULL, old_primary_dns);
8251 }
8252 }
8253
8254 if (S_primary_proxies != NULL) {
8255 old_primary_proxy
8256 = service_dict_get(S_primary_proxies, kSCEntNetProxies);
8257 if (old_primary_proxy != NULL) {
8258 old_primary_proxy = CFDictionaryCreateCopy(NULL, old_primary_proxy);
8259 }
8260 }
8261
8262 #if !TARGET_OS_IPHONE
8263 if (S_primary_smb != NULL) {
8264 old_primary_smb = service_dict_get(S_primary_smb, kSCEntNetSMB);
8265 if (old_primary_smb != NULL) {
8266 old_primary_smb = CFDictionaryCreateCopy(NULL, old_primary_smb);
8267 }
8268 }
8269 #endif // !TARGET_OS_IPHONE
8270
8271 keyChangeListInit(&keys);
8272 service_changes = CFArrayCreateMutable(NULL, 0,
8273 &kCFTypeArrayCallBacks);
8274 for (CFIndex i = 0; i < count; i++) {
8275 CFStringRef change;
8276 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8277 CFStringRef interface = NULL;
8278 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8279
8280 change = CFArrayGetValueAtIndex(changed_keys, i);
8281 if (CFEqual(change, S_setup_global_ipv4)) {
8282 global_ipv4_changed = TRUE;
8283 global_ipv6_changed = TRUE;
8284 }
8285 else if (CFEqual(change, S_multicast_resolvers)) {
8286 dnsinfo_changed = TRUE;
8287 }
8288 else if (CFEqual(change, S_private_resolvers)) {
8289 dnsinfo_changed = TRUE;
8290 }
8291 else if (CFEqual(change, CFSTR(_PATH_RESOLVER_DIR))) {
8292 dnsinfo_changed = TRUE;
8293 }
8294 else if (CFStringHasPrefix(change, S_interface_delegation_prefix) &&
8295 CFStringHasSuffix(change, kSCEntNetInterfaceDelegation)) {
8296 // ensure that we update the reachability flags in the NWI
8297 // state (including the non-interface-specific flags)
8298 reachability_changed = TRUE;
8299 // ensure that we update the reachability flags in the DNS
8300 // configuration
8301 dnsinfo_changed = TRUE;
8302 }
8303 else if (CFStringHasPrefix(change, S_state_service_prefix)) {
8304 CFStringRef protocol = NULL;
8305 CFStringRef serviceID;
8306
8307 serviceID = parseNetworkServiceString(change, &protocol);
8308 if (serviceID != NULL) {
8309 my_CFArrayAppendUniqueValue(service_changes, serviceID);
8310 if (protocol != NULL) {
8311 if (CFEqual(protocol, kSCEntNetIPv4)) {
8312 /* IPv4 service changed, remember that */
8313 my_CFSetAddValue(&ipv4_service_changes, serviceID);
8314 }
8315 else if (CFEqual(protocol, kSCEntNetIPv6)) {
8316 /* IPv6 service changed, remember that */
8317 my_CFSetAddValue(&ipv6_service_changes, serviceID);
8318 }
8319 }
8320 CFRelease(serviceID);
8321 }
8322 my_CFRelease(&protocol);
8323 }
8324 else if (CFStringHasPrefix(change, S_setup_service_prefix)) {
8325 CFStringRef serviceID;
8326
8327 serviceID = parseNetworkServiceString(change, NULL);
8328 if (serviceID != NULL) {
8329 my_CFArrayAppendUniqueValue(service_changes, serviceID);
8330 CFRelease(serviceID);
8331 }
8332
8333 for (size_t j = 0; j < countof(transientInterfaceEntityNames); j++) {
8334 if (CFStringHasSuffix(change,
8335 *transientInterfaceEntityNames[j])) {
8336 reachability_changed = TRUE;
8337 break;
8338 }
8339 }
8340
8341 if (CFStringHasSuffix(change, kSCEntNetInterface)) {
8342 reachability_changed = TRUE;
8343 }
8344 }
8345 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8346 else if (is_nat64_prefix_request(change, &interface)) {
8347 set_plat_discovery(kPLATDiscoveryOptionStart, interface);
8348 nat64_changed = TRUE;
8349 }
8350 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8351 }
8352
8353 /* determine which serviceIDs are impacted by the interface rank changes */
8354 if (if_rank_changes != NULL) {
8355 n = CFArrayGetCount(if_rank_changes);
8356 for (CFIndex i = 0; i < n; i++) {
8357 CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i);
8358
8359 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
8360 my_log(LOG_DEBUG, "Interface rank changed %@", ifname);
8361 }
8362 append_serviceIDs_for_interface(service_changes, ifname);
8363 }
8364 }
8365
8366 /* grab a snapshot of everything we need */
8367 services_info = services_info_copy(session, service_changes);
8368 assert(services_info != NULL);
8369
8370 /* grab the service order */
8371 service_order = service_order_get(services_info);
8372 if (service_order != NULL) {
8373 if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
8374 my_log(LOG_DEBUG, "service_order %@ ", service_order);
8375 }
8376 }
8377
8378 /*
8379 * process protocol (v4, v6, rank, ...) and
8380 * configuration (dns, proxies, smb, ...) changes
8381 */
8382 n = CFArrayGetCount(service_changes);
8383 for (CFIndex i = 0; i < n; i++) {
8384 uint32_t changes;
8385 CFStringRef serviceID;
8386
8387 serviceID = CFArrayGetValueAtIndex(service_changes, i);
8388 changes = service_changed(services_info, serviceID);
8389 if (my_CFSetContainsValue(ipv4_service_changes, serviceID)) {
8390 changes |= (1 << kEntityTypeIPv4);
8391 }
8392 if (my_CFSetContainsValue(ipv6_service_changes, serviceID)) {
8393 changes |= (1 << kEntityTypeIPv6);
8394 }
8395 if ((changes & (1 << kEntityTypeServiceOptions)) != 0) {
8396 /* if __Service__ (e.g. PrimaryRank) changed */
8397 global_ipv4_changed = TRUE;
8398 global_ipv6_changed = TRUE;
8399 }
8400 else {
8401 if ((changes & (1 << kEntityTypeIPv4)) != 0) {
8402 global_ipv4_changed = TRUE;
8403 dnsinfo_changed = TRUE;
8404 proxies_changed = TRUE;
8405 }
8406 if ((changes & (1 << kEntityTypeIPv6)) != 0) {
8407 global_ipv6_changed = TRUE;
8408 dnsinfo_changed = TRUE;
8409 proxies_changed = TRUE;
8410 nat64_changed = TRUE;
8411 }
8412 }
8413 if ((changes & (1 << kEntityTypeDNS)) != 0) {
8414 if (S_primary_dns != NULL && CFEqual(S_primary_dns, serviceID)) {
8415 dns_changed = TRUE;
8416 }
8417 dnsinfo_changed = TRUE;
8418 nat64_changed = TRUE;
8419 }
8420 if ((changes & (1 << kEntityTypeProxies)) != 0) {
8421 proxies_changed = TRUE;
8422 }
8423 #if !TARGET_OS_IPHONE
8424 if ((changes & (1 << kEntityTypeSMB)) != 0) {
8425 if (S_primary_smb != NULL && CFEqual(S_primary_smb, serviceID)) {
8426 smb_changed = TRUE;
8427 }
8428 }
8429 #endif
8430 if ((changes & (1 << kEntityTypeTransientStatus)) != 0
8431 && (service_dict_get(serviceID, kSCEntNetIPv4) != NULL
8432 || service_dict_get(serviceID, kSCEntNetIPv6) != NULL)) {
8433 dnsinfo_changed = TRUE;
8434 }
8435 }
8436
8437 /* ensure S_nwi_state can hold as many services as we have currently */
8438 n_services = (int)CFDictionaryGetCount(S_service_state_dict);
8439 old_nwi_state = nwi_state_make_copy(S_nwi_state);
8440 S_nwi_state = nwi_state_new(S_nwi_state, n_services);
8441
8442 if (global_ipv4_changed) {
8443 if (S_ipv4_results != NULL) {
8444 ElectionResultsRelease(S_ipv4_results);
8445 }
8446 S_ipv4_results
8447 = ElectionResultsCopy(AF_INET, service_order);
8448 ElectionResultsLog(LOG_INFO, S_ipv4_results, "IPv4");
8449 }
8450 if (global_ipv6_changed) {
8451 if (S_ipv6_results != NULL) {
8452 ElectionResultsRelease(S_ipv6_results);
8453 }
8454 S_ipv6_results
8455 = ElectionResultsCopy(AF_INET6, service_order);
8456 ElectionResultsLog(LOG_INFO, S_ipv6_results, "IPv6");
8457 }
8458 if (global_ipv4_changed || global_ipv6_changed || dnsinfo_changed) {
8459 CFStringRef new_primary;
8460 CFStringRef new_primary_dns = NULL;
8461 CFStringRef new_primary_proxies = NULL;
8462 #if !TARGET_OS_IPHONE
8463 CFStringRef new_primary_smb = NULL;
8464 #endif /* !TARGET_OS_IPHONE */
8465 RouteListUnion new_routelist;
8466 CandidateRef other_candidate;
8467 CandidateRef primary_candidate;
8468
8469 if (S_nwi_state != NULL) {
8470 nwi_state_clear(S_nwi_state, AF_INET);
8471 nwi_state_clear(S_nwi_state, AF_INET6);
8472 }
8473
8474 /* IPv4 */
8475 my_log(LOG_DEBUG, "electing IPv4 primary");
8476 new_routelist.ptr = NULL;
8477 other_candidate = (S_ipv6_results != NULL) /* get IPv6 primary */
8478 ? S_ipv6_results->candidates : NULL;
8479 primary_candidate = ElectionResultsGetPrimary(S_ipv4_results,
8480 other_candidate,
8481 S_nwi_state, AF_INET,
8482 &new_routelist.common,
8483 services_info,
8484 ipv4_service_changes);
8485 new_primary = (primary_candidate != NULL)
8486 ? primary_candidate->serviceID : NULL;
8487 (void)set_new_primary(&S_primary_ipv4, new_primary, "IPv4");
8488 update_ipv4(S_primary_ipv4, new_routelist.v4, &keys);
8489
8490 /* IPv6 */
8491 my_log(LOG_DEBUG, "electing IPv6 primary");
8492 new_routelist.ptr = NULL;
8493 other_candidate = primary_candidate; /* get IPv4 primary */
8494 primary_candidate = ElectionResultsGetPrimary(S_ipv6_results,
8495 other_candidate,
8496 S_nwi_state, AF_INET6,
8497 &new_routelist.common,
8498 services_info,
8499 ipv6_service_changes);
8500 new_primary = (primary_candidate != NULL)
8501 ? primary_candidate->serviceID : NULL;
8502 (void)set_new_primary(&S_primary_ipv6, new_primary, "IPv6");
8503 update_ipv6(S_primary_ipv6, new_routelist.v6, &keys);
8504
8505 nwi_state_finalize(S_nwi_state);
8506
8507 if (S_primary_ipv4 != NULL && S_primary_ipv6 != NULL) {
8508 /* decide between IPv4 and IPv6 */
8509 if (rank_service_entity(S_ipv4_service_rank_dict,
8510 S_primary_ipv4, kSCEntNetDNS)
8511 <= rank_service_entity(S_ipv6_service_rank_dict,
8512 S_primary_ipv6, kSCEntNetDNS)) {
8513 new_primary_dns = S_primary_ipv4;
8514 }
8515 else {
8516 new_primary_dns = S_primary_ipv6;
8517 }
8518 if (rank_service_entity(S_ipv4_service_rank_dict,
8519 S_primary_ipv4, kSCEntNetProxies)
8520 <= rank_service_entity(S_ipv6_service_rank_dict,
8521 S_primary_ipv6, kSCEntNetProxies)) {
8522 new_primary_proxies = S_primary_ipv4;
8523 }
8524 else {
8525 new_primary_proxies = S_primary_ipv6;
8526 }
8527 #if !TARGET_OS_IPHONE
8528 if (rank_service_entity(S_ipv4_service_rank_dict,
8529 S_primary_ipv4, kSCEntNetSMB)
8530 <= rank_service_entity(S_ipv6_service_rank_dict,
8531 S_primary_ipv6, kSCEntNetSMB)) {
8532 new_primary_smb = S_primary_ipv4;
8533 }
8534 else {
8535 new_primary_smb = S_primary_ipv6;
8536 }
8537 #endif /* !TARGET_OS_IPHONE */
8538
8539 }
8540 else if (S_primary_ipv6 != NULL) {
8541 new_primary_dns = S_primary_ipv6;
8542 new_primary_proxies = S_primary_ipv6;
8543 #if !TARGET_OS_IPHONE
8544 new_primary_smb = S_primary_ipv6;
8545 #endif /* !TARGET_OS_IPHONE */
8546 }
8547 else if (S_primary_ipv4 != NULL) {
8548 new_primary_dns = S_primary_ipv4;
8549 new_primary_proxies = S_primary_ipv4;
8550 #if !TARGET_OS_IPHONE
8551 new_primary_smb = S_primary_ipv4;
8552 #endif /* !TARGET_OS_IPHONE */
8553 }
8554
8555 if (set_new_primary(&S_primary_dns, new_primary_dns, "DNS")) {
8556 dns_changed = TRUE;
8557 dnsinfo_changed = TRUE;
8558 }
8559 if (set_new_primary(&S_primary_proxies, new_primary_proxies,
8560 "Proxies")) {
8561 proxies_changed = TRUE;
8562 }
8563 #if !TARGET_OS_IPHONE
8564 if (set_new_primary(&S_primary_smb, new_primary_smb, "SMB")) {
8565 smb_changed = TRUE;
8566 }
8567 #endif /* !TARGET_OS_IPHONE */
8568 }
8569
8570 if (!proxies_changed && dnsinfo_changed
8571 && ((G_supplemental_proxies_follow_dns != NULL)
8572 && CFBooleanGetValue(G_supplemental_proxies_follow_dns))) {
8573 proxies_changed = TRUE;
8574 }
8575
8576 changes_state = nwi_state_diff(old_nwi_state, S_nwi_state);
8577
8578 if (global_ipv4_changed || global_ipv6_changed
8579 || dnsinfo_changed || reachability_changed) {
8580 if (S_nwi_state != NULL) {
8581 S_nwi_state->generation_count = mach_absolute_time();
8582 if (global_ipv4_changed || global_ipv6_changed
8583 || reachability_changed) {
8584 SCNetworkReachabilityFlags reach_flags_v4 = 0;
8585 SCNetworkReachabilityFlags reach_flags_v6 = 0;
8586
8587 GetReachabilityFlagsFromTransientServices(services_info,
8588 &reach_flags_v4,
8589 &reach_flags_v6);
8590
8591 _nwi_state_set_reachability_flags(S_nwi_state, reach_flags_v4,
8592 reach_flags_v6);
8593 }
8594
8595 /* Update the per-interface generation count */
8596 _nwi_state_update_interface_generations(old_nwi_state, S_nwi_state,
8597 changes_state);
8598 }
8599
8600 if (update_nwi(S_nwi_state)) {
8601 changes |= NETWORK_CHANGE_NET;
8602
8603 /*
8604 * the DNS configuration includes per-resolver configuration
8605 * reachability flags that are based on the nwi state. Let's
8606 * make sure that we check for changes
8607 */
8608 dnsinfo_changed = TRUE;
8609 }
8610 }
8611 if (dns_changed) {
8612 if (update_dns(services_info, S_primary_dns, &keys)) {
8613 changes |= NETWORK_CHANGE_DNS;
8614 dnsinfo_changed = TRUE;
8615 } else {
8616 dns_changed = FALSE;
8617 }
8618 }
8619 if (dnsinfo_changed) {
8620 if (update_dnsinfo(services_info, S_primary_dns,
8621 &keys, service_order)) {
8622 changes |= NETWORK_CHANGE_DNS;
8623 } else {
8624 dnsinfo_changed = FALSE;
8625 }
8626 }
8627 if (proxies_changed) {
8628 // if proxy change OR supplemental Proxies follow supplemental DNS
8629 if (update_proxies(services_info, S_primary_proxies,
8630 &keys, service_order)) {
8631 changes |= NETWORK_CHANGE_PROXY;
8632 } else {
8633 proxies_changed = FALSE;
8634 }
8635 }
8636 #if !TARGET_OS_IPHONE
8637 if (smb_changed) {
8638 if (update_smb(services_info, S_primary_smb, &keys)) {
8639 changes |= NETWORK_CHANGE_SMB;
8640 } else {
8641 smb_changed = FALSE;
8642 }
8643 }
8644 #endif /* !TARGET_OS_IPHONE */
8645 if (nat64_changed) {
8646 changes |= NETWORK_CHANGE_NAT64;
8647 }
8648 my_CFRelease(&service_changes);
8649 my_CFRelease(&services_info);
8650 my_CFRelease(&ipv4_service_changes);
8651 my_CFRelease(&ipv6_service_changes);
8652
8653 if (changes != 0) {
8654 network_change_msg = CFStringCreateMutable(NULL, 0);
8655 process_nwi_changes(network_change_msg,
8656 changes_state,
8657 S_nwi_state,
8658 old_nwi_state,
8659 dns_changed,
8660 dnsinfo_changed,
8661 old_primary_dns,
8662 proxies_changed,
8663 old_primary_proxy,
8664 #if !TARGET_OS_IPHONE
8665 smb_changed,
8666 old_primary_smb
8667 #else // !TARGET_OS_IPHONE
8668 FALSE, // smb_changed
8669 NULL // old_primary_smb
8670 #endif // !TARGET_OS_IPHONE
8671 );
8672 }
8673
8674 keyChangeListApplyToStore(&keys, session);
8675 my_CFRelease(&old_primary_dns);
8676 my_CFRelease(&old_primary_proxy);
8677 #if !TARGET_OS_IPHONE
8678 my_CFRelease(&old_primary_smb);
8679 #endif // !TARGET_OS_IPHONE
8680
8681 if (changes != 0) {
8682 dispatch_async(__network_change_queue(), ^{
8683 post_network_change(changes);
8684 });
8685 }
8686
8687 if ((network_change_msg != NULL)
8688 && (CFStringGetLength(network_change_msg) != 0)) {
8689 my_log(LOG_NOTICE, "network changed:%@", network_change_msg);
8690 } else if (keyChangeListActive(&keys)) {
8691 my_log(LOG_NOTICE, "network changed");
8692 } else if (nat64_changed) {
8693 my_log(LOG_NOTICE, "nat64 update");
8694 } else {
8695 my_log(LOG_INFO, "network event w/no changes");
8696 }
8697
8698 my_CFRelease(&network_change_msg);
8699
8700 if (changes_state != NULL) {
8701 nwi_state_free(changes_state);
8702 }
8703 if (old_nwi_state != NULL) {
8704 nwi_state_free(old_nwi_state);
8705 }
8706 keyChangeListFree(&keys);
8707
8708 /* release the name/index cache */
8709 my_if_freenameindex();
8710
8711 return;
8712 }
8713
8714 static void
8715 IPMonitorNotify(SCDynamicStoreRef session, CFArrayRef changed_keys,
8716 void * info)
8717 {
8718 #pragma unused(info)
8719 IPMonitorProcessChanges(session, changed_keys, NULL);
8720 return;
8721 }
8722
8723 #if !TARGET_OS_IPHONE
8724 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_mcx
8725 #else
8726 #define PROXY_GLOBAL_OBSERVER_TYPE scprefs_observer_type_global
8727 #endif
8728
8729 static void
8730 watch_proxies()
8731 {
8732 static dispatch_queue_t proxy_cb_queue;
8733
8734 proxy_cb_queue = dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL);
8735 _scprefs_observer_watch(PROXY_GLOBAL_OBSERVER_TYPE,
8736 "com.apple.SystemConfiguration.plist",
8737 proxy_cb_queue,
8738 ^{
8739 SCDynamicStoreNotifyValue(NULL, S_state_global_proxies);
8740 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8741 /* Setup or Update config agents */
8742 process_AgentMonitor_Proxy();
8743 #endif //!TARGET_OS_SIMULATOR
8744 (void)notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
8745 my_log(LOG_INFO, "Notifying:\n%@",
8746 S_state_global_proxies);
8747 });
8748 return;
8749 }
8750
8751 #if TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST
8752
8753 static void
8754 prefs_changed_callback_init(void)
8755 {
8756 }
8757
8758 #else /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8759
8760 #include "IPMonitorControlPrefs.h"
8761
8762 static void
8763 prefs_changed(SCPreferencesRef prefs)
8764 {
8765 #pragma unused(prefs)
8766 if (S_bundle_logging_verbose || IPMonitorControlPrefsIsVerbose()) {
8767 S_IPMonitor_debug = kDebugFlagDefault;
8768 S_IPMonitor_verbose = TRUE;
8769 my_log(LOG_DEBUG, "Setting logging verbose mode on");
8770 } else {
8771 my_log(LOG_DEBUG, "Setting logging verbose mode off");
8772 S_IPMonitor_debug = 0;
8773 S_IPMonitor_verbose = FALSE;
8774 }
8775 return;
8776 }
8777
8778 static void
8779 prefs_changed_callback_init(void)
8780 {
8781 IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed);
8782 prefs_changed(NULL);
8783 return;
8784 }
8785
8786
8787 #endif /* TEST_IPV4_ROUTELIST || TEST_IPV6_ROUTELIST */
8788
8789 #if !TARGET_OS_SIMULATOR
8790 static int
8791 flush_routes(int s)
8792 {
8793 char * buf = NULL;
8794 int i;
8795 char * lim;
8796 #define N_MIB 6
8797 int mib[N_MIB];
8798 size_t needed;
8799 char * next;
8800 struct rt_msghdr * rtm;
8801 struct sockaddr_in *sin;
8802
8803 mib[0] = CTL_NET;
8804 mib[1] = PF_ROUTE;
8805 mib[2] = 0;
8806 mib[3] = AF_INET;
8807 mib[4] = NET_RT_FLAGS;
8808 mib[5] = RTF_STATIC | RTF_DYNAMIC;
8809 for (i = 0; i < 3; i++) {
8810 if (sysctl(mib, N_MIB, NULL, &needed, NULL, 0) < 0) {
8811 break;
8812 }
8813 if ((buf = malloc(needed)) == NULL) {
8814 break;
8815 }
8816 if (sysctl(mib, N_MIB, buf, &needed, NULL, 0) >= 0) {
8817 break;
8818 }
8819 free(buf);
8820 buf = NULL;
8821 }
8822 if (buf == NULL) {
8823 return (-1);
8824 }
8825 lim = buf + needed;
8826 for (next = buf; next < lim; next += rtm->rtm_msglen) {
8827 uint32_t addr;
8828
8829 /* ALIGN: assume kernel provides necessary alignment */
8830 rtm = (struct rt_msghdr *)(void *)next;
8831 sin = (struct sockaddr_in *)(rtm + 1);
8832
8833 addr = ntohl(sin->sin_addr.s_addr);
8834 if (IN_LOOPBACK(addr)) {
8835 my_log(LOG_DEBUG,
8836 "flush_routes: ignoring loopback route");
8837 continue;
8838 }
8839 if (IN_LOCAL_GROUP(addr)) {
8840 my_log(LOG_DEBUG,
8841 "flush_routes: ignoring multicast route");
8842 continue;
8843 }
8844 rtm->rtm_type = RTM_DELETE;
8845 rtm->rtm_seq = ++rtm_seq;
8846 if (write(s, rtm, rtm->rtm_msglen) < 0) {
8847 my_log(LOG_NOTICE,
8848 "flush_routes: removing route for "
8849 IP_FORMAT " failed: %s",
8850 IP_LIST(&sin->sin_addr),
8851 strerror(errno));
8852 }
8853 else {
8854 my_log(LOG_DEBUG,
8855 "flush_routes: removed route for " IP_FORMAT,
8856 IP_LIST(&sin->sin_addr));
8857 }
8858 }
8859 free(buf);
8860 return (0);
8861 }
8862
8863 static void
8864 flush_inet_routes(void)
8865 {
8866 int s;
8867
8868 s = open_routing_socket();
8869 if (s != -1) {
8870 flush_routes(s);
8871 close(s);
8872 }
8873 }
8874
8875 #else /* !TARGET_OS_SIMULATOR */
8876
8877 static void
8878 flush_inet_routes(void)
8879 {
8880 }
8881
8882 #endif /* !TARGET_OS_SIMULATOR */
8883
8884
8885 static CFArrayRef
8886 ip_plugin_copy_keys(void)
8887 {
8888 const void * values[]
8889 = {
8890 S_setup_global_ipv4, /* serviceOrder/PPPOverridePrimary */
8891 S_private_resolvers, /* private DNS config (Back to My Mac) */
8892 S_multicast_resolvers,/* multicast DNS config (Bonjour/.local) */
8893 };
8894 const CFIndex values_count = sizeof(values) / sizeof(values[0]);
8895
8896 return (CFArrayCreate(NULL, values, values_count,
8897 &kCFTypeArrayCallBacks));
8898 }
8899
8900 static CFArrayRef
8901 ip_plugin_copy_patterns(void)
8902 {
8903 CFStringRef pattern;
8904 CFMutableArrayRef patterns;
8905
8906 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
8907
8908 /* State: and Setup: per-service notifications */
8909 add_service_keys(kSCCompAnyRegex, patterns);
8910
8911 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetPPP);
8912 CFArrayAppendValue(patterns, pattern);
8913 CFRelease(pattern);
8914
8915 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
8916 CFArrayAppendValue(patterns, pattern);
8917 CFRelease(pattern);
8918
8919 pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetInterface);
8920 CFArrayAppendValue(patterns, pattern);
8921 CFRelease(pattern);
8922
8923 /* State: per-service PPP/VPN/IPSec status notifications */
8924 add_transient_status_keys(kSCCompAnyRegex, patterns);
8925
8926 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
8927 /* NAT64 prefix request pattern */
8928 nat64_prefix_request_add_pattern(patterns);
8929 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
8930
8931 /* interface delegation pattern */
8932 pattern = interface_entity_key_copy(kSCCompAnyRegex,
8933 kSCEntNetInterfaceDelegation);
8934 CFArrayAppendValue(patterns, pattern);
8935 CFRelease(pattern);
8936 return (patterns);
8937 }
8938
8939 static void
8940 ip_plugin_init()
8941 {
8942 if (S_is_network_boot() != 0) {
8943 S_netboot = TRUE;
8944 }
8945 else {
8946 /* flush routes */
8947 flush_inet_routes();
8948 }
8949
8950 /*
8951 * Initialize globals
8952 */
8953 S_session = SCDynamicStoreCreate(NULL, CFSTR("IPMonitor"),
8954 IPMonitorNotify, NULL);
8955 if (S_session == NULL) {
8956 my_log(LOG_ERR,
8957 "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
8958 SCErrorString(SCError()));
8959 return;
8960 }
8961 S_state_global_ipv4
8962 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8963 kSCDynamicStoreDomainState,
8964 kSCEntNetIPv4);
8965 S_state_global_ipv6
8966 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8967 kSCDynamicStoreDomainState,
8968 kSCEntNetIPv6);
8969 S_state_global_dns
8970 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8971 kSCDynamicStoreDomainState,
8972 kSCEntNetDNS);
8973 S_state_global_proxies
8974 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8975 kSCDynamicStoreDomainState,
8976 kSCEntNetProxies);
8977 #if !TARGET_OS_IPHONE
8978 S_state_global_smb
8979 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8980 kSCDynamicStoreDomainState,
8981 kSCEntNetSMB);
8982 #endif /* !TARGET_OS_IPHONE */
8983 S_setup_global_ipv4
8984 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
8985 kSCDynamicStoreDomainSetup,
8986 kSCEntNetIPv4);
8987 S_state_service_prefix
8988 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
8989 kSCDynamicStoreDomainState,
8990 CFSTR(""),
8991 NULL);
8992 S_setup_service_prefix
8993 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
8994 kSCDynamicStoreDomainSetup,
8995 CFSTR(""),
8996 NULL);
8997 S_interface_delegation_prefix
8998 = SCDynamicStoreKeyCreateNetworkInterface(NULL,
8999 kSCDynamicStoreDomainState);
9000 S_service_state_dict
9001 = CFDictionaryCreateMutable(NULL, 0,
9002 &kCFTypeDictionaryKeyCallBacks,
9003 &kCFTypeDictionaryValueCallBacks);
9004 S_ipv4_service_rank_dict
9005 = CFDictionaryCreateMutable(NULL, 0,
9006 &kCFTypeDictionaryKeyCallBacks,
9007 &kCFTypeDictionaryValueCallBacks);
9008 S_ipv6_service_rank_dict
9009 = CFDictionaryCreateMutable(NULL, 0,
9010 &kCFTypeDictionaryKeyCallBacks,
9011 &kCFTypeDictionaryValueCallBacks);
9012 S_multicast_resolvers
9013 = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
9014 kSCDynamicStoreDomainState,
9015 kSCCompNetwork,
9016 CFSTR(kDNSServiceCompMulticastDNS));
9017 S_private_resolvers
9018 = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
9019 kSCDynamicStoreDomainState,
9020 kSCCompNetwork,
9021 CFSTR(kDNSServiceCompPrivateDNS));
9022 /* initialize dns configuration */
9023 (void)dns_configuration_set(NULL, NULL, NULL, NULL, NULL, NULL);
9024 #if !TARGET_OS_IPHONE
9025 empty_dns();
9026 #endif /* !TARGET_OS_IPHONE */
9027 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_dns);
9028
9029 #if !TARGET_OS_IPHONE
9030 /* initialize SMB configuration */
9031 (void)SCDynamicStoreRemoveValue(S_session, S_state_global_smb);
9032 #endif /* !TARGET_OS_IPHONE */
9033
9034 watch_proxies();
9035
9036 return;
9037 }
9038
9039 static CFArrayRef
9040 copy_dictionary_keys(CFDictionaryRef dict)
9041 {
9042 CFIndex count;
9043 CFArrayRef ret_keys;
9044
9045 count = CFDictionaryGetCount(dict);
9046 if (count > 0) {
9047 const void * keys[count];
9048
9049 CFDictionaryGetKeysAndValues(dict, keys, NULL);
9050 ret_keys = CFArrayCreate(NULL, keys, count, &kCFTypeArrayCallBacks);
9051 }
9052 else {
9053 ret_keys = NULL;
9054 }
9055 return (ret_keys);
9056 }
9057
9058 static void
9059 prime_notifications(CFArrayRef keys, CFArrayRef patterns)
9060 {
9061 CFArrayRef changed_keys;
9062 CFDictionaryRef info;
9063
9064 info = SCDynamicStoreCopyMultiple(S_session, keys, patterns);
9065 if (info == NULL) {
9066 my_log(LOG_NOTICE, "%s: no content", __func__);
9067 return;
9068 }
9069 changed_keys = copy_dictionary_keys(info);
9070 CFRelease(info);
9071 if (changed_keys == NULL) {
9072 my_log(LOG_NOTICE, "%s: no keys", __func__);
9073 return;
9074 }
9075 my_log(LOG_NOTICE,
9076 "IPMonitor prime %ld keys %@",
9077 (long)CFArrayGetCount(changed_keys), changed_keys);
9078 IPMonitorProcessChanges(S_session, changed_keys, NULL);
9079 CFRelease(changed_keys);
9080 return;
9081 }
9082
9083 static void
9084 initialize_notifications(void)
9085 {
9086 CFArrayRef keys;
9087 CFArrayRef patterns;
9088 Boolean success;
9089
9090 /* register for notifications */
9091 keys = ip_plugin_copy_keys();
9092 patterns = ip_plugin_copy_patterns();
9093 success = SCDynamicStoreSetNotificationKeys(S_session, keys, patterns);
9094 if (!success) {
9095 my_log(LOG_ERR,
9096 "SCDynamicStoreSetNotificationKeys() failed: %s",
9097 SCErrorString(SCError()));
9098 }
9099 else {
9100 CFRunLoopSourceRef rls;
9101
9102 rls = SCDynamicStoreCreateRunLoopSource(NULL, S_session, 0);
9103 if (rls == NULL) {
9104 my_log(LOG_ERR,
9105 "SCDynamicStoreCreateRunLoopSource() failed: %s",
9106 SCErrorString(SCError()));
9107 }
9108 else {
9109 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls,
9110 kCFRunLoopDefaultMode);
9111 CFRelease(rls);
9112
9113 /* catch any changes that happened before registering */
9114 prime_notifications(keys, patterns);
9115 }
9116 }
9117 CFRelease(keys);
9118 CFRelease(patterns);
9119 return;
9120 }
9121
9122 __private_extern__
9123 void
9124 prime_IPMonitor(void)
9125 {
9126 /* initialize multicast route */
9127 update_ipv4(NULL, NULL, NULL);
9128
9129 if (S_session == NULL) {
9130 return;
9131 }
9132
9133 /* initialize notifications */
9134 CFRunLoopPerformBlock(CFRunLoopGetCurrent(),
9135 kCFRunLoopDefaultMode,
9136 ^{ initialize_notifications(); });
9137
9138 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9139 process_AgentMonitor();
9140 #endif // !TARGET_OS_SIMULATOR
9141
9142 return;
9143 }
9144
9145 static boolean_t
9146 S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
9147 boolean_t def)
9148 {
9149 CFBooleanRef b;
9150 boolean_t ret = def;
9151
9152 b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
9153 if (b != NULL) {
9154 ret = CFBooleanGetValue(b);
9155 }
9156 return (ret);
9157 }
9158
9159 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
9160 #include "IPMonitorControlServer.h"
9161
9162 static void
9163 InterfaceRankChanged(void * info)
9164 {
9165 #pragma unused(info)
9166 CFDictionaryRef assertions = NULL;
9167 CFArrayRef changes;
9168
9169 changes = IPMonitorControlServerCopyInterfaceRankInformation(&assertions);
9170 if (S_if_rank_dict != NULL) {
9171 CFRelease(S_if_rank_dict);
9172 }
9173 S_if_rank_dict = assertions;
9174 if (changes != NULL) {
9175 IPMonitorProcessChanges(S_session, NULL, changes);
9176 CFRelease(changes);
9177 }
9178
9179 return;
9180 }
9181
9182 static void
9183 StartIPMonitorControlServer(void)
9184 {
9185 CFRunLoopSourceContext context;
9186 CFRunLoopSourceRef rls;
9187
9188 memset(&context, 0, sizeof(context));
9189 context.perform = InterfaceRankChanged;
9190 rls = CFRunLoopSourceCreate(NULL, 0, &context);
9191 if (!IPMonitorControlServerStart(CFRunLoopGetCurrent(),
9192 rls,
9193 &S_bundle_logging_verbose)) {
9194 my_log(LOG_ERR, "IPMonitorControlServerStart failed");
9195 }
9196 else {
9197 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls,
9198 kCFRunLoopDefaultMode);
9199 }
9200 CFRelease(rls);
9201 return;
9202 }
9203
9204 #endif /* !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
9205
9206 __private_extern__
9207 void
9208 load_IPMonitor(CFBundleRef bundle, Boolean bundleVerbose)
9209 {
9210 CFDictionaryRef info_dict;
9211
9212 info_dict = CFBundleGetInfoDictionary(bundle);
9213
9214 if (info_dict != NULL) {
9215 S_append_state
9216 = S_get_plist_boolean(info_dict,
9217 CFSTR("AppendStateArrayToSetupArray"),
9218 FALSE);
9219 }
9220 if (bundleVerbose) {
9221 S_IPMonitor_debug = kDebugFlagDefault;
9222 S_bundle_logging_verbose = TRUE;
9223 S_IPMonitor_verbose = TRUE;
9224 }
9225
9226 /* register to receive changes to the "verbose" flag and read the initial setting */
9227 prefs_changed_callback_init();
9228
9229 #if !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
9230 /* start DNS configuration (dnsinfo) server */
9231 load_DNSConfiguration(bundle, // bundle
9232 ^(Boolean inSync) { // syncHandler
9233 dispatch_async(__network_change_queue(), ^{
9234 S_dnsinfo_synced = inSync;
9235
9236 if (inSync &&
9237 ((S_network_change_needed & NETWORK_CHANGE_DNS) == 0)) {
9238 // all of the DNS service ack's should result
9239 // in a [new] network change being posted
9240 post_network_change(NETWORK_CHANGE_DNS);
9241 } else {
9242 post_network_change_when_ready();
9243 }
9244 });
9245 });
9246
9247 /* start Network Information (nwi) server */
9248 load_NetworkInformation(bundle, // bundle
9249 ^(Boolean inSync) { // syncHandler
9250 dispatch_async(__network_change_queue(), ^{
9251 S_nwi_synced = inSync;
9252 post_network_change_when_ready();
9253 });
9254 });
9255 #endif /* !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
9256
9257 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER
9258 /* start IPMonitor Control (InterfaceRank) server */
9259 StartIPMonitorControlServer();
9260 #endif /* !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST && !TEST_DNS && !TEST_DNS_ORDER */
9261
9262 /* initialize DNS configuration */
9263 dns_configuration_init(bundle);
9264
9265 /* initialize proxy configuration */
9266 proxy_configuration_init(bundle);
9267
9268 ip_plugin_init();
9269
9270 if (S_session != NULL) {
9271 dns_configuration_monitor(S_session, IPMonitorNotify);
9272 }
9273
9274 #if !TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST
9275 load_hostname(TRUE);
9276 #endif /* TARGET_OS_SIMULATOR && !TEST_IPV4_ROUTELIST && !TEST_IPV6_ROUTELIST */
9277
9278 #if !TARGET_OS_IPHONE
9279 load_smb_configuration(TRUE);
9280 #endif /* !TARGET_OS_IPHONE */
9281
9282 return;
9283 }
9284
9285
9286 #pragma mark -
9287 #pragma mark Standalone test code
9288
9289
9290 #ifdef TEST_IPMONITOR
9291
9292 int
9293 main(int argc, char **argv)
9294 {
9295 _sc_log = kSCLogDestinationFile;
9296
9297 S_IPMonitor_debug = kDebugFlag1;
9298 if (argc > 1) {
9299 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
9300 }
9301
9302 load_IPMonitor(CFBundleGetMainBundle(), FALSE);
9303 prime_IPMonitor();
9304 S_IPMonitor_debug = kDebugFlag1;
9305 CFRunLoopRun();
9306 /* not reached */
9307 exit(0);
9308 return 0;
9309 }
9310 #endif /* TEST_IPMONITOR */
9311
9312 #if defined(TEST_ROUTELIST) || defined(TEST_DNS_ORDER)
9313
9314 #if defined(__has_feature)
9315 #if __has_feature(address_sanitizer)
9316 #define TEST_ROUTELIST_RUN_LEAKS FALSE
9317 #endif /* if __has_feature(address_sanitizer) */
9318 #endif /* if defined(__has_feature) */
9319
9320 #ifndef TEST_ROUTELIST_RUN_LEAKS
9321 #define TEST_ROUTELIST_RUN_LEAKS TRUE
9322 #endif
9323
9324 static boolean_t S_run_leaks = TEST_ROUTELIST_RUN_LEAKS;
9325
9326 static void
9327 test_complete(void)
9328 {
9329 printf("\nTest Complete\n");
9330 if (S_run_leaks) {
9331 char cmd[128];
9332
9333 sprintf(cmd, "leaks %d 2>&1", getpid());
9334 fflush(stdout);
9335 (void)system(cmd);
9336 }
9337 }
9338
9339 struct route {
9340 const char * dest;
9341 int prefix_length;
9342 const char * gateway;
9343 const char * ifname;
9344 };
9345
9346 #endif
9347
9348 #if TEST_IPV4_ROUTELIST
9349
9350 typedef struct {
9351 const char * addr;
9352 int prefix_length;
9353 const char * dest;
9354 const char * router;
9355 const char * ifname;
9356 Rank rank;
9357 const CFStringRef * primary_rank;
9358 struct route * additional_routes;
9359 int additional_routes_count;
9360 struct route * excluded_routes;
9361 int excluded_routes_count;
9362 } IPv4ServiceContents;
9363
9364 typedef const IPv4ServiceContents * IPv4ServiceContentsRef;
9365
9366 struct route loop_routelist[] = {
9367 { "1.1.1.1", 32, "1.1.1.2", NULL },
9368 { "1.1.1.2", 32, "1.1.1.3", NULL },
9369 { "1.1.1.3", 32, "1.1.1.4", NULL },
9370 { "1.1.1.4", 32, "1.1.1.5", NULL },
9371 { "1.1.1.5", 32, "1.1.1.6", NULL },
9372 { "1.1.1.6", 32, "1.1.1.7", NULL },
9373 { "1.1.1.7", 32, "1.1.1.8", NULL },
9374 { "1.1.1.8", 32, "1.1.1.9", NULL },
9375 { "1.1.1.9", 32, "1.1.1.10", NULL },
9376 { "1.1.1.10", 32, "1.1.1.11", NULL },
9377 { "1.1.1.11", 32, "1.1.1.1", NULL },
9378 };
9379
9380 struct route vpn_routelist[] = {
9381 { "10.1.3.0", 24, "17.153.46.24", NULL },
9382 { "10.1.4.0", 24, "17.153.46.24", NULL },
9383 { "10.1.5.0", 24, "17.153.46.24", NULL },
9384 { "10.1.6.0", 24, "17.153.46.24", NULL },
9385 { "10.1.7.0", 24, "17.153.46.24", NULL },
9386 { "10.16.0.0", 12, "17.153.46.24", NULL },
9387 { "10.45.0.0", 16, "17.153.46.24", NULL },
9388 { "10.53.0.0", 16, "17.153.46.24", NULL },
9389 { "10.70.0.0", 15, "17.153.46.24", NULL },
9390 { "10.74.0.0", 15, "17.153.46.24", NULL },
9391 { "10.90.0.0", 15, "17.153.46.24", NULL },
9392 { "10.91.0.0", 16, "17.153.46.24", NULL },
9393 { "10.100.0.0", 16, "17.153.46.24", NULL },
9394 { "10.113.0.0", 16, "17.153.46.24", NULL },
9395 { "10.128.0.0", 9, "17.153.46.24", NULL },
9396 { "17.0.0.0", 9, "17.153.46.24", NULL },
9397 { "17.34.0.0", 16, "17.153.46.24", NULL },
9398 { "17.112.156.53", 32, "17.153.46.24", NULL },
9399 { "17.128.0.0", 10, "17.153.46.24", NULL },
9400 { "17.149.0.121", 32, "17.153.46.24", NULL },
9401 { "17.149.7.200", 32, "17.153.46.24", NULL },
9402 { "17.153.46.24", 32, "17.153.46.24", NULL },
9403 { "17.192.0.0", 12, "17.153.46.24", NULL },
9404 { "17.208.0.0", 15, "17.153.46.24", NULL },
9405 { "17.211.0.0", 16, "17.153.46.24", NULL },
9406 { "17.212.0.0", 14, "17.153.46.24", NULL },
9407 { "17.216.0.0", 13, "17.153.46.24", NULL },
9408 { "17.224.0.0", 12, "17.153.46.24", NULL },
9409 { "17.240.0.0", 16, "17.153.46.24", NULL },
9410 { "17.241.0.0", 16, "17.153.46.24", NULL },
9411 { "17.248.0.0", 14, "17.153.46.24", NULL },
9412 { "17.251.104.200", 32, "17.153.46.24", NULL },
9413 { "17.252.0.0", 16, "17.153.46.24", NULL },
9414 { "17.253.0.0", 16, "17.153.46.24", NULL },
9415 { "17.254.0.0", 16, "17.153.46.24", NULL },
9416 { "17.255.0.0", 16, "17.153.46.24", NULL },
9417 { "151.193.141.0", 27, "17.153.46.24", NULL },
9418 { "172.16.2.0", 24, "17.153.46.24", NULL },
9419 { "192.35.50.0", 24, "17.153.46.24", NULL },
9420 { "204.179.20.0", 24, "17.153.46.24", NULL },
9421 { "206.112.116.0", 24, "17.153.46.24", NULL },
9422 };
9423
9424 struct route vpn_routelist_ext[] = {
9425 { "17.151.63.82", 32, "10.0.0.1", "en0" },
9426 { "17.151.63.81", 32, "17.151.63.81", "en0" },
9427 { "17.151.63.80", 32, NULL, NULL },
9428 { "17.1.0.0", 16, NULL, NULL },
9429 { "17.2.0.0", 24, NULL, NULL },
9430 { "10.0.0.0", 24, NULL, NULL },
9431 };
9432
9433 /*
9434 * addr prefix dest router ifname pri rank additional-routes+count excluded-routes+count
9435 */
9436 const IPv4ServiceContents en0_10 = {
9437 "10.0.0.10", 24, NULL, "10.0.0.1", "en0", 10, NULL, NULL, 0, NULL, 0
9438 };
9439
9440 const IPv4ServiceContents en0_15 = {
9441 "10.0.0.19", 24, NULL, "10.0.0.1", "en0", 15, NULL, NULL, 0, NULL, 0
9442 };
9443
9444 const IPv4ServiceContents en0_30 = {
9445 "10.0.0.11", 24, NULL, "10.0.0.1", "en0", 30, NULL, NULL, 0, NULL, 0
9446 };
9447
9448 const IPv4ServiceContents en0_40 = {
9449 "10.0.0.12", 24, NULL, "10.0.0.1", "en0", 40, NULL, NULL, 0, NULL, 0
9450 };
9451
9452 const IPv4ServiceContents en0_50 = {
9453 "10.0.0.13", 24, NULL, "10.0.0.1", "en0", 50, NULL, NULL, 0, NULL, 0
9454 };
9455
9456 const IPv4ServiceContents en0_110 = {
9457 "192.168.2.10", 24, NULL, "192.168.2.1", "en0", 110, NULL, NULL, 0, NULL, 0
9458 };
9459
9460 const IPv4ServiceContents en0_1 = {
9461 "17.202.40.191", 22, NULL, "17.202.20.1", "en0", 1, NULL, NULL, 0, NULL, 0
9462 };
9463
9464 const IPv4ServiceContents en1_20 = {
9465 "10.0.0.20", 24, NULL, "10.0.0.1", "en1", 20, NULL, NULL, 0, NULL, 0
9466 };
9467
9468 const IPv4ServiceContents en1_2 = {
9469 "17.202.42.24", 22, NULL, "17.202.20.1", "en1", 2, NULL, NULL, 0, NULL, 0
9470 };
9471
9472 const IPv4ServiceContents en1_125 = {
9473 "192.168.2.20", 24, NULL, "192.168.2.1", "en1", 125, NULL, NULL, 0, NULL, 0
9474 };
9475
9476 const IPv4ServiceContents fw0_25 = {
9477 "192.168.2.30", 24, NULL, "192.168.2.1", "fw0", 25, NULL, NULL, 0, NULL, 0
9478 };
9479
9480 const IPv4ServiceContents fw0_21 = {
9481 "192.168.3.30", 24, NULL, "192.168.3.1", "fw0", 21, NULL, NULL, 0, NULL, 0
9482 };
9483
9484 const IPv4ServiceContents ppp0_0_1 = {
9485 "17.219.156.22", -1, "17.219.156.1", "17.219.156.1", "ppp0", 0, NULL, NULL, 0, NULL, 0
9486 };
9487
9488 const IPv4ServiceContents utun0 = {
9489 "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)
9490 };
9491
9492 const IPv4ServiceContents en0_test6 = {
9493 "17.202.42.113", 22, NULL, "17.202.40.1", "en0", 2, NULL, NULL, 0, NULL, 0
9494 };
9495
9496 const IPv4ServiceContents en1_test6 = {
9497 "17.202.42.111", 22, NULL, "17.202.40.1", "en1", 3, NULL, NULL, 0, NULL, 0
9498 };
9499
9500 const IPv4ServiceContents en2_test6 = {
9501 "17.255.98.164", 20, NULL, "17.255.96.1", "en2", 1, NULL, NULL, 0, NULL, 0
9502 };
9503
9504 const IPv4ServiceContents en0_test7 = {
9505 "17.202.42.113", 22, NULL, "17.202.40.1", "en0", 3, NULL, NULL, 0, NULL, 0
9506 };
9507
9508 const IPv4ServiceContents en1_test7 = {
9509 "17.202.42.111", 22, NULL, "17.202.40.1", "en1", 2, NULL, NULL, 0, NULL, 0
9510 };
9511
9512 const IPv4ServiceContents en2_test7 = {
9513 "17.255.98.164", 20, NULL, "17.255.96.1", "en2", 1, NULL, NULL, 0, NULL, 0
9514 };
9515
9516 const IPv4ServiceContents fw0_test6_and_7 = {
9517 "169.254.11.33", 16, NULL, NULL, "fw0", 0x0ffffff, NULL, NULL, 0, NULL, 0
9518 };
9519
9520 const IPv4ServiceContents en0_10_last = {
9521 "10.0.0.10", 24, NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankLast, NULL, 0, NULL, 0
9522 };
9523
9524 const IPv4ServiceContents en0_10_never = {
9525 "10.0.0.10", 24, NULL, "10.0.0.1", "en0", 10, &kSCValNetServicePrimaryRankNever, NULL, 0, NULL, 0
9526 };
9527
9528 const IPv4ServiceContents en1_20_first = {
9529 "10.0.0.20", 24, NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankFirst, NULL, 0, NULL, 0
9530 };
9531
9532 const IPv4ServiceContents en1_20_never = {
9533 "10.0.0.20", 24, NULL, "10.0.0.1", "en1", 20, &kSCValNetServicePrimaryRankNever, NULL, 0, NULL, 0
9534 };
9535
9536 const IPv4ServiceContents en1_20_other_never = {
9537 "192.168.2.50", 24, NULL, "192.168.2.1", "en1", 20, &kSCValNetServicePrimaryRankNever, NULL, 0, NULL, 0
9538 };
9539
9540 const IPv4ServiceContents en0_linklocal = {
9541 "169.254.22.44", 16, NULL, NULL, "en0", 0xfffff, NULL, NULL, 0, NULL, 0
9542 };
9543
9544 const IPv4ServiceContents en0_route_loop = {
9545 "192.168.130.16", 24, NULL, "192.168.130.1", "en0", 2, NULL, loop_routelist, countof(loop_routelist), NULL, 0
9546 };
9547
9548 typedef struct {
9549 const char * name;
9550 IPv4ServiceContentsRef test[];
9551 } IPv4RouteTest, * IPv4RouteTestRef;
9552
9553 static IPv4RouteTest test1 = {
9554 "test1",
9555 {
9556 &en0_40,
9557 &en0_15,
9558 &fw0_25,
9559 &en0_30,
9560 &en1_20,
9561 &en0_50,
9562 &en0_10,
9563 NULL
9564 }
9565 };
9566
9567 static IPv4RouteTest test2 = {
9568 "test2",
9569 {
9570 &en0_40,
9571 &fw0_25,
9572 &en0_30,
9573 &en1_20,
9574 &en0_50,
9575 &en0_10,
9576 NULL
9577 }
9578 };
9579
9580 static IPv4RouteTest test3 = {
9581 "test3",
9582 {
9583 &en0_40,
9584 &en1_20,
9585 &en0_50,
9586 &en0_10,
9587 &en0_110,
9588 &en1_125,
9589 &fw0_25,
9590 &fw0_21,
9591 &en0_40,
9592 &en0_30,
9593 NULL
9594 }
9595 };
9596
9597 static IPv4RouteTest test4 = {
9598 "test4",
9599 {
9600 &en0_1,
9601 &en0_40,
9602 &en0_30,
9603 &en1_20,
9604 &en1_2,
9605 NULL
9606 }
9607 };
9608
9609 static IPv4RouteTest test5 = {
9610 "test5",
9611 {
9612 &ppp0_0_1,
9613 &en0_1,
9614 &en0_40,
9615 &en0_30,
9616 &en1_20,
9617 &en1_2,
9618 NULL
9619 }
9620 };
9621
9622 static IPv4RouteTest test6 = {
9623 "test6",
9624 {
9625 &en0_test6,
9626 &en1_test6,
9627 &en2_test6,
9628 &fw0_test6_and_7,
9629 NULL
9630 }
9631 };
9632
9633 static IPv4RouteTest test7 = {
9634 "test7",
9635 {
9636 &en0_test7,
9637 &en1_test7,
9638 &en2_test7,
9639 &fw0_test6_and_7,
9640 NULL
9641 }
9642 };
9643
9644 static IPv4RouteTest test8 = {
9645 "test8",
9646 {
9647 &en0_10,
9648 &en1_20,
9649 NULL
9650 }
9651 };
9652
9653 static IPv4RouteTest test9 = {
9654 "test9",
9655 {
9656 &en0_10,
9657 &en1_20_first,
9658 &fw0_25,
9659 NULL
9660 }
9661 };
9662
9663 static IPv4RouteTest test10 = {
9664 "test10",
9665 {
9666 &en0_10_last,
9667 &en1_20,
9668 &fw0_25,
9669 NULL
9670 }
9671 };
9672
9673 static IPv4RouteTest test11 = {
9674 "test11",
9675 {
9676 &en0_10_never,
9677 &en1_20,
9678 &fw0_25,
9679 NULL
9680 }
9681 };
9682
9683 static IPv4RouteTest test12 = {
9684 "test12",
9685 {
9686 &en0_10,
9687 &en1_20,
9688 NULL
9689 }
9690 };
9691
9692 static IPv4RouteTest test13 = {
9693 "test13",
9694 {
9695 &en0_10,
9696 &en1_20_never,
9697 NULL
9698 }
9699 };
9700
9701 static IPv4RouteTest test14 = {
9702 "test14",
9703 {
9704 &en1_20_never,
9705 NULL
9706 }
9707 };
9708
9709 static IPv4RouteTest test15 = {
9710 "test15",
9711 {
9712 &en0_linklocal,
9713 NULL
9714 }
9715 };
9716
9717 static IPv4RouteTest test16 = {
9718 "test16",
9719 {
9720 &en0_10,
9721 &utun0,
9722 NULL
9723 }
9724 };
9725
9726 static IPv4RouteTest test17 = {
9727 "test17",
9728 {
9729 &en0_10,
9730 &en1_20_other_never,
9731 NULL
9732 }
9733 };
9734
9735 static IPv4RouteTest test18 = {
9736 "test18",
9737 {
9738 &en0_route_loop,
9739 NULL
9740 }
9741 };
9742
9743 static IPv4RouteTestRef ipv4_tests[] = {
9744 &test1,
9745 &test2,
9746 &test3,
9747 &test4,
9748 &test5,
9749 &test6,
9750 &test7,
9751 &test8,
9752 &test9,
9753 &test10,
9754 &test11,
9755 &test12,
9756 &test13,
9757 &test14,
9758 &test15,
9759 &test16,
9760 &test17,
9761 &test18,
9762 NULL
9763 };
9764
9765 static boolean_t
9766 ipv4_prefix_length_is_valid(int prefix_length)
9767 {
9768 if (prefix_length < 0 || prefix_length > IPV4_ROUTE_ALL_BITS_SET) {
9769 return (FALSE);
9770 }
9771 return (TRUE);
9772 }
9773
9774 static void
9775 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
9776 const char * str)
9777 {
9778 CFStringRef prop_val;
9779
9780 if (str == NULL) {
9781 return;
9782 }
9783 prop_val = CFStringCreateWithCString(NULL,
9784 str,
9785 kCFStringEncodingASCII);
9786 CFDictionarySetValue(dict, prop_name, prop_val);
9787 CFRelease(prop_val);
9788 return;
9789 }
9790
9791 static void
9792 dict_add_string_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
9793 const char * str)
9794 {
9795 CFArrayRef array;
9796 CFStringRef prop_val;
9797
9798 if (str == NULL) {
9799 return;
9800 }
9801 prop_val = CFStringCreateWithCString(NULL,
9802 str,
9803 kCFStringEncodingASCII);
9804 array = CFArrayCreate(NULL,
9805 (const void **)&prop_val, 1,
9806 &kCFTypeArrayCallBacks);
9807 CFRelease(prop_val);
9808 CFDictionarySetValue(dict, prop_name, array);
9809 CFRelease(array);
9810 return;
9811 }
9812
9813 static void
9814 dict_add_ip(CFMutableDictionaryRef dict, CFStringRef prop_name,
9815 struct in_addr ip)
9816 {
9817 CFStringRef str;
9818
9819 str = my_CFStringCreateWithInAddr(ip);
9820 CFDictionarySetValue(dict, prop_name, str);
9821 CFRelease(str);
9822 return;
9823 }
9824
9825 static void
9826 dict_add_ip_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
9827 struct in_addr ip)
9828 {
9829 CFArrayRef array;
9830 CFStringRef str;
9831
9832 str = my_CFStringCreateWithInAddr(ip);
9833 array = CFArrayCreate(NULL,
9834 (const void **)&str, 1,
9835 &kCFTypeArrayCallBacks);
9836 CFRelease(str);
9837 CFDictionarySetValue(dict, prop_name, array);
9838 CFRelease(array);
9839 return;
9840 }
9841
9842 static void
9843 dict_insert_routes(CFMutableDictionaryRef dict, CFStringRef prop_name,
9844 struct route * routes, int routes_count)
9845 {
9846 int i;
9847 CFMutableArrayRef route_list;
9848 struct route * scan;
9849
9850 if (routes == NULL || routes_count == 0) {
9851 return;
9852 }
9853 route_list = CFArrayCreateMutable(NULL, routes_count,
9854 &kCFTypeArrayCallBacks);
9855 for (i = 0, scan = routes; i < routes_count; i++, scan++) {
9856 struct in_addr mask;
9857 CFMutableDictionaryRef route_dict;
9858
9859 route_dict
9860 = CFDictionaryCreateMutable(NULL, 0,
9861 &kCFTypeDictionaryKeyCallBacks,
9862 &kCFTypeDictionaryValueCallBacks);
9863 dict_add_string(route_dict, kSCPropNetIPv4RouteDestinationAddress,
9864 scan->dest);
9865 if (ipv4_prefix_length_is_valid(scan->prefix_length)) {
9866 mask.s_addr = htonl(prefix_to_mask32(scan->prefix_length));
9867 dict_add_ip(route_dict, kSCPropNetIPv4RouteSubnetMask, mask);
9868 }
9869 dict_add_string(route_dict, kSCPropNetIPv4RouteGatewayAddress,
9870 scan->gateway);
9871 dict_add_string(route_dict, kSCPropNetIPv4RouteInterfaceName,
9872 scan->ifname);
9873 CFArrayAppendValue(route_list, route_dict);
9874 CFRelease(route_dict);
9875 }
9876 CFDictionarySetValue(dict, prop_name, route_list);
9877 CFRelease(route_list);
9878 return;
9879 }
9880
9881 static CFDictionaryRef
9882 make_IPv4_dict(IPv4ServiceContentsRef t)
9883 {
9884 CFMutableDictionaryRef dict;
9885
9886 dict = CFDictionaryCreateMutable(NULL, 0,
9887 &kCFTypeDictionaryKeyCallBacks,
9888 &kCFTypeDictionaryValueCallBacks);
9889 dict_add_string_as_array(dict, kSCPropNetIPv4Addresses, t->addr);
9890 if (ipv4_prefix_length_is_valid(t->prefix_length)) {
9891 struct in_addr mask;
9892
9893 mask.s_addr = htonl(prefix_to_mask32(t->prefix_length));
9894 dict_add_ip_as_array(dict, kSCPropNetIPv4SubnetMasks, mask);
9895 }
9896 dict_add_string_as_array(dict, kSCPropNetIPv4DestAddresses, t->dest);
9897 dict_add_string(dict, kSCPropNetIPv4Router, t->router);
9898 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
9899 dict_add_string(dict, kSCPropConfirmedInterfaceName, t->ifname);
9900 dict_insert_routes(dict, kSCPropNetIPv4AdditionalRoutes,
9901 t->additional_routes, t->additional_routes_count);
9902 dict_insert_routes(dict, kSCPropNetIPv4ExcludedRoutes,
9903 t->excluded_routes, t->excluded_routes_count);
9904 return (dict);
9905 }
9906
9907 typedef enum {
9908 kDirectionForwards = 0,
9909 kDirectionBackwards = 1
9910 } Direction;
9911
9912 typedef enum {
9913 kLogRouteDisabled = 0,
9914 kLogRouteEnabled = 1
9915 } LogRoute;
9916
9917 static IPv4RouteListRef
9918 make_IPv4RouteList_for_test(IPv4RouteListRef list,
9919 IPv4ServiceContentsRef test,
9920 LogRoute log_it)
9921 {
9922 CFDictionaryRef dict;
9923 IPv4RouteListRef r;
9924 Rank rank;
9925 Rank rank_assertion = kRankAssertionDefault;
9926 CFNumberRef rank_assertion_cf = NULL;
9927 Boolean rank_assertion_is_set = FALSE;
9928 IPv4RouteListRef ret = NULL;
9929 IPV4_ROUTES_BUF_DECL(routes);
9930
9931 dict = make_IPv4_dict(test);
9932 if (dict == NULL) {
9933 fprintf(stderr, "make_IPv4_dict failed\n");
9934 exit(1);
9935 }
9936 if (test->primary_rank != NULL) {
9937 rank_assertion
9938 = PrimaryRankGetRankAssertion(*test->primary_rank,
9939 &rank_assertion_is_set);
9940 if (rank_assertion_is_set) {
9941 rank_assertion_cf
9942 = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank_assertion);
9943 }
9944 }
9945 r = IPv4RouteListCreateWithDictionary(routes, dict, rank_assertion_cf);
9946 my_CFRelease(&rank_assertion_cf);
9947 if (r == NULL) {
9948 fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
9949 exit(1);
9950 }
9951
9952 if (rank_assertion == kRankAssertionScoped) {
9953 rank_assertion = kRankAssertionNever;
9954 }
9955 rank = RankMake(test->rank, rank_assertion);
9956 if (log_it == kLogRouteEnabled
9957 && (S_IPMonitor_debug & kDebugFlag4) != 0) {
9958 CFStringRef descr;
9959
9960 descr = IPv4RouteListCopyDescription(r);
9961 SCPrint(TRUE, stdout, CFSTR("Adding %@\n"), descr);
9962 CFRelease(descr);
9963 }
9964 ret = IPv4RouteListAddRouteList(list, 1, r, rank);
9965 if (r != routes) {
9966 free(r);
9967 }
9968 CFRelease(dict);
9969 return (ret);
9970 }
9971
9972 static IPv4RouteListRef
9973 make_IPv4RouteList(IPv4ServiceContentsRef * test, Direction direction,
9974 LogRoute log_it)
9975 {
9976 IPv4RouteListRef ret = NULL;
9977 IPv4ServiceContentsRef * scan;
9978
9979 switch (direction) {
9980 case kDirectionBackwards:
9981 for (scan = test; *scan != NULL; scan++) {
9982 /* find the end of the list */
9983 }
9984 for (scan--; scan >= test; scan--) {
9985 ret = make_IPv4RouteList_for_test(ret, *scan, log_it);
9986 }
9987 break;
9988 default:
9989 case kDirectionForwards:
9990 for (scan = test; *scan != NULL; scan++) {
9991 ret = make_IPv4RouteList_for_test(ret, *scan, log_it);
9992 }
9993 break;
9994 }
9995 IPv4RouteListFinalize(ret);
9996 return (ret);
9997 }
9998
9999 #define EMPHASIS_CHARS "================="
10000
10001 /*
10002 * Function: routelist_build_test
10003 * Purpose:
10004 * Runs through the given set of routes first in the forward direction,
10005 * then again backwards. We should end up with exactly the same set of
10006 * routes at the end.
10007 */
10008 static boolean_t
10009 routelist_build_test(IPv4RouteTestRef test)
10010 {
10011 CFStringRef descr;
10012 boolean_t ret = FALSE;
10013 IPv4RouteListRef routes1;
10014 IPv4RouteListRef routes2;
10015
10016 printf("\n" EMPHASIS_CHARS "> RouteList Build '%s' <"
10017 EMPHASIS_CHARS "\n",
10018 test->name);
10019
10020 routes1 = make_IPv4RouteList(test->test, kDirectionForwards,
10021 kLogRouteEnabled);
10022 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
10023 if (routes1 != NULL) {
10024 descr = IPv4RouteListCopyDescription(routes1);
10025 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
10026 CFRelease(descr);
10027 }
10028 }
10029 routes2 = make_IPv4RouteList(test->test, kDirectionBackwards,
10030 kLogRouteEnabled);
10031 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
10032 if (routes2 != NULL) {
10033 descr = IPv4RouteListCopyDescription(routes2);
10034 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
10035 CFRelease(descr);
10036 }
10037 }
10038 if ((routes1 != NULL && routes2 == NULL)
10039 || (routes1 == NULL && routes2 != NULL)) {
10040 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
10041 (routes1 != NULL) ? "not " : "",
10042 (routes2 != NULL) ? "not " : "");
10043 }
10044 else if (routes1 != NULL && routes2 != NULL) {
10045 /* check if they are different */
10046 if (routes1->count != routes2->count) {
10047 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
10048 routes1->count, routes2->count);
10049 }
10050 else if (bcmp(routes1, routes2,
10051 IPv4RouteListComputeSize(routes1->count)) != 0) {
10052 fprintf(stderr, "routes1 and routes2 are different\n");
10053 }
10054 else {
10055 printf("routes1 and routes2 are the same\n");
10056 ret = TRUE;
10057 }
10058 }
10059 if (routes1 != NULL) {
10060 free(routes1);
10061 }
10062 if (routes2 != NULL) {
10063 free(routes2);
10064 }
10065 printf(EMPHASIS_CHARS "> RouteList Build '%s': %s <"
10066 EMPHASIS_CHARS "\n",
10067 test->name, ret ? "PASSED" : "FAILED");
10068 return (ret);
10069 }
10070
10071 static void
10072 apply_test(IPv4RouteTestRef old_test, IPv4RouteTestRef new_test)
10073 {
10074 IPv4RouteListRef new_routes;
10075 IPv4RouteListRef old_routes;
10076
10077 printf("\n" EMPHASIS_CHARS "> Apply '%s', '%s' Begin <"
10078 EMPHASIS_CHARS "\n",
10079 old_test->name, new_test->name);
10080
10081 old_routes = make_IPv4RouteList(old_test->test, kDirectionForwards,
10082 kLogRouteDisabled);
10083 new_routes = make_IPv4RouteList(new_test->test, kDirectionForwards,
10084 kLogRouteDisabled);
10085 if (old_routes == NULL) {
10086 printf("No Old Routes\n");
10087 }
10088 else {
10089 printf("Old routes ('%s') = ", old_test->name);
10090 IPv4RouteListPrint(old_routes);
10091 }
10092
10093 /* apply the old routes */
10094 IPv4RouteListApply(NULL, old_routes, -1);
10095
10096 if (new_routes == NULL) {
10097 printf("No New Routes\n");
10098 }
10099 else {
10100 printf("New Routes ('%s') = ", new_test->name);
10101 IPv4RouteListPrint(new_routes);
10102 }
10103
10104 /* apply the new routes */
10105 IPv4RouteListApply(old_routes, new_routes, -1);
10106
10107 if (old_routes != NULL) {
10108 free(old_routes);
10109 }
10110 if (new_routes != NULL) {
10111 free(new_routes);
10112 }
10113 printf(EMPHASIS_CHARS "> Apply '%s', '%s' End <"
10114 EMPHASIS_CHARS "\n",
10115 old_test->name, new_test->name);
10116 return;
10117 }
10118
10119 int
10120 main(int argc, char **argv)
10121 {
10122 IPv4RouteTestRef * test;
10123
10124 _sc_log = kSCLogDestinationFile;
10125 _sc_verbose = (argc > 1) ? TRUE : FALSE;
10126 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
10127 if (argc > 1) {
10128 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
10129 }
10130 for (test = ipv4_tests; *test != NULL; test++) {
10131 if (!routelist_build_test(*test)) {
10132 fprintf(stderr, "%s failed\n", (*test)->name);
10133 exit(1);
10134 }
10135 }
10136 for (test = ipv4_tests; *test != NULL; test++) {
10137 IPv4RouteTestRef * test2;
10138
10139 for (test2 = test + 1; *test2 != NULL; test2++) {
10140 apply_test(*test, *test2);
10141 apply_test(*test2, *test);
10142 }
10143 }
10144 test_complete();
10145 exit(0);
10146 return (0);
10147 }
10148
10149 #endif /* TEST_IPV4_ROUTELIST */
10150
10151 #if TEST_IPV6_ROUTELIST
10152
10153 typedef struct {
10154 const char * addr;
10155 int prefix_length;
10156 const char * dest;
10157 } IPv6Address;
10158
10159 typedef const IPv6Address * IPv6AddressRef;
10160
10161 typedef struct {
10162 IPv6AddressRef addr;
10163 int addr_count;
10164 const char * router;
10165 const char * ifname;
10166 Rank rank;
10167 const CFStringRef * primary_rank;
10168 struct route * additional_routes;
10169 int additional_routes_count;
10170 struct route * excluded_routes;
10171 int excluded_routes_count;
10172 } IPv6ServiceContents;
10173
10174 typedef const IPv6ServiceContents * IPv6ServiceContentsRef;
10175
10176 struct route loop_routelist[] = {
10177 { "2620:149:4:f01:225:ff:fecc:89a1", 128,
10178 "2620:149:4:f01:225:ff:fecc:89a2", NULL },
10179 { "2620:149:4:f01:225:ff:fecc:89a2", 128,
10180 "2620:149:4:f01:225:ff:fecc:89a3", NULL },
10181 { "2620:149:4:f01:225:ff:fecc:89a3", 128,
10182 "2620:149:4:f01:225:ff:fecc:89a4", NULL },
10183 { "2620:149:4:f01:225:ff:fecc:89a4", 128,
10184 "2620:149:4:f01:225:ff:fecc:89a5", NULL },
10185 { "2620:149:4:f01:225:ff:fecc:89a5", 128,
10186 "2620:149:4:f01:225:ff:fecc:89a6", NULL },
10187 { "2620:149:4:f01:225:ff:fecc:89a6", 128,
10188 "2620:149:4:f01:225:ff:fecc:89a7", NULL },
10189 { "2620:149:4:f01:225:ff:fecc:89a7", 128,
10190 "2620:149:4:f01:225:ff:fecc:89a8", NULL },
10191 { "2620:149:4:f01:225:ff:fecc:89a8", 128,
10192 "2620:149:4:f01:225:ff:fecc:89a9", NULL },
10193 { "2620:149:4:f01:225:ff:fecc:89a9", 128,
10194 "2620:149:4:f01:225:ff:fecc:89aa", NULL },
10195 { "2620:149:4:f01:225:ff:fecc:89aa", 128,
10196 "2620:149:4:f01:225:ff:fecc:89ab", NULL },
10197 { "2620:149:4:f01:225:ff:fecc:89ab", 128,
10198 "2620:149:4:f01:225:ff:fecc:89a1", NULL },
10199 };
10200
10201 struct route vpn_routelist[] = {
10202 { "2010:470:1f05:3cb::", 64,
10203 "fe80::2d0:bcff:fe3d:8c00", NULL },
10204 { "2010:222:3fa5:acb::", 48,
10205 "fe80::2d0:bcff:fe3d:8c00", NULL },
10206 { "2010:222:3fa5:1234::", 40,
10207 "fe80::2d0:bcff:fe3d:8c00", NULL },
10208 { "2010:222:3fa5:5678::", 40,
10209 NULL, NULL },
10210 };
10211
10212 struct route vpn_routelist_ext[] = {
10213 { "2020:299:a:e02:825:1ed:fecc:abab", 128, NULL, NULL },
10214 };
10215
10216 struct route en1_routelist_ext[] = {
10217 { "2020:299:abcd:ef12::", 64, NULL, NULL },
10218 };
10219
10220
10221 static const IPv6Address en0_addr1[] = {
10222 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9601", 64, NULL },
10223 { "2001:470:1f05:3cb:5c95:58b1:b956:6101", 64, NULL }
10224 };
10225
10226 static const IPv6Address en0_addr2[] = {
10227 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9602", 64, NULL },
10228 { "2001:470:1f05:3cb:5c95:58b1:b956:6102", 64, NULL }
10229 };
10230
10231 static const IPv6Address en0_addr3[] = {
10232 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9603", 64, NULL },
10233 { "2001:470:1f05:3cb:5c95:58b1:b956:6103", 64, NULL }
10234 };
10235
10236 static const IPv6Address en0_addr4[] = {
10237 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9604", 64, NULL },
10238 { "2001:470:1f05:3cb:5c95:58b1:b956:6104", 64, NULL }
10239 };
10240
10241 static const IPv6Address en0_addr5[] = {
10242 { "2001:470:1f05:3cb:cabc:c8ff:fe96:9605", 64, NULL },
10243 { "2001:470:1f05:3cb:5c95:58b1:b956:6105", 64, NULL }
10244 };
10245
10246 static const IPv6Address en0_addr6[] = {
10247 { "2020:299:abcd:ef12:1:2:3:4", 64, NULL },
10248 };
10249
10250 static const IPv6Address en0_lladdr[] = {
10251 { "fe80::cabc:c8ff:fe96:96af", 64, NULL }
10252 };
10253
10254 static const IPv6Address en1_addr[] = {
10255 { "2001:470:1f05:3cb:cabc:c8ff:fed9:125a", 64, NULL },
10256 { "2001:470:1f05:3cb:2d5e:4ec3:304:5b9c", 64, NULL }
10257 };
10258
10259 static const IPv6Address utun0_addr[] = {
10260 { "2620:149:4:f01:225:ff:fecc:89aa", 64, NULL },
10261 };
10262
10263 static const IPv6Address fw0_addr1[] = {
10264 { "2011:470:1f05:3cb:cabc:c8ff:fe96:ab01", 64, NULL },
10265 { "2011:470:1f05:3cb:5c95:58b1:b956:ab01", 64, NULL }
10266 };
10267
10268 /*
10269 * address+address-count
10270 * router ifname pri rank additional-routes+count excluded-routes+count
10271 */
10272
10273 static const IPv6ServiceContents en0_10 = {
10274 en0_addr1, countof(en0_addr1),
10275 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL, NULL, 0, NULL, 0
10276 };
10277
10278 static const IPv6ServiceContents en0_15 = {
10279 en0_addr2, countof(en0_addr2),
10280 "fe80::21f:f3ff:fe43:1abf", "en0", 15, NULL, NULL, 0, NULL, 0
10281 };
10282
10283 static const IPv6ServiceContents en0_30 = {
10284 en0_addr3, countof(en0_addr3),
10285 "fe80::21f:f3ff:fe43:1abf", "en0", 30, NULL, NULL, 0, NULL, 0
10286 };
10287
10288 static const IPv6ServiceContents en0_40 = {
10289 en0_addr4, countof(en0_addr4),
10290 "fe80::21f:f3ff:fe43:1abf", "en0", 40, NULL, NULL, 0, NULL, 0
10291 };
10292
10293 static const IPv6ServiceContents en0_50 = {
10294 en0_addr5, countof(en0_addr5),
10295 "fe80::21f:f3ff:fe43:1abf", "en0", 50, NULL, NULL, 0, NULL, 0
10296 };
10297
10298 static const IPv6ServiceContents en0_10_a = {
10299 en0_addr6, countof(en0_addr6),
10300 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL, NULL, 0, NULL, 0
10301 };
10302
10303 static const IPv6ServiceContents fw0_25 = {
10304 fw0_addr1, countof(fw0_addr1),
10305 "fe80::21f:f3ff:fe43:1abf", "fw0", 25, NULL, NULL, 0, NULL, 0
10306 };
10307
10308 static const IPv6ServiceContents en1_20 = {
10309 en1_addr, countof(en1_addr),
10310 "fe80::21f:f3ff:fe43:1abf", "en1", 20, NULL, NULL, 0, NULL, 0
10311 };
10312
10313 static const IPv6ServiceContents en1_10_ext = {
10314 en1_addr, countof(en1_addr),
10315 "fe80::21f:f3ff:fe43:1abf", "en1", 10, NULL, NULL, 0,
10316 en1_routelist_ext, countof(en1_routelist_ext)
10317 };
10318
10319 static const IPv6ServiceContents en0_0_lladdr = {
10320 en0_lladdr, countof(en0_lladdr),
10321 "fe80::21f:f3ff:fe43:1abf", "en0", 20, NULL, NULL, 0, NULL, 0
10322 };
10323
10324 static const IPv6ServiceContents en0_loop = {
10325 en0_addr1, countof(en0_addr1),
10326 "fe80::21f:f3ff:fe43:1abf", "en0", 10, NULL,
10327 loop_routelist, countof(loop_routelist), NULL, 0
10328 };
10329
10330 static const IPv6ServiceContents utun0 = {
10331 utun0_addr, countof(utun0_addr),
10332 "fe80::2d0:bcff:fe3d:8c00", "utun0", 40, NULL,
10333 vpn_routelist, countof(vpn_routelist),
10334 vpn_routelist_ext, countof(vpn_routelist_ext),
10335 };
10336
10337 typedef struct {
10338 const char * name;
10339 IPv6ServiceContentsRef test[];
10340 } IPv6RouteTest, * IPv6RouteTestRef;
10341
10342 static IPv6RouteTest test1 = {
10343 "test1",
10344 {
10345 &en0_40,
10346 &en0_15,
10347 &fw0_25,
10348 &en0_30,
10349 &en1_20,
10350 &en0_50,
10351 &en0_10,
10352 NULL
10353 }
10354 };
10355
10356 static IPv6RouteTest test2 = {
10357 "test2",
10358 {
10359 &en0_40,
10360 &fw0_25,
10361 &en0_30,
10362 &en1_20,
10363 &en0_50,
10364 &en0_10,
10365 NULL
10366 }
10367 };
10368
10369 static IPv6RouteTest test3 = {
10370 "test3",
10371 {
10372 &en0_10_a,
10373 &en1_10_ext,
10374 NULL
10375 }
10376 };
10377
10378 static IPv6RouteTest test4 = {
10379 "test4",
10380 {
10381 &en0_loop,
10382 &en1_20,
10383 NULL
10384 }
10385 };
10386
10387 static IPv6RouteTest test5 = {
10388 "test5",
10389 {
10390 &en0_10,
10391 &utun0,
10392 &en0_0_lladdr,
10393 &en1_20,
10394 NULL
10395 }
10396 };
10397
10398
10399 static IPv6RouteTestRef ipv6_tests[] = {
10400 &test1,
10401 &test2,
10402 &test3,
10403 &test4,
10404 &test5,
10405 NULL
10406 };
10407
10408
10409 static void
10410 dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
10411 const char * str)
10412 {
10413 CFStringRef prop_val;
10414
10415 if (str == NULL) {
10416 return;
10417 }
10418 prop_val = CFStringCreateWithCString(NULL,
10419 str,
10420 kCFStringEncodingASCII);
10421 CFDictionarySetValue(dict, prop_name, prop_val);
10422 CFRelease(prop_val);
10423 return;
10424 }
10425
10426 static void
10427 dict_add_int(CFMutableDictionaryRef dict, CFStringRef prop_name,
10428 int int_val)
10429 {
10430 CFNumberRef num;
10431
10432 num = CFNumberCreate(NULL, kCFNumberIntType, &int_val);
10433 CFDictionarySetValue(dict, prop_name, num);
10434 CFRelease(num);
10435 return;
10436 }
10437
10438 static void
10439 dict_insert_v6_routes(CFMutableDictionaryRef dict, CFStringRef prop_name,
10440 struct route * routes, int routes_count)
10441 {
10442 int i;
10443 CFMutableArrayRef route_list;
10444 struct route * scan;
10445
10446 if (routes == NULL || routes_count == 0) {
10447 return;
10448 }
10449 route_list = CFArrayCreateMutable(NULL, routes_count,
10450 &kCFTypeArrayCallBacks);
10451 for (i = 0, scan = routes; i < routes_count; i++, scan++) {
10452 CFMutableDictionaryRef route_dict;
10453
10454 route_dict = CFDictionaryCreateMutable(NULL, 0,
10455 &kCFTypeDictionaryKeyCallBacks,
10456 &kCFTypeDictionaryValueCallBacks);
10457 dict_add_string(route_dict, kSCPropNetIPv6RouteDestinationAddress,
10458 scan->dest);
10459 dict_add_int(route_dict, kSCPropNetIPv6PrefixLength,
10460 scan->prefix_length);
10461 dict_add_string(route_dict, kSCPropNetIPv6RouteGatewayAddress,
10462 scan->gateway);
10463 dict_add_string(route_dict, kSCPropNetIPv6RouteInterfaceName,
10464 scan->ifname);
10465 CFArrayAppendValue(route_list, route_dict);
10466 CFRelease(route_dict);
10467 }
10468 CFDictionarySetValue(dict, prop_name, route_list);
10469 CFRelease(route_list);
10470 return;
10471 }
10472
10473 static void
10474 array_add_string(CFMutableArrayRef array, const char * c_str)
10475 {
10476 CFStringRef str;
10477
10478 str = CFStringCreateWithCString(NULL,
10479 c_str,
10480 kCFStringEncodingUTF8);
10481 CFArrayAppendValue(array, str);
10482 CFRelease(str);
10483 return;
10484 }
10485
10486 static void
10487 array_add_int(CFMutableArrayRef array, int int_val)
10488 {
10489 CFNumberRef num;
10490
10491 num = CFNumberCreate(NULL, kCFNumberIntType, &int_val);
10492 CFArrayAppendValue(array, num);
10493 CFRelease(num);
10494 return;
10495 }
10496
10497 static void
10498 dict_add_ipv6_addressing(CFMutableDictionaryRef dict,
10499 IPv6AddressRef list, int list_count)
10500 {
10501 CFMutableArrayRef addr = NULL;
10502 CFMutableArrayRef dest = NULL;
10503 int i;
10504 CFMutableArrayRef prefix = NULL;
10505 IPv6AddressRef scan;
10506
10507 if (list == NULL || list_count == 0) {
10508 return;
10509 }
10510 for (i = 0, scan = list; i < list_count; i++, scan++) {
10511 if (scan->addr != NULL) {
10512 if (addr == NULL) {
10513 addr = CFArrayCreateMutable(NULL, list_count,
10514 &kCFTypeArrayCallBacks);
10515 }
10516 array_add_string(addr, scan->addr);
10517 }
10518 if (scan->prefix_length >= 0) {
10519 if (prefix == NULL) {
10520 prefix = CFArrayCreateMutable(NULL, list_count,
10521 &kCFTypeArrayCallBacks);
10522 }
10523 array_add_int(prefix, scan->prefix_length);
10524 }
10525 if (scan->dest != NULL) {
10526 if (dest == NULL) {
10527 dest = CFArrayCreateMutable(NULL, list_count,
10528 &kCFTypeArrayCallBacks);
10529 }
10530 array_add_string(dest, scan->dest);
10531 }
10532 }
10533 if (addr != NULL) {
10534 CFDictionarySetValue(dict, kSCPropNetIPv6Addresses, addr);
10535 CFRelease(addr);
10536 }
10537 if (dest != NULL) {
10538 CFDictionarySetValue(dict, kSCPropNetIPv6DestAddresses, dest);
10539 CFRelease(dest);
10540 }
10541 if (prefix != NULL) {
10542 CFDictionarySetValue(dict, kSCPropNetIPv6PrefixLength, prefix);
10543 CFRelease(prefix);
10544 }
10545 return;
10546 }
10547
10548 static CFDictionaryRef
10549 make_IPv6_dict(IPv6ServiceContentsRef t)
10550 {
10551 CFMutableDictionaryRef dict;
10552
10553 dict = CFDictionaryCreateMutable(NULL, 0,
10554 &kCFTypeDictionaryKeyCallBacks,
10555 &kCFTypeDictionaryValueCallBacks);
10556 dict_add_ipv6_addressing(dict, t->addr, t->addr_count);
10557 dict_add_string(dict, kSCPropNetIPv6Router, t->router);
10558 dict_add_string(dict, kSCPropInterfaceName, t->ifname);
10559 dict_insert_v6_routes(dict, kSCPropNetIPv6AdditionalRoutes,
10560 t->additional_routes, t->additional_routes_count);
10561 dict_insert_v6_routes(dict, kSCPropNetIPv6ExcludedRoutes,
10562 t->excluded_routes, t->excluded_routes_count);
10563 return (dict);
10564 }
10565
10566 typedef enum {
10567 kDirectionForwards = 0,
10568 kDirectionBackwards = 1
10569 } Direction;
10570
10571 typedef enum {
10572 kLogRouteDisabled = 0,
10573 kLogRouteEnabled = 1
10574 } LogRoute;
10575
10576 static IPv6RouteListRef
10577 make_IPv6RouteList_for_test(IPv6RouteListRef list,
10578 IPv6ServiceContentsRef test,
10579 LogRoute log_it)
10580 {
10581 CFDictionaryRef dict;
10582 IPv6RouteListRef r;
10583 Rank rank;
10584 Rank rank_assertion = kRankAssertionDefault;
10585 CFNumberRef rank_assertion_cf = NULL;
10586 Boolean rank_assertion_is_set = FALSE;
10587 IPv6RouteListRef ret = NULL;
10588 IPV6_ROUTES_BUF_DECL(routes);
10589
10590 dict = make_IPv6_dict(test);
10591 if (dict == NULL) {
10592 fprintf(stderr, "make_IPv6_dict failed\n");
10593 exit(1);
10594 }
10595 if (test->primary_rank != NULL) {
10596 rank_assertion
10597 = PrimaryRankGetRankAssertion(*test->primary_rank,
10598 &rank_assertion_is_set);
10599 if (rank_assertion_is_set) {
10600 rank_assertion_cf
10601 = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank_assertion);
10602 }
10603 }
10604 r = IPv6RouteListCreateWithDictionary(routes, dict, rank_assertion_cf);
10605 my_CFRelease(&rank_assertion_cf);
10606 if (r == NULL) {
10607 fprintf(stderr, "IPv6RouteListCreateWithDictionary failed\n");
10608 exit(1);
10609 }
10610
10611 if (rank_assertion == kRankAssertionScoped) {
10612 rank_assertion = kRankAssertionNever;
10613 }
10614 rank = RankMake(test->rank, rank_assertion);
10615 if (log_it == kLogRouteEnabled
10616 && (S_IPMonitor_debug & kDebugFlag4) != 0) {
10617 CFStringRef descr;
10618
10619 descr = IPv6RouteListCopyDescription(r);
10620 SCPrint(TRUE, stdout, CFSTR("Adding %@\n"), descr);
10621 CFRelease(descr);
10622 }
10623 ret = IPv6RouteListAddRouteList(list, 1, r, rank);
10624 if (r != routes) {
10625 free(r);
10626 }
10627 CFRelease(dict);
10628 return (ret);
10629 }
10630
10631 static IPv6RouteListRef
10632 make_IPv6RouteList(IPv6ServiceContentsRef * test, Direction direction,
10633 LogRoute log_it)
10634 {
10635 IPv6RouteListRef ret = NULL;
10636 IPv6ServiceContentsRef * scan;
10637
10638 switch (direction) {
10639 case kDirectionBackwards:
10640 for (scan = test; *scan != NULL; scan++) {
10641 /* find the end of the list */
10642 }
10643 for (scan--; scan >= test; scan--) {
10644 ret = make_IPv6RouteList_for_test(ret, *scan, log_it);
10645 }
10646 break;
10647 default:
10648 case kDirectionForwards:
10649 for (scan = test; *scan != NULL; scan++) {
10650 ret = make_IPv6RouteList_for_test(ret, *scan, log_it);
10651 }
10652 break;
10653 }
10654 IPv6RouteListFinalize(ret);
10655 return (ret);
10656 }
10657
10658 #define EMPHASIS_CHARS "================="
10659
10660 /*
10661 * Function: routelist_build_test
10662 * Purpose:
10663 * Runs through the given set of routes first in the forward direction,
10664 * then again backwards. We should end up with exactly the same set of
10665 * routes at the end.
10666 */
10667 static boolean_t
10668 routelist_build_test(IPv6RouteTestRef test)
10669 {
10670 CFStringRef descr;
10671 boolean_t ret = FALSE;
10672 IPv6RouteListRef routes1;
10673 IPv6RouteListRef routes2;
10674
10675 printf("\n" EMPHASIS_CHARS "> RouteList Build '%s' <"
10676 EMPHASIS_CHARS "\n",
10677 test->name);
10678
10679 routes1 = make_IPv6RouteList(test->test, kDirectionForwards,
10680 kLogRouteEnabled);
10681 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
10682 if (routes1 != NULL) {
10683 descr = IPv6RouteListCopyDescription(routes1);
10684 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
10685 CFRelease(descr);
10686 }
10687 }
10688 routes2 = make_IPv6RouteList(test->test, kDirectionBackwards,
10689 kLogRouteEnabled);
10690 if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
10691 if (routes2 != NULL) {
10692 descr = IPv6RouteListCopyDescription(routes2);
10693 SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
10694 CFRelease(descr);
10695 }
10696 }
10697 if ((routes1 != NULL && routes2 == NULL)
10698 || (routes1 == NULL && routes2 != NULL)) {
10699 fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
10700 (routes1 != NULL) ? "not " : "",
10701 (routes2 != NULL) ? "not " : "");
10702 }
10703 else if (routes1 != NULL && routes2 != NULL) {
10704 /* check if they are different */
10705 if (routes1->count != routes2->count) {
10706 fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
10707 routes1->count, routes2->count);
10708 }
10709 else if (bcmp(routes1, routes2,
10710 IPv6RouteListComputeSize(routes1->count)) != 0) {
10711 fprintf(stderr, "routes1 and routes2 are different\n");
10712 }
10713 else {
10714 printf("routes1 and routes2 are the same\n");
10715 ret = TRUE;
10716 }
10717 }
10718 if (routes1 != NULL) {
10719 free(routes1);
10720 }
10721 if (routes2 != NULL) {
10722 free(routes2);
10723 }
10724 printf(EMPHASIS_CHARS "> RouteList Build '%s': %s <"
10725 EMPHASIS_CHARS "\n",
10726 test->name, ret ? "PASSED" : "FAILED");
10727 return (ret);
10728 }
10729
10730 static void
10731 apply_test(IPv6RouteTestRef old_test, IPv6RouteTestRef new_test)
10732 {
10733 IPv6RouteListRef new_routes;
10734 IPv6RouteListRef old_routes;
10735
10736 printf("\n" EMPHASIS_CHARS "> Apply '%s', '%s' Begin <"
10737 EMPHASIS_CHARS "\n",
10738 old_test->name, new_test->name);
10739
10740 old_routes = make_IPv6RouteList(old_test->test, kDirectionForwards,
10741 kLogRouteDisabled);
10742 new_routes = make_IPv6RouteList(new_test->test, kDirectionForwards,
10743 kLogRouteDisabled);
10744 if (old_routes == NULL) {
10745 printf("No Old Routes\n");
10746 }
10747 else {
10748 printf("Old routes ('%s') = ", old_test->name);
10749 IPv6RouteListPrint(old_routes);
10750 }
10751
10752 /* apply the old routes */
10753 IPv6RouteListApply(NULL, old_routes, -1);
10754 if (new_routes == NULL) {
10755 printf("No New Routes\n");
10756 }
10757 else {
10758 printf("New Routes ('%s') = ", new_test->name);
10759 IPv6RouteListPrint(new_routes);
10760 }
10761
10762 /* apply the new routes */
10763 IPv6RouteListApply(old_routes, new_routes, -1);
10764 if (old_routes != NULL) {
10765 free(old_routes);
10766 }
10767 if (new_routes != NULL) {
10768 free(new_routes);
10769 }
10770 printf(EMPHASIS_CHARS "> Apply '%s', '%s' End <"
10771 EMPHASIS_CHARS "\n",
10772 old_test->name, new_test->name);
10773 return;
10774 }
10775
10776 int
10777 main(int argc, char **argv)
10778 {
10779 IPv6RouteTestRef * test;
10780
10781 _sc_log = kSCLogDestinationFile;
10782 _sc_verbose = (argc > 1) ? TRUE : FALSE;
10783 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
10784 if (argc > 1) {
10785 S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
10786 }
10787 for (test = ipv6_tests; *test != NULL; test++) {
10788 if (!routelist_build_test(*test)) {
10789 fprintf(stderr, "%s failed\n", (*test)->name);
10790 exit(1);
10791 }
10792 }
10793 for (test = ipv6_tests; *test != NULL; test++) {
10794 IPv6RouteTestRef * test2;
10795
10796 for (test2 = test + 1; *test2 != NULL; test2++) {
10797 apply_test(*test, *test2);
10798 apply_test(*test2, *test);
10799 }
10800 }
10801 test_complete();
10802 exit(0);
10803 return (0);
10804 }
10805
10806 #endif /* TEST_IPV6_ROUTELIST */
10807
10808 #ifdef TEST_DNS_ORDER
10809
10810 #define kProtocolFlagsIPv4v6 (kProtocolFlagsIPv4 | kProtocolFlagsIPv6)
10811
10812 #define V4_ADDR_LOOP CFSTR("127.0.0.1")
10813 #define V4_ADDR_1 CFSTR("192.168.1.1")
10814 #define V4_ADDR_2 CFSTR("192.168.1.2")
10815 #define V4_ADDR_3 CFSTR("8.8.8.8")
10816 #define V4_ADDR_4 CFSTR("8.8.4.4")
10817
10818 #define V6_ADDR_LOOP CFSTR("::1")
10819 #define V6_ADDR_1 CFSTR("fe80::0123:4567:89ab:cdef%en0")
10820 #define V6_ADDR_2 CFSTR("fd00::2acf:e9ff:fe14:8c59")
10821 #define V6_ADDR_3 CFSTR("2001:4860:4860::8888")
10822
10823 typedef struct {
10824 const char * name;
10825 const ProtocolFlags flags;
10826 const CFStringRef server_addrs[];
10827 } DNSOrderTest, * DNSOrderTestRef;
10828
10829 static DNSOrderTest test0a = {
10830 "test0a",
10831 kProtocolFlagsIPv4,
10832 {
10833 V4_ADDR_1, V4_ADDR_2, V4_ADDR_3, V4_ADDR_4, NULL
10834 }
10835 };
10836
10837 static DNSOrderTest test0b = {
10838 "test0b",
10839 kProtocolFlagsIPv6,
10840 {
10841 V6_ADDR_1, V6_ADDR_2, V6_ADDR_3, NULL
10842 }
10843 };
10844
10845 static DNSOrderTest test1a = {
10846 "test1a",
10847 kProtocolFlagsIPv4v6,
10848 {
10849 V4_ADDR_1, V6_ADDR_1, NULL
10850 }
10851 };
10852
10853 static DNSOrderTest test2a = {
10854 "test2a",
10855 kProtocolFlagsIPv4v6,
10856 {
10857 V4_ADDR_1, V6_ADDR_2, NULL
10858 }
10859 };
10860
10861 static DNSOrderTest test3a = {
10862 "test3a",
10863 kProtocolFlagsIPv4v6,
10864 {
10865 V4_ADDR_1, V6_ADDR_3, NULL
10866 }
10867 };
10868
10869 static DNSOrderTest test1b = {
10870 "test1b",
10871 kProtocolFlagsIPv4v6,
10872 {
10873 V4_ADDR_3, V6_ADDR_1, NULL
10874 }
10875 };
10876
10877 static DNSOrderTest test2b = {
10878 "test2b",
10879 kProtocolFlagsIPv4v6,
10880 {
10881 V4_ADDR_3, V6_ADDR_2, NULL
10882 }
10883 };
10884
10885 static DNSOrderTest test3b = {
10886 "test3b",
10887 kProtocolFlagsIPv4v6,
10888 {
10889 V4_ADDR_3, V6_ADDR_3, NULL
10890 }
10891 };
10892
10893 static DNSOrderTest test1c = {
10894 "test1c",
10895 kProtocolFlagsIPv4v6,
10896 {
10897 V6_ADDR_1, V4_ADDR_1, NULL
10898 }
10899 };
10900
10901 static DNSOrderTest test2c = {
10902 "test2c",
10903 kProtocolFlagsIPv4v6,
10904 {
10905 V6_ADDR_2, V4_ADDR_1, NULL
10906 }
10907 };
10908
10909 static DNSOrderTest test3c = {
10910 "test3c",
10911 kProtocolFlagsIPv4v6,
10912 {
10913 V6_ADDR_3, V4_ADDR_1, NULL
10914 }
10915 };
10916
10917 static DNSOrderTest test1d = {
10918 "test1d",
10919 kProtocolFlagsIPv4v6,
10920 {
10921 V6_ADDR_1, V4_ADDR_3, NULL
10922 }
10923 };
10924
10925 static DNSOrderTest test2d = {
10926 "test2d",
10927 kProtocolFlagsIPv4v6,
10928 {
10929 V6_ADDR_2, V4_ADDR_3, NULL
10930 }
10931 };
10932
10933 static DNSOrderTest test3d = {
10934 "test3d",
10935 kProtocolFlagsIPv4v6,
10936 {
10937 V6_ADDR_3, V4_ADDR_3, NULL
10938 }
10939 };
10940
10941 static DNSOrderTest test4 = {
10942 "test4",
10943 kProtocolFlagsIPv4v6,
10944 {
10945 V4_ADDR_LOOP, V4_ADDR_3, V6_ADDR_2, NULL
10946 }
10947 };
10948
10949 static DNSOrderTest test5 = {
10950 "test5",
10951 kProtocolFlagsIPv4v6,
10952 {
10953 V4_ADDR_3, V6_ADDR_LOOP, V6_ADDR_2, NULL
10954 }
10955 };
10956
10957 static DNSOrderTest test6 = {
10958 "test6",
10959 kProtocolFlagsIPv4v6,
10960 {
10961 V4_ADDR_1, V4_ADDR_2, V4_ADDR_3, V4_ADDR_4, V6_ADDR_1, V6_ADDR_2, V6_ADDR_3, NULL
10962 }
10963 };
10964
10965 static DNSOrderTest test7 = {
10966 "test7",
10967 kProtocolFlagsIPv4v6,
10968 {
10969 V4_ADDR_1, V6_ADDR_1, V4_ADDR_3, V6_ADDR_2, NULL
10970 }
10971 };
10972
10973 static DNSOrderTestRef dns_order_tests[] = {
10974 &test0a, &test0b,
10975 &test1a, &test2a, &test3a,
10976 &test1b, &test2b, &test3b,
10977 &test1c, &test2c, &test3c,
10978 &test1d, &test2d, &test3d,
10979 &test4,
10980 &test5,
10981 &test6,
10982 &test7,
10983 NULL
10984 };
10985
10986 #define EMPHASIS_CHARS "================="
10987
10988 static void
10989 apply_order(CFArrayRef servers, ProtocolFlags flags)
10990 {
10991 CFArrayRef ordered_servers;
10992
10993 ordered_servers = order_dns_servers(servers, flags);
10994 if (ordered_servers != NULL) {
10995 SCPrint(TRUE, stdout, CFSTR("After :\n%@\n"), ordered_servers);
10996 CFRelease(ordered_servers);
10997 } else {
10998 printf("FAIL: No ordered servers\n");
10999 }
11000
11001 return;
11002 }
11003
11004 static void
11005 apply_test(DNSOrderTestRef test)
11006 {
11007 CFMutableArrayRef servers;
11008
11009 printf("\n" EMPHASIS_CHARS "> '%s' Begin <" EMPHASIS_CHARS "\n\n", test->name);
11010
11011 servers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
11012 for (int i = 0; test->server_addrs[i] != NULL; i++) {
11013 CFStringRef server_addr = test->server_addrs[i];
11014
11015 CFArrayAppendValue(servers, server_addr);
11016 }
11017
11018 SCPrint(TRUE, stdout, CFSTR("Before :\n%@\n"), servers);
11019
11020 apply_order(servers, test->flags);
11021
11022 CFRelease(servers);
11023
11024 return;
11025 }
11026
11027 int
11028 main(int argc, char **argv)
11029 {
11030
11031 _sc_log = kSCLogDestinationFile;
11032 _sc_verbose = (argc > 1) ? TRUE : FALSE;
11033 S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
11034 if (argc > 1) {
11035 S_IPMonitor_debug = (uint32)strtoul(argv[1], NULL, 0);
11036 }
11037
11038 for (DNSOrderTestRef * test = dns_order_tests; *test != NULL; test++) {
11039 apply_test(*test);
11040 }
11041 test_complete();
11042 exit(0);
11043 return (0);
11044 }
11045
11046 #endif /* TEST_DNS_ORDER */