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