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