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