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