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