1/*
2 * Copyright (c) 2000-2013 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
71#include <stdlib.h>
72#include <unistd.h>
73#include <string.h>
74#include <stdio.h>
75#include <sys/fcntl.h>
76#include <sys/ioctl.h>
77#include <sys/types.h>
78#include <sys/socket.h>
79#include <net/route.h>
80#include <net/if.h>
81#include <net/if_dl.h>
82#include <netinet/in.h>
83#include <netinet/icmp6.h>
84#include <netinet6/in6_var.h>
85#include <netinet6/nd6.h>
86#include <arpa/inet.h>
87#include <sys/sysctl.h>
88#include <limits.h>
89#include <notify.h>
90#include <mach/mach_time.h>
91#include <dispatch/dispatch.h>
92#include <CommonCrypto/CommonDigest.h>
93
94#include <SystemConfiguration/SystemConfiguration.h>
95#include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h>
96#include <SystemConfiguration/SCValidation.h>
97#include <SystemConfiguration/scprefs_observer.h>
98#include <SystemConfiguration/SCPrivate.h>	/* for SCLog() */
99#include "SCNetworkReachabilityInternal.h"
100#include "SCNetworkSignaturePrivate.h"
101#include <dnsinfo.h>
102#include "dnsinfo_server.h"
103
104#if	defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS)
105#include <ppp/PPPControllerPriv.h>
106#endif	// !defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS)
107
108#include <dns_sd.h>
109#ifndef	kDNSServiceCompMulticastDNS
110#define	kDNSServiceCompMulticastDNS	"MulticastDNS"
111#endif
112#ifndef	kDNSServiceCompPrivateDNS
113#define	kDNSServiceCompPrivateDNS	"PrivateDNS"
114#endif
115#include <network_information.h>
116#include "network_information_priv.h"
117#include "network_information_server.h"
118#include <ppp/ppp_msg.h>
119
120enum {
121    kProtocolFlagsNone		= 0x0,
122    kProtocolFlagsIPv4		= 0x1,
123    kProtocolFlagsIPv6		= 0x2
124};
125typedef uint8_t	ProtocolFlags;
126
127enum {
128    kDebugFlag1		= 0x00000001,
129    kDebugFlag2		= 0x00000002,
130    kDebugFlag4		= 0x00000004,
131    kDebugFlag8		= 0x00000008,
132    kDebugFlagDefault	= kDebugFlag1,
133    kDebugFlagAll	= 0xffffffff
134};
135
136#ifdef TEST_IPV4_ROUTELIST
137#define ROUTELIST_DEBUG(a, f) { if ((S_IPMonitor_debug & (f)) != 0)  printf a ;}
138#else
139#define ROUTELIST_DEBUG(a, f)
140#endif
141
142#if	!TARGET_IPHONE_SIMULATOR
143#include "set-hostname.h"
144#endif	/* !TARGET_IPHONE_SIMULATOR */
145
146#include "dns-configuration.h"
147#include "proxy-configuration.h"
148
149#if	!TARGET_OS_IPHONE
150#include "smb-configuration.h"
151#endif	/* !TARGET_OS_IPHONE */
152
153/*
154 * Property: kIPIsCoupled
155 * Purpose:
156 *   Used to indicate that the IPv4 and IPv6 services are coupled.
157 *   Neither the IPv4 part nor the IPv6 part of a coupled service
158 *   may become primary if IPv4 or IPv6 is primary for another interface.
159 *
160 *   For example, if the service over en3 is "coupled" and has IPv6,
161 *   and en0 is primary for just IPv4, IPv6 over en3 is not eligible
162 *   to become primary for IPv6.
163 */
164#define kIPIsCoupled	CFSTR("IPIsCoupled")
165
166#define PPP_PREFIX	"ppp"
167
168#define IP_FORMAT	"%d.%d.%d.%d"
169#define IP_CH(ip)	((u_char *)(ip))
170#define IP_LIST(ip)	IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
171
172#include "ip_plugin.h"
173#if	((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
174static SCLoggerRef	S_IPMonitor_logger;
175#endif	// ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
176
177static boolean_t	S_bundle_logging_verbose;
178
179/*
180 * IPv4 Route management
181 */
182
183typedef uint32_t 	RouteFlags;
184
185enum {
186    kRouteIsDirectToInterfaceFlag	= 0x00000001,
187    kRouteIsNotSubnetLocalFlag		= 0x00000002,
188    kRouteIsScopedFlag			= 0x00000004,
189    kRouteIsNULLFlag			= 0x00000008
190};
191
192typedef struct {
193    struct in_addr	dest;
194    struct in_addr	mask;
195    struct in_addr	gateway;
196    char		ifname[IFNAMSIZ];
197    unsigned int	ifindex;
198    struct in_addr	ifa;
199    Rank		rank;
200    RouteFlags		flags;
201} IPv4Route, *IPv4RouteRef;
202
203typedef struct {
204    int			count;
205    int			size;
206    boolean_t		exclude_from_nwi;
207    IPv4Route		list[1];	/* variable length */
208} IPv4RouteList, *IPv4RouteListRef;
209
210enum {
211    kIPv4RouteListAddRouteCommand,
212    kIPv4RouteListRemoveRouteCommand
213};
214
215/*
216 * Election Information
217 * - information about the current best services
218 */
219typedef struct Candidate {
220    CFStringRef		serviceID;
221    CFStringRef		if_name;
222    union {
223	struct in_addr	v4;
224	struct in6_addr	v6;
225    } addr;
226    Rank				rank;
227    boolean_t				ip_is_coupled;
228    SCNetworkReachabilityFlags		reachability_flags;
229    union {
230	struct sockaddr_in vpn_server_addr4;
231	struct sockaddr_in6 vpn_server_addr6;
232    } vpn_server_addr;
233    CFStringRef				signature;
234} Candidate, * CandidateRef;
235
236typedef struct ElectionResults {
237    int			count;
238    int			size;
239    Candidate		candidates[1];
240} ElectionResults, * ElectionResultsRef;
241
242static __inline__ unsigned int
243ElectionResultsComputeSize(unsigned int n)
244{
245    return (offsetof(ElectionResults, candidates[n]));
246}
247
248/*
249 * Type: Rank
250 * Purpose:
251 *   A 32-bit value to encode the relative rank of a service.
252 *
253 *   The top 8 bits are used to hold the rank assertion (first, last
254 *   never, default).
255 *
256 *   The bottom 24 bits are used to store the service index (i.e. the
257 *   position within the service order array).
258 */
259#define RANK_ASSERTION_MAKE(r)		((Rank)(r) << 24)
260#define kRankAssertionFirst		RANK_ASSERTION_MAKE(0)
261#define kRankAssertionDefault		RANK_ASSERTION_MAKE(1)
262#define kRankAssertionLast		RANK_ASSERTION_MAKE(2)
263#define kRankAssertionNever		RANK_ASSERTION_MAKE(3)
264#define kRankAssertionMask		RANK_ASSERTION_MAKE(0xff)
265#define RANK_ASSERTION_MASK(r)		((Rank)(r) & kRankAssertionMask)
266
267#define RANK_INDEX_MAKE(r)		((Rank)(r))
268#define kRankIndexMask			RANK_INDEX_MAKE(0xffffff)
269#define RANK_INDEX_MASK(r)		((Rank)(r) & kRankIndexMask)
270
271static __inline__ Rank
272RankMake(uint32_t service_index, Rank primary_rank)
273{
274    return (RANK_INDEX_MASK(service_index) | RANK_ASSERTION_MASK(primary_rank));
275}
276
277static __inline__ Rank
278PrimaryRankGetRankAssertion(CFStringRef primaryRank)
279{
280    if (CFEqual(primaryRank, kSCValNetServicePrimaryRankNever)) {
281	return kRankAssertionNever;
282    } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankFirst)) {
283	return kRankAssertionFirst;
284    } else if (CFEqual(primaryRank, kSCValNetServicePrimaryRankLast)) {
285	return kRankAssertionLast;
286    }
287    return kRankAssertionDefault;
288}
289
290typedef uint32_t	IPv4RouteListApplyCommand;
291
292typedef void IPv4RouteListApplyCallBackFunc(IPv4RouteListApplyCommand cmd,
293					    IPv4RouteRef route, void * arg);
294typedef IPv4RouteListApplyCallBackFunc * IPv4RouteListApplyCallBackFuncPtr;
295
296/* SCDynamicStore session */
297static SCDynamicStoreRef	S_session = NULL;
298
299/* debug output flags */
300static uint32_t			S_IPMonitor_debug = 0;
301static Boolean			S_IPMonitor_verbose = FALSE;
302
303/* are we netbooted?  If so, don't touch the default route */
304static boolean_t		S_netboot = FALSE;
305
306/* is scoped routing enabled? */
307#ifdef RTF_IFSCOPE
308static boolean_t		S_scopedroute = FALSE;
309static boolean_t		S_scopedroute_v6 = FALSE;
310#endif /* RTF_IFSCOPE */
311
312/* dictionary to hold per-service state: key is the serviceID */
313static CFMutableDictionaryRef	S_service_state_dict = NULL;
314static CFMutableDictionaryRef	S_ipv4_service_rank_dict = NULL;
315static CFMutableDictionaryRef	S_ipv6_service_rank_dict = NULL;
316
317/* dictionary to hold per-interface rank information */
318static CFMutableDictionaryRef	S_if_rank_dict = NULL;
319
320/* if set, a PPP interface overrides the primary */
321static boolean_t		S_ppp_override_primary = FALSE;
322
323/* the current primary serviceID's */
324static CFStringRef		S_primary_ipv4 = NULL;
325static CFStringRef		S_primary_ipv6 = NULL;
326static CFStringRef		S_primary_dns = NULL;
327static CFStringRef		S_primary_proxies = NULL;
328
329/* the current election results */
330static ElectionResultsRef	S_ipv4_results;
331static ElectionResultsRef	S_ipv6_results;
332
333static CFStringRef		S_state_global_ipv4 = NULL;
334static CFStringRef		S_state_global_ipv6 = NULL;
335static CFStringRef		S_state_global_dns = NULL;
336static CFStringRef		S_state_global_proxies = NULL;
337static CFStringRef		S_state_service_prefix = NULL;
338static CFStringRef		S_setup_global_ipv4 = NULL;
339static CFStringRef		S_setup_service_prefix = NULL;
340
341static CFStringRef		S_multicast_resolvers = NULL;
342static CFStringRef		S_private_resolvers = NULL;
343
344#if	!TARGET_IPHONE_SIMULATOR
345static IPv4RouteListRef		S_ipv4_routelist = NULL;
346#endif	/* !TARGET_IPHONE_SIMULATOR */
347
348static const struct in_addr	S_ip_zeros = { 0 };
349static const struct in6_addr	S_ip6_zeros = IN6ADDR_ANY_INIT;
350
351static boolean_t		S_append_state = FALSE;
352
353static CFDictionaryRef		S_dns_dict = NULL;
354
355static Boolean			S_dnsinfo_synced = TRUE;
356
357static nwi_state_t		S_nwi_state = NULL;
358static Boolean			S_nwi_synced = TRUE;
359
360static CFDictionaryRef		S_proxies_dict = NULL;
361
362// Note: access should be gated with __network_change_queue()
363static uint32_t			S_network_change_needed = 0;
364#define	NETWORK_CHANGE_NET	1<<0
365#define	NETWORK_CHANGE_DNS	1<<1
366#define	NETWORK_CHANGE_PROXY	1<<2
367#if	!TARGET_OS_IPHONE
368#define	NETWORK_CHANGE_SMB	1<<3
369#endif	/* !TARGET_OS_IPHONE */
370static struct timeval		S_network_change_start;
371static Boolean			S_network_change_timeout = FALSE;
372static dispatch_source_t	S_network_change_timer = NULL;
373
374#if	!TARGET_OS_IPHONE
375static CFStringRef		S_primary_smb = NULL;
376static CFStringRef		S_state_global_smb = NULL;
377static CFDictionaryRef		S_smb_dict = NULL;
378#endif	/* !TARGET_OS_IPHONE */
379
380#if	!TARGET_OS_IPHONE
381#define VAR_RUN_RESOLV_CONF	"/var/run/resolv.conf"
382#endif	/* !TARGET_OS_IPHONE */
383
384#ifndef KERN_NETBOOT
385#define KERN_NETBOOT		40	/* int: are we netbooted? 1=yes,0=no */
386#endif //KERN_NETBOOT
387
388/**
389 ** entityType*, GetEntityChanges*
390 ** - definitions for the entity types we handle
391 **/
392enum {
393    kEntityTypeIPv4	= 0,
394    kEntityTypeIPv6,
395    kEntityTypeDNS,
396    kEntityTypeProxies,
397#if	!TARGET_OS_IPHONE
398    kEntityTypeSMB,
399#endif	/* !TARGET_OS_IPHONE */
400    ENTITY_TYPES_COUNT,
401    kEntityTypeVPNStatus,
402    kEntityTypeServiceOptions	= 31
403};
404typedef uint32_t	EntityType;
405
406static const CFStringRef *entityTypeNames[ENTITY_TYPES_COUNT] = {
407    &kSCEntNetIPv4,	/* 0 */
408    &kSCEntNetIPv6,	/* 1 */
409    &kSCEntNetDNS,	/* 2 */
410    &kSCEntNetProxies,	/* 3 */
411#if	!TARGET_OS_IPHONE
412    &kSCEntNetSMB,	/* 4 */
413#endif	/* !TARGET_OS_IPHONE */
414};
415
416static Boolean
417S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value);
418
419static __inline__ char
420ipvx_char(int af)
421{
422    return ((af == AF_INET) ? '4' : '6');
423}
424
425static __inline__ char
426ipvx_other_char(int af)
427{
428    return ((af == AF_INET) ? '6' : '4');
429}
430
431static IPv4RouteListRef
432ipv4_dict_get_routelist(CFDictionaryRef ipv4_dict)
433{
434    CFDataRef 		routes;
435    IPv4RouteListRef 	routes_list = NULL;
436
437    if (isA_CFDictionary(ipv4_dict) == NULL) {
438	return (NULL);
439    }
440
441    routes = CFDictionaryGetValue(ipv4_dict, kIPv4DictRoutes);
442
443    if (routes != NULL) {
444	routes_list = (IPv4RouteListRef)(void*)CFDataGetBytePtr(routes);
445    }
446    return (routes_list);
447}
448
449static CFStringRef
450ipv4_dict_get_ifname(CFDictionaryRef ipv4_dict)
451{
452    CFDictionaryRef ipv4_service_dict = NULL;
453
454    if (isA_CFDictionary(ipv4_dict) == NULL) {
455	return (NULL);
456    }
457
458    ipv4_service_dict = CFDictionaryGetValue(ipv4_dict,
459					     kIPv4DictService);
460
461    if (isA_CFDictionary(ipv4_service_dict) == NULL) {
462	return NULL;
463    }
464
465    return CFDictionaryGetValue(ipv4_service_dict, kSCPropInterfaceName);
466}
467
468typedef boolean_t GetEntityChangesFunc(CFStringRef serviceID,
469				       CFDictionaryRef state_dict,
470				       CFDictionaryRef setup_dict,
471				       CFDictionaryRef info);
472typedef GetEntityChangesFunc * GetEntityChangesFuncRef;
473
474static GetEntityChangesFunc get_ipv4_changes;
475static GetEntityChangesFunc get_ipv6_changes;
476static GetEntityChangesFunc get_dns_changes;
477static GetEntityChangesFunc get_proxies_changes;
478#if	!TARGET_OS_IPHONE
479static GetEntityChangesFunc get_smb_changes;
480#endif	/* !TARGET_OS_IPHONE */
481
482static void
483my_CFRelease(void * t);
484
485static void
486my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new);
487
488static void
489my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key);
490
491static const GetEntityChangesFuncRef entityChangeFunc[ENTITY_TYPES_COUNT] = {
492    get_ipv4_changes,	/* 0 */
493    get_ipv6_changes,	/* 1 */
494    get_dns_changes,	/* 2 */
495    get_proxies_changes,/* 3 */
496#if	!TARGET_OS_IPHONE
497    get_smb_changes,	/* 4 */
498#endif	/* !TARGET_OS_IPHONE */
499};
500
501/**
502 ** keyChangeList
503 ** - mechanism to do an atomic update of the SCDynamicStore
504 **   when the content needs to be changed across multiple functions
505 **/
506typedef struct {
507    CFMutableArrayRef		notify;
508    CFMutableArrayRef		remove;
509    CFMutableDictionaryRef	set;
510} keyChangeList, * keyChangeListRef;
511
512static CFStringRef
513my_CFStringCopyComponent(CFStringRef path, CFStringRef separator,
514			 CFIndex component_index)
515{
516    CFArrayRef			arr;
517    CFStringRef			component = NULL;
518
519    arr = CFStringCreateArrayBySeparatingStrings(NULL, path, separator);
520    if (arr == NULL) {
521	goto done;
522    }
523    if (CFArrayGetCount(arr) <= component_index) {
524	goto done;
525    }
526    component = CFRetain(CFArrayGetValueAtIndex(arr, component_index));
527
528 done:
529    my_CFRelease(&arr);
530    return (component);
531}
532
533static void
534keyChangeListInit(keyChangeListRef keys)
535{
536    keys->notify = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
537    keys->remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
538    keys->set = CFDictionaryCreateMutable(NULL, 0,
539					  &kCFTypeDictionaryKeyCallBacks,
540					  &kCFTypeDictionaryValueCallBacks);
541    return;
542}
543
544static void
545keyChangeListFree(keyChangeListRef keys)
546{
547    my_CFRelease(&keys->notify);
548    my_CFRelease(&keys->remove);
549    my_CFRelease(&keys->set);
550    return;
551}
552
553static Boolean
554keyChangeListActive(keyChangeListRef keys)
555{
556    return ((CFDictionaryGetCount(keys->set) > 0) ||
557	    (CFArrayGetCount(keys->remove) > 0) ||
558	    (CFArrayGetCount(keys->notify) > 0));
559}
560
561static void
562keyChangeListNotifyKey(keyChangeListRef keys, CFStringRef key)
563{
564    my_CFArrayAppendUniqueValue(keys->notify, key);
565    return;
566}
567
568static void
569keyChangeListRemoveValue(keyChangeListRef keys, CFStringRef key)
570{
571    my_CFArrayAppendUniqueValue(keys->remove, key);
572    CFDictionaryRemoveValue(keys->set, key);
573    return;
574}
575
576static void
577keyChangeListSetValue(keyChangeListRef keys, CFStringRef key, CFTypeRef value)
578{
579    my_CFArrayRemoveValue(keys->remove, key);
580    CFDictionarySetValue(keys->set, key, value);
581    return;
582}
583
584static void
585keyChangeListApplyToStore(keyChangeListRef keys, SCDynamicStoreRef session)
586{
587    CFArrayRef		notify = keys->notify;
588    CFArrayRef		remove = keys->remove;
589    CFDictionaryRef	set = keys->set;
590
591    if (CFArrayGetCount(notify) == 0) {
592	notify = NULL;
593    }
594    if (CFArrayGetCount(remove) == 0) {
595	remove = NULL;
596    }
597    if (CFDictionaryGetCount(set) == 0) {
598	set = NULL;
599    }
600    if (set == NULL && remove == NULL && notify == NULL) {
601	return;
602    }
603    if (S_IPMonitor_debug & kDebugFlag1) {
604	if (set != NULL) {
605	    my_log(LOG_DEBUG, "IPMonitor: Setting:\n%@", set);
606	}
607	if (remove != NULL) {
608	    my_log(LOG_DEBUG, "IPMonitor: Removing:\n%@", remove);
609	}
610	if (notify != NULL) {
611	    my_log(LOG_DEBUG, "IPMonitor: Notifying:\n%@", notify);
612	}
613    }
614    (void)SCDynamicStoreSetMultiple(session, set, remove, notify);
615
616    return;
617}
618
619static void
620S_nwi_ifstate_dump(nwi_ifstate_t ifstate, int i)
621{
622    const char *		addr_str;
623    void *			address;
624    char 			ntopbuf[INET6_ADDRSTRLEN];
625    char 			vpn_ntopbuf[INET6_ADDRSTRLEN];
626    const struct sockaddr * 	vpn_addr;
627
628    address = nwi_ifstate_get_address(ifstate);
629    addr_str = inet_ntop(ifstate->af, address, ntopbuf, sizeof(ntopbuf));
630    vpn_addr = nwi_ifstate_get_vpn_server(ifstate);
631    if (vpn_addr != NULL) {
632	_SC_sockaddr_to_string(nwi_ifstate_get_vpn_server(ifstate),
633			       vpn_ntopbuf,
634			       sizeof(vpn_ntopbuf));
635    }
636    my_log(LOG_DEBUG,
637	   "    [%d]: %s%s%s%s rank 0x%x iaddr: %s%s%s reachability_flags %u",
638	   i, ifstate->ifname,
639	   ifstate->diff_str != NULL ? ifstate->diff_str : "",
640	   (ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0
641	   ? " dns" : "",
642	   (ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0
643	   ? " never" : "",
644	   ifstate->rank,
645	   addr_str,
646	   (vpn_addr != NULL) ? " vpn_server_addr: " : "",
647	   (vpn_addr != NULL) ? vpn_ntopbuf : "",
648	   ifstate->reach_flags);
649    return;
650}
651
652static void
653S_nwi_state_dump(nwi_state_t state)
654{
655    int			i;
656    nwi_ifstate_t 	scan;
657
658    if (state == NULL) {
659	my_log(LOG_DEBUG, "nwi_state = <none>");
660	return;
661    }
662    my_log(LOG_DEBUG,
663	   "nwi_state = { "
664	   "gen = %llu size = %u #ipv4 = %u #ipv6 = %u "
665	   "reach_flags_v4 = %u reach_flags_v6 %u }",
666	   state->generation_count,
667	   state->size,
668	   state->ipv4_count,
669	   state->ipv6_count,
670	   nwi_state_get_reachability_flags(state, AF_INET),
671	   nwi_state_get_reachability_flags(state, AF_INET6));
672    if (state->ipv4_count) {
673	my_log(LOG_DEBUG, "IPv4:");
674	for (i = 0, scan = state->nwi_ifstates;
675	     i < state->ipv4_count; i++, scan++) {
676	    S_nwi_ifstate_dump(scan, i);
677	}
678    }
679    if (state->ipv6_count) {
680	my_log(LOG_DEBUG, "IPv6:");
681	for (i = 0, scan = state->nwi_ifstates + state->ipv6_start;
682	     i < state->ipv6_count; i++, scan++) {
683	    S_nwi_ifstate_dump(scan, i);
684	}
685    }
686    return;
687}
688
689static boolean_t
690S_is_network_boot()
691{
692    int mib[2];
693    size_t len;
694    int netboot = 0;
695
696    mib[0] = CTL_KERN;
697    mib[1] = KERN_NETBOOT;
698    len = sizeof(netboot);
699    sysctl(mib, 2, &netboot, &len, NULL, 0);
700    return (netboot);
701}
702
703#if	!TARGET_IPHONE_SIMULATOR
704static __inline__ int
705inet6_dgram_socket()
706{
707    return (socket(AF_INET6, SOCK_DGRAM, 0));
708}
709
710#ifdef SIOCDRADD_IN6
711static int
712siocdradd_in6(int s, int if_index, const struct in6_addr * addr, u_char flags)
713{
714    struct in6_defrouter	dr;
715    struct sockaddr_in6 *	sin6;
716
717    bzero(&dr, sizeof(dr));
718    sin6 = &dr.rtaddr;
719    sin6->sin6_len = sizeof(struct sockaddr_in6);
720    sin6->sin6_family = AF_INET6;
721    sin6->sin6_addr = *addr;
722    dr.flags = flags;
723    dr.if_index = if_index;
724    return (ioctl(s, SIOCDRADD_IN6, &dr));
725}
726
727static int
728siocdrdel_in6(int s, int if_index, const struct in6_addr * addr)
729{
730    struct in6_defrouter	dr;
731    struct sockaddr_in6 *	sin6;
732
733    bzero(&dr, sizeof(dr));
734    sin6 = &dr.rtaddr;
735    sin6->sin6_len = sizeof(struct sockaddr_in6);
736    sin6->sin6_family = AF_INET6;
737    sin6->sin6_addr = *addr;
738    dr.if_index = if_index;
739    return (ioctl(s, SIOCDRDEL_IN6, &dr));
740}
741#endif /* SIOCDRADD_IN6 */
742#endif	/* !TARGET_IPHONE_SIMULATOR */
743
744#ifdef	RTF_IFSCOPE
745static boolean_t
746S_is_scoped_routing_enabled()
747{
748    int	    scopedroute	= 0;
749    size_t  len		= sizeof(scopedroute);
750
751    if ((sysctlbyname("net.inet.ip.scopedroute",
752		      &scopedroute, &len,
753		      NULL, 0) == -1)
754	&& (errno != ENOENT)) {
755	my_log(LOG_ERR, "sysctlbyname() failed: %s", strerror(errno));
756    }
757    return (scopedroute);
758}
759
760static boolean_t
761S_is_scoped_v6_routing_enabled()
762{
763    int	    scopedroute_v6	= 0;
764    size_t  len			= sizeof(scopedroute_v6);
765
766    if ((sysctlbyname("net.inet6.ip6.scopedroute",
767		      &scopedroute_v6, &len,
768		      NULL, 0) == -1)
769	&& (errno != ENOENT)) {
770	my_log(LOG_ERR, "sysctlbyname() failed: %s", strerror(errno));
771    }
772    return (scopedroute_v6);
773}
774#endif /* RTF_IFSCOPE */
775
776static void
777my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new)
778{
779    CFIndex	n = CFArrayGetCount(arr);
780
781    if (CFArrayContainsValue(arr, CFRangeMake(0, n), new)) {
782	return;
783    }
784    CFArrayAppendValue(arr, new);
785    return;
786}
787
788static void
789my_CFArrayRemoveValue(CFMutableArrayRef arr, CFStringRef key)
790{
791    CFIndex	i;
792
793    i = CFArrayGetFirstIndexOfValue(arr,
794				    CFRangeMake(0, CFArrayGetCount(arr)),
795				    key);
796    if (i != kCFNotFound) {
797	CFArrayRemoveValueAtIndex(arr, i);
798    }
799    return;
800}
801
802static void
803my_CFRelease(void * t)
804{
805    void * * obj = (void * *)t;
806
807    if (obj && *obj) {
808	CFRelease(*obj);
809	*obj = NULL;
810    }
811    return;
812}
813
814static CFDictionaryRef
815my_CFDictionaryGetDictionary(CFDictionaryRef dict, CFStringRef key)
816{
817    if (isA_CFDictionary(dict) == NULL) {
818	return (NULL);
819    }
820    return (isA_CFDictionary(CFDictionaryGetValue(dict, key)));
821}
822
823static CFArrayRef
824my_CFDictionaryGetArray(CFDictionaryRef dict, CFStringRef key)
825{
826    if (isA_CFDictionary(dict) == NULL) {
827	return (NULL);
828    }
829    return (isA_CFArray(CFDictionaryGetValue(dict, key)));
830}
831
832static boolean_t
833cfstring_to_ipvx(int family, CFStringRef str, void * addr, int addr_size)
834{
835    char        buf[128];
836
837    if (isA_CFString(str) == NULL) {
838	goto done;
839    }
840
841    switch (family) {
842    case AF_INET:
843	if (addr_size < sizeof(struct in_addr)) {
844	    goto done;
845	}
846	break;
847    case AF_INET6:
848	if (addr_size < sizeof(struct in6_addr)) {
849	    goto done;
850	}
851	break;
852    default:
853	goto done;
854    }
855    (void)_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII);
856    if (inet_pton(family, buf, addr) == 1) {
857	return (TRUE);
858    }
859 done:
860    bzero(addr, addr_size);
861    return (FALSE);
862}
863
864__private_extern__
865boolean_t
866cfstring_to_ip(CFStringRef str, struct in_addr * ip_p)
867{
868    return (cfstring_to_ipvx(AF_INET, str, ip_p, sizeof(*ip_p)));
869}
870
871__private_extern__
872boolean_t
873cfstring_to_ip6(CFStringRef str, struct in6_addr * ip6_p)
874{
875    return (cfstring_to_ipvx(AF_INET6, str, ip6_p, sizeof(*ip6_p)));
876}
877
878static CF_RETURNS_RETAINED CFStringRef
879setup_service_key(CFStringRef serviceID, CFStringRef entity)
880{
881    return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
882							kSCDynamicStoreDomainSetup,
883							serviceID,
884							entity));
885}
886
887static CF_RETURNS_RETAINED CFStringRef
888state_service_key(CFStringRef serviceID, CFStringRef entity)
889{
890    return (SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
891							kSCDynamicStoreDomainState,
892							serviceID,
893							entity));
894}
895
896static CFDictionaryRef
897get_service_setup_entity(CFDictionaryRef services_info, CFStringRef serviceID,
898			 CFStringRef entity)
899{
900    CFStringRef		setup_key;
901    CFDictionaryRef	setup_dict;
902
903    setup_key = setup_service_key(serviceID, entity);
904    setup_dict = my_CFDictionaryGetDictionary(services_info, setup_key);
905    my_CFRelease(&setup_key);
906    return (setup_dict);
907}
908
909static CFDictionaryRef
910get_service_state_entity(CFDictionaryRef services_info, CFStringRef serviceID,
911			 CFStringRef entity)
912{
913    CFStringRef		state_key;
914    CFDictionaryRef	state_dict;
915
916    state_key = state_service_key(serviceID, entity);
917    state_dict = my_CFDictionaryGetDictionary(services_info, state_key);
918    my_CFRelease(&state_key);
919    return (state_dict);
920}
921
922static boolean_t
923dict_get_first_ip(CFDictionaryRef dict, CFStringRef prop, struct in_addr * ip_p)
924{
925    CFArrayRef		ip_list;
926
927    ip_list = CFDictionaryGetValue(dict, prop);
928    if (isA_CFArray(ip_list) != NULL
929	&& CFArrayGetCount(ip_list) > 0
930	&& cfstring_to_ip(CFArrayGetValueAtIndex(ip_list, 0), ip_p)) {
931	return (TRUE);
932    }
933    return (FALSE);
934}
935
936static boolean_t
937get_override_primary(CFDictionaryRef dict)
938{
939    CFTypeRef	override;
940
941    override = CFDictionaryGetValue(dict, kSCPropNetOverridePrimary);
942    if (isA_CFNumber(override) != NULL) {
943	int	val = 0;
944
945	CFNumberGetValue((CFNumberRef)override, kCFNumberIntType, &val);
946	if (val != 0) {
947	    return (TRUE);
948	}
949    }
950    else if (isA_CFBoolean(override) != NULL) {
951	if (CFBooleanGetValue(override)) {
952	    return (TRUE);
953	}
954    }
955    return (FALSE);
956}
957
958/**
959 ** IPv4Route*
960 **/
961
962static __inline__ struct in_addr
963subnet_addr(struct in_addr addr, struct in_addr mask)
964{
965    struct in_addr	net;
966
967    net.s_addr = addr.s_addr & mask.s_addr;
968    return (net);
969}
970
971static __inline__ int
972in_addr_cmp(struct in_addr a, struct in_addr b)
973{
974    return (uint32_cmp(ntohl(a.s_addr), ntohl(b.s_addr)));
975}
976
977static __inline__ int
978RouteFlagsCompare(RouteFlags a, RouteFlags b)
979{
980    return (uint32_cmp(a, b));
981}
982
983static void
984IPv4RouteCopyDescriptionWithString(IPv4RouteRef r, CFMutableStringRef str)
985{
986    Rank rank_assertion = RANK_ASSERTION_MASK(r->rank);
987
988    CFStringAppendFormat(str, NULL,
989			 CFSTR("Dest " IP_FORMAT
990			       " Mask " IP_FORMAT
991			       " Gate " IP_FORMAT
992			       " Ifp %s Ifa " IP_FORMAT),
993			 IP_LIST(&r->dest),
994			 IP_LIST(&r->mask),
995			 IP_LIST(&r->gateway),
996			 (r->ifname[0] != '\0') ? r->ifname : "<none>",
997			 IP_LIST(&r->ifa));
998    if ((r->flags & kRouteIsNULLFlag) != 0) {
999	CFStringAppend(str, CFSTR(" [null]"));
1000    }
1001    else {
1002	if ((r->flags & kRouteIsNotSubnetLocalFlag) != 0) {
1003	    CFStringAppend(str, CFSTR(" [non-local]"));
1004	}
1005	else if ((r->flags & kRouteIsDirectToInterfaceFlag) != 0) {
1006	    CFStringAppend(str, CFSTR(" [direct]"));
1007	}
1008	switch (rank_assertion) {
1009	case kRankAssertionFirst:
1010	    CFStringAppend(str, CFSTR(" [first]"));
1011	    break;
1012	case kRankAssertionLast:
1013	    CFStringAppend(str, CFSTR(" [last]"));
1014	    break;
1015	case kRankAssertionNever:
1016	    CFStringAppend(str, CFSTR(" [never]"));
1017	    break;
1018	default:
1019	    break;
1020	}
1021	if ((r->flags & kRouteIsScopedFlag) != 0) {
1022	    CFStringAppend(str, CFSTR(" [SCOPED]"));
1023	}
1024    }
1025    return;
1026}
1027
1028static CFStringRef
1029IPv4RouteCopyDescription(IPv4RouteRef r)
1030{
1031    CFMutableStringRef	str;
1032
1033    str = CFStringCreateMutable(NULL, 0);
1034    IPv4RouteCopyDescriptionWithString(r, str);
1035    return (str);
1036}
1037
1038static __inline__ void
1039IPv4RoutePrint(IPv4RouteRef route)
1040{
1041    CFStringRef	str = IPv4RouteCopyDescription(route);
1042
1043    SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
1044    CFRelease(str);
1045    return;
1046}
1047
1048static __inline__ void
1049IPv4RouteLog(int level, IPv4RouteRef route)
1050{
1051    CFStringRef	str = IPv4RouteCopyDescription(route);
1052
1053    my_log(level, "%@", str);
1054    CFRelease(str);
1055    return;
1056}
1057
1058static int
1059IPv4RouteCompare(IPv4RouteRef a, Rank a_rank,
1060		 IPv4RouteRef b, Rank b_rank, boolean_t * same_dest)
1061{
1062    int		cmp;
1063
1064    *same_dest = FALSE;
1065    cmp = in_addr_cmp(a->dest, b->dest);
1066    if (cmp == 0) {
1067	cmp = in_addr_cmp(a->mask, b->mask);
1068	if (cmp == 0) {
1069	    int		name_cmp = strcmp(a->ifname, b->ifname);
1070
1071	    if (name_cmp == 0) {
1072		cmp = 0;
1073	    }
1074	    else {
1075		*same_dest = TRUE;
1076		cmp = RankCompare(a_rank, b_rank);
1077		if (cmp == 0) {
1078		    cmp = name_cmp;
1079		}
1080	    }
1081	}
1082    }
1083    if ((S_IPMonitor_debug & kDebugFlag8) != 0) {
1084	CFStringRef	a_str;
1085	CFStringRef	b_str;
1086	char		ch;
1087
1088	if (cmp < 0) {
1089	    ch = '<';
1090	}
1091	else if (cmp == 0) {
1092	    ch = '=';
1093	}
1094	else {
1095	    ch = '>';
1096	}
1097	a_str = IPv4RouteCopyDescription(a);
1098	b_str = IPv4RouteCopyDescription(b);
1099	my_log(LOG_DEBUG, "%@ rank 0x%x %c %@ rank 0x%x",
1100	       a_str, a_rank, ch, b_str, b_rank);
1101	CFRelease(a_str);
1102	CFRelease(b_str);
1103    }
1104    return (cmp);
1105}
1106
1107static CFMutableStringRef
1108IPv4RouteListCopyDescription(IPv4RouteListRef routes)
1109{
1110    int			i;
1111    IPv4RouteRef	r;
1112    CFMutableStringRef	str;
1113
1114    str = CFStringCreateMutable(NULL, 0);
1115    CFStringAppendFormat(str, NULL, CFSTR("<IPv4RouteList[%d]> = {"),
1116			 routes->count);
1117    for (i = 0, r = routes->list; i < routes->count; i++, r++) {
1118	CFStringAppendFormat(str, NULL, CFSTR("\n%2d. "), i);
1119	IPv4RouteCopyDescriptionWithString(r, str);
1120    }
1121    CFStringAppend(str, CFSTR("\n}"));
1122    return (str);
1123}
1124
1125static __inline__ void
1126IPv4RouteListPrint(IPv4RouteListRef routes)
1127{
1128    CFStringRef	str = IPv4RouteListCopyDescription(routes);
1129
1130    SCPrint(TRUE, stdout, CFSTR("%@\n"), str);
1131    CFRelease(str);
1132    return;
1133}
1134
1135static __inline__ void
1136IPv4RouteListLog(int level, IPv4RouteListRef routes)
1137{
1138    CFStringRef	str = IPv4RouteListCopyDescription(routes);
1139
1140    my_log(level, "%@", str);
1141    CFRelease(str);
1142    return;
1143}
1144
1145static __inline__ unsigned int
1146IPv4RouteListComputeSize(unsigned int n)
1147{
1148    return (offsetof(IPv4RouteList, list[n]));
1149}
1150
1151#if	!TARGET_IPHONE_SIMULATOR
1152static IPv4RouteRef
1153IPv4RouteListFindRoute(IPv4RouteListRef routes, IPv4RouteRef route)
1154{
1155    int			i;
1156    IPv4RouteRef	scan_result = NULL;
1157    IPv4RouteRef	scan;
1158
1159    for (i = 0, scan = routes->list; i < routes->count; i++, scan++) {
1160	if ((scan->dest.s_addr == route->dest.s_addr)
1161	    && (scan->mask.s_addr == route->mask.s_addr)
1162	    && (strcmp(scan->ifname, route->ifname) == 0)
1163	    && (scan->ifa.s_addr == route->ifa.s_addr)
1164	    && (scan->gateway.s_addr == route->gateway.s_addr)
1165	    && (scan->flags == route->flags)) {
1166		scan_result = scan;
1167		break;
1168	}
1169    }
1170    return (scan_result);
1171}
1172
1173static void
1174IPv4RouteListApply(IPv4RouteListRef old_routes, IPv4RouteListRef new_routes,
1175		   IPv4RouteListApplyCallBackFuncPtr func, void * arg)
1176{
1177    int			i;
1178    IPv4RouteRef	scan;
1179
1180    if (old_routes == new_routes && old_routes == NULL) {
1181	/* both old and new are NULL, so there's nothing to do */
1182	return;
1183    }
1184    if (old_routes != NULL) {
1185	for (i = 0, scan = old_routes->list;
1186	     i < old_routes->count;
1187	     i++, scan++) {
1188	    IPv4RouteRef	new_route = NULL;
1189
1190	    if (new_routes != NULL) {
1191		new_route = IPv4RouteListFindRoute(new_routes, scan);
1192	    }
1193	    if (new_route == NULL) {
1194		if (func != NULL) {
1195		    (*func)(kIPv4RouteListRemoveRouteCommand, scan, arg);
1196		}
1197	    }
1198	}
1199    }
1200    if (new_routes != NULL) {
1201	for (i = 0, scan = new_routes->list;
1202	     i < new_routes->count;
1203	     i++, scan++) {
1204	    IPv4RouteRef	old_route = NULL;
1205
1206	    if (old_routes != NULL) {
1207		old_route = IPv4RouteListFindRoute(old_routes, scan);
1208	    }
1209	    if (old_route == NULL) {
1210		if (func != NULL) {
1211		    (*func)(kIPv4RouteListAddRouteCommand, scan, arg);
1212		}
1213	    }
1214	}
1215    }
1216    return;
1217}
1218#endif	/* !TARGET_IPHONE_SIMULATOR */
1219
1220/*
1221 * Function: IPv4RouteListAddRoute
1222 *
1223 * Purpose:
1224 *   Add the given IPv4Route to the list of routes, eliminating lower-ranked
1225 *   duplicates on the same interface, and marking any lower ranked duplicates
1226 *   on other interfaces with kRouteIsScopedFlag.
1227 *
1228 *   This routine assumes that if routes is not NULL, it is malloc'd memory.
1229 *
1230 * Returns:
1231 *   Route list updated with the given route, possibly a different pointer,
1232 *   due to using realloc'd memory.
1233 */
1234
1235enum {
1236    kScopeNone	= 0,
1237    kScopeThis	= 1,
1238    kScopeNext	= 2
1239};
1240
1241static IPv4RouteListRef
1242IPv4RouteListAddRoute(IPv4RouteListRef routes, int init_size,
1243		      IPv4RouteRef this_route, Rank this_rank)
1244{
1245    int			i;
1246    IPv4RouteRef	first_scan = NULL;
1247    int			scope_which = kScopeNone;
1248    IPv4RouteRef	scan;
1249    int			where = -1;
1250
1251    if (routes == NULL) {
1252	routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(init_size));
1253	bzero(routes, sizeof(*routes));
1254	routes->size = init_size;
1255	routes->count = 0;
1256    }
1257    for (i = 0, scan = routes->list; i < routes->count;
1258	 i++, scan++) {
1259	int		cmp;
1260	boolean_t	same_dest;
1261
1262	cmp = IPv4RouteCompare(this_route, this_rank, scan, scan->rank, &same_dest);
1263
1264	if (same_dest == TRUE && first_scan == NULL) {
1265	    first_scan = scan;
1266	}
1267
1268	if (cmp < 0) {
1269	    if (where == -1) {
1270		if (same_dest == TRUE
1271		    && (first_scan->flags & kRouteIsScopedFlag) == 0) {
1272		    if ((scan->flags & kRouteIsScopedFlag) != 0) {
1273			ROUTELIST_DEBUG(("Hit 1: set scope on self\n"),
1274					kDebugFlag8);
1275			scope_which = kScopeThis;
1276		    }
1277		    else {
1278			ROUTELIST_DEBUG(("Hit 2: set scope on next\n"),
1279					kDebugFlag8);
1280			scope_which = kScopeNext;
1281		    }
1282		}
1283		/* remember our insertion point, but keep going to find a dup */
1284		where = i;
1285	    }
1286	}
1287	else if (cmp == 0) {
1288	    /* exact match */
1289	    if (where != -1) {
1290		/* this route is a duplicate */
1291		ROUTELIST_DEBUG(("Hit 3: removing [%d]\n", i), kDebugFlag8);
1292		routes->count--;
1293		if (i == routes->count) {
1294		    /* last slot, decrementing gets rid of it */
1295		}
1296		else {
1297		    bcopy(routes->list + i + 1,
1298			  routes->list + i,
1299			  sizeof(routes->list[0]) * (routes->count - i));
1300		}
1301		break;
1302	    }
1303	    /* resolve conflict using rank */
1304	    if (this_rank < scan->rank) {
1305		boolean_t	is_scoped = FALSE;
1306
1307		if (scan->flags & kRouteIsScopedFlag) {
1308		    is_scoped = TRUE;
1309		}
1310		ROUTELIST_DEBUG(("Hit 4:replacing [%d] rank 0x%x < 0x%x\n",
1311				 i,
1312				 this_rank,
1313				 scan->rank), kDebugFlag8);
1314		*scan = *this_route;
1315		scan->rank = this_rank;
1316		if (is_scoped) {
1317		    /* preserve whether route was scoped */
1318		    ROUTELIST_DEBUG(("Hit 5: preserved scope\n"), kDebugFlag8);
1319		    scan->flags |= kRouteIsScopedFlag;
1320		}
1321	    }
1322	    /* we're done */
1323	    goto done;
1324	}
1325	else {
1326	    if (same_dest == TRUE) {
1327		if (scope_which == kScopeNone) {
1328		    ROUTELIST_DEBUG(("Hit 10: set scope on self\n"),
1329				    kDebugFlag8);
1330		    scope_which = kScopeThis;
1331		}
1332	    }
1333#ifdef TEST_IPV4_ROUTELIST
1334	    else if (where != -1) {
1335		/* not possible because we maintain a sorted list */
1336		ROUTELIST_DEBUG(("Hit 11: moved past routes - can't happen\n"),
1337				kDebugFlag8);
1338		break;
1339	    }
1340#endif /* TEST_IPV4_ROUTELIST */
1341	}
1342    }
1343    if (routes->size == routes->count) {
1344	int			how_many;
1345	IPv4RouteListRef	new_routes;
1346	int			old_size;
1347
1348	/* double the size */
1349	old_size = routes->size;
1350	how_many = old_size * 2;
1351	new_routes = (IPv4RouteListRef)
1352	    realloc(routes, IPv4RouteListComputeSize(how_many));
1353	if (new_routes == NULL) {
1354	    /* no memory */
1355	    goto done;
1356	}
1357	ROUTELIST_DEBUG(("increasing size from %d to %d\n", old_size,
1358			 how_many), kDebugFlag8);
1359	new_routes->size = how_many;
1360	routes = new_routes;
1361    }
1362    if (where == -1) {
1363	/* add it to the end */
1364	where = routes->count;
1365    }
1366    else {
1367	/* insert it at [where] */
1368	bcopy(routes->list + where,
1369	      routes->list + where + 1,
1370	      sizeof(routes->list[0]) * (routes->count - where));
1371    }
1372    /* copy the route */
1373    routes->list[where] = *this_route;
1374    routes->list[where].rank = this_rank;
1375    if (RANK_ASSERTION_MASK(this_rank) == kRankAssertionNever) {
1376	routes->list[where].flags |= kRouteIsScopedFlag;
1377    }
1378
1379    /* set the scope */
1380    switch (scope_which) {
1381    case kScopeThis:
1382	routes->list[where].flags |= kRouteIsScopedFlag;
1383	break;
1384    case kScopeNext:
1385	routes->list[where + 1].flags |= kRouteIsScopedFlag;
1386	break;
1387    default:
1388    case kScopeNone:
1389	break;
1390    }
1391    routes->count++;
1392 done:
1393    return (routes);
1394}
1395
1396/*
1397 * Function: IPv4RouteListAddRouteList
1398 *
1399 * Purpose:
1400 *   Invoke IPv4RouteListAddRoute for each route in the given list.
1401 *
1402 * Returns:
1403 *   See IPv4RouteListAddRoute for more information.
1404 */
1405static IPv4RouteListRef
1406IPv4RouteListAddRouteList(IPv4RouteListRef routes, int init_size,
1407			  IPv4RouteListRef service_routes, Rank rank)
1408{
1409    int			i;
1410    IPv4RouteRef	scan;
1411
1412    for (i = 0, scan = service_routes->list;
1413	 i < service_routes->count; i++, scan++) {
1414	routes = IPv4RouteListAddRoute(routes, init_size, scan, rank);
1415    }
1416    return (routes);
1417}
1418
1419static boolean_t
1420plist_get_cstring(CFDictionaryRef dict, CFStringRef prop_name,
1421		  char * buf, int buf_size)
1422{
1423    CFStringRef	val;
1424
1425    val = CFDictionaryGetValue(dict, prop_name);
1426    if (isA_CFString(val) == NULL) {
1427	return (FALSE);
1428    }
1429    if (CFStringGetCString(val, buf, buf_size, kCFStringEncodingASCII)
1430	== FALSE) {
1431	return (FALSE);
1432    }
1433    return (TRUE);
1434}
1435
1436/*
1437 * Function: IPv4RouteListCreateWithDictionary
1438 *
1439 * Purpose:
1440 *   Given the service ipv4 entity dictionary, generate the list of routes.
1441 *   Currently, this includes just the default route and subnet route,
1442 *   if the service has a subnet mask.
1443 *
1444 * Returns:
1445 *   If the passed in route_list is NULL or too small, this routine
1446 *   allocates malloc'd memory to hold the routes.
1447 */
1448static IPv4RouteListRef
1449IPv4RouteListCreateWithDictionary(IPv4RouteListRef routes,
1450				  CFDictionaryRef dict,
1451				  CFStringRef primaryRank)
1452{
1453    struct in_addr	addr = { 0 };
1454    boolean_t		exclude_from_nwi = FALSE;
1455    RouteFlags		flags = 0;
1456    unsigned int	ifindex;
1457    char		ifn[IFNAMSIZ];
1458    struct in_addr	mask = { 0 };
1459    int			n = 0;
1460    boolean_t		add_default = FALSE;
1461    boolean_t		add_subnet = FALSE;
1462    IPv4RouteRef	r;
1463    struct in_addr	subnet = { 0 };
1464    struct in_addr	router = { 0 };
1465    Rank		rank = kRankAssertionDefault;
1466
1467    if (dict == NULL) {
1468	return (NULL);
1469    }
1470    if (plist_get_cstring(dict, kSCPropInterfaceName, ifn, sizeof(ifn))
1471	== FALSE) {
1472	return (NULL);
1473    }
1474#ifdef TEST_IPV4_ROUTELIST
1475    ifindex = 0;
1476#else /* TEST_IPV4_ROUTELIST */
1477    ifindex = if_nametoindex(ifn);
1478    if (ifindex == 0) {
1479	/* interface doesn't exist */
1480	return (NULL);
1481    }
1482#endif /* TEST_IPV4_ROUTELIST */
1483    if (cfstring_to_ip(CFDictionaryGetValue(dict, kSCPropNetIPv4Router),
1484		       &router) == 0) {
1485	(void)dict_get_first_ip(dict, kSCPropNetIPv4DestAddresses, &router);
1486    }
1487    if (dict_get_first_ip(dict, kSCPropNetIPv4Addresses, &addr)
1488	&& dict_get_first_ip(dict, kSCPropNetIPv4SubnetMasks, &mask)) {
1489	/* subnet route */
1490	subnet = subnet_addr(addr, mask);
1491	/* ignore link-local subnets, let IPConfiguration handle them for now */
1492	if (ntohl(subnet.s_addr) != IN_LINKLOCALNETNUM) {
1493	    add_subnet = TRUE;
1494	    n++;
1495	} else if (router.s_addr == 0) {
1496	    exclude_from_nwi = TRUE;
1497	}
1498    }
1499    if (addr.s_addr == 0) {
1500	/* thanks for playing */
1501	return (NULL);
1502    }
1503    if (router.s_addr == 0) {
1504	/*
1505	 * If no router is configured, demote the rank. If there's already
1506	 * a rank assertion that indicates RankNever, use that, otherwise
1507	 * use RankLast.
1508	 */
1509	flags |= kRouteIsDirectToInterfaceFlag;
1510	if (primaryRank != NULL
1511	    && PrimaryRankGetRankAssertion(primaryRank) == kRankAssertionNever) {
1512	    rank = kRankAssertionNever;
1513	}
1514	else {
1515	    rank = kRankAssertionLast;
1516	}
1517    }
1518    else {
1519	/*
1520	 * If the router address is our address and the subnet mask is
1521	 * not 255.255.255.255, assume all routes are local to the interface.
1522	 */
1523	if (addr.s_addr == router.s_addr
1524	    && mask.s_addr != INADDR_BROADCAST) {
1525	    flags |= kRouteIsDirectToInterfaceFlag;
1526	}
1527	if (primaryRank != NULL) {
1528	    rank = PrimaryRankGetRankAssertion(primaryRank);
1529	} else if (get_override_primary(dict)) {
1530	    rank = kRankAssertionFirst;
1531	}
1532    }
1533
1534    if (S_dict_get_boolean(dict, kIsNULL, FALSE)) {
1535	exclude_from_nwi = TRUE;
1536	flags |= kRouteIsNULLFlag;
1537    }
1538
1539    if (rank == kRankAssertionNever) {
1540	flags |= kRouteIsScopedFlag;
1541    }
1542
1543    if (add_subnet && (flags & kRouteIsDirectToInterfaceFlag) == 0
1544	&& subnet.s_addr != subnet_addr(router, mask).s_addr) {
1545	flags |= kRouteIsNotSubnetLocalFlag;
1546    }
1547
1548    if (strncmp(ifn, "lo0", sizeof(ifn)) != 0) {
1549	add_default = TRUE;
1550	n++;
1551    }
1552
1553    if (routes == NULL || routes->size < n) {
1554	routes = (IPv4RouteListRef)malloc(IPv4RouteListComputeSize(n));
1555	routes->size = n;
1556    }
1557    bzero(routes, IPv4RouteListComputeSize(n));
1558    routes->count = n;
1559    routes->exclude_from_nwi = exclude_from_nwi;
1560
1561    /* start at the beginning */
1562    r = routes->list;
1563
1564    if (add_default) {
1565	/* add the default route */
1566	r->ifindex = ifindex;
1567	strlcpy(r->ifname, ifn, sizeof(r->ifname));
1568	r->ifa = addr;
1569	r->flags = flags;
1570	if ((flags & kRouteIsDirectToInterfaceFlag) == 0) {
1571	    r->gateway = router;
1572	}
1573	else {
1574	    r->gateway = addr;
1575	}
1576	r->rank = rank;
1577	r++;
1578    }
1579
1580    /* add the subnet route */
1581    if (add_subnet) {
1582	if ((flags & kRouteIsNULLFlag) != 0) {
1583	    r->flags |= kRouteIsNULLFlag;
1584	}
1585	r->ifindex = ifindex;
1586	r->gateway = addr;
1587	r->dest = subnet;
1588	r->mask = mask;
1589	strlcpy(r->ifname, ifn, sizeof(r->ifname));
1590	r->ifa = addr;
1591	r->rank = rank;
1592    }
1593    return (routes);
1594}
1595
1596/*
1597 * Function: parse_component
1598 * Purpose:
1599 *   Given a string 'key' and a string prefix 'prefix',
1600 *   return the next component in the slash '/' separated
1601 *   key.
1602 *
1603 * Examples:
1604 * 1. key = "a/b/c" prefix = "a/"
1605 *    returns "b"
1606 * 2. key = "a/b/c" prefix = "a/b/"
1607 *    returns "c"
1608 */
1609static CF_RETURNS_RETAINED CFStringRef
1610parse_component(CFStringRef key, CFStringRef prefix)
1611{
1612    CFMutableStringRef	comp;
1613    CFRange		range;
1614
1615    if (CFStringHasPrefix(key, prefix) == FALSE) {
1616	return (NULL);
1617    }
1618    comp = CFStringCreateMutableCopy(NULL, 0, key);
1619    if (comp == NULL) {
1620	return (NULL);
1621    }
1622    CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
1623    range = CFStringFind(comp, CFSTR("/"), 0);
1624    if (range.location == kCFNotFound) {
1625	return (comp);
1626    }
1627    range.length = CFStringGetLength(comp) - range.location;
1628    CFStringDelete(comp, range);
1629    return (comp);
1630}
1631
1632static CFMutableDictionaryRef
1633service_dict_copy(CFStringRef serviceID)
1634{
1635    CFDictionaryRef		d = NULL;
1636    CFMutableDictionaryRef	service_dict;
1637
1638    /* create a modifyable dictionary, a copy or a new one */
1639    d = CFDictionaryGetValue(S_service_state_dict, serviceID);
1640    if (d == NULL) {
1641	service_dict
1642	    = CFDictionaryCreateMutable(NULL, 0,
1643					&kCFTypeDictionaryKeyCallBacks,
1644					&kCFTypeDictionaryValueCallBacks);
1645    }
1646    else {
1647	service_dict = CFDictionaryCreateMutableCopy(NULL, 0, d);
1648    }
1649    return (service_dict);
1650}
1651
1652static void
1653log_service_entity(int level, CFStringRef serviceID, CFStringRef entity,
1654		   CFStringRef operation, CFTypeRef val)
1655{
1656
1657    CFDataRef	route_list;
1658    CFMutableStringRef this_val = NULL;
1659
1660    if (CFEqual(entity, kSCEntNetIPv4) && isA_CFDictionary(val) != NULL) {
1661	CFDictionaryRef    service_dict = NULL;
1662
1663	route_list = CFDictionaryGetValue(val, kIPv4DictRoutes);
1664	if (route_list != NULL) {
1665	    /* ALIGN: CF should align to at least 8-byte boundaries */
1666	    this_val = IPv4RouteListCopyDescription((IPv4RouteListRef)
1667						    (void *)CFDataGetBytePtr(route_list));
1668	}
1669
1670	service_dict = CFDictionaryGetValue(val, kIPv4DictService);
1671
1672	if (service_dict != NULL && isA_CFDictionary(service_dict) != NULL) {
1673	    if (this_val == NULL) {
1674		this_val = CFStringCreateMutable(NULL, 0);
1675	    }
1676	    CFStringAppendFormat(this_val, NULL, CFSTR("\n <IPv4Dictionary>: %@"), service_dict);
1677	}
1678	val = this_val;
1679    }
1680    if (val == NULL) {
1681	val = CFSTR("<none>");
1682    }
1683    my_log(level, "IPMonitor: serviceID %@ %@ %@ value = %@",
1684	   serviceID, operation, entity, val);
1685    my_CFRelease(&this_val);
1686    return;
1687}
1688
1689static boolean_t
1690service_dict_set(CFStringRef serviceID, CFStringRef entity,
1691		 CFTypeRef new_val)
1692{
1693    boolean_t			changed = FALSE;
1694    CFTypeRef			old_val;
1695    CFMutableDictionaryRef	service_dict;
1696
1697    service_dict = service_dict_copy(serviceID);
1698    old_val = CFDictionaryGetValue(service_dict, entity);
1699    if (new_val == NULL) {
1700	if (old_val != NULL) {
1701	    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1702		log_service_entity(LOG_DEBUG, serviceID, entity,
1703				   CFSTR("Removed:"), old_val);
1704	    }
1705	    CFDictionaryRemoveValue(service_dict, entity);
1706	    changed = TRUE;
1707	}
1708    }
1709    else {
1710	if (old_val == NULL || CFEqual(new_val, old_val) == FALSE) {
1711	    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
1712		log_service_entity(LOG_DEBUG, serviceID, entity,
1713				   CFSTR("Changed: old"), old_val);
1714		log_service_entity(LOG_DEBUG, serviceID, entity,
1715				   CFSTR("Changed: new"), new_val);
1716	    }
1717	    CFDictionarySetValue(service_dict, entity, new_val);
1718	    changed = TRUE;
1719	}
1720    }
1721    if (CFDictionaryGetCount(service_dict) == 0) {
1722	CFDictionaryRemoveValue(S_service_state_dict, serviceID);
1723    }
1724    else {
1725	CFDictionarySetValue(S_service_state_dict, serviceID, service_dict);
1726    }
1727    my_CFRelease(&service_dict);
1728    return (changed);
1729}
1730
1731static CFDictionaryRef
1732service_dict_get(CFStringRef serviceID, CFStringRef entity)
1733{
1734    CFDictionaryRef	service_dict;
1735
1736    service_dict = CFDictionaryGetValue(S_service_state_dict, serviceID);
1737    if (service_dict == NULL) {
1738	return (NULL);
1739    }
1740    return (CFDictionaryGetValue(service_dict, entity));
1741}
1742
1743#ifndef kSCPropNetHostname
1744#define kSCPropNetHostname CFSTR("Hostname")
1745#endif
1746
1747__private_extern__
1748CFStringRef
1749copy_dhcp_hostname(CFStringRef serviceID)
1750{
1751    CFDictionaryRef 	dict = NULL;
1752    CFStringRef		hostname = NULL;
1753    CFDictionaryRef 	service_dict = NULL;
1754
1755    dict = service_dict_get(serviceID, kSCEntNetIPv4);
1756
1757    if (dict == NULL || isA_CFDictionary(dict) == NULL) {
1758	return (NULL);
1759    }
1760
1761    service_dict =
1762	CFDictionaryGetValue(dict, kIPv4DictService);
1763
1764    if (service_dict == NULL
1765	|| isA_CFDictionary(service_dict) == NULL) {
1766	return (NULL);
1767    }
1768
1769    hostname =
1770	CFDictionaryGetValue(service_dict, kSCPropNetHostname);
1771
1772    if (hostname != NULL) {
1773	CFRetain(hostname);
1774    }
1775
1776    return (hostname);
1777}
1778
1779#if	!TARGET_IPHONE_SIMULATOR
1780static void
1781ipv6_service_update_router(CFStringRef serviceID, CFDictionaryRef new_val)
1782{
1783#ifdef SIOCDRADD_IN6
1784    int			if_index;
1785    char		ifn[IFNAMSIZ];
1786    CFStringRef		new_router = NULL;
1787    char		ntopbuf[INET6_ADDRSTRLEN];
1788    CFDictionaryRef	old_val = NULL;
1789    CFStringRef		old_router = NULL;
1790    struct in6_addr	router_ip;
1791    int			s = -1;
1792
1793    ifn[0] = '\0';
1794    old_val = service_dict_get(serviceID, kSCEntNetIPv6);
1795    if (old_val != NULL) {
1796	plist_get_cstring(old_val, kSCPropInterfaceName, ifn, sizeof(ifn));
1797	old_router = CFDictionaryGetValue(old_val, kSCPropNetIPv6Router);
1798    }
1799    if (ifn[0] == '\0') {
1800	if (new_val == NULL
1801	    || plist_get_cstring(new_val, kSCPropInterfaceName,
1802				 ifn, sizeof(ifn)) == FALSE) {
1803	    /* no InterfaceName property, ignore it */
1804	    goto done;
1805	}
1806    }
1807    if_index = if_nametoindex(ifn);
1808    if (if_index == 0) {
1809	goto done;
1810    }
1811    s = inet6_dgram_socket();
1812    if (s < 0) {
1813	my_log(LOG_ERR,
1814	       "IPMonitor: ipv6_service_update_router: socket failed, %s",
1815	       strerror(errno));
1816	goto done;
1817    }
1818    if (new_val != NULL) {
1819	new_router = CFDictionaryGetValue(new_val, kSCPropNetIPv6Router);
1820    }
1821    if (S_dict_get_boolean(old_val, kIsNULL, FALSE) == FALSE
1822	&& old_router != NULL
1823	&& (new_router == NULL || CFEqual(old_router, new_router) == FALSE)) {
1824	/* remove the old Router */
1825	if (cfstring_to_ip6(old_router, &router_ip)) {
1826	    if (IN6_IS_ADDR_LINKLOCAL(&router_ip) ||
1827		IN6_IS_ADDR_MC_LINKLOCAL(&router_ip)) {
1828		/* scope it */
1829		router_ip.__u6_addr.__u6_addr16[1] = htons(if_index);
1830	    }
1831	    if (siocdrdel_in6(s, if_index, &router_ip) < 0) {
1832		if (errno != EINVAL) {
1833		    my_log(LOG_ERR,
1834			   "IPMonitor: siocdrdel_in6(%s, %s) failed, %s",
1835			   ifn,
1836			   inet_ntop(AF_INET6, &router_ip,
1837				     ntopbuf, sizeof(ntopbuf)),
1838			   strerror(errno));
1839		}
1840	    }
1841	    else if (S_IPMonitor_debug & kDebugFlag1) {
1842		my_log(LOG_DEBUG,
1843		       "IPMonitor: %s removed default route %s",
1844		       ifn,
1845		       inet_ntop(AF_INET6, &router_ip,
1846				 ntopbuf, sizeof(ntopbuf)));
1847	    }
1848	}
1849    }
1850    /* add the new Router */
1851    if (S_dict_get_boolean(new_val, kIsNULL, FALSE) == FALSE
1852	&& cfstring_to_ip6(new_router, &router_ip)) {
1853	if (IN6_IS_ADDR_LINKLOCAL(&router_ip) ||
1854	    IN6_IS_ADDR_MC_LINKLOCAL(&router_ip)) {
1855	    /* scope it */
1856	    router_ip.__u6_addr.__u6_addr16[1] = htons(if_index);
1857	}
1858	if (siocdradd_in6(s, if_index, &router_ip, 0) < 0) {
1859	    if (errno != EINVAL) {
1860		my_log(LOG_ERR,
1861		       "IPMonitor: siocdradd_in6(%s, %s) failed, %s",
1862		       ifn,
1863		       inet_ntop(AF_INET6, &router_ip,
1864				 ntopbuf, sizeof(ntopbuf)),
1865		       strerror(errno));
1866	    }
1867	}
1868	else if (S_IPMonitor_debug & kDebugFlag1) {
1869	    my_log(LOG_DEBUG,
1870		   "IPMonitor: %s added default route %s",
1871		   ifn,
1872		   inet_ntop(AF_INET6, &router_ip,
1873			     ntopbuf, sizeof(ntopbuf)));
1874	}
1875    }
1876    close(s);
1877
1878  done:
1879#endif /* SIOCDRADD_IN6 */
1880    return;
1881}
1882#endif	/* !TARGET_IPHONE_SIMULATOR */
1883
1884#define	ALLOW_EMPTY_STRING	0x1
1885
1886static CF_RETURNS_RETAINED CFTypeRef
1887sanitize_prop(CFTypeRef val, uint32_t flags)
1888{
1889    if (val != NULL) {
1890	if (isA_CFString(val)) {
1891	    CFMutableStringRef	str;
1892
1893	    str = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)val);
1894	    CFStringTrimWhitespace(str);
1895	    if (!(flags & ALLOW_EMPTY_STRING) && (CFStringGetLength(str) == 0)) {
1896		CFRelease(str);
1897		str = NULL;
1898	    }
1899	    val = str;
1900	} else {
1901	    CFRetain(val);
1902	}
1903    }
1904
1905    return val;
1906}
1907
1908static void
1909merge_array_prop(CFMutableDictionaryRef	dict,
1910		 CFStringRef		key,
1911		 CFDictionaryRef	state_dict,
1912		 CFDictionaryRef	setup_dict,
1913		 uint32_t		flags,
1914		 Boolean		append)
1915{
1916    CFMutableArrayRef	merge_prop;
1917    CFArrayRef		setup_prop = NULL;
1918    CFArrayRef		state_prop = NULL;
1919
1920    if (setup_dict != NULL) {
1921	setup_prop = isA_CFArray(CFDictionaryGetValue(setup_dict, key));
1922    }
1923    if (state_dict != NULL) {
1924	state_prop = isA_CFArray(CFDictionaryGetValue(state_dict, key));
1925    }
1926
1927    if ((setup_prop == NULL) && (state_prop == NULL)) {
1928	return;
1929    }
1930
1931    merge_prop = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1932    if (setup_prop != NULL) {
1933	CFIndex	i;
1934	CFIndex	n;
1935
1936	n = CFArrayGetCount(setup_prop);
1937	for (i = 0; i < n; i++) {
1938	    CFTypeRef   val;
1939
1940	    val = CFArrayGetValueAtIndex(setup_prop, i);
1941	    val = sanitize_prop(val, flags);
1942	    if (val != NULL) {
1943		CFArrayAppendValue(merge_prop, val);
1944		CFRelease(val);
1945	    }
1946	}
1947    }
1948    if (state_prop != NULL
1949	&& (setup_prop == NULL || S_append_state)) {
1950	CFIndex	i;
1951	CFIndex	n;
1952	CFRange	setup_range	= CFRangeMake(0, CFArrayGetCount(merge_prop));
1953
1954	n = CFArrayGetCount(state_prop);
1955	for (i = 0; i < n; i++) {
1956	    CFTypeRef   val;
1957
1958	    val = CFArrayGetValueAtIndex(state_prop, i);
1959	    val = sanitize_prop(val, flags);
1960	    if (val != NULL) {
1961		if (append || !CFArrayContainsValue(merge_prop, setup_range, val)) {
1962		    CFArrayAppendValue(merge_prop, val);
1963		}
1964		CFRelease(val);
1965	    }
1966	}
1967    }
1968    if (CFArrayGetCount(merge_prop) > 0) {
1969	CFDictionarySetValue(dict, key, merge_prop);
1970    }
1971    CFRelease(merge_prop);
1972    return;
1973}
1974
1975static void
1976pick_prop(CFMutableDictionaryRef	dict,
1977	  CFStringRef			key,
1978	  CFDictionaryRef		state_dict,
1979	  CFDictionaryRef		setup_dict,
1980	  uint32_t			flags)
1981{
1982	CFTypeRef	val	= NULL;
1983
1984	if (setup_dict != NULL) {
1985	    val = CFDictionaryGetValue(setup_dict, key);
1986	    val = sanitize_prop(val, flags);
1987	}
1988	if (val == NULL && state_dict != NULL) {
1989	    val = CFDictionaryGetValue(state_dict, key);
1990	    val = sanitize_prop(val, flags);
1991	}
1992	if (val != NULL) {
1993	    CFDictionarySetValue(dict, key, val);
1994	    CFRelease(val);
1995	}
1996
1997	return;
1998}
1999
2000/**
2001 ** GetEntityChangesFunc functions
2002 **/
2003static boolean_t
2004get_ipv4_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2005		 CFDictionaryRef setup_dict, CFDictionaryRef info)
2006{
2007    CFDictionaryRef		aggregated_dict = NULL;
2008    boolean_t			changed		= FALSE;
2009    CFMutableDictionaryRef	dict		= NULL;
2010    CFStringRef			primaryRank	= NULL;
2011    IPv4RouteListRef		r;
2012#define R_STATIC		3
2013    IPv4RouteListRef		routes;
2014    /* ALIGN: force align */
2015    uint32_t			routes_buf[roundup(IPv4RouteListComputeSize(R_STATIC), sizeof(uint32_t))];
2016    CFDataRef			routes_data	= NULL;
2017    CFDictionaryRef		service_options;
2018
2019    if (state_dict == NULL) {
2020	goto done;
2021    }
2022    service_options = service_dict_get(serviceID, kSCEntNetService);
2023    if (service_options != NULL) {
2024	primaryRank = CFDictionaryGetValue(service_options, kSCPropNetServicePrimaryRank);
2025    }
2026    dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2027    if (setup_dict != NULL) {
2028	CFStringRef	router;
2029	struct in_addr	router_ip;
2030
2031	router = CFDictionaryGetValue(setup_dict,
2032				      kSCPropNetIPv4Router);
2033	if (router != NULL
2034	    && cfstring_to_ip(router, &router_ip)) {
2035	    CFDictionarySetValue(dict,
2036				 kSCPropNetIPv4Router,
2037				 router);
2038	}
2039    }
2040    routes = (IPv4RouteListRef)(void *)routes_buf;
2041    routes->size = R_STATIC;
2042    routes->count = 0;
2043    r = IPv4RouteListCreateWithDictionary(routes, dict, primaryRank);
2044    if (r != NULL) {
2045	routes_data = CFDataCreate(NULL,
2046				   (const void *)r,
2047				   IPv4RouteListComputeSize(r->count));
2048	if (r != routes) {
2049	    free(r);
2050	}
2051    }
2052    else {
2053	my_log(LOG_NOTICE,
2054	       "IPMonitor: %@ invalid IPv4 dictionary = %@",
2055	       serviceID,
2056	       dict);
2057    }
2058  done:
2059    if (routes_data != NULL) {
2060	CFStringRef	keys[2];
2061	CFTypeRef	values[2];
2062
2063	keys[0] = kIPv4DictService;
2064	values[0] = dict;
2065	keys[1] = kIPv4DictRoutes;
2066	values[1] = routes_data;
2067
2068	aggregated_dict = CFDictionaryCreate(NULL,
2069					     (const void**)keys,
2070					     values,
2071					     sizeof(keys)/sizeof(keys[0]),
2072					     &kCFTypeDictionaryKeyCallBacks,
2073					     &kCFTypeDictionaryValueCallBacks);
2074
2075    }
2076    changed = service_dict_set(serviceID, kSCEntNetIPv4, aggregated_dict);
2077    if (routes_data == NULL) {
2078	/* clean up the rank too */
2079	CFDictionaryRemoveValue(S_ipv4_service_rank_dict, serviceID);
2080    }
2081    my_CFRelease(&dict);
2082    my_CFRelease(&aggregated_dict);
2083    my_CFRelease(&routes_data);
2084    return (changed);
2085}
2086
2087static boolean_t
2088get_ipv6_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2089		 CFDictionaryRef setup_dict, CFDictionaryRef info)
2090{
2091    struct in6_addr		addr;
2092    CFArrayRef			addrs;
2093    boolean_t			changed = FALSE;
2094    CFMutableDictionaryRef	dict = NULL;
2095    CFDictionaryRef		new_dict = NULL;
2096    CFStringRef			router = NULL;
2097    struct in6_addr		router_ip;
2098    boolean_t			valid_ip = FALSE;
2099
2100    if (state_dict == NULL) {
2101	goto done;
2102    }
2103    addrs = isA_CFArray(CFDictionaryGetValue(state_dict,
2104					     kSCPropNetIPv6Addresses));
2105    if (addrs != NULL && CFArrayGetCount(addrs) > 0) {
2106	valid_ip = cfstring_to_ip6(CFArrayGetValueAtIndex(addrs, 0), &addr);
2107    }
2108    if (valid_ip == FALSE) {
2109	if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2110	    my_log(LOG_DEBUG,
2111		   "IPMonitor: %@ has no valid IPv6 address, ignoring",
2112		   serviceID);
2113	}
2114	goto done;
2115    }
2116    dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2117    if (setup_dict != NULL) {
2118	router = CFDictionaryGetValue(setup_dict,
2119				      kSCPropNetIPv6Router);
2120	if (router != NULL && cfstring_to_ip6(router, &router_ip)) {
2121	    CFDictionarySetValue(dict,
2122				 kSCPropNetIPv6Router,
2123				 router);
2124	}
2125    }
2126    else {
2127	router = CFDictionaryGetValue(dict,
2128				      kSCPropNetIPv6Router);
2129	if (router != NULL
2130	    && cfstring_to_ip6(router, &router_ip) == FALSE) {
2131	    CFDictionaryRemoveValue(dict, kSCPropNetIPv6Router);
2132	}
2133    }
2134    new_dict = dict;
2135
2136 done:
2137
2138#if	!TARGET_IPHONE_SIMULATOR
2139    ipv6_service_update_router(serviceID, new_dict);
2140#endif	/* !TARGET_IPHONE_SIMULATOR */
2141
2142    changed = service_dict_set(serviceID, kSCEntNetIPv6, new_dict);
2143    if (new_dict == NULL) {
2144	/* clean up the rank too */
2145	CFDictionaryRemoveValue(S_ipv6_service_rank_dict, serviceID);
2146    }
2147    my_CFRelease(&new_dict);
2148    return (changed);
2149}
2150
2151static void
2152accumulate_dns_servers(CFArrayRef in_servers, ProtocolFlags active_protos,
2153		       CFMutableArrayRef out_servers, CFStringRef interface)
2154{
2155    int			count;
2156    int			i;
2157
2158    count = CFArrayGetCount(in_servers);
2159    for (i = 0; i < count; i++) {
2160	CFStringRef	addr;
2161	struct in6_addr	ipv6_addr;
2162	struct in_addr	ip_addr;
2163
2164	addr = CFArrayGetValueAtIndex(in_servers, i);
2165	assert(addr != NULL);
2166
2167	if (cfstring_to_ip(addr, &ip_addr)) {
2168	    /* IPv4 address */
2169	    if ((active_protos & kProtocolFlagsIPv4) == 0
2170		&& ntohl(ip_addr.s_addr) != INADDR_LOOPBACK) {
2171		if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2172		    my_log(LOG_DEBUG,
2173			   "IPMonitor: no IPv4 connectivity, "
2174			   "ignoring DNS server address ", IP_FORMAT,
2175			   IP_LIST(&ip_addr));
2176		}
2177		continue;
2178	    }
2179
2180	    CFRetain(addr);
2181	}
2182	else if (cfstring_to_ip6(addr, &ipv6_addr)) {
2183	    /* IPv6 address */
2184	    if ((active_protos & kProtocolFlagsIPv6) == 0
2185		&& !IN6_IS_ADDR_LOOPBACK(&ipv6_addr)) {
2186		if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
2187		    char	ntopbuf[INET6_ADDRSTRLEN];
2188
2189		    my_log(LOG_DEBUG,
2190			   "IPMonitor: no IPv6 connectivity, "
2191			   "ignoring DNS server address %s",
2192			    inet_ntop(AF_INET6, &ipv6_addr,
2193				      ntopbuf, sizeof(ntopbuf)));
2194		}
2195		continue;
2196	    }
2197
2198	    if ((IN6_IS_ADDR_LINKLOCAL(&ipv6_addr) ||
2199		 IN6_IS_ADDR_MC_LINKLOCAL(&ipv6_addr))
2200		&& (interface != NULL)
2201		&& (CFStringFind(addr, CFSTR("%"), 0).location == kCFNotFound)) {
2202		// append interface name to IPv6 link local address
2203		addr = CFStringCreateWithFormat(NULL, NULL,
2204						CFSTR("%@%%%@"),
2205						addr,
2206						interface);
2207	    } else {
2208		CFRetain(addr);
2209	    }
2210	}
2211	else {
2212	    /* bad IP address */
2213	    my_log(LOG_NOTICE,
2214		   "IPMonitor: ignoring bad DNS server address '%@'",
2215		   addr);
2216	    continue;
2217	}
2218
2219	/* DNS server is valid and one we want */
2220	CFArrayAppendValue(out_servers, addr);
2221	CFRelease(addr);
2222    }
2223    return;
2224}
2225
2226static void
2227merge_dns_servers(CFMutableDictionaryRef new_dict,
2228		  CFArrayRef state_servers,
2229		  CFArrayRef setup_servers,
2230		  Boolean have_setup,
2231		  ProtocolFlags active_protos,
2232		  CFStringRef interface)
2233{
2234    CFMutableArrayRef	dns_servers;
2235    Boolean		have_dns_setup	= FALSE;
2236
2237    if (state_servers == NULL && setup_servers == NULL) {
2238	/* no DNS servers */
2239	return;
2240    }
2241    dns_servers = CFArrayCreateMutable(NULL, 0,
2242				       &kCFTypeArrayCallBacks);
2243    if (setup_servers != NULL) {
2244	accumulate_dns_servers(setup_servers, active_protos,
2245			       dns_servers, interface);
2246	if (CFArrayGetCount(dns_servers) > 0) {
2247	    have_dns_setup = TRUE;
2248	}
2249    }
2250    if ((CFArrayGetCount(dns_servers) == 0 || S_append_state)
2251	&& state_servers != NULL) {
2252	accumulate_dns_servers(state_servers, active_protos,
2253			       dns_servers, NULL);
2254    }
2255
2256    /*
2257     * Here, we determine whether or not we want all queries for this DNS
2258     * configuration to be bound to the associated network interface.
2259     *
2260     * For dynamically derived network configurations (i.e. from State:)
2261     * this would be the preferred option using the argument "Hey, the
2262     * server told us to use these servers on this network so let's not
2263     * argue".
2264     *
2265     * But, when a DNS configuration has been provided by the user/admin
2266     * via the Network pref pane (i.e. from Setup:) we opt to not force
2267     * binding of the outbound queries.  The simplest example why we take
2268     * this stance is with a multi-homing configuration.  Consider a system
2269     * with one network service associated with "en0" and a second service
2270     * associated with "en1".  The "en0" service has been set higher in
2271     * the network service order so it would be primary but the user/admin
2272     * wants the DNS queries to go to a server only accessible via "en1".
2273     * Without this exception we would take the DNS server addresses from
2274     * the Network pref pane (for "en0") and have the queries bound to
2275     * "en0" where they'd never reach their intended destination (via
2276     * "en1").  So, our exception to the rule is that we will not bind
2277     * user/admin configurations to any specific network interface.
2278     *
2279     * We also add an exception to the "follow the dynamically derived
2280     * network configuration" path for on-the-fly (no Setup: content)
2281     * network services.
2282     */
2283    if (CFArrayGetCount(dns_servers) != 0) {
2284	CFDictionarySetValue(new_dict,
2285			     kSCPropNetDNSServerAddresses, dns_servers);
2286	if (have_setup && !have_dns_setup) {
2287	    CFDictionarySetValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue);
2288	}
2289    }
2290
2291    my_CFRelease(&dns_servers);
2292    return;
2293}
2294
2295
2296static boolean_t
2297get_dns_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2298		CFDictionaryRef setup_dict, CFDictionaryRef info)
2299{
2300    ProtocolFlags		active_protos	= kProtocolFlagsNone;
2301    boolean_t			changed		= FALSE;
2302    CFStringRef			domain;
2303    Boolean			have_setup	= FALSE;
2304    CFStringRef			interface	= NULL;
2305    CFDictionaryRef		ipv4;
2306    CFDictionaryRef		ipv6;
2307    int				i;
2308    const struct {
2309	CFStringRef     key;
2310	uint32_t	flags;
2311	Boolean		append;
2312    } merge_list[] = {
2313	{ kSCPropNetDNSSearchDomains,			0,			FALSE },
2314	{ kSCPropNetDNSSortList,			0,			FALSE },
2315	{ kSCPropNetDNSSupplementalMatchDomains,	ALLOW_EMPTY_STRING,	TRUE  },
2316	{ kSCPropNetDNSSupplementalMatchOrders,		0,			TRUE  },
2317    };
2318    CFMutableDictionaryRef      new_dict = NULL;
2319    const CFStringRef		pick_list[] = {
2320	kSCPropNetDNSDomainName,
2321	kSCPropNetDNSOptions,
2322	kSCPropNetDNSSearchOrder,
2323	kSCPropNetDNSServerPort,
2324	kSCPropNetDNSServerTimeout,
2325	kSCPropNetDNSServiceIdentifier,
2326	kSCPropNetDNSSupplementalMatchDomainsNoSearch,
2327    };
2328    IPv4RouteListRef		routes = NULL;
2329
2330    if ((state_dict == NULL) && (setup_dict == NULL)) {
2331	/* there is no DNS content */
2332	goto done;
2333    }
2334
2335    ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
2336    routes = ipv4_dict_get_routelist(ipv4);
2337
2338    if (routes != NULL) {
2339	if (get_service_setup_entity(info, serviceID, kSCEntNetIPv4) != NULL) {
2340	    have_setup = TRUE;
2341	}
2342
2343	active_protos |= kProtocolFlagsIPv4;
2344
2345	interface = ipv4_dict_get_ifname(ipv4);
2346    }
2347
2348    ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
2349    if (ipv6 != NULL) {
2350	if (!have_setup &&
2351	    get_service_setup_entity(info, serviceID, kSCEntNetIPv6) != NULL) {
2352	    have_setup = TRUE;
2353	}
2354
2355	active_protos |= kProtocolFlagsIPv6;
2356
2357	if (interface == NULL) {
2358	    interface = CFDictionaryGetValue(ipv6,
2359					     kSCPropInterfaceName);
2360	}
2361    }
2362
2363
2364    if (active_protos == kProtocolFlagsNone) {
2365	/* there is no IPv4 nor IPv6 */
2366	if (state_dict == NULL) {
2367	    /* ... and no DNS content that we care about */
2368	    goto done;
2369	}
2370	setup_dict = NULL;
2371    }
2372
2373    /* merge DNS configuration */
2374    new_dict = CFDictionaryCreateMutable(NULL, 0,
2375					 &kCFTypeDictionaryKeyCallBacks,
2376					 &kCFTypeDictionaryValueCallBacks);
2377
2378    if (active_protos == kProtocolFlagsNone) {
2379	merge_dns_servers(new_dict,
2380			  my_CFDictionaryGetArray(state_dict,
2381						  kSCPropNetDNSServerAddresses),
2382			  NULL,
2383			  FALSE,
2384			  kProtocolFlagsIPv4 | kProtocolFlagsIPv6,
2385			  NULL);
2386    }
2387    else {
2388	merge_dns_servers(new_dict,
2389			  my_CFDictionaryGetArray(state_dict,
2390						  kSCPropNetDNSServerAddresses),
2391			  my_CFDictionaryGetArray(setup_dict,
2392						  kSCPropNetDNSServerAddresses),
2393			  have_setup,
2394			  active_protos,
2395			  interface);
2396    }
2397
2398    for (i = 0; i < sizeof(merge_list)/sizeof(merge_list[0]); i++) {
2399	merge_array_prop(new_dict,
2400			 merge_list[i].key,
2401			 state_dict,
2402			 setup_dict,
2403			 merge_list[i].flags,
2404			 merge_list[i].append);
2405    }
2406
2407    for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2408	pick_prop(new_dict,
2409		  pick_list[i],
2410		  state_dict,
2411		  setup_dict,
2412		  0);
2413    }
2414
2415    if (active_protos == kProtocolFlagsNone) {
2416	/* there is no IPv4 nor IPv6, only supplemental or service-specific DNS */
2417	if (CFDictionaryContainsKey(new_dict,
2418				    kSCPropNetDNSSupplementalMatchDomains)) {
2419	    /* only keep State: supplemental */
2420	    CFDictionaryRemoveValue(new_dict, kSCPropNetDNSDomainName);
2421	    CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchDomains);
2422	    CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSearchOrder);
2423	    CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSortList);
2424
2425	    if ((interface == NULL) && (setup_dict == NULL) && (state_dict != NULL)) {
2426		/*
2427		 * for supplemental-only configurations, add any scoped (or
2428		 * wild-card "*") interface
2429		 */
2430		interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName);
2431	    }
2432	} else if (CFDictionaryContainsKey(new_dict, kSCPropNetDNSServiceIdentifier) &&
2433		   (interface == NULL) &&
2434		   (state_dict != NULL)) {
2435	    interface = CFDictionaryGetValue(state_dict, kSCPropInterfaceName);
2436	} else {
2437	    goto done;
2438	}
2439    }
2440
2441    if (CFDictionaryGetCount(new_dict) == 0) {
2442	my_CFRelease(&new_dict);
2443	goto done;
2444    }
2445
2446    if (interface != NULL) {
2447	CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
2448    }
2449
2450    if (S_append_state) {
2451	/*
2452	 * ensure any specified domain name (e.g. the domain returned by
2453	 * a DHCP server) is in the search list.
2454	 */
2455	domain = CFDictionaryGetValue(new_dict, kSCPropNetDNSDomainName);
2456	if (isA_CFString(domain)) {
2457	    CFArrayRef      search;
2458
2459	    search = CFDictionaryGetValue(new_dict, kSCPropNetDNSSearchDomains);
2460	    if (isA_CFArray(search) &&
2461		!CFArrayContainsValue(search, CFRangeMake(0, CFArrayGetCount(search)), domain)) {
2462		CFMutableArrayRef   new_search;
2463
2464		new_search = CFArrayCreateMutableCopy(NULL, 0, search);
2465		CFArrayAppendValue(new_search, domain);
2466		CFDictionarySetValue(new_dict, kSCPropNetDNSSearchDomains, new_search);
2467		my_CFRelease(&new_search);
2468	    }
2469	}
2470    }
2471
2472 done:
2473    changed = service_dict_set(serviceID, kSCEntNetDNS, new_dict);
2474    my_CFRelease(&new_dict);
2475    return (changed);
2476}
2477
2478static void
2479merge_dict(const void *key, const void *value, void *context)
2480{
2481	CFMutableDictionaryRef	dict	= (CFMutableDictionaryRef)context;
2482
2483	CFDictionarySetValue(dict, key, value);
2484	return;
2485}
2486
2487#define	PROXY_AUTO_DISCOVERY_URL	252
2488
2489static CF_RETURNS_RETAINED CFStringRef
2490wpadURL_dhcp(CFDictionaryRef dhcp_options)
2491{
2492    CFStringRef	urlString	= NULL;
2493
2494    if (isA_CFDictionary(dhcp_options)) {
2495	CFDataRef	data;
2496
2497	data = DHCPInfoGetOptionData(dhcp_options, PROXY_AUTO_DISCOVERY_URL);
2498	if (data != NULL) {
2499	    CFURLRef    url;
2500	    const UInt8	*urlBytes;
2501	    CFIndex	urlLen;
2502
2503	    urlBytes = CFDataGetBytePtr(data);
2504	    urlLen   = CFDataGetLength(data);
2505	    while ((urlLen > 0) && (urlBytes[urlLen - 1] == 0)) {
2506		// remove trailing NUL
2507		urlLen--;
2508	    }
2509
2510	    if (urlLen <= 0) {
2511		return NULL;
2512	    }
2513
2514	    url = CFURLCreateWithBytes(NULL, urlBytes, urlLen, kCFStringEncodingUTF8, NULL);
2515	    if (url != NULL) {
2516		urlString = CFURLGetString(url);
2517		if (urlString != NULL) {
2518		    CFRetain(urlString);
2519		}
2520		CFRelease(url);
2521	    }
2522	}
2523    }
2524
2525    return urlString;
2526}
2527
2528static CF_RETURNS_RETAINED CFStringRef
2529wpadURL_dns(void)
2530{
2531    CFURLRef	url;
2532    CFStringRef	urlString	= NULL;
2533
2534    url = CFURLCreateWithString(NULL, CFSTR("http://wpad/wpad.dat"), NULL);
2535    if (url != NULL) {
2536	urlString = CFURLGetString(url);
2537	if (urlString != NULL) {
2538	    CFRetain(urlString);
2539	}
2540	CFRelease(url);
2541    }
2542
2543    return urlString;
2544}
2545
2546static boolean_t
2547get_proxies_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2548		    CFDictionaryRef setup_dict, CFDictionaryRef info)
2549{
2550    ProtocolFlags		active_protos	= kProtocolFlagsNone;
2551    boolean_t			changed		= FALSE;
2552    CFStringRef			interface	= NULL;
2553    CFDictionaryRef		ipv4;
2554    CFDictionaryRef		ipv6;
2555    CFMutableDictionaryRef	new_dict	= NULL;
2556    const struct {
2557	CFStringRef     key;
2558	uint32_t	flags;
2559	Boolean		append;
2560    } merge_list[] = {
2561	{ kSCPropNetProxiesSupplementalMatchDomains,	ALLOW_EMPTY_STRING,	TRUE  },
2562	{ kSCPropNetProxiesSupplementalMatchOrders,	0,			TRUE  },
2563    };
2564    const struct {
2565	    CFStringRef	key1;	/* an "enable" key */
2566	    CFStringRef	key2;
2567	    CFStringRef	key3;
2568    } pick_list[] = {
2569	    { kSCPropNetProxiesFTPEnable,	kSCPropNetProxiesFTPProxy,	kSCPropNetProxiesFTPPort	},
2570	    { kSCPropNetProxiesGopherEnable,	kSCPropNetProxiesGopherProxy,	kSCPropNetProxiesGopherPort	},
2571	    { kSCPropNetProxiesHTTPEnable,	kSCPropNetProxiesHTTPProxy,	kSCPropNetProxiesHTTPPort	},
2572	    { kSCPropNetProxiesHTTPSEnable,	kSCPropNetProxiesHTTPSProxy,	kSCPropNetProxiesHTTPSPort	},
2573	    { kSCPropNetProxiesRTSPEnable,	kSCPropNetProxiesRTSPProxy,	kSCPropNetProxiesRTSPPort	},
2574	    { kSCPropNetProxiesSOCKSEnable,	kSCPropNetProxiesSOCKSProxy,	kSCPropNetProxiesSOCKSPort	},
2575	    { kSCPropNetProxiesProxyAutoConfigEnable,
2576	      kSCPropNetProxiesProxyAutoConfigURLString,
2577	      kSCPropNetProxiesProxyAutoConfigJavaScript, },
2578	    { kSCPropNetProxiesProxyAutoDiscoveryEnable,
2579	      NULL,
2580	      NULL, }
2581    };
2582    IPv4RouteListRef		routes		= NULL;
2583
2584    if ((state_dict == NULL) && (setup_dict == NULL)) {
2585	/* there is no proxy content */
2586	goto done;
2587    }
2588
2589    ipv4 = service_dict_get(serviceID, kSCEntNetIPv4);
2590    routes = ipv4_dict_get_routelist(ipv4);
2591
2592    if (routes != NULL) {
2593	active_protos |= kProtocolFlagsIPv4;
2594
2595	interface = ipv4_dict_get_ifname(ipv4);
2596    }
2597
2598    ipv6 = service_dict_get(serviceID, kSCEntNetIPv6);
2599    if (ipv6 != NULL) {
2600	active_protos |= kProtocolFlagsIPv6;
2601
2602	if (interface == NULL) {
2603	    interface = CFDictionaryGetValue(ipv6,
2604					     kSCPropInterfaceName);
2605	}
2606    }
2607
2608    if (active_protos == kProtocolFlagsNone) {
2609	/* there is no IPv4 nor IPv6 */
2610	if (state_dict == NULL) {
2611	    /* ... and no proxy content that we care about */
2612	    goto done;
2613	}
2614	setup_dict = NULL;
2615    }
2616
2617    if ((setup_dict != NULL) && (state_dict != NULL)) {
2618	CFIndex			i;
2619	CFMutableDictionaryRef	setup_copy;
2620
2621	/*
2622	 * Merge the per-service "Setup:" and "State:" proxy information with
2623	 * the "Setup:" information always taking precedence.  Additionally,
2624	 * ensure that if any group of "Setup:" values (e.g. Enabled, Proxy,
2625	 * Port) is defined than all of the values for that group will be
2626	 * used.  That is, we don't allow mixing some of the values from
2627	 * the "Setup:" keys and others from the "State:" keys.
2628	 */
2629	new_dict   = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2630
2631	for (i = 0; i < sizeof(merge_list)/sizeof(merge_list[0]); i++) {
2632	    merge_array_prop(new_dict,
2633			     merge_list[i].key,
2634			     state_dict,
2635			     setup_dict,
2636			     merge_list[i].flags,
2637			     merge_list[i].append);
2638	}
2639
2640	setup_copy = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
2641	for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2642	    if (CFDictionaryContainsKey(setup_copy, pick_list[i].key1)) {
2643		/*
2644		 * if a "Setup:" enabled key has been provided than we want to
2645		 * ignore all of the "State:" keys
2646		 */
2647		CFDictionaryRemoveValue(new_dict, pick_list[i].key1);
2648		if (pick_list[i].key2 != NULL) {
2649		    CFDictionaryRemoveValue(new_dict, pick_list[i].key2);
2650		}
2651		if (pick_list[i].key3 != NULL) {
2652		    CFDictionaryRemoveValue(new_dict, pick_list[i].key3);
2653		}
2654	    } else if (CFDictionaryContainsKey(state_dict, pick_list[i].key1) ||
2655		       ((pick_list[i].key2 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key2)) ||
2656		       ((pick_list[i].key3 != NULL) && CFDictionaryContainsKey(state_dict, pick_list[i].key3))) {
2657		/*
2658		 * if a "Setup:" enabled key has not been provided and we have
2659		 * some" "State:" keys than we remove all of of "Setup:" keys
2660		 */
2661		CFDictionaryRemoveValue(setup_copy, pick_list[i].key1);
2662		if (pick_list[i].key2 != NULL) {
2663		    CFDictionaryRemoveValue(setup_copy, pick_list[i].key2);
2664		}
2665		if (pick_list[i].key3 != NULL) {
2666		    CFDictionaryRemoveValue(setup_copy, pick_list[i].key3);
2667		}
2668	    }
2669	}
2670
2671	/* merge the "Setup:" keys */
2672	CFDictionaryApplyFunction(setup_copy, merge_dict, new_dict);
2673	CFRelease(setup_copy);
2674    }
2675    else if (setup_dict != NULL) {
2676	new_dict = CFDictionaryCreateMutableCopy(NULL, 0, setup_dict);
2677    }
2678    else if (state_dict != NULL) {
2679	new_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
2680    }
2681
2682    if ((new_dict != NULL) && (CFDictionaryGetCount(new_dict) == 0)) {
2683	CFRelease(new_dict);
2684	new_dict = NULL;
2685    }
2686
2687    if ((new_dict != NULL) && (interface != NULL)) {
2688	CFDictionarySetValue(new_dict, kSCPropInterfaceName, interface);
2689    }
2690
2691    /* process WPAD */
2692    if (new_dict != NULL) {
2693	CFDictionaryRef	dhcp_options;
2694	CFNumberRef	num;
2695	CFNumberRef	wpad	    = NULL;
2696	int		wpadEnabled = 0;
2697	CFStringRef	wpadURL	    = NULL;
2698
2699	if (CFDictionaryGetValueIfPresent(new_dict,
2700					  kSCPropNetProxiesProxyAutoDiscoveryEnable,
2701					  (const void **)&num) &&
2702	    isA_CFNumber(num)) {
2703	    /* if we have a WPAD key */
2704	    wpad = num;
2705	    if (!CFNumberGetValue(num, kCFNumberIntType, &wpadEnabled)) {
2706		/* if we don't like the enabled key/value */
2707		wpadEnabled = 0;
2708	    }
2709	}
2710
2711	if (wpadEnabled) {
2712	    int	pacEnabled  = 0;
2713
2714	    num = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigEnable);
2715	    if (!isA_CFNumber(num) ||
2716		!CFNumberGetValue(num, kCFNumberIntType, &pacEnabled)) {
2717		/* if we don't like the enabled key/value */
2718		pacEnabled = 0;
2719	    }
2720
2721	    if (pacEnabled) {
2722		CFStringRef	pacURL;
2723
2724		pacURL = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigURLString);
2725		if (pacURL != NULL) {
2726		    if (!isA_CFString(pacURL)) {
2727			/* if we don't like the PAC URL */
2728			pacEnabled = 0;
2729		    }
2730		} else {
2731		    CFStringRef	pacJS;
2732
2733		    pacJS = CFDictionaryGetValue(new_dict, kSCPropNetProxiesProxyAutoConfigJavaScript);
2734		    if (!isA_CFString(pacJS)) {
2735			/* if we don't have (or like) the PAC JavaScript */
2736			pacEnabled = 0;
2737		    }
2738		}
2739	    }
2740
2741	    if (pacEnabled) {
2742		/*
2743		 * we already have a PAC URL so disable WPAD.
2744		 */
2745		wpadEnabled = 0;
2746		goto setWPAD;
2747	    }
2748
2749	    /*
2750	     * if WPAD is enabled and we don't already have a PAC URL then
2751	     * we check for a DHCP provided URL.  If not available, we use
2752	     * a PAC URL pointing to a well-known file (wpad.dat) on a
2753	     * well-known host (wpad.<domain>).
2754	     */
2755	    dhcp_options = get_service_state_entity(info, serviceID, kSCEntNetDHCP);
2756	    wpadURL = wpadURL_dhcp(dhcp_options);
2757	    if (wpadURL == NULL) {
2758		wpadURL = wpadURL_dns();
2759	    }
2760	    if (wpadURL == NULL) {
2761		wpadEnabled = 0;    /* if we don't have a WPAD URL */
2762		goto setWPAD;
2763	    }
2764
2765	    pacEnabled = 1;
2766	    num = CFNumberCreate(NULL, kCFNumberIntType, &pacEnabled);
2767	    CFDictionarySetValue(new_dict,
2768				 kSCPropNetProxiesProxyAutoConfigEnable,
2769				 num);
2770	    CFRelease(num);
2771	    CFDictionarySetValue(new_dict,
2772				 kSCPropNetProxiesProxyAutoConfigURLString,
2773				 wpadURL);
2774	    CFRelease(wpadURL);
2775	}
2776
2777     setWPAD:
2778	if (wpad != NULL) {
2779	    num = CFNumberCreate(NULL, kCFNumberIntType, &wpadEnabled);
2780	    CFDictionarySetValue(new_dict,
2781				 kSCPropNetProxiesProxyAutoDiscoveryEnable,
2782				 num);
2783	    CFRelease(num);
2784	}
2785    }
2786
2787 done:
2788    changed = service_dict_set(serviceID, kSCEntNetProxies, new_dict);
2789    my_CFRelease(&new_dict);
2790    return (changed);
2791}
2792
2793#if	!TARGET_OS_IPHONE
2794static boolean_t
2795get_smb_changes(CFStringRef serviceID, CFDictionaryRef state_dict,
2796		CFDictionaryRef setup_dict, CFDictionaryRef info)
2797{
2798    boolean_t			changed = FALSE;
2799    int				i;
2800    CFMutableDictionaryRef      new_dict = NULL;
2801    const CFStringRef		pick_list[] = {
2802	kSCPropNetSMBNetBIOSName,
2803	kSCPropNetSMBNetBIOSNodeType,
2804#ifdef	ADD_NETBIOS_SCOPE
2805	kSCPropNetSMBNetBIOSScope,
2806#endif	// ADD_NETBIOS_SCOPE
2807	kSCPropNetSMBWorkgroup,
2808    };
2809
2810    if (service_dict_get(serviceID, kSCEntNetIPv4) == NULL) {
2811	/* there is no IPv4 */
2812	goto done;
2813    }
2814
2815    if (state_dict == NULL && setup_dict == NULL) {
2816	/* there is no SMB */
2817	goto done;
2818    }
2819
2820    /* merge SMB configuration */
2821    new_dict = CFDictionaryCreateMutable(NULL, 0,
2822					 &kCFTypeDictionaryKeyCallBacks,
2823					 &kCFTypeDictionaryValueCallBacks);
2824
2825    merge_array_prop(new_dict,
2826		     kSCPropNetSMBWINSAddresses,
2827		     state_dict,
2828		     setup_dict,
2829		     0,
2830		     FALSE);
2831    for (i = 0; i < sizeof(pick_list)/sizeof(pick_list[0]); i++) {
2832	pick_prop(new_dict,
2833		  pick_list[i],
2834		  state_dict,
2835		  setup_dict,
2836		  0);
2837    }
2838
2839    if (CFDictionaryGetCount(new_dict) == 0) {
2840	my_CFRelease(&new_dict);
2841	goto done;
2842    }
2843
2844 done:
2845    changed = service_dict_set(serviceID, kSCEntNetSMB, new_dict);
2846    my_CFRelease(&new_dict);
2847    return (changed);
2848}
2849#endif	/* !TARGET_OS_IPHONE */
2850
2851static CFStringRef
2852services_info_get_interface(CFDictionaryRef services_info,
2853			    CFStringRef serviceID)
2854{
2855    CFStringRef		interface = NULL;
2856    CFDictionaryRef	ipv4_dict;
2857
2858    ipv4_dict = get_service_state_entity(services_info, serviceID,
2859					 kSCEntNetIPv4);
2860    if (isA_CFDictionary(ipv4_dict) != NULL) {
2861	interface = CFDictionaryGetValue(ipv4_dict, kSCPropInterfaceName);
2862    }
2863    else {
2864	CFDictionaryRef		ipv6_dict;
2865
2866	ipv6_dict = get_service_state_entity(services_info, serviceID,
2867					     kSCEntNetIPv6);
2868	if (isA_CFDictionary(ipv6_dict) != NULL) {
2869	    interface = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
2870	}
2871    }
2872    return (interface);
2873}
2874
2875
2876
2877static const CFStringRef *statusEntityNames[] = {
2878    &kSCEntNetIPSec,
2879    &kSCEntNetPPP,
2880    &kSCEntNetVPN,
2881};
2882
2883static Boolean
2884get_transient_service_changes(CFStringRef serviceID, CFDictionaryRef services_info)
2885{
2886    boolean_t	changed = FALSE;
2887    int		i;
2888
2889    static const struct {
2890	const CFStringRef	*entityName;
2891	const CFStringRef	*statusKey;
2892    } transientServiceInfo[] = {
2893	{ &kSCEntNetIPSec,	&kSCPropNetIPSecStatus	},
2894	{ &kSCEntNetPPP,	&kSCPropNetPPPStatus	},
2895	{ &kSCEntNetVPN,	&kSCPropNetVPNStatus	},
2896    };
2897
2898    for (i = 0; i < sizeof(transientServiceInfo)/sizeof(transientServiceInfo[0]); i++) {
2899	CFDictionaryRef		dict;
2900	CFNumberRef		status		= NULL;
2901	CFMutableDictionaryRef	ts_dict		= NULL;
2902
2903	dict = get_service_state_entity(services_info, serviceID, *transientServiceInfo[i].entityName);
2904
2905	if (isA_CFDictionary(dict) != NULL) {
2906	    status = CFDictionaryGetValue(dict, *transientServiceInfo[i].statusKey);
2907	}
2908
2909	if (isA_CFNumber(status) != NULL) {
2910	    ts_dict = CFDictionaryCreateMutable(NULL,
2911						 0,
2912						 &kCFTypeDictionaryKeyCallBacks,
2913						 &kCFTypeDictionaryValueCallBacks);
2914	    CFDictionaryAddValue(ts_dict,
2915				 *transientServiceInfo[i].statusKey,
2916				 status);
2917	}
2918
2919	if (service_dict_set(serviceID, *transientServiceInfo[i].entityName, ts_dict)) {
2920	    changed = TRUE;
2921	}
2922
2923	if (ts_dict != NULL) {
2924	    CFRelease(ts_dict);
2925	}
2926    }
2927    return (changed);
2928}
2929
2930static boolean_t
2931get_rank_changes(CFStringRef serviceID, CFDictionaryRef state_options,
2932		 CFDictionaryRef setup_options, CFDictionaryRef services_info)
2933{
2934    boolean_t			changed		= FALSE;
2935    CFBooleanRef		ip_is_coupled 	= NULL;
2936    CFMutableDictionaryRef      new_dict	= NULL;
2937    CFStringRef			new_rank	= NULL;
2938    CFStringRef			setup_rank	= NULL;
2939    CFStringRef			state_rank	= NULL;
2940
2941
2942    /*
2943     * Check "PrimaryRank" setting
2944     *
2945     * Note 1: Rank setting in setup/state option overwrites the
2946     *         Rank setting in interface
2947     * Within each rank setting, the following precedence is defined:
2948     *
2949     * Note 2: Rank Never > Rank Last > Rank First > Rank None
2950     */
2951    if (isA_CFDictionary(state_options)) {
2952	CFBooleanRef	coupled;
2953
2954	state_rank
2955	    = CFDictionaryGetValue(state_options, kSCPropNetServicePrimaryRank);
2956	state_rank = isA_CFString(state_rank);
2957	coupled = CFDictionaryGetValue(state_options, kIPIsCoupled);
2958	if (isA_CFBoolean(coupled) != NULL) {
2959	    ip_is_coupled = coupled;
2960	}
2961    }
2962    if (isA_CFDictionary(setup_options)) {
2963	CFBooleanRef	coupled;
2964
2965	setup_rank
2966	    = CFDictionaryGetValue(setup_options, kSCPropNetServicePrimaryRank);
2967	setup_rank = isA_CFString(setup_rank);
2968
2969	coupled = CFDictionaryGetValue(setup_options, kIPIsCoupled);
2970	if (isA_CFBoolean(coupled) != NULL) {
2971	    ip_is_coupled = coupled;
2972	}
2973    }
2974
2975    if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankNever)) ||
2976	((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankNever))) {
2977	new_rank = kSCValNetServicePrimaryRankNever;
2978    }
2979    else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankLast)) ||
2980	     ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankLast))) {
2981	new_rank = kSCValNetServicePrimaryRankLast;
2982    }
2983    else if (((setup_rank != NULL) && CFEqual(setup_rank, kSCValNetServicePrimaryRankFirst)) ||
2984	     ((state_rank != NULL) && CFEqual(state_rank, kSCValNetServicePrimaryRankFirst))) {
2985	new_rank = kSCValNetServicePrimaryRankFirst;
2986    }
2987
2988    /* This corresponds to Note 1 */
2989    if (setup_rank == NULL && state_rank == NULL) {
2990	/* Fetch the interface associated with the service */
2991	CFStringRef interface;
2992
2993	interface = services_info_get_interface(services_info, serviceID);
2994
2995	/* Get the rank on that interface */
2996	if (interface != NULL) {
2997	    new_rank = CFDictionaryGetValue(S_if_rank_dict, interface);
2998	    if (S_IPMonitor_debug & kDebugFlag1) {
2999		my_log(LOG_DEBUG,
3000		       "serviceID %@ interface %@ rank = %@",
3001		       serviceID, interface,
3002		       (new_rank != NULL) ? new_rank : CFSTR("<none>"));
3003	    }
3004	}
3005    }
3006
3007
3008    if (ip_is_coupled != NULL && CFBooleanGetValue(ip_is_coupled) == FALSE) {
3009	/* don't bother setting a value if it's the default */
3010	ip_is_coupled = NULL;
3011    }
3012    if (new_rank != NULL || ip_is_coupled != NULL) {
3013	new_dict = CFDictionaryCreateMutable(NULL, 0,
3014					     &kCFTypeDictionaryKeyCallBacks,
3015					     &kCFTypeDictionaryValueCallBacks);
3016	if (new_rank != NULL) {
3017	    CFDictionarySetValue(new_dict, kSCPropNetServicePrimaryRank,
3018				 new_rank);
3019	}
3020	if (ip_is_coupled != NULL) {
3021	    CFDictionarySetValue(new_dict, kIPIsCoupled, kCFBooleanTrue);
3022	}
3023    }
3024    changed = service_dict_set(serviceID, kSCEntNetService, new_dict);
3025    my_CFRelease(&new_dict);
3026    return (changed);
3027}
3028
3029static CFStringRef
3030if_rank_key_copy(CFStringRef ifname)
3031{
3032    return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
3033							  kSCDynamicStoreDomainState,
3034							  ifname,
3035							  kSCEntNetService));
3036}
3037
3038static void
3039if_rank_set(CFStringRef ifname, CFDictionaryRef rank_dict)
3040{
3041    CFStringRef		rank = NULL;
3042
3043    if (isA_CFDictionary(rank_dict) != NULL) {
3044	rank = CFDictionaryGetValue(rank_dict, kSCPropNetServicePrimaryRank);
3045	rank = isA_CFString(rank);
3046    }
3047
3048    /* specific rank is asserted */
3049    if (rank != NULL) {
3050	if (S_IPMonitor_debug & kDebugFlag1) {
3051	    my_log(LOG_DEBUG, "Interface %@ asserted rank %@",
3052		   ifname, rank);
3053	}
3054	CFDictionarySetValue(S_if_rank_dict, ifname, rank);
3055    } else {
3056	if (S_IPMonitor_debug & kDebugFlag1) {
3057	    my_log(LOG_DEBUG, "Interface %@ removed rank",
3058		   ifname);
3059	}
3060	CFDictionaryRemoveValue(S_if_rank_dict, ifname);
3061    }
3062    return;
3063}
3064
3065static void
3066if_rank_apply(const void * key, const void * value, void * context)
3067{
3068    CFStringRef		ifname;
3069    CFDictionaryRef	rank_dict = (CFDictionaryRef)value;
3070
3071    /* State:/Network/Interface/<ifname>/Service, <ifname> is at index 3 */
3072    ifname = my_CFStringCopyComponent(key, CFSTR("/"), 3);
3073    if (ifname != NULL) {
3074	if_rank_set(ifname, rank_dict);
3075	CFRelease(ifname);
3076    }
3077    return;
3078}
3079
3080static void
3081if_rank_dict_init(void)
3082{
3083    CFDictionaryRef	info;
3084    CFStringRef		pattern;
3085    CFArrayRef		patterns;
3086
3087    S_if_rank_dict
3088	= CFDictionaryCreateMutable(NULL, 0,
3089				    &kCFTypeDictionaryKeyCallBacks,
3090				    &kCFTypeDictionaryValueCallBacks);
3091    pattern = if_rank_key_copy(kSCCompAnyRegex);
3092    patterns = CFArrayCreate(NULL,
3093			     (const void **)&pattern, 1,
3094			     &kCFTypeArrayCallBacks);
3095    CFRelease(pattern);
3096    info = SCDynamicStoreCopyMultiple(S_session, NULL, patterns);
3097    CFRelease(patterns);
3098    if (info != NULL) {
3099	CFDictionaryApplyFunction(info, if_rank_apply, NULL);
3100	CFRelease(info);
3101    }
3102    return;
3103
3104}
3105
3106static void
3107add_service_keys(CFStringRef serviceID, CFMutableArrayRef keys, CFMutableArrayRef patterns)
3108{
3109    int			i;
3110    CFStringRef		key;
3111
3112    if (CFEqual(serviceID, kSCCompAnyRegex)) {
3113	keys = patterns;
3114    }
3115
3116    for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
3117	key = setup_service_key(serviceID, *entityTypeNames[i]);
3118	CFArrayAppendValue(keys, key);
3119	CFRelease(key);
3120	key = state_service_key(serviceID, *entityTypeNames[i]);
3121	CFArrayAppendValue(keys, key);
3122	CFRelease(key);
3123    }
3124
3125    key = state_service_key(serviceID, kSCEntNetDHCP);
3126    CFArrayAppendValue(patterns, key);
3127    CFRelease(key);
3128
3129    key = setup_service_key(serviceID, NULL);
3130    CFArrayAppendValue(patterns, key);
3131    CFRelease(key);
3132    key = state_service_key(serviceID, NULL);
3133    CFArrayAppendValue(patterns, key);
3134    CFRelease(key);
3135
3136    return;
3137}
3138
3139static void
3140add_status_keys(CFStringRef service_id, CFMutableArrayRef patterns)
3141{
3142    int	    i;
3143
3144    for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) {
3145	CFStringRef	pattern;
3146
3147	pattern = state_service_key(service_id, *statusEntityNames[i]);
3148	CFArrayAppendValue(patterns, pattern);
3149	CFRelease(pattern);
3150    }
3151
3152    return;
3153}
3154
3155static const CFStringRef *reachabilitySetupKeys[] = {
3156    &kSCEntNetPPP,
3157    &kSCEntNetInterface,
3158    &kSCEntNetIPv4,
3159    &kSCEntNetIPv6,
3160};
3161
3162
3163static void
3164add_reachability_keys(CFMutableArrayRef patterns)
3165{
3166    int		i;
3167
3168    for (i = 0; i < sizeof(reachabilitySetupKeys)/(sizeof(reachabilitySetupKeys[0])); i++)
3169    {
3170	CFStringRef pattern;
3171	pattern = setup_service_key(kSCCompAnyRegex, *reachabilitySetupKeys[i]);
3172	CFArrayAppendValue(patterns, pattern);
3173	CFRelease(pattern);
3174    }
3175}
3176
3177
3178static void
3179add_vpn_keys(CFMutableArrayRef patterns)
3180{
3181    CFStringRef	pattern;
3182
3183    pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
3184    CFArrayAppendValue(patterns, pattern);
3185    CFRelease(pattern);
3186}
3187
3188
3189static CFDictionaryRef
3190services_info_copy(SCDynamicStoreRef session, CFArrayRef service_list,
3191		   CFArrayRef if_rank_list)
3192{
3193    int			count;
3194    CFMutableArrayRef	get_keys;
3195    CFMutableArrayRef	get_patterns;
3196    int			if_count;
3197    CFDictionaryRef	info;
3198    int			s;
3199
3200    count = CFArrayGetCount(service_list);
3201    get_keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3202    get_patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3203
3204    CFArrayAppendValue(get_keys, S_setup_global_ipv4);
3205    CFArrayAppendValue(get_keys, S_multicast_resolvers);
3206    CFArrayAppendValue(get_keys, S_private_resolvers);
3207
3208    for (s = 0; s < count; s++) {
3209	CFStringRef	serviceID = CFArrayGetValueAtIndex(service_list, s);
3210
3211	add_service_keys(serviceID, get_keys, get_patterns);
3212	add_status_keys(serviceID, get_keys);
3213    }
3214
3215    add_reachability_keys(get_patterns);
3216
3217    add_vpn_keys(get_patterns);
3218
3219    if_count = (if_rank_list != NULL)
3220	       ? CFArrayGetCount(if_rank_list) : 0;
3221    for (s = 0; s < if_count; s++) {
3222	CFStringRef	ifname = CFArrayGetValueAtIndex(if_rank_list, s);
3223	CFStringRef	key;
3224
3225	key = if_rank_key_copy(ifname);
3226	CFArrayAppendValue(get_keys, key);
3227	CFRelease(key);
3228    }
3229
3230    info = SCDynamicStoreCopyMultiple(session, get_keys, get_patterns);
3231    my_CFRelease(&get_keys);
3232    my_CFRelease(&get_patterns);
3233    return (info);
3234}
3235
3236#if	!TARGET_IPHONE_SIMULATOR
3237static int	rtm_seq = 0;
3238#endif	/* !TARGET_IPHONE_SIMULATOR */
3239
3240#if	!TARGET_IPHONE_SIMULATOR
3241static int
3242route_open_socket(void)
3243{
3244    int sockfd;
3245
3246    if ((sockfd = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE)) == -1) {
3247	my_log(LOG_NOTICE,
3248	       "IPMonitor: route_open_socket: socket failed, %s",
3249	       strerror(errno));
3250    }
3251    return (sockfd);
3252}
3253#endif	/* !TARGET_IPHONE_SIMULATOR */
3254
3255/*
3256 * Define: ROUTE_MSG_ADDRS_SPACE
3257 * Purpose:
3258 *   Since sizeof(sockaddr_dl) > sizeof(sockaddr_in), we need space for
3259 *   3 sockaddr_in's and 2 sockaddr_dl's, but pad it just in case
3260 *   someone changes the code and doesn't think to modify this.
3261 */
3262#define ROUTE_MSG_ADDRS_SPACE	(3 * sizeof(struct sockaddr_in)	\
3263				 + 2 * sizeof(struct sockaddr_dl) \
3264				 + 128)
3265typedef struct {
3266    struct rt_msghdr	hdr;
3267    char		addrs[ROUTE_MSG_ADDRS_SPACE];
3268} route_msg;
3269
3270#if	!TARGET_IPHONE_SIMULATOR
3271static int
3272ipv4_route(int sockfd,
3273	   int cmd, struct in_addr gateway, struct in_addr netaddr,
3274	   struct in_addr netmask, char * ifname, unsigned int ifindex,
3275	   struct in_addr ifa, RouteFlags flags)
3276{
3277    boolean_t			default_route = (netaddr.s_addr == 0);
3278    int				len;
3279    int				ret = 0;
3280    route_msg			rtmsg;
3281    union {
3282	struct sockaddr_in *	in_p;
3283	struct sockaddr_dl *	dl_p;
3284	void *			ptr;
3285    } rtaddr;
3286
3287    if (default_route && S_netboot) {
3288	return (0);
3289    }
3290
3291    if (ifname == NULL) {
3292	/* this should not happen, but rather than crash, return an error */
3293	my_log(LOG_NOTICE,
3294	       "IPMonitor: ipv4_route ifname is NULL on network address %s",
3295	       inet_ntoa(netaddr));
3296	return (EBADF);
3297    }
3298    if ((flags & kRouteIsNULLFlag) != 0) {
3299	my_log(LOG_DEBUG, "IPMonitor: ignoring route %s on %s",
3300	       inet_ntoa(netaddr), ifname);
3301	return (0);
3302    }
3303    memset(&rtmsg, 0, sizeof(rtmsg));
3304    rtmsg.hdr.rtm_type = cmd;
3305    rtmsg.hdr.rtm_version = RTM_VERSION;
3306    rtmsg.hdr.rtm_seq = ++rtm_seq;
3307    rtmsg.hdr.rtm_addrs
3308	= RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP | RTA_IFA;
3309    if (default_route
3310	&& (flags & kRouteIsDirectToInterfaceFlag) == 0) {
3311	rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
3312    }
3313    else {
3314	rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC;
3315    }
3316    if ((flags & kRouteIsScopedFlag) != 0) {
3317#ifdef RTF_IFSCOPE
3318	if (!S_scopedroute) {
3319	    return (0);
3320	}
3321	if (ifindex == 0) {
3322	    /* specifically asked for a scoped route, yet no index supplied */
3323	    my_log(LOG_NOTICE,
3324		   "IPMonitor: ipv4_route index is 0 on %s-scoped route %s",
3325		   ifname, inet_ntoa(netaddr));
3326	    return (EBADF);
3327	}
3328	rtmsg.hdr.rtm_index = ifindex;
3329	rtmsg.hdr.rtm_flags |= RTF_IFSCOPE;
3330#else /* RTF_IFSCOPE */
3331	return (0);
3332#endif /* RTF_IFSCOPE */
3333    }
3334
3335    rtaddr.ptr = rtmsg.addrs;
3336
3337    /* dest */
3338    rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3339    rtaddr.in_p->sin_family = AF_INET;
3340    rtaddr.in_p->sin_addr = netaddr;
3341    rtaddr.ptr += sizeof(*rtaddr.in_p);
3342
3343    /* gateway */
3344    if ((rtmsg.hdr.rtm_flags & RTF_GATEWAY) != 0) {
3345	/* gateway is an IP address */
3346	rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3347	rtaddr.in_p->sin_family = AF_INET;
3348	rtaddr.in_p->sin_addr = gateway;
3349	rtaddr.ptr += sizeof(*rtaddr.in_p);
3350    }
3351    else {
3352	/* gateway is the interface itself */
3353	rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3354	rtaddr.dl_p->sdl_family = AF_LINK;
3355	rtaddr.dl_p->sdl_nlen = strlen(ifname);
3356	bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen);
3357	rtaddr.ptr += sizeof(*rtaddr.dl_p);
3358    }
3359
3360    /* mask */
3361    rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3362    rtaddr.in_p->sin_family = AF_INET;
3363    rtaddr.in_p->sin_addr = netmask;
3364    rtaddr.ptr += sizeof(*rtaddr.in_p);
3365
3366    /* interface name */
3367    rtaddr.dl_p->sdl_len = sizeof(*rtaddr.dl_p);
3368    rtaddr.dl_p->sdl_family = AF_LINK;
3369    rtaddr.dl_p->sdl_nlen = strlen(ifname);
3370    bcopy(ifname, rtaddr.dl_p->sdl_data, rtaddr.dl_p->sdl_nlen);
3371    rtaddr.ptr += sizeof(*rtaddr.dl_p);
3372
3373    /* interface address */
3374    rtaddr.in_p->sin_len = sizeof(*rtaddr.in_p);
3375    rtaddr.in_p->sin_family = AF_INET;
3376    rtaddr.in_p->sin_addr = ifa;
3377    rtaddr.ptr += sizeof(*rtaddr.in_p);
3378
3379    len = sizeof(rtmsg.hdr) + (rtaddr.ptr - (void *)rtmsg.addrs);
3380    rtmsg.hdr.rtm_msglen = len;
3381    if (write(sockfd, &rtmsg, len) == -1) {
3382	ret = errno;
3383    }
3384    return (ret);
3385}
3386#endif	/* !TARGET_IPHONE_SIMULATOR */
3387
3388#if	!TARGET_IPHONE_SIMULATOR
3389static boolean_t
3390ipv6_route(int cmd, struct in6_addr gateway, struct in6_addr netaddr,
3391	   struct in6_addr netmask, char * ifname, boolean_t is_direct)
3392{
3393    boolean_t			default_route;
3394    int				len;
3395    boolean_t			ret = TRUE;
3396    struct {
3397	struct rt_msghdr	hdr;
3398	struct sockaddr_in6	dst;
3399	struct sockaddr_in6	gway;
3400	struct sockaddr_in6	mask;
3401	struct sockaddr_dl	ifp;
3402    }				rtmsg;
3403    int				sockfd = -1;
3404    struct in6_addr		zeroes = IN6ADDR_ANY_INIT;
3405
3406    default_route = (bcmp(&zeroes, &netaddr, sizeof(netaddr)) == 0);
3407
3408    if ((IN6_IS_ADDR_LINKLOCAL(&gateway) ||
3409	 IN6_IS_ADDR_MC_LINKLOCAL(&gateway)) &&
3410	(ifname != NULL)) {
3411	unsigned int	index = if_nametoindex(ifname);
3412
3413	/* add the scope id to the link local address */
3414	gateway.__u6_addr.__u6_addr16[1] = (uint16_t)htons(index);
3415    }
3416    sockfd = route_open_socket();
3417    if (sockfd == -1) {
3418	return (FALSE);
3419    }
3420    memset(&rtmsg, 0, sizeof(rtmsg));
3421    rtmsg.hdr.rtm_type = cmd;
3422    if (default_route) {
3423	if (is_direct) {
3424	    /* if router is directly reachable, don't set the gateway flag */
3425	    rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC;
3426	}
3427	else {
3428	    rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
3429	}
3430    }
3431    else {
3432	rtmsg.hdr.rtm_flags = RTF_UP | RTF_CLONING | RTF_STATIC;
3433    }
3434    rtmsg.hdr.rtm_version = RTM_VERSION;
3435    rtmsg.hdr.rtm_seq = ++rtm_seq;
3436    rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
3437    rtmsg.dst.sin6_len = sizeof(rtmsg.dst);
3438    rtmsg.dst.sin6_family = AF_INET6;
3439    rtmsg.dst.sin6_addr = netaddr;
3440    rtmsg.gway.sin6_len = sizeof(rtmsg.gway);
3441    rtmsg.gway.sin6_family = AF_INET6;
3442    rtmsg.gway.sin6_addr = gateway;
3443    rtmsg.mask.sin6_len = sizeof(rtmsg.mask);
3444    rtmsg.mask.sin6_family = AF_INET6;
3445    rtmsg.mask.sin6_addr = netmask;
3446
3447    len = sizeof(rtmsg);
3448    if (ifname) {
3449	rtmsg.ifp.sdl_len = sizeof(rtmsg.ifp);
3450	rtmsg.ifp.sdl_family = AF_LINK;
3451	rtmsg.ifp.sdl_nlen = strlen(ifname);
3452	rtmsg.hdr.rtm_addrs |= RTA_IFP;
3453	bcopy(ifname, rtmsg.ifp.sdl_data, rtmsg.ifp.sdl_nlen);
3454    }
3455    else {
3456	/* no ifp information */
3457	len -= sizeof(rtmsg.ifp);
3458    }
3459    rtmsg.hdr.rtm_msglen = len;
3460    if (write(sockfd, &rtmsg, len) == -1) {
3461	if ((cmd == RTM_ADD) && (errno == EEXIST)) {
3462	    /* no sense complaining about a route that already exists */
3463	}
3464	else if ((cmd == RTM_DELETE) && (errno == ESRCH)) {
3465	    /* no sense complaining about a route that isn't there */
3466	}
3467	else {
3468	    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3469		my_log(LOG_DEBUG,
3470		       "IPMonitor ipv6_route: write routing"
3471		       " socket failed, %s", strerror(errno));
3472	    }
3473	    ret = FALSE;
3474	}
3475    }
3476
3477    close(sockfd);
3478    return (ret);
3479}
3480
3481static boolean_t
3482ipv6_default_route_delete(void)
3483{
3484    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3485	my_log(LOG_DEBUG, "IPMonitor: IPv6 route delete default");
3486    }
3487    return (ipv6_route(RTM_DELETE, S_ip6_zeros, S_ip6_zeros, S_ip6_zeros,
3488		       NULL, FALSE));
3489}
3490
3491static boolean_t
3492ipv6_default_route_add(struct in6_addr router, char * ifname,
3493		       boolean_t is_direct)
3494{
3495    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3496	char	ntopbuf[INET6_ADDRSTRLEN];
3497
3498	my_log(LOG_DEBUG,
3499	       "IPMonitor: IPv6 route add default"
3500	       " %s interface %s direct %d",
3501	       inet_ntop(AF_INET6, &router, ntopbuf, sizeof(ntopbuf)),
3502	       ifname, is_direct);
3503    }
3504    return (ipv6_route(RTM_ADD, router, S_ip6_zeros, S_ip6_zeros,
3505		       ifname, is_direct));
3506}
3507#endif	/* !TARGET_IPHONE_SIMULATOR */
3508
3509
3510#if	!TARGET_IPHONE_SIMULATOR
3511static int
3512multicast_route_delete(int sockfd)
3513{
3514    struct in_addr gateway = { htonl(INADDR_LOOPBACK) };
3515    struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) };
3516    struct in_addr netmask = { htonl(IN_CLASSD_NET) };
3517
3518    return (ipv4_route(sockfd, RTM_DELETE, gateway, netaddr, netmask, "lo0", 0,
3519		       gateway, 0));
3520}
3521
3522static int
3523multicast_route_add(int sockfd)
3524{
3525    struct in_addr gateway = { htonl(INADDR_LOOPBACK) };
3526    struct in_addr netaddr = { htonl(INADDR_UNSPEC_GROUP) };
3527    struct in_addr netmask = { htonl(IN_CLASSD_NET) };
3528
3529    return (ipv4_route(sockfd, RTM_ADD, gateway, netaddr, netmask, "lo0", 0,
3530		       gateway, 0));
3531}
3532#endif	/* !TARGET_IPHONE_SIMULATOR */
3533
3534#if	!TARGET_IPHONE_SIMULATOR
3535#ifdef RTF_IFSCOPE
3536static void
3537set_ipv6_default_interface(char * ifname)
3538{
3539    struct in6_ndifreq	ndifreq;
3540    int			sock;
3541
3542    bzero((char *)&ndifreq, sizeof(ndifreq));
3543    if (ifname != NULL) {
3544	strlcpy(ndifreq.ifname, ifname, sizeof(ndifreq.ifname));
3545	ndifreq.ifindex = if_nametoindex(ifname);
3546    } else {
3547	strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname));
3548	ndifreq.ifindex = 0;
3549    }
3550
3551    sock = inet6_dgram_socket();
3552    if (sock == -1) {
3553	my_log(LOG_ERR,
3554	       "IPMonitor: set_ipv6_default_interface: socket failed, %s",
3555	       strerror(errno));
3556	return;
3557    }
3558    if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) == -1) {
3559	my_log(LOG_ERR,
3560	       "IPMonitor: set_ipv6_default_interface: ioctl(SIOCSDEFIFACE_IN6) failed, %s",
3561	       strerror(errno));
3562    }
3563    close(sock);
3564    return;
3565}
3566#endif /* RTF_IFSCOPE */
3567
3568static void
3569set_ipv6_router(struct in6_addr * router, char * ifname, boolean_t is_direct)
3570{
3571    /* assign the new default route, ensure local multicast route available */
3572    (void)ipv6_default_route_delete();
3573    if (router != NULL) {
3574	(void)ipv6_default_route_add(*router, ifname, is_direct);
3575    }
3576    return;
3577}
3578#endif	/* !TARGET_IPHONE_SIMULATOR */
3579
3580#if	!TARGET_OS_IPHONE
3581static __inline__ void
3582empty_dns()
3583{
3584    (void)unlink(VAR_RUN_RESOLV_CONF);
3585}
3586
3587static void
3588set_dns(CFArrayRef val_search_domains,
3589	CFStringRef val_domain_name,
3590	CFArrayRef val_servers,
3591	CFArrayRef val_sortlist)
3592{
3593    FILE * f = fopen(VAR_RUN_RESOLV_CONF "-", "w");
3594
3595    /* publish new resolv.conf */
3596    if (f) {
3597	CFIndex	i;
3598	CFIndex	n;
3599
3600	SCPrint(TRUE, f, CFSTR("#\n"));
3601	SCPrint(TRUE, f, CFSTR("# Mac OS X Notice\n"));
3602	SCPrint(TRUE, f, CFSTR("#\n"));
3603	SCPrint(TRUE, f, CFSTR("# This file is not used by the host name and address resolution\n"));
3604	SCPrint(TRUE, f, CFSTR("# or the DNS query routing mechanisms used by most processes on\n"));
3605	SCPrint(TRUE, f, CFSTR("# this Mac OS X system.\n"));
3606	SCPrint(TRUE, f, CFSTR("#\n"));
3607	SCPrint(TRUE, f, CFSTR("# This file is automatically generated.\n"));
3608	SCPrint(TRUE, f, CFSTR("#\n"));
3609
3610	if (isA_CFArray(val_search_domains)) {
3611	    SCPrint(TRUE, f, CFSTR("search"));
3612	    n = CFArrayGetCount(val_search_domains);
3613	    for (i = 0; i < n; i++) {
3614		CFStringRef	domain;
3615
3616		domain = CFArrayGetValueAtIndex(val_search_domains, i);
3617		if (isA_CFString(domain)) {
3618		    SCPrint(TRUE, f, CFSTR(" %@"), domain);
3619		}
3620	    }
3621	    SCPrint(TRUE, f, CFSTR("\n"));
3622	}
3623	else if (isA_CFString(val_domain_name)) {
3624		SCPrint(TRUE, f, CFSTR("domain %@\n"), val_domain_name);
3625	}
3626
3627	if (isA_CFArray(val_servers)) {
3628	    n = CFArrayGetCount(val_servers);
3629	    for (i = 0; i < n; i++) {
3630		CFStringRef	nameserver;
3631
3632		nameserver = CFArrayGetValueAtIndex(val_servers, i);
3633		if (isA_CFString(nameserver)) {
3634		    SCPrint(TRUE, f, CFSTR("nameserver %@\n"), nameserver);
3635		}
3636	    }
3637	}
3638
3639	if (isA_CFArray(val_sortlist)) {
3640	    SCPrint(TRUE, f, CFSTR("sortlist"));
3641	    n = CFArrayGetCount(val_sortlist);
3642	    for (i = 0; i < n; i++) {
3643		CFStringRef	address;
3644
3645		address = CFArrayGetValueAtIndex(val_sortlist, i);
3646		if (isA_CFString(address)) {
3647		    SCPrint(TRUE, f, CFSTR(" %@"), address);
3648		}
3649	    }
3650	    SCPrint(TRUE, f, CFSTR("\n"));
3651	}
3652
3653	fclose(f);
3654	rename(VAR_RUN_RESOLV_CONF "-", VAR_RUN_RESOLV_CONF);
3655    }
3656    return;
3657}
3658#endif	/* !TARGET_OS_IPHONE */
3659
3660#if	!TARGET_IPHONE_SIMULATOR
3661static boolean_t
3662router_is_our_ipv6_address(CFStringRef router, CFArrayRef addr_list)
3663{
3664    CFIndex		i;
3665    CFIndex		n = CFArrayGetCount(addr_list);
3666    struct in6_addr	r;
3667
3668    (void)cfstring_to_ip6(router, &r);
3669    for (i = 0; i < n; i++) {
3670	struct in6_addr	ip;
3671
3672	if (cfstring_to_ip6(CFArrayGetValueAtIndex(addr_list, i), &ip)
3673	    && bcmp(&r, &ip, sizeof(r)) == 0) {
3674	    return (TRUE);
3675	}
3676    }
3677    return (FALSE);
3678}
3679#endif	/* !TARGET_IPHONE_SIMULATOR */
3680
3681static IPv4RouteListRef
3682service_dict_get_ipv4_routelist(CFDictionaryRef service_dict)
3683{
3684    CFDictionaryRef	dict;
3685
3686    dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
3687
3688    return (ipv4_dict_get_routelist(dict));
3689}
3690
3691static CFStringRef
3692service_dict_get_ipv4_ifname(CFDictionaryRef service_dict)
3693{
3694    CFDictionaryRef	dict;
3695
3696    dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
3697    return (ipv4_dict_get_ifname(dict));
3698}
3699
3700static boolean_t
3701service_get_ip_is_coupled(CFStringRef serviceID)
3702{
3703    CFDictionaryRef	dict;
3704    boolean_t		ip_is_coupled = FALSE;
3705
3706    dict = service_dict_get(serviceID, kSCEntNetService);
3707    if (dict != NULL) {
3708	if (CFDictionaryContainsKey(dict, kIPIsCoupled)) {
3709	    ip_is_coupled = TRUE;
3710	}
3711    }
3712    return (ip_is_coupled);
3713}
3714
3715#if	!TARGET_IPHONE_SIMULATOR
3716
3717typedef struct apply_ipv4_route_context {
3718    IPv4RouteListRef	old;
3719    IPv4RouteListRef	new;
3720    int			sockfd;
3721} apply_ipv4_route_context_t;
3722
3723/* add/remove a router/32 subnet */
3724static int
3725ipv4_route_gateway(int sockfd, int cmd, char * ifn_p,
3726		   IPv4RouteRef def_route)
3727{
3728    struct in_addr		mask;
3729
3730    mask.s_addr = htonl(INADDR_BROADCAST);
3731    return (ipv4_route(sockfd, cmd, def_route->ifa,
3732		       def_route->gateway, mask, ifn_p, def_route->ifindex,
3733		       def_route->ifa, def_route->flags));
3734}
3735
3736/*
3737 * Function: apply_ipv4_route
3738 * Purpose:
3739 *   Callback function that adds/removes the specified route.
3740 */
3741static void
3742apply_ipv4_route(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg)
3743{
3744    apply_ipv4_route_context_t *context = (apply_ipv4_route_context_t *)arg;
3745    char *			ifn_p;
3746    int				retval;
3747
3748    ifn_p = route->ifname;
3749    switch (cmd) {
3750    case kIPv4RouteListAddRouteCommand:
3751	if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) {
3752	    retval = ipv4_route_gateway(context->sockfd, RTM_ADD,
3753					ifn_p, route);
3754	    if (retval == EEXIST) {
3755		/* delete and add again */
3756		(void)ipv4_route_gateway(context->sockfd, RTM_DELETE,
3757					 ifn_p, route);
3758		retval = ipv4_route_gateway(context->sockfd, RTM_ADD,
3759					    ifn_p, route);
3760	    }
3761	    if (retval != 0) {
3762		my_log(LOG_NOTICE,
3763		       "IPMonitor apply_ipv4_route failed to add"
3764		       " %s/32 route, %s",
3765		       inet_ntoa(route->gateway), strerror(retval));
3766	    }
3767	    else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3768		my_log(LOG_DEBUG, "Added IPv4 Route %s/32",
3769		       inet_ntoa(route->gateway));
3770	    }
3771	}
3772	retval = ipv4_route(context->sockfd,
3773			    RTM_ADD, route->gateway,
3774			    route->dest, route->mask, ifn_p, route->ifindex,
3775			    route->ifa, route->flags);
3776	if (retval == EEXIST) {
3777	    /* delete and add again */
3778	    (void)ipv4_route(context->sockfd,
3779			     RTM_DELETE, route->gateway,
3780			     route->dest, route->mask, ifn_p, route->ifindex,
3781			     route->ifa, route->flags);
3782	    retval = ipv4_route(context->sockfd,
3783				RTM_ADD, route->gateway,
3784				route->dest, route->mask,
3785				ifn_p, route->ifindex,
3786				route->ifa, route->flags);
3787	}
3788	if (retval != 0) {
3789	    my_log(LOG_NOTICE,
3790		   "IPMonitor apply_ipv4_route failed to add"
3791		   " route, %s:", strerror(retval));
3792	    IPv4RouteLog(LOG_NOTICE, route);
3793	}
3794	else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3795	    my_log(LOG_DEBUG,
3796		   "Added IPv4 route new[%d] = ",
3797		   route - context->new->list);
3798	    IPv4RouteLog(LOG_DEBUG, route);
3799	}
3800	break;
3801    case kIPv4RouteListRemoveRouteCommand:
3802	retval = ipv4_route(context->sockfd,
3803			    RTM_DELETE, route->gateway,
3804			    route->dest, route->mask, ifn_p, route->ifindex,
3805			    route->ifa, route->flags);
3806	if (retval != 0) {
3807	    if (retval != ESRCH) {
3808		my_log(LOG_NOTICE,
3809		       "IPMonitor apply_ipv4_route failed to remove"
3810		       " route, %s: ", strerror(retval));
3811		IPv4RouteLog(LOG_NOTICE, route);
3812	    }
3813	}
3814	else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3815	    my_log(LOG_DEBUG,
3816		   "Removed IPv4 route old[%d] = ",
3817		   route - context->old->list);
3818	    IPv4RouteLog(LOG_DEBUG, route);
3819	}
3820	if ((route->flags & kRouteIsNotSubnetLocalFlag) != 0) {
3821	    retval = ipv4_route_gateway(context->sockfd, RTM_DELETE,
3822					ifn_p, route);
3823	    if (retval != 0) {
3824		my_log(LOG_NOTICE,
3825		       "IPMonitor apply_ipv4_route failed to remove"
3826		       " %s/32 route, %s: ",
3827		       inet_ntoa(route->gateway), strerror(retval));
3828	    }
3829	    else if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3830		my_log(LOG_DEBUG, "Removed IPv4 Route %s/32",
3831		       inet_ntoa(route->gateway));
3832	    }
3833	}
3834	break;
3835    default:
3836	break;
3837    }
3838    return;
3839}
3840#endif	/* !TARGET_IPHONE_SIMULATOR */
3841
3842/*
3843 * Function: update_ipv4
3844 *
3845 * Purpose:
3846 *   Update the IPv4 configuration based on the latest information.
3847 *   Publish the State:/Network/Global/IPv4 information, and update the
3848 *   IPv4 routing table.  IPv4RouteListApply() invokes our callback,
3849 *   apply_ipv4_route(), to install/remove the routes.
3850 */
3851static void
3852update_ipv4(CFStringRef		primary,
3853	    IPv4RouteListRef	new_routelist,
3854	    keyChangeListRef	keys)
3855{
3856#if	!TARGET_IPHONE_SIMULATOR
3857    apply_ipv4_route_context_t	context;
3858#endif	/* !TARGET_IPHONE_SIMULATOR */
3859
3860    if (keys != NULL) {
3861	if (new_routelist != NULL && primary != NULL) {
3862	    char *			ifn_p = NULL;
3863	    IPv4RouteRef		r;
3864	    CFMutableDictionaryRef	dict = NULL;
3865
3866	    dict = CFDictionaryCreateMutable(NULL, 0,
3867					     &kCFTypeDictionaryKeyCallBacks,
3868					     &kCFTypeDictionaryValueCallBacks);
3869	    /* the first entry is the default route */
3870	    r = new_routelist->list;
3871	    if (r->gateway.s_addr != 0) {
3872		CFStringRef		router;
3873
3874		router = CFStringCreateWithCString(NULL,
3875						   inet_ntoa(r->gateway),
3876						   kCFStringEncodingASCII);
3877		if (router != NULL) {
3878		    CFDictionarySetValue(dict, kSCPropNetIPv4Router, router);
3879		    CFRelease(router);
3880		}
3881	    }
3882	    if (r->ifname[0] != '\0') {
3883		ifn_p = r->ifname;
3884	    }
3885	    if (ifn_p != NULL) {
3886		CFStringRef		ifname_cf;
3887
3888		ifname_cf = CFStringCreateWithCString(NULL,
3889						      ifn_p,
3890						      kCFStringEncodingASCII);
3891		if (ifname_cf != NULL) {
3892		    CFDictionarySetValue(dict,
3893					 kSCDynamicStorePropNetPrimaryInterface,
3894					 ifname_cf);
3895		    CFRelease(ifname_cf);
3896		}
3897	    }
3898	    CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
3899				 primary);
3900	    keyChangeListSetValue(keys, S_state_global_ipv4, dict);
3901	    CFRelease(dict);
3902	}
3903	else {
3904	    keyChangeListRemoveValue(keys, S_state_global_ipv4);
3905	}
3906    }
3907
3908#if	!TARGET_IPHONE_SIMULATOR
3909    bzero(&context, sizeof(context));
3910    context.sockfd = route_open_socket();
3911    if (context.sockfd != -1) {
3912	if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
3913	    if (S_ipv4_routelist == NULL) {
3914		my_log(LOG_DEBUG, "Old Routes = <none>");
3915	    }
3916	    else {
3917		my_log(LOG_DEBUG, "Old Routes = ");
3918		IPv4RouteListLog(LOG_DEBUG, S_ipv4_routelist);
3919	    }
3920	    if (new_routelist == NULL) {
3921		my_log(LOG_DEBUG, "New Routes = <none>");
3922	    }
3923	    else {
3924		my_log(LOG_DEBUG, "New Routes = ");
3925		IPv4RouteListLog(LOG_DEBUG, new_routelist);
3926	    }
3927	}
3928	context.old = S_ipv4_routelist;
3929	context.new = new_routelist;
3930	IPv4RouteListApply(S_ipv4_routelist, new_routelist,
3931			   &apply_ipv4_route, (void *)&context);
3932	if (new_routelist != NULL) {
3933	    (void)multicast_route_delete(context.sockfd);
3934	}
3935	else {
3936	    (void)multicast_route_add(context.sockfd);
3937	}
3938	close(context.sockfd);
3939    }
3940    if (S_ipv4_routelist != NULL) {
3941	free(S_ipv4_routelist);
3942    }
3943    S_ipv4_routelist = new_routelist;
3944#endif	/* !TARGET_IPHONE_SIMULATOR */
3945
3946    return;
3947}
3948
3949static void
3950update_ipv6(CFStringRef		primary,
3951	    keyChangeListRef	keys)
3952{
3953    CFDictionaryRef	ipv6_dict = NULL;
3954
3955    if (primary != NULL) {
3956	ipv6_dict = service_dict_get(primary, kSCEntNetIPv6);
3957    }
3958    if (ipv6_dict != NULL) {
3959#if	!TARGET_IPHONE_SIMULATOR
3960	CFArrayRef		addrs;
3961#endif	/* !TARGET_IPHONE_SIMULATOR */
3962	CFMutableDictionaryRef	dict = NULL;
3963	CFStringRef		if_name = NULL;
3964#if	!TARGET_IPHONE_SIMULATOR
3965	char			ifn[IFNAMSIZ] = { '\0' };
3966	char *			ifn_p = NULL;
3967	boolean_t		is_direct = FALSE;
3968#endif	/* !TARGET_IPHONE_SIMULATOR */
3969	CFStringRef		val_router = NULL;
3970
3971	dict = CFDictionaryCreateMutable(NULL, 0,
3972					 &kCFTypeDictionaryKeyCallBacks,
3973					 &kCFTypeDictionaryValueCallBacks);
3974
3975#if	!TARGET_IPHONE_SIMULATOR
3976	addrs = CFDictionaryGetValue(ipv6_dict,
3977				     kSCPropNetIPv6Addresses);
3978#endif	/* !TARGET_IPHONE_SIMULATOR */
3979
3980	val_router = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Router);
3981	if (val_router != NULL) {
3982#if	!TARGET_IPHONE_SIMULATOR
3983	    is_direct = router_is_our_ipv6_address(val_router, addrs);
3984#endif	/* !TARGET_IPHONE_SIMULATOR */
3985	    /* no router if router is one of our IP addresses */
3986	    CFDictionarySetValue(dict, kSCPropNetIPv6Router,
3987				 val_router);
3988	}
3989#if	!TARGET_IPHONE_SIMULATOR
3990	else {
3991	    val_router = CFArrayGetValueAtIndex(addrs, 0);
3992	    is_direct = TRUE;
3993	}
3994#endif	/* !TARGET_IPHONE_SIMULATOR */
3995	if_name = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
3996	if (if_name) {
3997	    CFDictionarySetValue(dict,
3998				 kSCDynamicStorePropNetPrimaryInterface,
3999				 if_name);
4000#if	!TARGET_IPHONE_SIMULATOR
4001	    if (CFStringGetCString(if_name, ifn, sizeof(ifn),
4002				   kCFStringEncodingASCII)) {
4003		ifn_p = ifn;
4004	    }
4005#endif	/* !TARGET_IPHONE_SIMULATOR */
4006	}
4007	CFDictionarySetValue(dict, kSCDynamicStorePropNetPrimaryService,
4008			     primary);
4009	keyChangeListSetValue(keys, S_state_global_ipv6, dict);
4010	CFRelease(dict);
4011
4012#if	!TARGET_IPHONE_SIMULATOR
4013#ifdef RTF_IFSCOPE
4014	if (S_scopedroute_v6) {
4015	    set_ipv6_default_interface(ifn_p);
4016	} else
4017#endif /* RTF_IFSCOPE */
4018	{ /* route add default ... */
4019	    struct in6_addr	router;
4020
4021	    (void)cfstring_to_ip6(val_router, &router);
4022	    set_ipv6_router(&router, ifn_p, is_direct);
4023	}
4024#endif	/* !TARGET_IPHONE_SIMULATOR */
4025    }
4026    else {
4027	keyChangeListRemoveValue(keys, S_state_global_ipv6);
4028#if	!TARGET_IPHONE_SIMULATOR
4029#ifdef RTF_IFSCOPE
4030	if (S_scopedroute_v6) {
4031	    set_ipv6_default_interface(NULL);
4032	} else
4033#endif /* RTF_IFSCOPE */
4034	{ /* route delete default ... */
4035	    set_ipv6_router(NULL, NULL, FALSE);
4036	}
4037#endif	/* !TARGET_IPHONE_SIMULATOR */
4038    }
4039    return;
4040}
4041
4042static Boolean
4043update_dns(CFDictionaryRef	services_info,
4044	   CFStringRef		primary,
4045	   keyChangeListRef	keys)
4046{
4047    Boolean		changed	= FALSE;
4048    CFDictionaryRef	dict	= NULL;
4049
4050    if (primary != NULL) {
4051	CFDictionaryRef	service_dict;
4052
4053	service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
4054	if (service_dict != NULL) {
4055	    dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
4056	}
4057    }
4058
4059    if (!_SC_CFEqual(S_dns_dict, dict)) {
4060	if (dict == NULL) {
4061#if	!TARGET_OS_IPHONE
4062	    empty_dns();
4063#endif	/* !TARGET_OS_IPHONE */
4064	    keyChangeListRemoveValue(keys, S_state_global_dns);
4065	} else {
4066	    CFMutableDictionaryRef	new_dict;
4067
4068#if	!TARGET_OS_IPHONE
4069	    set_dns(CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains),
4070		    CFDictionaryGetValue(dict, kSCPropNetDNSDomainName),
4071		    CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses),
4072		    CFDictionaryGetValue(dict, kSCPropNetDNSSortList));
4073#endif	/* !TARGET_OS_IPHONE */
4074	    new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
4075	    CFDictionaryRemoveValue(new_dict, kSCPropInterfaceName);
4076	    CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchDomains);
4077	    CFDictionaryRemoveValue(new_dict, kSCPropNetDNSSupplementalMatchOrders);
4078	    CFDictionaryRemoveValue(new_dict, DNS_CONFIGURATION_SCOPED_QUERY_KEY);
4079	    keyChangeListSetValue(keys, S_state_global_dns, new_dict);
4080	    CFRelease(new_dict);
4081	}
4082	changed = TRUE;
4083    }
4084
4085    if (dict != NULL) CFRetain(dict);
4086    if (S_dns_dict != NULL) CFRelease(S_dns_dict);
4087    S_dns_dict = dict;
4088
4089    return changed;
4090}
4091
4092static Boolean
4093update_dnsinfo(CFDictionaryRef	services_info,
4094	       CFStringRef	primary,
4095	       keyChangeListRef	keys,
4096	       CFArrayRef	service_order)
4097{
4098    Boolean		changed;
4099    CFDictionaryRef	dict	= NULL;
4100    CFArrayRef		multicastResolvers;
4101    CFArrayRef		privateResolvers;
4102
4103    multicastResolvers = CFDictionaryGetValue(services_info, S_multicast_resolvers);
4104    privateResolvers   = CFDictionaryGetValue(services_info, S_private_resolvers);
4105
4106    if (primary != NULL) {
4107	CFDictionaryRef	service_dict;
4108
4109	service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
4110	if (service_dict != NULL) {
4111	    dict = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
4112	}
4113    }
4114
4115    changed = dns_configuration_set(dict,
4116				    S_service_state_dict,
4117				    service_order,
4118				    multicastResolvers,
4119				    privateResolvers);
4120    if (changed) {
4121	keyChangeListNotifyKey(keys, S_state_global_dns);
4122    }
4123    return changed;
4124}
4125
4126static Boolean
4127update_nwi(nwi_state_t state)
4128{
4129    unsigned char		signature[CC_SHA1_DIGEST_LENGTH];
4130    static unsigned char	signature_last[CC_SHA1_DIGEST_LENGTH];
4131
4132    _nwi_state_signature(state, signature, sizeof(signature));
4133    if (bcmp(signature, signature_last, sizeof(signature)) == 0) {
4134	return FALSE;
4135    }
4136
4137    // save [new] signature
4138    bcopy(signature, signature_last, sizeof(signature));
4139
4140    // save [new] configuration
4141    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
4142	my_log(LOG_DEBUG, "Updating network information");
4143	S_nwi_state_dump(state);
4144    }
4145    if (_nwi_state_store(state) == FALSE) {
4146	my_log(LOG_ERR, "Notifying nwi_state_store failed");
4147    }
4148
4149    return TRUE;
4150}
4151
4152static Boolean
4153update_proxies(CFDictionaryRef	services_info,
4154	       CFStringRef	primary,
4155	       keyChangeListRef	keys,
4156	       CFArrayRef	service_order)
4157{
4158    Boolean	    changed	= FALSE;
4159    CFDictionaryRef dict	= NULL;
4160    CFDictionaryRef new_dict;
4161
4162    if (primary != NULL) {
4163	CFDictionaryRef	service_dict;
4164
4165	service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
4166	if (service_dict != NULL) {
4167	    dict = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
4168	}
4169    }
4170
4171    new_dict = proxy_configuration_update(dict,
4172					  S_service_state_dict,
4173					  service_order,
4174					  services_info);
4175    if (!_SC_CFEqual(S_proxies_dict, new_dict)) {
4176	if (new_dict == NULL) {
4177	    keyChangeListRemoveValue(keys, S_state_global_proxies);
4178	} else {
4179	    keyChangeListSetValue(keys, S_state_global_proxies, new_dict);
4180	}
4181	changed = TRUE;
4182    }
4183
4184    if (S_proxies_dict != NULL) CFRelease(S_proxies_dict);
4185    S_proxies_dict = new_dict;
4186
4187    return changed;
4188}
4189
4190#if	!TARGET_OS_IPHONE
4191static Boolean
4192update_smb(CFDictionaryRef	services_info,
4193	   CFStringRef		primary,
4194	   keyChangeListRef	keys)
4195{
4196    Boolean		changed	= FALSE;
4197    CFDictionaryRef	dict	= NULL;
4198
4199    if (primary != NULL) {
4200	CFDictionaryRef	service_dict;
4201
4202	service_dict = CFDictionaryGetValue(S_service_state_dict, primary);
4203	if (service_dict != NULL) {
4204	    dict = CFDictionaryGetValue(service_dict, kSCEntNetSMB);
4205	}
4206    }
4207
4208    if (!_SC_CFEqual(S_smb_dict, dict)) {
4209	if (dict == NULL) {
4210	    keyChangeListRemoveValue(keys, S_state_global_smb);
4211	} else {
4212	    keyChangeListSetValue(keys, S_state_global_smb, dict);
4213	}
4214	changed = TRUE;
4215    }
4216
4217    if (dict != NULL) CFRetain(dict);
4218    if (S_smb_dict != NULL) CFRelease(S_smb_dict);
4219    S_smb_dict = dict;
4220
4221    return changed;
4222}
4223#endif	/* !TARGET_OS_IPHONE */
4224
4225static Rank
4226get_service_rank(CFArrayRef order, int n_order, CFStringRef serviceID)
4227{
4228    CFIndex		i;
4229    Rank		rank = kRankIndexMask;
4230
4231    if (serviceID != NULL && order != NULL && n_order > 0) {
4232	for (i = 0; i < n_order; i++) {
4233	    CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(order, i));
4234
4235	    if (s == NULL) {
4236		continue;
4237	    }
4238	    if (CFEqual(serviceID, s)) {
4239		rank = i + 1;
4240		break;
4241	    }
4242	}
4243    }
4244    return (rank);
4245}
4246
4247/**
4248 ** Service election:
4249 **/
4250/*
4251 * Function: rank_dict_get_service_rank
4252 * Purpose:
4253 *   Retrieve the service rank in the given dictionary.
4254 */
4255static Rank
4256rank_dict_get_service_rank(CFDictionaryRef rank_dict, CFStringRef serviceID)
4257{
4258    CFNumberRef		rank;
4259    Rank		rank_val = RankMake(kRankIndexMask, kRankAssertionDefault);
4260
4261    rank = CFDictionaryGetValue(rank_dict, serviceID);
4262    if (rank != NULL) {
4263	CFNumberGetValue(rank, kCFNumberSInt32Type, &rank_val);
4264    }
4265    return (rank_val);
4266}
4267
4268/*
4269 * Function: rank_dict_set_service_rank
4270 * Purpose:
4271 *   Save the results of ranking the service so we can look it up later without
4272 *   repeating all of the ranking code.
4273 */
4274static void
4275rank_dict_set_service_rank(CFMutableDictionaryRef rank_dict,
4276			   CFStringRef serviceID, Rank rank_val)
4277{
4278    CFNumberRef		rank;
4279
4280    rank = CFNumberCreate(NULL, kCFNumberSInt32Type, (const void *)&rank_val);
4281    if (rank != NULL) {
4282	CFDictionarySetValue(rank_dict, serviceID, rank);
4283	CFRelease(rank);
4284    }
4285    return;
4286}
4287
4288static const CFStringRef *transientInterfaceEntityNames[] = {
4289    &kSCEntNetPPP,
4290};
4291
4292
4293static void
4294CollectTransientServices(const void * key,
4295			 const void * value,
4296			 void * context)
4297{
4298    int			i;
4299    CFStringRef		service = key;
4300    CFMutableArrayRef	vif_setup_keys = context;
4301
4302    /* This service is either a vpn type service or a comm center service */
4303    if (!CFStringHasPrefix(service, kSCDynamicStoreDomainSetup)) {
4304	return;
4305    }
4306
4307    for (i = 0; i < sizeof(transientInterfaceEntityNames)/sizeof(transientInterfaceEntityNames[0]); i++) {
4308	if (!CFStringHasSuffix(service, *transientInterfaceEntityNames[i])) {
4309	    continue;
4310	}
4311
4312	CFArrayAppendValue(vif_setup_keys, service);
4313    }
4314    return;
4315}
4316
4317
4318static SCNetworkReachabilityFlags
4319GetReachabilityFlagsFromVPN(CFDictionaryRef services_info,
4320			    CFStringRef	    service_id,
4321			    CFStringRef	    entity,
4322			    CFStringRef	    vpn_setup_key)
4323{
4324    CFStringRef			key;
4325    CFDictionaryRef		dict;
4326    SCNetworkReachabilityFlags	flags = 0;
4327
4328
4329    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4330							kSCDynamicStoreDomainSetup,
4331							service_id,
4332							kSCEntNetInterface);
4333    dict = CFDictionaryGetValue(services_info, key);
4334    CFRelease(key);
4335
4336    if (isA_CFDictionary(dict)
4337	&& CFDictionaryContainsKey(dict, kSCPropNetInterfaceDeviceName)) {
4338
4339	flags = (kSCNetworkReachabilityFlagsReachable
4340		| kSCNetworkReachabilityFlagsTransientConnection
4341		| kSCNetworkReachabilityFlagsConnectionRequired);
4342
4343	if (CFEqual(entity, kSCEntNetPPP)) {
4344	    CFNumberRef	num;
4345	    CFDictionaryRef p_dict = CFDictionaryGetValue(services_info, vpn_setup_key);
4346
4347	    if (!isA_CFDictionary(p_dict)) {
4348		return (flags);
4349	    }
4350
4351	    // get PPP dial-on-traffic status
4352	    num = CFDictionaryGetValue(p_dict, kSCPropNetPPPDialOnDemand);
4353	    if (isA_CFNumber(num)) {
4354		int32_t	ppp_demand;
4355
4356		if (CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand)) {
4357		    if (ppp_demand) {
4358			flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
4359		    }
4360		}
4361	    }
4362	}
4363    }
4364    return (flags);
4365}
4366
4367static Boolean
4368S_dict_get_boolean(CFDictionaryRef dict, CFStringRef key, Boolean def_value)
4369{
4370    Boolean		ret = def_value;
4371
4372    if (dict != NULL) {
4373	CFBooleanRef	val;
4374
4375	val = CFDictionaryGetValue(dict, key);
4376	if (isA_CFBoolean(val) != NULL) {
4377	    ret = CFBooleanGetValue(val);
4378	}
4379    }
4380    return (ret);
4381}
4382
4383
4384static void
4385GetReachabilityFlagsFromTransientServices(CFDictionaryRef services_info,
4386					  SCNetworkReachabilityFlags *reach_flags_v4,
4387					  SCNetworkReachabilityFlags *reach_flags_v6)
4388{
4389    int i;
4390    int count;
4391    CFMutableArrayRef vif_setup_keys;
4392
4393    vif_setup_keys = CFArrayCreateMutable(NULL,
4394					  0,
4395					  &kCFTypeArrayCallBacks);
4396
4397    CFDictionaryApplyFunction(services_info, CollectTransientServices, vif_setup_keys);
4398
4399    count = CFArrayGetCount(vif_setup_keys);
4400
4401    if (count != 0) {
4402	my_log(LOG_DEBUG, "Collected the following VIF Setup Keys: %@", vif_setup_keys);
4403    }
4404
4405    for (i = 0; i < count; i++) {
4406	CFArrayRef	    components = NULL;
4407	CFStringRef	    entity;
4408	CFStringRef	    service_id;
4409	CFStringRef	    vif_setup_key;
4410
4411	vif_setup_key = CFArrayGetValueAtIndex(vif_setup_keys, i);
4412
4413	/*
4414	 * setup key in the following format:
4415	 * Setup:/Network/Service/<Service ID>/<Entity>
4416	 */
4417	components = CFStringCreateArrayBySeparatingStrings(NULL, vif_setup_key, CFSTR("/"));
4418
4419	if (CFArrayGetCount(components) != 5) {
4420	    my_log(LOG_ERR, "Invalid Setup Key encountered: %@", vif_setup_key);
4421	    goto skip;
4422	}
4423
4424	/* service id is the 3rd element */
4425	service_id = CFArrayGetValueAtIndex(components, 3);
4426
4427	/* entity id is the 4th element */
4428	entity = CFArrayGetValueAtIndex(components, 4);
4429
4430	my_log(LOG_DEBUG, "Service %@ is a %@ Entity", service_id, entity);
4431
4432
4433	if (CFEqual(entity, kSCEntNetPPP)) {
4434	    SCNetworkReachabilityFlags	flags;
4435	    CFStringRef			key;
4436
4437	    flags = GetReachabilityFlagsFromVPN(services_info,
4438						service_id,
4439						entity,
4440						vif_setup_key);
4441
4442	    /* Check for the v4 reachability flags */
4443	    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4444							      kSCDynamicStoreDomainSetup,
4445							      service_id,
4446							      kSCEntNetIPv4);
4447
4448	    if (CFDictionaryContainsKey(services_info, key)) {
4449		*reach_flags_v4 |= flags;
4450		my_log(LOG_DEBUG,"Service %@ setting ipv4 reach flags: %d", service_id, *reach_flags_v4);
4451	    }
4452
4453	    CFRelease(key);
4454
4455	    /* Check for the v6 reachability flags */
4456	    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4457							      kSCDynamicStoreDomainSetup,
4458							      service_id,
4459							      kSCEntNetIPv6);
4460
4461	    if (CFDictionaryContainsKey(services_info, key)) {
4462		*reach_flags_v6 |= flags;
4463		my_log(LOG_DEBUG,"Service %@ setting ipv6 reach flags: %d", service_id, *reach_flags_v6);
4464	    }
4465	    CFRelease(key);
4466
4467	    if (flags != 0) {
4468		if (components != NULL) {
4469		    CFRelease(components);
4470		}
4471		goto done;
4472	    }
4473	}
4474skip:
4475	if (components != NULL) {
4476	    CFRelease(components);
4477	}
4478    }
4479done:
4480    CFRelease(vif_setup_keys);
4481    return;
4482}
4483
4484static SCNetworkReachabilityFlags
4485GetReachFlagsFromStatus(CFStringRef entity, int status)
4486{
4487    SCNetworkReachabilityFlags flags = 0;
4488
4489    if (CFEqual(entity, kSCEntNetPPP)) {
4490	switch (status) {
4491	    case PPP_RUNNING :
4492		/* if we're really UP and RUNNING */
4493		break;
4494	    case PPP_ONHOLD :
4495		/* if we're effectively UP and RUNNING */
4496		break;
4497	    case PPP_IDLE :
4498		/* if we're not connected at all */
4499		my_log(LOG_INFO, "PPP link idle");
4500		flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4501		break;
4502	    case PPP_STATERESERVED :
4503		// if we're not connected at all
4504		my_log(LOG_INFO, "PPP link idle, dial-on-traffic to connect");
4505		flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4506		break;
4507	    default :
4508		/* if we're in the process of [dis]connecting */
4509		my_log(LOG_INFO, "PPP link, connection in progress");
4510		flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4511		break;
4512	}
4513    }
4514#ifdef	HAVE_IPSEC_STATUS
4515    else if (CFEqual(entity, kSCEntNetIPSec)) {
4516	switch (status) {
4517	    case IPSEC_RUNNING :
4518		/* if we're really UP and RUNNING */
4519		break;
4520	    case IPSEC_IDLE :
4521		/* if we're not connected at all */
4522		my_log(LOG_INFO, "IPSec link idle");
4523		flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4524		break;
4525	    default :
4526		/* if we're in the process of [dis]connecting */
4527		my_log(LOG_INFO, "IPSec link, connection in progress");
4528		flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4529		break;
4530	}
4531    }
4532#endif	// HAVE_IPSEC_STATUS
4533#ifdef	HAVE_VPN_STATUS
4534    else if  (CFEqual(entity, kSCEntNetVPN)) {
4535	switch (status) {
4536	    case VPN_RUNNING :
4537		/* if we're really UP and RUNNING */
4538		break;
4539	    case VPN_IDLE :
4540	    case VPN_LOADING :
4541	    case VPN_LOADED :
4542	    case VPN_UNLOADING :
4543		/* if we're not connected at all */
4544		my_log(LOG_INFO, "%s  VPN link idle");
4545		flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4546		break;
4547	    default :
4548		/* if we're in the process of [dis]connecting */
4549		my_log(LOG_INFO, "VPN link, connection in progress");
4550		flags |= kSCNetworkReachabilityFlagsConnectionRequired;
4551		break;
4552	}
4553    }
4554#endif	// HAVE_VPN_STATUS
4555    return (flags);
4556}
4557
4558static void
4559VPNAttributesGet(CFStringRef		    service_id,
4560		 CFDictionaryRef	    services_info,
4561		 SCNetworkReachabilityFlags *flags,
4562		 CFStringRef		    *server_address,
4563		 int			    af)
4564{
4565    int				i;
4566    CFDictionaryRef		entity_dict;
4567    boolean_t			found = FALSE;
4568    CFNumberRef			num;
4569    CFDictionaryRef		p_state = NULL;
4570    int				status = 0;
4571
4572    /* if the IPv[4/6] exist */
4573    entity_dict = service_dict_get(service_id, (af == AF_INET) ? kSCEntNetIPv4 : kSCEntNetIPv6);
4574    if (!isA_CFDictionary(entity_dict)) {
4575	return;
4576    }
4577
4578    if (af == AF_INET) {
4579	entity_dict = CFDictionaryGetValue(entity_dict, kIPv4DictService);
4580	if (!isA_CFDictionary(entity_dict)) {
4581	    return;
4582	}
4583    }
4584
4585    for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) {
4586	p_state = service_dict_get(service_id, *statusEntityNames[i]);
4587	/* ensure that this is a VPN Type service */
4588	if (isA_CFDictionary(p_state)) {
4589	    found = TRUE;
4590	    break;
4591	}
4592    }
4593
4594    /* Did we find a vpn type service?  If not, we are done.*/
4595    if (!found) {
4596	return;
4597    }
4598
4599    *flags |= (kSCNetworkReachabilityFlagsReachable| kSCNetworkReachabilityFlagsTransientConnection);
4600
4601    /* Get the Server Address */
4602    if (server_address != NULL) {
4603	*server_address = CFDictionaryGetValue(entity_dict, CFSTR("ServerAddress"));
4604	*server_address = isA_CFString(*server_address);
4605	if (*server_address != NULL) {
4606	    CFRetain(*server_address);
4607	}
4608    }
4609
4610    /* get status */
4611    if (!CFDictionaryGetValueIfPresent(p_state,
4612				       kSCPropNetVPNStatus,
4613				       (const void **)&num) ||
4614	!isA_CFNumber(num) ||
4615	!CFNumberGetValue(num, kCFNumberSInt32Type, &status)) {
4616	return;
4617    }
4618
4619    *flags |= GetReachFlagsFromStatus(*statusEntityNames[i], status);
4620
4621    if (CFEqual(*statusEntityNames[i], kSCEntNetPPP)) {
4622	CFStringRef	key;
4623	CFDictionaryRef p_setup;
4624	int		ppp_demand;
4625
4626	key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
4627							  kSCDynamicStoreDomainSetup,
4628							  service_id,
4629							  kSCEntNetPPP);
4630	p_setup = CFDictionaryGetValue(services_info, key);
4631	CFRelease(key);
4632
4633	/* get dial-on-traffic status */
4634	if (isA_CFDictionary(p_setup) &&
4635	    CFDictionaryGetValueIfPresent(p_setup,
4636					  kSCPropNetPPPDialOnDemand,
4637					  (const void **)&num) &&
4638	    isA_CFNumber(num) &&
4639	    CFNumberGetValue(num, kCFNumberSInt32Type, &ppp_demand) &&
4640	    (ppp_demand != 0)) {
4641	    *flags |= kSCNetworkReachabilityFlagsConnectionOnTraffic;
4642	    if (status == PPP_IDLE) {
4643		*flags |= kSCNetworkReachabilityFlagsInterventionRequired;
4644	    }
4645	}
4646    }
4647    return;
4648}
4649
4650
4651typedef struct ElectionInfo {
4652    int			n_services;
4653    CFArrayRef		order;
4654    int			n_order;
4655    ElectionResultsRef	results;
4656} ElectionInfo, * ElectionInfoRef;
4657
4658typedef CFDictionaryApplierFunction	ElectionFuncRef;
4659
4660static void
4661CandidateRelease(CandidateRef candidate)
4662{
4663    my_CFRelease(&candidate->serviceID);
4664    my_CFRelease(&candidate->if_name);
4665    my_CFRelease(&candidate->signature);
4666    return;
4667}
4668
4669static void
4670CandidateCopy(CandidateRef dest, CandidateRef src)
4671{
4672    *dest = *src;
4673    if (dest->serviceID) {
4674	CFRetain(dest->serviceID);
4675    }
4676    if (dest->if_name) {
4677	CFRetain(dest->if_name);
4678    }
4679    if(dest->signature) {
4680	CFRetain(dest->signature);
4681    }
4682    return;
4683}
4684
4685static ElectionResultsRef
4686ElectionResultsAlloc(int size)
4687{
4688    ElectionResultsRef results;
4689
4690    results = (ElectionResultsRef)malloc(ElectionResultsComputeSize(size));
4691    results->count = 0;
4692    results->size = size;
4693    return (results);
4694}
4695
4696static void
4697ElectionResultsRelease(ElectionResultsRef results)
4698{
4699    int			i;
4700    CandidateRef	scan;
4701
4702    for (i = 0, scan = results->candidates;
4703	 i < results->count;
4704	 i++, scan++) {
4705	CandidateRelease(scan);
4706    }
4707    free(results);
4708    return;
4709}
4710
4711static void
4712ElectionResultsLog(int level, ElectionResultsRef results, const char * prefix)
4713{
4714    int			i;
4715    CandidateRef	scan;
4716
4717    if (results == NULL) {
4718	my_log(level, "%s: no candidates", prefix);
4719	return;
4720    }
4721    my_log(level, "%s: %d candidates", prefix, results->count);
4722    for (i = 0, scan = results->candidates;
4723	 i < results->count;
4724	 i++, scan++) {
4725	my_log(level, "%d. %@ Rank=0x%x serviceID=%@", i, scan->if_name,
4726	       scan->rank, scan->serviceID);
4727    }
4728    return;
4729}
4730
4731/*
4732 * Function: ElectionResultsAddCandidate
4733 * Purpose:
4734 *   Add the candidate into the election results. Find the insertion point
4735 *   by comparing the rank of the candidate with existing entries.
4736 */
4737static void
4738ElectionResultsAddCandidate(ElectionResultsRef results, CandidateRef candidate)
4739{
4740    int			i;
4741    int			where;
4742
4743#define BAD_INDEX	(-1)
4744    if (results->count == results->size) {
4745	/* this should not happen */
4746	my_log(LOG_NOTICE, "can't fit another candidate");
4747	return;
4748    }
4749
4750    /* find the insertion point */
4751    where = BAD_INDEX;
4752    for (i = 0; i < results->count; i++) {
4753	CandidateRef	this_candidate = results->candidates + i;
4754
4755	if (candidate->rank < this_candidate->rank) {
4756	    where = i;
4757	    break;
4758	}
4759    }
4760    /* add it to the end */
4761    if (where == BAD_INDEX) {
4762	CandidateCopy(results->candidates + results->count, candidate);
4763	results->count++;
4764	return;
4765    }
4766    /* slide existing entries over */
4767    for (i = results->count; i > where; i--) {
4768	results->candidates[i] = results->candidates[i - 1];
4769    }
4770    /* insert element */
4771    CandidateCopy(results->candidates + where, candidate);
4772    results->count++;
4773    return;
4774}
4775
4776/*
4777 * Function: ElectionResultsCopy
4778 * Purpose:
4779 *   Visit all of the services and invoke the protocol-specific election
4780 *   function.  Return the results of the election.
4781 */
4782static ElectionResultsRef
4783ElectionResultsCopy(ElectionFuncRef elect_func, CFArrayRef order, int n_order)
4784{
4785    int			count;
4786    ElectionInfo	info;
4787
4788    count = CFDictionaryGetCount(S_service_state_dict);
4789    if (count == 0) {
4790	return (NULL);
4791    }
4792    info.results = ElectionResultsAlloc(count);
4793    info.n_services = count;
4794    info.order = order;
4795    info.n_order = n_order;
4796    CFDictionaryApplyFunction(S_service_state_dict, elect_func, (void *)&info);
4797    if (info.results->count == 0) {
4798	ElectionResultsRelease(info.results);
4799	info.results = NULL;
4800    }
4801    return (info.results);
4802}
4803
4804/*
4805 * Function: ElectionResultsCandidateNeedsDemotion
4806 * Purpose:
4807 *   Check whether the given candidate requires demotion. A candidate
4808 *   might need to be demoted if its IPv4 and IPv6 services must be coupled
4809 *   but a higher ranked service has IPv4 or IPv6.
4810 */
4811static Boolean
4812ElectionResultsCandidateNeedsDemotion(ElectionResultsRef other_results,
4813				      CandidateRef candidate)
4814{
4815    CandidateRef	other_candidate;
4816    Boolean		ret = FALSE;
4817
4818    if (other_results == NULL
4819	|| candidate->ip_is_coupled == FALSE
4820	|| RANK_ASSERTION_MASK(candidate->rank) == kRankAssertionNever) {
4821	goto done;
4822    }
4823    other_candidate = other_results->candidates;
4824    if (CFEqual(other_candidate->if_name, candidate->if_name)) {
4825	/* they are over the same interface, no need to demote */
4826	goto done;
4827    }
4828    if (CFStringHasPrefix(other_candidate->if_name, CFSTR("stf"))) {
4829	/* avoid creating a feedback loop */
4830	goto done;
4831    }
4832    if (RANK_ASSERTION_MASK(other_candidate->rank) == kRankAssertionNever) {
4833	/* the other candidate isn't eligible to become primary, ignore */
4834	goto done;
4835    }
4836    if (candidate->rank < other_candidate->rank) {
4837	/* we're higher ranked than the other candidate, ignore */
4838	goto done;
4839    }
4840    ret = TRUE;
4841
4842 done:
4843    return (ret);
4844
4845}
4846
4847
4848static void
4849get_signature_sha1(CFStringRef		signature,
4850		   unsigned char	* sha1)
4851{
4852    CC_SHA1_CTX	    ctx;
4853    CFDataRef	    signature_data;
4854
4855    signature_data = CFStringCreateExternalRepresentation(NULL,
4856							  signature,
4857							  kCFStringEncodingUTF8,
4858							  0);
4859
4860    CC_SHA1_Init(&ctx);
4861    CC_SHA1_Update(&ctx,
4862		   signature_data,
4863		   CFDataGetLength(signature_data));
4864    CC_SHA1_Final(sha1, &ctx);
4865
4866    CFRelease(signature_data);
4867
4868    return;
4869}
4870
4871
4872static void
4873add_candidate_to_nwi_state(nwi_state_t nwi_state, int af,
4874			   CandidateRef candidate, Rank rank)
4875{
4876    uint64_t		flags = 0;
4877    char		ifname[IFNAMSIZ];
4878    nwi_ifstate_t	ifstate;
4879
4880    if (nwi_state == NULL) {
4881	/* can't happen */
4882	return;
4883    }
4884    if (RANK_ASSERTION_MASK(rank) == kRankAssertionNever) {
4885	flags |= NWI_IFSTATE_FLAGS_NOT_IN_LIST;
4886    }
4887    if (service_dict_get(candidate->serviceID, kSCEntNetDNS) != NULL) {
4888	flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
4889    }
4890    CFStringGetCString(candidate->if_name, ifname, sizeof(ifname),
4891		       kCFStringEncodingASCII);
4892    if ((S_IPMonitor_debug & kDebugFlag2) != 0) {
4893	my_log(LOG_DEBUG,
4894	       "Inserting IPv%c [%s] with flags 0x%x primary_rank 0x%x reach_flags %d",
4895	       ipvx_char(af), ifname, rank, candidate->reachability_flags);
4896    }
4897    ifstate = nwi_insert_ifstate(nwi_state, ifname, af, flags, rank,
4898				 (void *)&candidate->addr,
4899				 (void *)&candidate->vpn_server_addr,
4900				 candidate->reachability_flags);
4901    if (ifstate != NULL && candidate->signature) {
4902	uint8_t	    hash[CC_SHA1_DIGEST_LENGTH];
4903
4904	get_signature_sha1(candidate->signature, hash);
4905	nwi_ifstate_set_signature(ifstate, hash);
4906    }
4907    return;
4908}
4909
4910
4911static void
4912add_reachability_flags_to_candidate(CandidateRef candidate, CFDictionaryRef services_info, int af)
4913{
4914    SCNetworkReachabilityFlags	flags = kSCNetworkReachabilityFlagsReachable;
4915    CFStringRef			vpn_server_address = NULL;
4916
4917    VPNAttributesGet(candidate->serviceID,
4918		     services_info,
4919		     &flags,
4920		     &vpn_server_address,
4921		     af);
4922
4923    candidate->reachability_flags = flags;
4924
4925    if (vpn_server_address == NULL) {
4926	bzero(&candidate->vpn_server_addr, sizeof(candidate->vpn_server_addr));
4927    } else {
4928	char buf[128];
4929	CFStringGetCString(vpn_server_address, buf, sizeof(buf), kCFStringEncodingASCII);
4930
4931	_SC_string_to_sockaddr(buf,
4932			       AF_UNSPEC,
4933			       (void *)&candidate->vpn_server_addr,
4934			       sizeof(candidate->vpn_server_addr));
4935
4936	CFRelease(vpn_server_address);
4937    }
4938    return;
4939}
4940/*
4941 * Function: ElectionResultsCopyPrimary
4942 * Purpose:
4943 *   Use the results of the current protocol and the other protocol to
4944 *   determine which service should become primary.
4945 *
4946 *   At the same time, generate the nwi_state for the protocol.
4947 *
4948 *   For IPv4, also generate the IPv4 routing table.
4949 */
4950static CFStringRef
4951ElectionResultsCopyPrimary(ElectionResultsRef results,
4952			   ElectionResultsRef other_results,
4953			   nwi_state_t nwi_state, int af,
4954			   IPv4RouteListRef * ret_routes,
4955			   CFDictionaryRef services_info)
4956{
4957    CFStringRef		primary = NULL;
4958    Boolean		primary_is_null = FALSE;
4959    IPv4RouteListRef	routes = NULL;
4960
4961    if (nwi_state != NULL) {
4962	nwi_state_clear(nwi_state, af);
4963    }
4964    if (results != NULL) {
4965	CandidateRef	deferred[results->count];
4966	int		deferred_count;
4967	int		i;
4968	CandidateRef	scan;
4969
4970	deferred_count = 0;
4971	for (i = 0, scan = results->candidates;
4972	     i < results->count;
4973	     i++, scan++) {
4974	    Boolean		is_primary = FALSE;
4975	    Rank		rank = scan->rank;
4976	    Boolean		skip = FALSE;
4977
4978	    if (primary == NULL
4979		&& RANK_ASSERTION_MASK(rank) != kRankAssertionNever) {
4980		if (ElectionResultsCandidateNeedsDemotion(other_results,
4981							  scan)) {
4982		    /* demote to RankNever */
4983		    my_log(LOG_NOTICE,
4984			   "IPv%c over %@ demoted: not primary for IPv%c",
4985			   ipvx_char(af), scan->if_name, ipvx_other_char(af));
4986		    rank = RankMake(rank, kRankAssertionNever);
4987		    deferred[deferred_count++] = scan;
4988		    skip = TRUE;
4989		}
4990		else {
4991		    primary = CFRetain(scan->serviceID);
4992		    is_primary = TRUE;
4993		}
4994	    }
4995	    if (af == AF_INET) {
4996		/* generate the routing table for IPv4 */
4997		CFDictionaryRef		service_dict;
4998		IPv4RouteListRef	service_routes;
4999
5000		service_dict
5001		    = service_dict_get(scan->serviceID, kSCEntNetIPv4);
5002		service_routes = ipv4_dict_get_routelist(service_dict);
5003		if (service_routes != NULL) {
5004		    routes = IPv4RouteListAddRouteList(routes,
5005						       results->count * 3,
5006						       service_routes,
5007						       rank);
5008		    if (service_routes->exclude_from_nwi) {
5009			skip = TRUE;
5010		    }
5011		}
5012		else {
5013		    skip = TRUE;
5014		}
5015	    }
5016	    else {
5017		/* a NULL service must be excluded from nwi */
5018		CFDictionaryRef		ipv6_dict;
5019
5020		ipv6_dict = service_dict_get(scan->serviceID, kSCEntNetIPv6);
5021
5022		if (S_dict_get_boolean(ipv6_dict, kIsNULL, FALSE)) {
5023		    skip = TRUE;
5024		}
5025	    }
5026	    if (skip) {
5027		/* if we're skipping the primary, it's NULL */
5028		if (is_primary) {
5029		    primary_is_null = TRUE;
5030		}
5031	    }
5032	    else {
5033		if (primary_is_null) {
5034		    /* everything after the primary must be Never */
5035		    rank = RankMake(rank, kRankAssertionNever);
5036		}
5037		add_reachability_flags_to_candidate(scan, services_info, af);
5038		add_candidate_to_nwi_state(nwi_state, af, scan, rank);
5039	    }
5040	}
5041	for (i = 0; i < deferred_count; i++) {
5042	    CandidateRef	candidate = deferred[i];
5043	    Rank		rank;
5044
5045	    /* demote to RankNever */
5046	    rank = RankMake(candidate->rank, kRankAssertionNever);
5047	    add_reachability_flags_to_candidate(candidate, services_info, af);
5048	    add_candidate_to_nwi_state(nwi_state, af, candidate, rank);
5049	}
5050    }
5051    if (nwi_state != NULL) {
5052	nwi_state_set_last(nwi_state, af);
5053    }
5054    if (ret_routes != NULL) {
5055	*ret_routes = routes;
5056    }
5057    else if (routes != NULL) {
5058	free(routes);
5059    }
5060    if (primary_is_null) {
5061	my_CFRelease(&primary);
5062    }
5063    return (primary);
5064}
5065
5066
5067static inline
5068CFStringRef
5069service_dict_get_signature(CFDictionaryRef service_dict)
5070{
5071    return (CFDictionaryGetValue(service_dict, kStoreKeyNetworkSignature));
5072}
5073
5074
5075/*
5076 * Function: elect_ipv4
5077 * Purpose:
5078 *   Evaluate the service and determine what rank the service should have.
5079 *   If it's a suitable candidate, add it to the election results.
5080 */
5081static void
5082elect_ipv4(const void * key, const void * value, void * context)
5083{
5084    Candidate		candidate;
5085    CFStringRef		if_name;
5086    ElectionInfoRef 	info;
5087    Rank		primary_rank;
5088    CFDictionaryRef	service_dict = (CFDictionaryRef)value;
5089    IPv4RouteListRef	service_routes;
5090    CFDictionaryRef	v4_dict;
5091    CFDictionaryRef	v4_service_dict;
5092
5093    service_routes = service_dict_get_ipv4_routelist(service_dict);
5094    if (service_routes == NULL) {
5095	/* no service routes, no service */
5096	return;
5097    }
5098    if_name = service_dict_get_ipv4_ifname(service_dict);
5099    if (if_name == NULL) {
5100	/* need an interface name */
5101	return;
5102    }
5103    if (CFEqual(if_name, CFSTR("lo0"))) {
5104	/* don't ever elect loopback */
5105	return;
5106    }
5107    info = (ElectionInfoRef)context;
5108    bzero(&candidate, sizeof(candidate));
5109    candidate.serviceID = (CFStringRef)key;
5110    candidate.rank = get_service_rank(info->order, info->n_order,
5111				      candidate.serviceID);
5112    primary_rank = RANK_ASSERTION_MASK(service_routes->list->rank);
5113    if (S_ppp_override_primary
5114	&& (strncmp(PPP_PREFIX, service_routes->list->ifname,
5115		    sizeof(PPP_PREFIX) - 1) == 0)) {
5116	/* PPP override: make ppp* look the best */
5117	/* Hack: should use interface type, not interface name */
5118	primary_rank = kRankAssertionFirst;
5119    }
5120    candidate.rank = RankMake(candidate.rank, primary_rank);
5121    candidate.ip_is_coupled = service_get_ip_is_coupled(candidate.serviceID);
5122    candidate.if_name = if_name;
5123    candidate.addr.v4 = service_routes->list->ifa;
5124    rank_dict_set_service_rank(S_ipv4_service_rank_dict,
5125			       candidate.serviceID, candidate.rank);
5126    v4_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
5127    v4_service_dict = CFDictionaryGetValue(v4_dict, kIPv4DictService);
5128    candidate.signature = service_dict_get_signature(v4_service_dict);
5129    ElectionResultsAddCandidate(info->results, &candidate);
5130    return;
5131}
5132
5133
5134/*
5135 * Function: elect_ipv6
5136 * Purpose:
5137 *   Evaluate the service and determine what rank the service should have.
5138 *   If it's a suitable candidate, add it to the election results.
5139 */
5140static void
5141elect_ipv6(const void * key, const void * value, void * context)
5142{
5143    CFArrayRef		addrs;
5144    Candidate		candidate;
5145    CFStringRef		if_name;
5146    ElectionInfoRef 	info;
5147    Rank		primary_rank = kRankAssertionDefault;
5148    CFDictionaryRef	ipv6_dict;
5149    CFStringRef		router;
5150    CFDictionaryRef	service_dict = (CFDictionaryRef)value;
5151    CFDictionaryRef	service_options;
5152
5153
5154    ipv6_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
5155    if (ipv6_dict == NULL) {
5156	/* no IPv6 */
5157	return;
5158    }
5159    if_name = CFDictionaryGetValue(ipv6_dict, kSCPropInterfaceName);
5160    if (if_name == NULL) {
5161	/* need an interface name */
5162	return;
5163    }
5164    if (CFEqual(if_name, CFSTR("lo0"))) {
5165	/* don't ever elect loopback */
5166	return;
5167    }
5168    router = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Router);
5169    if (router == NULL) {
5170	/* don't care about services without a router */
5171	return;
5172    }
5173    info = (ElectionInfoRef)context;
5174    bzero(&candidate, sizeof(candidate));
5175    candidate.serviceID = (CFStringRef)key;
5176    candidate.if_name = if_name;
5177    addrs = CFDictionaryGetValue(ipv6_dict, kSCPropNetIPv6Addresses);
5178    (void)cfstring_to_ip6(CFArrayGetValueAtIndex(addrs, 0),
5179			  &candidate.addr.v6);
5180    candidate.rank = get_service_rank(info->order, info->n_order,
5181				      candidate.serviceID);
5182    service_options
5183	= service_dict_get(candidate.serviceID, kSCEntNetService);
5184    if (service_options != NULL) {
5185	CFStringRef	primaryRankStr = NULL;
5186
5187	primaryRankStr = CFDictionaryGetValue(service_options,
5188					      kSCPropNetServicePrimaryRank);
5189	if (primaryRankStr != NULL) {
5190	    primary_rank = PrimaryRankGetRankAssertion(primaryRankStr);
5191	}
5192	candidate.ip_is_coupled
5193	    = CFDictionaryContainsKey(service_options, kIPIsCoupled);
5194    }
5195    if (primary_rank != kRankAssertionNever) {
5196	if (get_override_primary(ipv6_dict)) {
5197	    primary_rank = kRankAssertionFirst;
5198	}
5199	else if (S_ppp_override_primary
5200		 && CFStringHasPrefix(if_name, CFSTR(PPP_PREFIX))) {
5201	    /* PPP override: make ppp* look the best */
5202	    /* Hack: should use interface type, not interface name */
5203	    primary_rank = kRankAssertionFirst;
5204	}
5205    }
5206    candidate.rank = RankMake(candidate.rank, primary_rank);
5207    rank_dict_set_service_rank(S_ipv6_service_rank_dict,
5208			       candidate.serviceID, candidate.rank);
5209    candidate.signature = service_dict_get_signature(ipv6_dict);
5210    ElectionResultsAddCandidate(info->results, &candidate);
5211    return;
5212}
5213
5214static uint32_t
5215service_changed(CFDictionaryRef services_info, CFStringRef serviceID)
5216{
5217    uint32_t		changed = 0;
5218    int			i;
5219
5220    /* update service options first (e.g. rank) */
5221    if (get_rank_changes(serviceID,
5222			 get_service_state_entity(services_info, serviceID,
5223						  NULL),
5224			 get_service_setup_entity(services_info, serviceID,
5225						  NULL),
5226			 services_info)) {
5227	changed |= (1 << kEntityTypeServiceOptions);
5228    }
5229    /* update IPv4, IPv6, DNS, Proxies, SMB, ... */
5230    for (i = 0; i < ENTITY_TYPES_COUNT; i++) {
5231	GetEntityChangesFuncRef func = entityChangeFunc[i];
5232	if ((*func)(serviceID,
5233		    get_service_state_entity(services_info, serviceID,
5234					     *entityTypeNames[i]),
5235		    get_service_setup_entity(services_info, serviceID,
5236					     *entityTypeNames[i]),
5237		    services_info)) {
5238	    changed |= (1 << i);
5239	}
5240    }
5241
5242    if (get_transient_service_changes(serviceID, services_info)) {
5243	changed |= (1 << kEntityTypeVPNStatus);
5244    }
5245
5246    return (changed);
5247}
5248
5249static CFArrayRef
5250service_order_get(CFDictionaryRef services_info)
5251{
5252    CFArrayRef		order = NULL;
5253    CFDictionaryRef	ipv4_dict;
5254
5255    ipv4_dict = my_CFDictionaryGetDictionary(services_info,
5256					     S_setup_global_ipv4);
5257    if (ipv4_dict != NULL) {
5258	CFNumberRef	ppp_override;
5259	int		ppp_val = 0;
5260
5261	order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
5262	order = isA_CFArray(order);
5263
5264	/* get ppp override primary */
5265	ppp_override = CFDictionaryGetValue(ipv4_dict,
5266					    kSCPropNetPPPOverridePrimary);
5267	ppp_override = isA_CFNumber(ppp_override);
5268	if (ppp_override != NULL) {
5269	    CFNumberGetValue(ppp_override, kCFNumberIntType, &ppp_val);
5270	}
5271	S_ppp_override_primary = (ppp_val != 0) ? TRUE : FALSE;
5272    }
5273    else {
5274	S_ppp_override_primary = FALSE;
5275    }
5276    return (order);
5277}
5278
5279static boolean_t
5280set_new_primary(CFStringRef * primary_p, CFStringRef new_primary,
5281		const char * entity)
5282{
5283    boolean_t		changed = FALSE;
5284    CFStringRef		primary = *primary_p;
5285
5286    if (new_primary != NULL) {
5287	if (primary != NULL && CFEqual(new_primary, primary)) {
5288	    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5289		my_log(LOG_DEBUG,
5290		       "IPMonitor: %@ is still primary %s",
5291		       new_primary, entity);
5292	    }
5293	}
5294	else {
5295	    my_CFRelease(primary_p);
5296	    *primary_p = CFRetain(new_primary);
5297	    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5298		my_log(LOG_DEBUG,
5299		       "IPMonitor: %@ is the new primary %s",
5300		       new_primary, entity);
5301	    }
5302	    changed = TRUE;
5303	}
5304    }
5305    else if (primary != NULL) {
5306	if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5307	    my_log(LOG_DEBUG,
5308		   "IPMonitor: %@ is no longer primary %s",
5309		   primary, entity);
5310	}
5311	my_CFRelease(primary_p);
5312	changed = TRUE;
5313    }
5314    return (changed);
5315}
5316
5317static Rank
5318rank_service_entity(CFDictionaryRef rank_dict, CFStringRef serviceID,
5319		    CFStringRef entity)
5320{
5321    if (service_dict_get(serviceID, entity) == NULL) {
5322	return (RankMake(kRankIndexMask, kRankAssertionDefault));
5323    }
5324    return (rank_dict_get_service_rank(rank_dict, serviceID));
5325}
5326
5327static void
5328update_interface_rank(CFDictionaryRef services_info, CFStringRef ifname)
5329{
5330    CFStringRef		if_rank_key;
5331    CFDictionaryRef	rank_dict;
5332
5333    if_rank_key = if_rank_key_copy(ifname);
5334    rank_dict = CFDictionaryGetValue(services_info, if_rank_key);
5335    CFRelease(if_rank_key);
5336    if_rank_set(ifname, rank_dict);
5337    return;
5338}
5339
5340static void
5341append_serviceIDs_for_interface(CFMutableArrayRef services_changed,
5342				CFStringRef ifname)
5343{
5344    int			count;
5345    int			i;
5346    void * *		keys;
5347#define N_KEYS_VALUES_STATIC    10
5348    void *		keys_values_buf[N_KEYS_VALUES_STATIC * 2];
5349    void * *		values;
5350
5351    count = CFDictionaryGetCount(S_service_state_dict);
5352    if (count <= N_KEYS_VALUES_STATIC) {
5353	keys = keys_values_buf;
5354    } else {
5355	keys = (void * *)malloc(sizeof(*keys) * count * 2);
5356    }
5357    values = keys + count;
5358    CFDictionaryGetKeysAndValues(S_service_state_dict,
5359				 (const void * *)keys,
5360				 (const void * *)values);
5361
5362    for (i = 0; i < count; i++) {
5363	CFDictionaryRef		ipv4 = NULL;
5364	CFStringRef		interface = NULL;
5365	CFStringRef		serviceID;
5366	CFDictionaryRef		service_dict;
5367
5368	serviceID = (CFStringRef)keys[i];
5369	service_dict = (CFDictionaryRef)values[i];
5370
5371	/* check if this is a ipv4 dictionary */
5372	ipv4  = CFDictionaryGetValue(service_dict, kSCEntNetIPv4);
5373	if (ipv4 != NULL) {
5374	    interface = ipv4_dict_get_ifname(ipv4);
5375	    if (interface != NULL && CFEqual(interface, ifname)) {
5376		if (S_IPMonitor_debug & kDebugFlag1) {
5377		    my_log(LOG_DEBUG,
5378			   "Found ipv4 service %@ on interface %@.",
5379			   serviceID, ifname);
5380		}
5381
5382		my_CFArrayAppendUniqueValue(services_changed, serviceID);
5383	    }
5384	} else {
5385	    CFDictionaryRef	proto_dict;
5386
5387	    /* check if this is a ipv6 dictionary */
5388	    proto_dict = CFDictionaryGetValue(service_dict, kSCEntNetIPv6);
5389	    if (proto_dict == NULL) {
5390		continue;
5391	    }
5392	    interface = CFDictionaryGetValue(proto_dict, kSCPropInterfaceName);
5393	    if (interface != NULL && CFEqual(interface, ifname)) {
5394		if (S_IPMonitor_debug & kDebugFlag1) {
5395		    my_log(LOG_DEBUG, "Found ipv6 service %@ on interface %@.",
5396			   serviceID, ifname);
5397		}
5398
5399		my_CFArrayAppendUniqueValue(services_changed, serviceID);
5400	    }
5401	}
5402    }
5403
5404    if (keys != keys_values_buf) {
5405	free(keys);
5406    }
5407}
5408
5409static __inline__ const char *
5410get_changed_str(CFStringRef serviceID, CFStringRef entity, CFDictionaryRef old_dict)
5411{
5412    CFDictionaryRef new_dict    = NULL;
5413
5414    if (serviceID != NULL) {
5415	new_dict = service_dict_get(serviceID, entity);
5416    }
5417
5418    if (old_dict == NULL) {
5419	if (new_dict != NULL) {
5420	    return "+";
5421	}
5422    } else {
5423	if (new_dict == NULL) {
5424	    return "-";
5425	} else if (!CFEqual(old_dict, new_dict)) {
5426	    return "!";
5427	}
5428    }
5429    return "";
5430}
5431
5432static CF_RETURNS_RETAINED CFStringRef
5433generate_log_changes(nwi_state_t	changes_state,
5434		     boolean_t		dns_changed,
5435		     boolean_t		dnsinfo_changed,
5436		     CFDictionaryRef	old_primary_dns,
5437		     boolean_t		proxy_changed,
5438		     CFDictionaryRef	old_primary_proxy,
5439		     boolean_t		smb_changed,
5440		     CFDictionaryRef	old_primary_smb
5441		     )
5442{
5443    int idx;
5444    CFMutableStringRef log_output;
5445    nwi_ifstate_t scan;
5446
5447    log_output = CFStringCreateMutable(NULL, 0);
5448
5449    if (changes_state != NULL) {
5450	for (idx = 0; idx < sizeof(nwi_af_list)/sizeof(nwi_af_list[0]); idx++) {
5451	    CFMutableStringRef changes = NULL;
5452	    CFMutableStringRef primary_str = NULL;
5453
5454	    scan = nwi_state_get_first_ifstate(changes_state, nwi_af_list[idx]);
5455
5456	    while (scan != NULL) {
5457		const char * changed_str;
5458
5459		changed_str = nwi_ifstate_get_diff_str(scan);
5460		if (changed_str != NULL) {
5461		    void *		address;
5462		    const char *	addr_str;
5463		    char		ntopbuf[INET6_ADDRSTRLEN];
5464
5465		    address = (void *)nwi_ifstate_get_address(scan);
5466		    addr_str = inet_ntop(scan->af, address,
5467				ntopbuf, sizeof(ntopbuf));
5468
5469		    if (primary_str ==  NULL) {
5470			primary_str = CFStringCreateMutable(NULL, 0);
5471			CFStringAppendFormat(primary_str, NULL, CFSTR("%s%s:%s"),
5472					     nwi_ifstate_get_ifname(scan),
5473					     changed_str, addr_str);
5474		    } else {
5475			if (changes == NULL) {
5476			    changes = CFStringCreateMutable(NULL, 0);
5477			}
5478			CFStringAppendFormat(changes, NULL, CFSTR(", %s"),
5479					     nwi_ifstate_get_ifname(scan));
5480			if (strcmp(changed_str,  "") != 0) {
5481			    CFStringAppendFormat(changes, NULL, CFSTR("%s:%s"),
5482						 changed_str, addr_str);
5483			}
5484		    }
5485		}
5486		scan = nwi_ifstate_get_next(scan, scan->af);
5487	    }
5488
5489	    if (primary_str != NULL) {
5490		CFStringAppendFormat(log_output, NULL, CFSTR(" %s(%@"),
5491				     nwi_af_list[idx] == AF_INET ? "v4" : "v6",
5492				     primary_str);
5493
5494		if (changes != NULL && CFStringGetLength(changes) != 0) {
5495		    CFStringAppendFormat(log_output, NULL, CFSTR("%@"),
5496					 changes);
5497		}
5498		CFStringAppendFormat(log_output, NULL, CFSTR(")"));
5499
5500		my_CFRelease(&primary_str);
5501		my_CFRelease(&changes);
5502	    }
5503	}
5504    }
5505
5506    if (dns_changed || dnsinfo_changed) {
5507	const char    *str;
5508
5509	str = get_changed_str(S_primary_dns, kSCEntNetDNS, old_primary_dns);
5510	if ((strcmp(str, "") == 0) && dnsinfo_changed) {
5511	    str = "*";	    // dnsinfo change w/no change to primary
5512	}
5513	CFStringAppendFormat(log_output, NULL, CFSTR(" DNS%s"), str);
5514    } else if (S_primary_dns != NULL) {
5515	CFStringAppendFormat(log_output, NULL, CFSTR(" DNS"));
5516    }
5517
5518    if (proxy_changed) {
5519	const char    *str;
5520
5521	str = get_changed_str(S_primary_proxies, kSCEntNetProxies, old_primary_proxy);
5522	CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy%s"), str);
5523    } else if (S_primary_proxies != NULL) {
5524	CFStringAppendFormat(log_output, NULL, CFSTR(" Proxy"));
5525    }
5526
5527#if	!TARGET_OS_IPHONE
5528    if (smb_changed) {
5529	const char    *str;
5530
5531	str = get_changed_str(S_primary_smb, kSCEntNetSMB, old_primary_smb);
5532	CFStringAppendFormat(log_output, NULL, CFSTR(" SMB%s"), str);
5533    } else if (S_primary_smb != NULL) {
5534	CFStringAppendFormat(log_output, NULL, CFSTR(" SMB"));
5535    }
5536#endif	// !TARGET_OS_IPHONE
5537
5538    return log_output;
5539}
5540
5541#pragma mark -
5542#pragma mark Network changed notification
5543
5544static dispatch_queue_t
5545__network_change_queue()
5546{
5547    static dispatch_once_t	once;
5548    static dispatch_queue_t	q;
5549
5550    dispatch_once(&once, ^{
5551	q = dispatch_queue_create("network change queue", NULL);
5552    });
5553
5554    return q;
5555}
5556
5557// Note: must run on __network_change_queue()
5558static void
5559post_network_change_when_ready()
5560{
5561    int		    status;
5562
5563    if (S_network_change_needed == 0) {
5564	return;
5565    }
5566
5567    if (!S_network_change_timeout &&
5568	(!S_dnsinfo_synced || !S_nwi_synced)) {
5569	// if we [still] need to wait for the DNS configuration
5570	// or network information changes to be ack'd
5571
5572	if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5573	    my_log(LOG_DEBUG,
5574		   "Defer \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s, %s)",
5575		   S_dnsinfo_synced ? "DNS" : "!DNS",
5576		   S_nwi_synced     ? "nwi" : "!nwi");
5577	}
5578	return;
5579    }
5580
5581    // cancel any running timer
5582    if (S_network_change_timer != NULL) {
5583	dispatch_source_cancel(S_network_change_timer);
5584	dispatch_release(S_network_change_timer);
5585	S_network_change_timer = NULL;
5586	S_network_change_timeout = FALSE;
5587    }
5588
5589    // set (and log?) the post time
5590    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5591	struct timeval  elapsed;
5592	struct timeval  end;
5593
5594	(void) gettimeofday(&end, NULL);
5595	timersub(&end, &S_network_change_start, &elapsed);
5596
5597#define	QUERY_TIME__FMT	"%d.%6.6d"
5598#define	QUERY_TIME__DIV	1
5599
5600	my_log(LOG_DEBUG,
5601	       "Post \"" _SC_NOTIFY_NETWORK_CHANGE "\" (%s: " QUERY_TIME__FMT ": 0x%x)",
5602	       S_network_change_timeout ? "timeout" : "delayed",
5603	       elapsed.tv_sec,
5604	       elapsed.tv_usec / QUERY_TIME__DIV,
5605	       S_network_change_needed);
5606    }
5607
5608    if ((S_network_change_needed & NETWORK_CHANGE_NET) != 0) {
5609	status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_NWI);
5610	if (status != NOTIFY_STATUS_OK) {
5611	    my_log(LOG_ERR,
5612		   "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_NWI ") failed: error=%ld", status);
5613	}
5614    }
5615
5616    if ((S_network_change_needed & NETWORK_CHANGE_DNS) != 0) {
5617	status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_DNS);
5618	if (status != NOTIFY_STATUS_OK) {
5619	    my_log(LOG_ERR,
5620		   "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_DNS ") failed: error=%ld", status);
5621	}
5622    }
5623
5624    if ((S_network_change_needed & NETWORK_CHANGE_PROXY) != 0) {
5625	status = notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
5626	if (status != NOTIFY_STATUS_OK) {
5627	    my_log(LOG_ERR,
5628		   "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE_PROXY ") failed: error=%ld", status);
5629	}
5630    }
5631
5632    status = notify_post(_SC_NOTIFY_NETWORK_CHANGE);
5633    if (status != NOTIFY_STATUS_OK) {
5634	my_log(LOG_ERR,
5635	       "IPMonitor: notify_post(" _SC_NOTIFY_NETWORK_CHANGE ") failed: error=%ld", status);
5636    }
5637
5638    S_network_change_needed = 0;
5639    return;
5640}
5641
5642#define TRAILING_EDGE_TIMEOUT_NSEC	5 * NSEC_PER_SEC    // 5s
5643
5644// Note: must run on __network_change_queue()
5645static void
5646post_network_change(uint32_t change)
5647{
5648    if (S_network_change_needed == 0) {
5649	// set the start time
5650	(void) gettimeofday(&S_network_change_start, NULL);
5651    }
5652
5653    // indicate that we need to post a change for ...
5654    S_network_change_needed |= change;
5655
5656    // cancel any running timer
5657    if (S_network_change_timer != NULL) {
5658	dispatch_source_cancel(S_network_change_timer);
5659	dispatch_release(S_network_change_timer);
5660	S_network_change_timer = NULL;
5661	S_network_change_timeout = FALSE;
5662    }
5663
5664    // if needed, start new timer
5665    if (!S_dnsinfo_synced || !S_nwi_synced) {
5666	S_network_change_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
5667							0,
5668							0,
5669							__network_change_queue());
5670	dispatch_source_set_event_handler(S_network_change_timer, ^{
5671	    S_network_change_timeout = TRUE;
5672	    post_network_change_when_ready();
5673	});
5674	dispatch_source_set_timer(S_network_change_timer,
5675				  dispatch_time(DISPATCH_TIME_NOW,
5676						TRAILING_EDGE_TIMEOUT_NSEC),	// start
5677				  0,						// interval
5678				  10 * NSEC_PER_MSEC);				// leeway
5679	dispatch_resume(S_network_change_timer);
5680    }
5681
5682    post_network_change_when_ready();
5683
5684    return;
5685}
5686
5687#pragma mark -
5688#pragma mark Process network (SCDynamicStore) changes
5689
5690static void
5691IPMonitorNotify(SCDynamicStoreRef session, CFArrayRef changed_keys,
5692		void * not_used)
5693{
5694    CFIndex		count;
5695    uint32_t		changes			= 0;
5696    nwi_state_t		changes_state		= NULL;
5697    boolean_t		dns_changed		= FALSE;
5698    boolean_t		dnsinfo_changed		= FALSE;
5699    boolean_t		global_ipv4_changed	= FALSE;
5700    boolean_t		global_ipv6_changed	= FALSE;
5701    int			i;
5702    CFMutableArrayRef	if_rank_changes		= NULL;
5703    keyChangeList	keys;
5704    CFIndex		n;
5705    CFStringRef		network_change_msg	= NULL;
5706    int			n_services;
5707    int			n_service_order		= 0;
5708    nwi_state_t		old_nwi_state		= NULL;
5709    CFDictionaryRef	old_primary_dns		= NULL;
5710    CFDictionaryRef	old_primary_proxy	= NULL;
5711#if	!TARGET_OS_IPHONE
5712    CFDictionaryRef	old_primary_smb		= NULL;
5713#endif	// !TARGET_OS_IPHONE
5714    boolean_t		proxies_changed		= FALSE;
5715    boolean_t		reachability_changed	= FALSE;
5716    CFArrayRef		service_order;
5717    CFMutableArrayRef	service_changes		= NULL;
5718    CFDictionaryRef	services_info		= NULL;
5719#if	!TARGET_OS_IPHONE
5720    boolean_t		smb_changed		= FALSE;
5721#endif	// !TARGET_OS_IPHONE
5722
5723    count = CFArrayGetCount(changed_keys);
5724    if (count == 0) {
5725	return;
5726    }
5727
5728    if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5729	my_log(LOG_DEBUG,
5730	       "IPMonitor: changes %@ (%d)", changed_keys, count);
5731    }
5732
5733    if (S_primary_dns != NULL) {
5734	old_primary_dns = service_dict_get(S_primary_dns, kSCEntNetDNS);
5735	if (old_primary_dns != NULL) {
5736	    old_primary_dns = CFDictionaryCreateCopy(NULL, old_primary_dns);
5737	}
5738    }
5739
5740    if (S_primary_proxies != NULL) {
5741	old_primary_proxy = service_dict_get(S_primary_proxies, kSCEntNetProxies);
5742	if (old_primary_proxy != NULL) {
5743	    old_primary_proxy = CFDictionaryCreateCopy(NULL, old_primary_proxy);
5744	}
5745    }
5746
5747#if	!TARGET_OS_IPHONE
5748    if (S_primary_smb != NULL) {
5749	old_primary_smb = service_dict_get(S_primary_smb, kSCEntNetSMB);
5750	if (old_primary_smb != NULL) {
5751	    old_primary_smb = CFDictionaryCreateCopy(NULL, old_primary_smb);
5752	}
5753    }
5754#endif	// !TARGET_OS_IPHONE
5755
5756    keyChangeListInit(&keys);
5757    service_changes = CFArrayCreateMutable(NULL, 0,
5758					   &kCFTypeArrayCallBacks);
5759
5760    for (i = 0; i < count; i++) {
5761	CFStringRef	change = CFArrayGetValueAtIndex(changed_keys, i);
5762	if (CFEqual(change, S_setup_global_ipv4)) {
5763	    global_ipv4_changed = TRUE;
5764	    global_ipv6_changed = TRUE;
5765	}
5766	else if (CFEqual(change, S_multicast_resolvers)) {
5767	    dnsinfo_changed = TRUE;
5768	}
5769	else if (CFEqual(change, S_private_resolvers)) {
5770	    dnsinfo_changed = TRUE;
5771	}
5772#if	!TARGET_OS_IPHONE
5773	else if (CFEqual(change, CFSTR(_PATH_RESOLVER_DIR))) {
5774	    dnsinfo_changed = TRUE;
5775	}
5776#endif	/* !TARGET_OS_IPHONE */
5777	else if (CFStringHasPrefix(change, S_state_service_prefix)) {
5778	    int		i;
5779	    CFStringRef serviceID;
5780
5781	    for (i = 0; i < sizeof(statusEntityNames)/sizeof(statusEntityNames[0]); i++) {
5782		if (CFStringHasSuffix(change, *statusEntityNames[i])) {
5783		    dnsinfo_changed = TRUE;
5784		    break;
5785		}
5786	    }
5787
5788	    serviceID = parse_component(change, S_state_service_prefix);
5789	    if (serviceID) {
5790		my_CFArrayAppendUniqueValue(service_changes, serviceID);
5791		CFRelease(serviceID);
5792	    }
5793	}
5794	else if (CFStringHasPrefix(change, S_setup_service_prefix)) {
5795	    int j;
5796
5797	    CFStringRef serviceID = parse_component(change,
5798						    S_setup_service_prefix);
5799	    if (serviceID) {
5800		my_CFArrayAppendUniqueValue(service_changes, serviceID);
5801		CFRelease(serviceID);
5802	    }
5803
5804	    for (j = 0; j < sizeof(transientInterfaceEntityNames)/sizeof(transientInterfaceEntityNames[0]); j++) {
5805		if (CFStringHasSuffix(change, *transientInterfaceEntityNames[j])) {
5806		    reachability_changed = TRUE;
5807		    break;
5808		}
5809	    }
5810
5811	    if (CFStringHasSuffix(change, kSCEntNetInterface)) {
5812		 reachability_changed = TRUE;
5813	    }
5814
5815
5816	}
5817	else if (CFStringHasSuffix(change, kSCEntNetService)) {
5818	    CFStringRef ifname = my_CFStringCopyComponent(change, CFSTR("/"), 3);
5819
5820	    if (ifname != NULL) {
5821		if (if_rank_changes == NULL) {
5822		    if_rank_changes = CFArrayCreateMutable(NULL, 0,
5823							   &kCFTypeArrayCallBacks);
5824		}
5825		my_CFArrayAppendUniqueValue(if_rank_changes, ifname);
5826		CFRelease(ifname);
5827	    }
5828	}
5829    }
5830
5831    /* determine which serviceIDs are impacted by the interface rank changes */
5832    if (if_rank_changes != NULL) {
5833	n = CFArrayGetCount(if_rank_changes);
5834	for (i = 0; i < n; i++) {
5835	    CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i);
5836
5837	    if (S_IPMonitor_debug & kDebugFlag1) {
5838		my_log(LOG_DEBUG, "Interface rank changed %@",
5839		       ifname);
5840	    }
5841	    append_serviceIDs_for_interface(service_changes, ifname);
5842	}
5843    }
5844
5845    /* grab a snapshot of everything we need */
5846    services_info = services_info_copy(session, service_changes,
5847				       if_rank_changes);
5848    service_order = service_order_get(services_info);
5849    if (service_order != NULL) {
5850	n_service_order = CFArrayGetCount(service_order);
5851	if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5852	    my_log(LOG_DEBUG,
5853		   "IPMonitor: service_order %@ ", service_order);
5854	}
5855    }
5856
5857    if (if_rank_changes != NULL) {
5858	for (i = 0; i < n; i++) {
5859	    CFStringRef ifname = CFArrayGetValueAtIndex(if_rank_changes, i);
5860	    update_interface_rank(services_info, ifname);
5861	}
5862    }
5863
5864    n = CFArrayGetCount(service_changes);
5865    for (i = 0; i < n; i++) {
5866	uint32_t	changes;
5867	CFStringRef	serviceID;
5868
5869	serviceID = CFArrayGetValueAtIndex(service_changes, i);
5870	changes = service_changed(services_info, serviceID);
5871	if ((changes & (1 << kEntityTypeServiceOptions)) != 0) {
5872	    /* if __Service__ (e.g. PrimaryRank) changed */
5873	    global_ipv4_changed = TRUE;
5874	    global_ipv6_changed = TRUE;
5875	}
5876	else {
5877	    if ((changes & (1 << kEntityTypeIPv4)) != 0) {
5878		global_ipv4_changed = TRUE;
5879		dnsinfo_changed = TRUE;
5880		proxies_changed = TRUE;
5881	    }
5882	    if ((changes & (1 << kEntityTypeIPv6)) != 0) {
5883		global_ipv6_changed = TRUE;
5884		dnsinfo_changed = TRUE;
5885		proxies_changed = TRUE;
5886	    }
5887	}
5888	if ((changes & (1 << kEntityTypeDNS)) != 0) {
5889	    if (S_primary_dns != NULL && CFEqual(S_primary_dns, serviceID)) {
5890		dns_changed = TRUE;
5891	    }
5892	    dnsinfo_changed = TRUE;
5893	}
5894	if ((changes & (1 << kEntityTypeProxies)) != 0) {
5895	    proxies_changed = TRUE;
5896	}
5897#if	!TARGET_OS_IPHONE
5898	if ((changes & (1 << kEntityTypeSMB)) != 0) {
5899	    if (S_primary_smb != NULL && CFEqual(S_primary_smb, serviceID)) {
5900		smb_changed = TRUE;
5901	    }
5902	}
5903#endif
5904    }
5905
5906	if ((changes & (1 <<kEntityTypeVPNStatus)) != 0) {
5907	    global_ipv4_changed = TRUE;
5908	    global_ipv6_changed = TRUE;
5909	}
5910
5911    /* ensure S_nwi_state can hold as many services as we have currently */
5912    n_services = CFDictionaryGetCount(S_service_state_dict);
5913    old_nwi_state = nwi_state_copy_priv(S_nwi_state);
5914    S_nwi_state = nwi_state_new(S_nwi_state, n_services);
5915
5916    if (global_ipv4_changed) {
5917	if (S_ipv4_results != NULL) {
5918	    ElectionResultsRelease(S_ipv4_results);
5919	}
5920	S_ipv4_results
5921	    = ElectionResultsCopy(elect_ipv4, service_order, n_service_order);
5922	if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5923	    ElectionResultsLog(LOG_DEBUG, S_ipv4_results, "IPv4");
5924	}
5925    }
5926    if (global_ipv6_changed) {
5927	if (S_ipv6_results != NULL) {
5928	    ElectionResultsRelease(S_ipv6_results);
5929	}
5930	S_ipv6_results
5931	    = ElectionResultsCopy(elect_ipv6, service_order, n_service_order);
5932	if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5933	    ElectionResultsLog(LOG_DEBUG, S_ipv6_results, "IPv6");
5934	}
5935    }
5936    if (global_ipv4_changed || global_ipv6_changed || dnsinfo_changed) {
5937	CFStringRef		new_primary;
5938	IPv4RouteListRef	new_routelist = NULL;
5939
5940	/* IPv4 */
5941	if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5942	    my_log(LOG_DEBUG,
5943		   "IPMonitor: electing IPv4 primary");
5944	}
5945	new_primary = ElectionResultsCopyPrimary(S_ipv4_results,
5946						 S_ipv6_results,
5947						 S_nwi_state, AF_INET,
5948						 &new_routelist,
5949						 services_info);
5950	(void)set_new_primary(&S_primary_ipv4, new_primary, "IPv4");
5951	update_ipv4(S_primary_ipv4, new_routelist, &keys);
5952	my_CFRelease(&new_primary);
5953
5954	/* IPv6 */
5955	if ((S_IPMonitor_debug & kDebugFlag1) != 0) {
5956	    my_log(LOG_DEBUG,
5957		   "IPMonitor: electing IPv6 primary");
5958	}
5959	new_primary = ElectionResultsCopyPrimary(S_ipv6_results,
5960						 S_ipv4_results,
5961						 S_nwi_state, AF_INET6,
5962						 NULL,
5963						 services_info);
5964	(void)set_new_primary(&S_primary_ipv6, new_primary, "IPv6");
5965	update_ipv6(S_primary_ipv6, &keys);
5966	my_CFRelease(&new_primary);
5967    }
5968
5969    if (global_ipv4_changed || global_ipv6_changed) {
5970	CFStringRef	new_primary_dns	    = NULL;
5971	CFStringRef	new_primary_proxies = NULL;
5972#if	!TARGET_OS_IPHONE
5973	CFStringRef	new_primary_smb	    = NULL;
5974#endif	/* !TARGET_OS_IPHONE */
5975
5976	if (S_primary_ipv4 != NULL && S_primary_ipv6 != NULL) {
5977	    /* decide between IPv4 and IPv6 */
5978	    if (rank_service_entity(S_ipv4_service_rank_dict,
5979				    S_primary_ipv4, kSCEntNetDNS)
5980		<= rank_service_entity(S_ipv6_service_rank_dict,
5981				       S_primary_ipv6, kSCEntNetDNS)) {
5982		new_primary_dns = S_primary_ipv4;
5983	    }
5984	    else {
5985		new_primary_dns = S_primary_ipv6;
5986	    }
5987	    if (rank_service_entity(S_ipv4_service_rank_dict,
5988				    S_primary_ipv4, kSCEntNetProxies)
5989		<= rank_service_entity(S_ipv6_service_rank_dict,
5990				       S_primary_ipv6, kSCEntNetProxies)) {
5991		new_primary_proxies = S_primary_ipv4;
5992	    }
5993	    else {
5994		new_primary_proxies = S_primary_ipv6;
5995	    }
5996#if	!TARGET_OS_IPHONE
5997	    if (rank_service_entity(S_ipv4_service_rank_dict,
5998				    S_primary_ipv4, kSCEntNetSMB)
5999		<= rank_service_entity(S_ipv6_service_rank_dict,
6000				       S_primary_ipv6, kSCEntNetSMB)) {
6001		new_primary_smb = S_primary_ipv4;
6002	    }
6003	    else {
6004		new_primary_smb = S_primary_ipv6;
6005	    }
6006#endif	/* !TARGET_OS_IPHONE */
6007
6008	}
6009	else if (S_primary_ipv6 != NULL) {
6010	    new_primary_dns     = S_primary_ipv6;
6011	    new_primary_proxies = S_primary_ipv6;
6012#if	!TARGET_OS_IPHONE
6013	    new_primary_smb     = S_primary_ipv6;
6014#endif	/* !TARGET_OS_IPHONE */
6015	}
6016	else if (S_primary_ipv4 != NULL) {
6017	    new_primary_dns     = S_primary_ipv4;
6018	    new_primary_proxies = S_primary_ipv4;
6019#if	!TARGET_OS_IPHONE
6020	    new_primary_smb     = S_primary_ipv4;
6021#endif	/* !TARGET_OS_IPHONE */
6022	}
6023
6024	if (set_new_primary(&S_primary_dns, new_primary_dns, "DNS")) {
6025	    dns_changed = TRUE;
6026	    dnsinfo_changed = TRUE;
6027	}
6028	if (set_new_primary(&S_primary_proxies, new_primary_proxies, "Proxies")) {
6029	    proxies_changed = TRUE;
6030	}
6031#if	!TARGET_OS_IPHONE
6032	if (set_new_primary(&S_primary_smb, new_primary_smb, "SMB")) {
6033	    smb_changed = TRUE;
6034	}
6035#endif	/* !TARGET_OS_IPHONE */
6036    }
6037
6038    if (!proxies_changed && dnsinfo_changed &&
6039	((G_supplemental_proxies_follow_dns != NULL) && CFBooleanGetValue(G_supplemental_proxies_follow_dns))) {
6040	proxies_changed = TRUE;
6041    }
6042
6043    changes_state = nwi_state_diff(old_nwi_state, S_nwi_state);
6044
6045    if (global_ipv4_changed || global_ipv6_changed || dnsinfo_changed || reachability_changed) {
6046	if (S_nwi_state != NULL) {
6047	    S_nwi_state->generation_count = mach_absolute_time();
6048	    if (global_ipv4_changed || global_ipv6_changed || reachability_changed) {
6049		SCNetworkReachabilityFlags reach_flags_v4 = 0;
6050		SCNetworkReachabilityFlags reach_flags_v6 = 0;
6051
6052		GetReachabilityFlagsFromTransientServices(services_info,
6053							  &reach_flags_v4,
6054							  &reach_flags_v6);
6055
6056		_nwi_state_set_reachability_flags(S_nwi_state, reach_flags_v4, reach_flags_v6);
6057	    }
6058
6059	    /* Update the per-interface generation count */
6060	    _nwi_state_update_interface_generations(old_nwi_state, S_nwi_state, changes_state);
6061	}
6062
6063	if (update_nwi(S_nwi_state)) {
6064	    changes |= NETWORK_CHANGE_NET;
6065
6066	    /*
6067	     * the DNS configuration includes per-resolver configuration
6068	     * reachability flags that are based on the nwi state.  Let's
6069	     * make sure that we check for changes
6070	     */
6071	    dnsinfo_changed = TRUE;
6072	}
6073    }
6074    if (dns_changed) {
6075	if (update_dns(services_info, S_primary_dns, &keys)) {
6076	    changes |= NETWORK_CHANGE_DNS;
6077	    dnsinfo_changed = TRUE;
6078	} else {
6079	    dns_changed = FALSE;
6080	}
6081    }
6082    if (dnsinfo_changed) {
6083	if (update_dnsinfo(services_info, S_primary_dns, &keys, service_order)) {
6084	    changes |= NETWORK_CHANGE_DNS;
6085	} else {
6086	    dnsinfo_changed = FALSE;
6087	}
6088    }
6089    if (proxies_changed) {
6090	// if proxy change OR supplemental Proxies follow supplemental DNS
6091	if (update_proxies(services_info, S_primary_proxies, &keys, service_order)) {
6092	    changes |= NETWORK_CHANGE_PROXY;
6093	} else {
6094	    proxies_changed = FALSE;
6095	}
6096    }
6097#if	!TARGET_OS_IPHONE
6098    if (smb_changed) {
6099	if (update_smb(services_info, S_primary_smb, &keys)) {
6100	    changes |= NETWORK_CHANGE_SMB;
6101	} else {
6102	    smb_changed = FALSE;
6103	}
6104    }
6105#endif	/* !TARGET_OS_IPHONE */
6106    my_CFRelease(&service_changes);
6107    my_CFRelease(&services_info);
6108    my_CFRelease(&if_rank_changes);
6109
6110    if (changes != 0) {
6111	network_change_msg =
6112	    generate_log_changes(changes_state,
6113				 dns_changed,
6114				 dnsinfo_changed,
6115				 old_primary_dns,
6116				 proxies_changed,
6117				 old_primary_proxy,
6118#if	!TARGET_OS_IPHONE
6119				 smb_changed,
6120				 old_primary_smb
6121#else	// !TARGET_OS_IPHONE
6122				 FALSE,		// smb_changed
6123				 NULL		// old_primary_smb
6124#endif	// !TARGET_OS_IPHONE
6125				 );
6126    }
6127
6128    keyChangeListApplyToStore(&keys, session);
6129    my_CFRelease(&old_primary_dns);
6130    my_CFRelease(&old_primary_proxy);
6131#if	!TARGET_OS_IPHONE
6132    my_CFRelease(&old_primary_smb);
6133#endif	// !TARGET_OS_IPHONE
6134
6135    if (changes != 0) {
6136	dispatch_async(__network_change_queue(), ^{
6137	    post_network_change(changes);
6138	});
6139    }
6140
6141    if ((network_change_msg != NULL) && (CFStringGetLength(network_change_msg) != 0)) {
6142	my_log(LOG_NOTICE, "network changed:%@", network_change_msg);
6143    } else if (keyChangeListActive(&keys)) {
6144	my_log(LOG_NOTICE, "network changed.");
6145    } else {
6146	my_log(LOG_DEBUG, "network event w/no changes");
6147    }
6148
6149    my_CFRelease(&network_change_msg);
6150
6151    if (changes_state != NULL) {
6152	nwi_state_release(changes_state);
6153    }
6154    if (old_nwi_state != NULL) {
6155	nwi_state_release(old_nwi_state);
6156    }
6157    keyChangeListFree(&keys);
6158    return;
6159}
6160
6161static void
6162watch_proxies()
6163{
6164#if	!TARGET_OS_IPHONE
6165    const _scprefs_observer_type type = scprefs_observer_type_mcx;
6166#else
6167    const _scprefs_observer_type type = scprefs_observer_type_global;
6168#endif
6169    static dispatch_queue_t proxy_cb_queue;
6170
6171    proxy_cb_queue = dispatch_queue_create("com.apple.SystemConfiguration.IPMonitor.proxy", NULL);
6172    _scprefs_observer_watch(type,
6173			     "com.apple.SystemConfiguration.plist",
6174			     proxy_cb_queue,
6175			     ^{
6176				 SCDynamicStoreNotifyValue(NULL, S_state_global_proxies);
6177				 notify_post(_SC_NOTIFY_NETWORK_CHANGE_PROXY);
6178				 my_log(LOG_DEBUG, "IPMonitor: Notifying:\n%@",
6179					S_state_global_proxies);
6180			     });
6181    return;
6182}
6183
6184#if	((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6185#include "IPMonitorControlPrefs.h"
6186
6187__private_extern__ SCLoggerRef
6188my_log_get_logger()
6189{
6190    return (S_IPMonitor_logger);
6191}
6192
6193static void
6194prefs_changed(__unused SCPreferencesRef prefs)
6195{
6196    if (S_bundle_logging_verbose || IPMonitorControlPrefsIsVerbose()) {
6197	S_IPMonitor_debug = kDebugFlagDefault;
6198	S_IPMonitor_verbose = TRUE;
6199	SCLoggerSetFlags(S_IPMonitor_logger, kSCLoggerFlagsFile | kSCLoggerFlagsDefault);
6200	my_log(LOG_DEBUG, "IPMonitor: Setting logging verbose mode on.");
6201    } else {
6202	my_log(LOG_DEBUG, "IPMonitor: Setting logging verbose mode off.");
6203	S_IPMonitor_debug = 0;
6204	S_IPMonitor_verbose = FALSE;
6205	SCLoggerSetFlags(S_IPMonitor_logger, kSCLoggerFlagsDefault);
6206    }
6207    return;
6208}
6209
6210#define LOGGER_ID CFSTR("com.apple.networking.IPMonitor")
6211static void
6212my_log_init()
6213{
6214    if (S_IPMonitor_logger != NULL) {
6215	return;
6216    }
6217    S_IPMonitor_logger = SCLoggerCreate(LOGGER_ID);
6218    return;
6219
6220}
6221
6222#else	// ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6223
6224static void
6225my_log_init()
6226{
6227    return;
6228}
6229
6230#endif	// ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6231
6232
6233static void
6234ip_plugin_init()
6235{
6236    CFMutableArrayRef	keys = NULL;
6237    CFStringRef		pattern;
6238    CFMutableArrayRef	patterns = NULL;
6239    CFRunLoopSourceRef	rls = NULL;
6240
6241    if (S_is_network_boot() != 0) {
6242	S_netboot = TRUE;
6243    }
6244
6245#ifdef RTF_IFSCOPE
6246    if (S_is_scoped_routing_enabled() != 0) {
6247	S_scopedroute = TRUE;
6248    }
6249
6250    if (S_is_scoped_v6_routing_enabled() != 0) {
6251	S_scopedroute_v6 = TRUE;
6252    }
6253#endif /* RTF_IFSCOPE */
6254
6255    S_session = SCDynamicStoreCreate(NULL, CFSTR("IPMonitor"),
6256				   IPMonitorNotify, NULL);
6257    if (S_session == NULL) {
6258	my_log(LOG_ERR,
6259	       "IPMonitor ip_plugin_init SCDynamicStoreCreate failed: %s",
6260	       SCErrorString(SCError()));
6261	return;
6262    }
6263    S_state_global_ipv4
6264	= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6265						     kSCDynamicStoreDomainState,
6266						     kSCEntNetIPv4);
6267    S_state_global_ipv6
6268	= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6269						     kSCDynamicStoreDomainState,
6270						     kSCEntNetIPv6);
6271    S_state_global_dns
6272	= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6273						     kSCDynamicStoreDomainState,
6274						     kSCEntNetDNS);
6275    S_state_global_proxies
6276	= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6277						     kSCDynamicStoreDomainState,
6278						     kSCEntNetProxies);
6279#if	!TARGET_OS_IPHONE
6280    S_state_global_smb
6281	= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6282						     kSCDynamicStoreDomainState,
6283						     kSCEntNetSMB);
6284#endif	/* !TARGET_OS_IPHONE */
6285    S_setup_global_ipv4
6286	= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6287						     kSCDynamicStoreDomainSetup,
6288						     kSCEntNetIPv4);
6289    S_state_service_prefix
6290	= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6291						      kSCDynamicStoreDomainState,
6292						      CFSTR(""),
6293						      NULL);
6294    S_setup_service_prefix
6295	= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
6296						      kSCDynamicStoreDomainSetup,
6297						      CFSTR(""),
6298						      NULL);
6299    S_service_state_dict
6300	= CFDictionaryCreateMutable(NULL, 0,
6301				    &kCFTypeDictionaryKeyCallBacks,
6302				    &kCFTypeDictionaryValueCallBacks);
6303
6304    S_ipv4_service_rank_dict
6305	= CFDictionaryCreateMutable(NULL, 0,
6306				    &kCFTypeDictionaryKeyCallBacks,
6307				    &kCFTypeDictionaryValueCallBacks);
6308
6309    S_ipv6_service_rank_dict
6310	= CFDictionaryCreateMutable(NULL, 0,
6311				    &kCFTypeDictionaryKeyCallBacks,
6312				    &kCFTypeDictionaryValueCallBacks);
6313
6314    keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
6315    patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
6316
6317    /* register for State: and Setup: per-service notifications */
6318    add_service_keys(kSCCompAnyRegex, keys, patterns);
6319
6320    pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetPPP);
6321    CFArrayAppendValue(patterns, pattern);
6322    CFRelease(pattern);
6323
6324    pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetVPN);
6325    CFArrayAppendValue(patterns, pattern);
6326    CFRelease(pattern);
6327
6328    pattern = setup_service_key(kSCCompAnyRegex, kSCEntNetInterface);
6329    CFArrayAppendValue(patterns, pattern);
6330    CFRelease(pattern);
6331
6332    /* register for State: per-service PPP/VPN/IPSec status notifications */
6333    add_status_keys(kSCCompAnyRegex, patterns);
6334
6335    /* register for interface rank notifications */
6336    pattern = if_rank_key_copy(kSCCompAnyRegex);
6337    CFArrayAppendValue(patterns, pattern);
6338    CFRelease(pattern);
6339
6340    /* add notifier for ServiceOrder/PPPOverridePrimary changes for IPv4 */
6341    CFArrayAppendValue(keys, S_setup_global_ipv4);
6342
6343    /* add notifier for multicast DNS configuration (Bonjour/.local) */
6344    S_multicast_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
6345						    kSCDynamicStoreDomainState,
6346						    kSCCompNetwork,
6347						    CFSTR(kDNSServiceCompMulticastDNS));
6348    CFArrayAppendValue(keys, S_multicast_resolvers);
6349
6350    /* add notifier for private DNS configuration (Back to My Mac) */
6351    S_private_resolvers = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
6352						  kSCDynamicStoreDomainState,
6353						  kSCCompNetwork,
6354						  CFSTR(kDNSServiceCompPrivateDNS));
6355    CFArrayAppendValue(keys, S_private_resolvers);
6356
6357    if (!SCDynamicStoreSetNotificationKeys(S_session, keys, patterns)) {
6358	my_log(LOG_ERR,
6359	       "IPMonitor ip_plugin_init "
6360	       "SCDynamicStoreSetNotificationKeys failed: %s",
6361	      SCErrorString(SCError()));
6362	goto done;
6363    }
6364
6365    rls = SCDynamicStoreCreateRunLoopSource(NULL, S_session, 0);
6366    if (rls == NULL) {
6367	my_log(LOG_ERR,
6368	       "IPMonitor ip_plugin_init "
6369	       "SCDynamicStoreCreateRunLoopSource failed: %s",
6370	       SCErrorString(SCError()));
6371	goto done;
6372    }
6373
6374    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
6375    CFRelease(rls);
6376
6377    /* initialize dns configuration */
6378    (void)dns_configuration_set(NULL, NULL, NULL, NULL, NULL);
6379#if	!TARGET_OS_IPHONE
6380    empty_dns();
6381#endif	/* !TARGET_OS_IPHONE */
6382    (void)SCDynamicStoreRemoveValue(S_session, S_state_global_dns);
6383
6384#if	!TARGET_OS_IPHONE
6385    /* initialize SMB configuration */
6386    (void)SCDynamicStoreRemoveValue(S_session, S_state_global_smb);
6387#endif	/* !TARGET_OS_IPHONE */
6388
6389    if_rank_dict_init();
6390    watch_proxies();
6391
6392  done:
6393    my_CFRelease(&keys);
6394    my_CFRelease(&patterns);
6395    return;
6396}
6397
6398__private_extern__
6399void
6400prime_IPMonitor()
6401{
6402    /* initialize multicast route */
6403    update_ipv4(NULL, NULL, NULL);
6404    return;
6405}
6406
6407static boolean_t
6408S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
6409		    boolean_t def)
6410{
6411    CFBooleanRef	b;
6412    boolean_t		ret = def;
6413
6414    b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
6415    if (b != NULL) {
6416	ret = CFBooleanGetValue(b);
6417    }
6418    return (ret);
6419}
6420
6421__private_extern__
6422void
6423load_IPMonitor(CFBundleRef bundle, Boolean bundleVerbose)
6424{
6425    CFDictionaryRef	info_dict;
6426
6427    info_dict = CFBundleGetInfoDictionary(bundle);
6428
6429    if (info_dict != NULL) {
6430	S_append_state
6431	    = S_get_plist_boolean(info_dict,
6432				  CFSTR("AppendStateArrayToSetupArray"),
6433				  FALSE);
6434    }
6435    if (bundleVerbose) {
6436	S_IPMonitor_debug = kDebugFlagDefault;
6437	S_bundle_logging_verbose = bundleVerbose;
6438	S_IPMonitor_verbose = TRUE;
6439    }
6440
6441    my_log_init();
6442
6443#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6444    /* register to receive changes to verbose and read the initial setting  */
6445    IPMonitorControlPrefsInit(CFRunLoopGetCurrent(), prefs_changed);
6446    prefs_changed(NULL);
6447
6448#endif	// ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
6449
6450    load_DNSConfiguration(bundle,			// bundle
6451			  S_IPMonitor_logger,		// SCLogger
6452			  &S_IPMonitor_verbose,		// bundleVerbose
6453			  ^(Boolean inSync) {		// syncHandler
6454			      dispatch_async(__network_change_queue(), ^{
6455				  S_dnsinfo_synced = inSync;
6456
6457				  if (inSync &&
6458				      ((S_network_change_needed & NETWORK_CHANGE_DNS) == 0)) {
6459				      // all of the mDNSResponder ack's should result
6460				      // in a [new] network change being posted
6461				      post_network_change(NETWORK_CHANGE_DNS);
6462				  } else {
6463				      post_network_change_when_ready();
6464				  }
6465			      });
6466			  });
6467
6468    load_NetworkInformation(bundle,			// bundle
6469			    S_IPMonitor_logger,		// SCLogger
6470			    &S_IPMonitor_verbose,	// bundleVerbose
6471			    ^(Boolean inSync) {		// syncHandler
6472				dispatch_async(__network_change_queue(), ^{
6473				    S_nwi_synced = inSync;
6474				    post_network_change_when_ready();
6475				});
6476			    });
6477
6478    dns_configuration_init(bundle);
6479
6480    proxy_configuration_init(bundle);
6481
6482    ip_plugin_init();
6483
6484#if	!TARGET_OS_IPHONE
6485    if (S_session != NULL) {
6486	dns_configuration_monitor(S_session, IPMonitorNotify);
6487    }
6488#endif	/* !TARGET_OS_IPHONE */
6489
6490#if	!TARGET_IPHONE_SIMULATOR
6491    load_hostname((S_IPMonitor_debug & kDebugFlag1) != 0);
6492#endif	/* !TARGET_IPHONE_SIMULATOR */
6493
6494#if	!TARGET_OS_IPHONE
6495    load_smb_configuration((S_IPMonitor_debug & kDebugFlag1) != 0);
6496#endif	/* !TARGET_OS_IPHONE */
6497
6498    return;
6499}
6500
6501
6502#pragma mark -
6503#pragma mark Standalone test code
6504
6505
6506#ifdef TEST_IPMONITOR
6507
6508#include "dns-configuration.c"
6509
6510#if	!TARGET_IPHONE_SIMULATOR
6511#include "set-hostname.c"
6512#endif	/* !TARGET_IPHONE_SIMULATOR */
6513
6514int
6515main(int argc, char **argv)
6516{
6517    _sc_log     = FALSE;
6518
6519    S_IPMonitor_debug = kDebugFlag1;
6520    if (argc > 1) {
6521	S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
6522    }
6523
6524    load_IPMonitor(CFBundleGetMainBundle(), FALSE);
6525    prime_IPMonitor();
6526    S_IPMonitor_debug = kDebugFlag1;
6527    CFRunLoopRun();
6528    /* not reached */
6529    exit(0);
6530    return 0;
6531}
6532#endif /* TEST_IPMONITOR */
6533
6534#ifdef TEST_IPV4_ROUTELIST
6535
6536#include "dns-configuration.c"
6537
6538#if	!TARGET_IPHONE_SIMULATOR
6539#include "set-hostname.c"
6540#endif	/* !TARGET_IPHONE_SIMULATOR */
6541
6542struct ipv4_service_contents {
6543    const char *	addr;
6544    const char *	mask;
6545    const char *	dest;
6546    const char *	router;
6547    const char *	ifname;
6548    Rank		rank;
6549    const CFStringRef	*primaryRank;
6550};
6551
6552/*
6553 *  addr	mask		dest		router		ifname	pri  primaryRank
6554 */
6555struct ipv4_service_contents en0_10 = {
6556    "10.0.0.10", "255.255.255.0", NULL,		"10.0.0.1",	"en0",	10,  NULL
6557};
6558
6559struct ipv4_service_contents en0_15 = {
6560    "10.0.0.19", "255.255.255.0", NULL,		"10.0.0.1",	"en0",	15,  NULL
6561};
6562
6563struct ipv4_service_contents en0_30 = {
6564    "10.0.0.11", "255.255.255.0", NULL,		"10.0.0.1",	"en0",	30,  NULL
6565};
6566
6567struct ipv4_service_contents en0_40 = {
6568    "10.0.0.12", "255.255.255.0", NULL,		"10.0.0.1",	"en0",	40,  NULL
6569};
6570
6571struct ipv4_service_contents en0_50 = {
6572    "10.0.0.13", "255.255.255.0", NULL,		"10.0.0.1",	"en0",	50,  NULL
6573};
6574
6575struct ipv4_service_contents en0_110 = {
6576    "192.168.2.10", "255.255.255.0", NULL,	"192.168.2.1",	"en0",	110, NULL
6577};
6578
6579struct ipv4_service_contents en0_1 = {
6580    "17.202.40.191", "255.255.252.0", NULL,	"17.202.20.1",	"en0",	1,   NULL
6581};
6582
6583struct ipv4_service_contents en1_20 = {
6584    "10.0.0.20", "255.255.255.0", NULL,		"10.0.0.1",	"en1",	20,  NULL
6585};
6586
6587struct ipv4_service_contents en1_2 = {
6588    "17.202.42.24", "255.255.252.0", NULL,	"17.202.20.1",	"en1",	2,   NULL
6589};
6590
6591struct ipv4_service_contents en1_125 = {
6592    "192.168.2.20", "255.255.255.0", NULL,	"192.168.2.1",	"en1",	125, NULL
6593};
6594
6595struct ipv4_service_contents fw0_25 = {
6596    "192.168.2.30", "255.255.255.0", NULL,	"192.168.2.1",	"fw0",	25,  NULL
6597};
6598
6599struct ipv4_service_contents fw0_21 = {
6600    "192.168.3.30", "255.255.255.0", NULL,	"192.168.3.1",	"fw0",	21,  NULL
6601};
6602
6603struct ipv4_service_contents ppp0_0_1 = {
6604    "17.219.156.22", NULL, "17.219.156.1",	"17.219.156.1",	"ppp0", 0,   NULL
6605};
6606
6607struct ipv4_service_contents en0_test6 = {
6608    "17.202.42.113",  "255.255.252.0", NULL,	"17.202.40.1",	"en0",  2,   NULL
6609};
6610struct ipv4_service_contents en1_test6 = {
6611    "17.202.42.111",  "255.255.252.0", NULL,	"17.202.40.1",	"en1",  3,   NULL
6612};
6613struct ipv4_service_contents en2_test6 = {
6614    "17.255.98.164",  "255.255.240.0", NULL,	"17.255.96.1",	"en2",  1,   NULL
6615};
6616
6617struct ipv4_service_contents en0_test7 = {
6618    "17.202.42.113",  "255.255.252.0", NULL,	"17.202.40.1",	"en0",  3,   NULL
6619};
6620struct ipv4_service_contents en1_test7 = {
6621    "17.202.42.111",  "255.255.252.0", NULL,	"17.202.40.1",	"en1",  2,   NULL
6622};
6623struct ipv4_service_contents en2_test7 = {
6624    "17.255.98.164",  "255.255.240.0", NULL,	"17.255.96.1",	"en2",  1,   NULL
6625};
6626struct ipv4_service_contents fw0_test6_and_7 = {
6627    "169.254.11.33",  "255.255.0.0", NULL,	NULL,		"fw0", 0x0ffffff,  NULL
6628};
6629
6630struct ipv4_service_contents en0_10_last = {
6631    "10.0.0.10", "255.255.255.0", NULL,		"10.0.0.1",	"en0",	10,  &kSCValNetServicePrimaryRankLast
6632};
6633
6634struct ipv4_service_contents en0_10_never = {
6635    "10.0.0.10", "255.255.255.0", NULL,		"10.0.0.1",	"en0",	10,  &kSCValNetServicePrimaryRankNever
6636};
6637
6638struct ipv4_service_contents en1_20_first = {
6639    "10.0.0.20", "255.255.255.0", NULL,		"10.0.0.1",	"en1",	20,  &kSCValNetServicePrimaryRankFirst
6640};
6641
6642struct ipv4_service_contents en1_20_never = {
6643    "10.0.0.20", "255.255.255.0", NULL,		"10.0.0.1",	"en1",	20,  &kSCValNetServicePrimaryRankNever
6644};
6645
6646struct ipv4_service_contents en0_linklocal = {
6647    "169.254.22.44", "255.255.0.0", NULL,	NULL,		"en0",	0xfffff,  NULL
6648};
6649
6650struct ipv4_service_contents * test1[] = {
6651    &en0_40,
6652    &en0_15,
6653    &fw0_25,
6654    &en0_30,
6655    &en1_20,
6656    &en0_50,
6657    &en0_10,
6658    NULL
6659};
6660
6661struct ipv4_service_contents * test2[] = {
6662    &en0_40,
6663    &fw0_25,
6664    &en0_30,
6665    &en1_20,
6666    &en0_50,
6667    &en0_10,
6668    NULL
6669};
6670
6671struct ipv4_service_contents * test3[] = {
6672    &en0_40,
6673    &en1_20,
6674    &en0_50,
6675    &en0_10,
6676    &en0_110,
6677    &en1_125,
6678    &fw0_25,
6679    &fw0_21,
6680    &en0_40,
6681    &en0_30,
6682    NULL
6683};
6684
6685struct ipv4_service_contents * test4[] = {
6686    &en0_1,
6687    &en0_40,
6688    &en0_30,
6689    &en1_20,
6690    &en1_2,
6691    NULL
6692};
6693
6694struct ipv4_service_contents * test5[] = {
6695    &ppp0_0_1,
6696    &en0_1,
6697    &en0_40,
6698    &en0_30,
6699    &en1_20,
6700    &en1_2,
6701    NULL
6702};
6703
6704struct ipv4_service_contents * test6[] = {
6705    &en0_test6,
6706    &en1_test6,
6707    &en2_test6,
6708    &fw0_test6_and_7,
6709    NULL
6710};
6711
6712struct ipv4_service_contents * test7[] = {
6713    &en0_test7,
6714    &en1_test7,
6715    &en2_test7,
6716    &fw0_test6_and_7,
6717    NULL
6718};
6719
6720struct ipv4_service_contents * test8[] = {
6721    &en0_10,
6722    &en1_20,
6723    NULL
6724};
6725
6726struct ipv4_service_contents * test9[] = {
6727    &en0_10,
6728    &en1_20_first,
6729    &fw0_25,
6730    NULL
6731};
6732
6733struct ipv4_service_contents * test10[] = {
6734    &en0_10_last,
6735    &en1_20,
6736    &fw0_25,
6737    NULL
6738};
6739
6740struct ipv4_service_contents * test11[] = {
6741    &en0_10_never,
6742    &en1_20,
6743    &fw0_25,
6744    NULL
6745};
6746
6747struct ipv4_service_contents * test12[] = {
6748    &en0_10,
6749    &en1_20,
6750    NULL
6751};
6752
6753struct ipv4_service_contents * test13[] = {
6754    &en0_10,
6755    &en1_20_never,
6756    NULL
6757};
6758
6759struct ipv4_service_contents * test14[] = {
6760    &en1_20_never,
6761    NULL
6762};
6763
6764struct ipv4_service_contents * test15[] = {
6765    &en0_linklocal,
6766    NULL
6767};
6768
6769static void
6770dict_add_string(CFMutableDictionaryRef dict, CFStringRef prop_name,
6771		const char * str)
6772{
6773    CFStringRef		prop_val;
6774
6775    if (str == NULL) {
6776	return;
6777    }
6778    prop_val = CFStringCreateWithCString(NULL,
6779					 str,
6780					 kCFStringEncodingASCII);
6781    CFDictionarySetValue(dict, prop_name, prop_val);
6782    CFRelease(prop_val);
6783    return;
6784}
6785
6786static void
6787dict_add_string_as_array(CFMutableDictionaryRef dict, CFStringRef prop_name,
6788			 const char * str)
6789{
6790    CFArrayRef		array;
6791    CFStringRef		prop_val;
6792
6793    if (str == NULL) {
6794	return;
6795    }
6796    prop_val = CFStringCreateWithCString(NULL,
6797					 str,
6798					 kCFStringEncodingASCII);
6799    array = CFArrayCreate(NULL,
6800			  (const void **)&prop_val, 1,
6801			  &kCFTypeArrayCallBacks);
6802    CFRelease(prop_val);
6803    CFDictionarySetValue(dict, prop_name, array);
6804    CFRelease(array);
6805    return;
6806}
6807
6808static CFDictionaryRef
6809make_IPv4_dict(struct ipv4_service_contents * t)
6810{
6811    CFMutableDictionaryRef	dict;
6812
6813    dict = CFDictionaryCreateMutable(NULL, 0,
6814				     &kCFTypeDictionaryKeyCallBacks,
6815				     &kCFTypeDictionaryValueCallBacks);
6816    dict_add_string_as_array(dict, kSCPropNetIPv4Addresses, t->addr);
6817    dict_add_string_as_array(dict, kSCPropNetIPv4SubnetMasks, t->mask);
6818    dict_add_string_as_array(dict, kSCPropNetIPv4DestAddresses, t->dest);
6819    dict_add_string(dict, kSCPropNetIPv4Router, t->router);
6820    dict_add_string(dict, kSCPropInterfaceName, t->ifname);
6821    return (dict);
6822}
6823
6824static IPv4RouteListRef
6825make_IPv4RouteList(struct ipv4_service_contents * * this_test)
6826{
6827    IPv4RouteListRef		r;
6828    IPv4RouteListRef		routes;
6829    char			routes_buf[IPv4RouteListComputeSize(R_STATIC)];
6830    IPv4RouteListRef		ret = NULL;
6831    struct ipv4_service_contents * *	scan_test;
6832
6833    for (scan_test = this_test; *scan_test != NULL; scan_test++) {
6834	CFDictionaryRef		dict;
6835
6836	dict = make_IPv4_dict(*scan_test);
6837	if (dict == NULL) {
6838	    fprintf(stderr, "make_IPv4_dict failed\n");
6839	    exit(1);
6840	}
6841	routes = (IPv4RouteListRef)routes_buf;
6842	routes->size = R_STATIC;
6843	routes->count = 0;
6844	r = IPv4RouteListCreateWithDictionary(routes, dict,
6845		(*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
6846	if (r == NULL) {
6847	    fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
6848	    exit(1);
6849	}
6850
6851	(*scan_test)->rank = RankMake((*scan_test)->rank, kRankAssertionDefault);
6852
6853	if ((*scan_test)->primaryRank != NULL) {
6854	    (*scan_test)->rank = RankMake((*scan_test)->rank,
6855					     PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank));
6856	}
6857
6858	if ((*scan_test)->router == NULL) {
6859	    (*scan_test)->rank = RankMake((*scan_test)->rank,
6860					    PrimaryRankGetRankAssertion(kSCValNetServicePrimaryRankLast));
6861	}
6862
6863	ret = IPv4RouteListAddRouteList(ret, 1, r, (*scan_test)->rank);
6864	if (r != routes) {
6865	    free(r);
6866	}
6867	CFRelease(dict);
6868    }
6869    return (ret);
6870}
6871
6872/*
6873 * Function: run_test
6874 * Purpose:
6875 *   Runs through the given set of routes first in the forward direction,
6876 *   then again backwards.  We should end up with exactly the same set of
6877 *   routes at the end.
6878 */
6879static boolean_t
6880run_test(const char * name, struct ipv4_service_contents * * this_test)
6881{
6882    CFStringRef			descr;
6883    boolean_t			ret = FALSE;
6884    IPv4RouteListRef		r;
6885    IPv4RouteListRef		routes;
6886    char			routes_buf[IPv4RouteListComputeSize(R_STATIC)];
6887    IPv4RouteListRef		routes1 = NULL, routes2 = NULL;
6888    struct ipv4_service_contents * *	scan_test;
6889
6890    printf("\nStarting test %s\n", name);
6891    for (scan_test = this_test; *scan_test != NULL; scan_test++) {
6892	CFDictionaryRef		dict;
6893
6894	dict = make_IPv4_dict(*scan_test);
6895	if (dict == NULL) {
6896	    fprintf(stderr, "make_IPv4_dict failed\n");
6897	    exit(1);
6898	}
6899	routes = (IPv4RouteListRef)routes_buf;
6900	routes->size = R_STATIC;
6901	routes->count = 0;
6902	r = IPv4RouteListCreateWithDictionary(routes, dict,
6903		(*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
6904	if (r == NULL) {
6905	    fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
6906	    exit(1);
6907	}
6908	if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
6909	    descr = IPv4RouteListCopyDescription(r);
6910	    SCPrint(TRUE, stdout, CFSTR("test: Adding %@\n"), descr);
6911	    CFRelease(descr);
6912	}
6913
6914	(*scan_test)->rank = RankMake((*scan_test)->rank, kRankAssertionDefault);
6915
6916	if ((*scan_test)->primaryRank != NULL) {
6917	    (*scan_test)->rank = RankMake((*scan_test)->rank,
6918					    PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank));
6919	}
6920
6921	routes1 = IPv4RouteListAddRouteList(routes1, 1, r, (*scan_test)->rank);
6922	if (r != routes) {
6923	    free(r);
6924	}
6925	CFRelease(dict);
6926    }
6927    if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
6928	if (routes1 != NULL) {
6929	    descr = IPv4RouteListCopyDescription(routes1);
6930	    SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
6931	    CFRelease(descr);
6932	}
6933    }
6934    for (scan_test--; scan_test >= this_test; scan_test--) {
6935	CFDictionaryRef		dict;
6936
6937	dict = make_IPv4_dict(*scan_test);
6938	if (dict == NULL) {
6939	    fprintf(stderr, "make_IPv4_dict failed\n");
6940	    exit(1);
6941	}
6942	routes = (IPv4RouteListRef)routes_buf;
6943	routes->size = R_STATIC;
6944	routes->count = 0;
6945	r = IPv4RouteListCreateWithDictionary(routes, dict,
6946		(*scan_test)->primaryRank ? *(*scan_test)->primaryRank : NULL);
6947	if (r == NULL) {
6948	    fprintf(stderr, "IPv4RouteListCreateWithDictionary failed\n");
6949	    exit(1);
6950	}
6951	if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
6952	    descr = IPv4RouteListCopyDescription(r);
6953	    SCPrint(TRUE, stdout, CFSTR("test: Adding %@\n"), descr);
6954	    CFRelease(descr);
6955	}
6956	if ((*scan_test)->primaryRank != NULL) {
6957	    (*scan_test)->rank = RankMake((*scan_test)->rank,
6958					    PrimaryRankGetRankAssertion(*(*scan_test)->primaryRank));
6959	}
6960
6961	routes2 = IPv4RouteListAddRouteList(routes2, 1, r, (*scan_test)->rank);
6962	if (r != routes) {
6963	    free(r);
6964	}
6965	CFRelease(dict);
6966    }
6967    if ((S_IPMonitor_debug & kDebugFlag4) != 0) {
6968	if (routes2 != NULL) {
6969	    descr = IPv4RouteListCopyDescription(routes2);
6970	    SCPrint(TRUE, stdout, CFSTR("Routes are %@\n"), descr);
6971	    CFRelease(descr);
6972	}
6973    }
6974    if ((routes1 != NULL && routes2 == NULL)
6975	|| (routes1 == NULL && routes2 != NULL)) {
6976	fprintf(stderr, "routes1 is %sNULL but routes2 is %sNULL\n",
6977	       (routes1 != NULL) ? "not " : "",
6978	       (routes2 != NULL) ? "not " : "");
6979    }
6980    else if (routes1 != NULL && routes2 != NULL) {
6981	/* check if they are different */
6982	if (routes1->count != routes2->count) {
6983	    fprintf(stderr, "routes1 count %d != routes 2 count %d\n",
6984		    routes1->count, routes2->count);
6985	}
6986	else if (bcmp(routes1, routes2,
6987		      IPv4RouteListComputeSize(routes1->count)) != 0) {
6988	    fprintf(stderr, "routes1 and routes2 are different\n");
6989	}
6990	else {
6991	    printf("routes1 and routes2 are the same\n");
6992	    ret = TRUE;
6993	}
6994    }
6995    if (routes1 != NULL) {
6996	free(routes1);
6997    }
6998    if (routes2 != NULL) {
6999	free(routes2);
7000    }
7001    return (ret);
7002}
7003
7004typedef struct compare_context {
7005    IPv4RouteListRef	old;
7006    IPv4RouteListRef	new;
7007} compare_context_t;
7008
7009static void
7010compare_callback(IPv4RouteListApplyCommand cmd, IPv4RouteRef route, void * arg)
7011{
7012    compare_context_t *	context = (compare_context_t *)arg;
7013
7014    switch (cmd) {
7015    case kIPv4RouteListAddRouteCommand:
7016	printf("Add new[%ld] = ", route - context->new->list);
7017	IPv4RoutePrint(route);
7018	break;
7019    case kIPv4RouteListRemoveRouteCommand:
7020	printf("Remove old[%ld] = ", route - context->old->list);
7021	IPv4RoutePrint(route);
7022	break;
7023    default:
7024	break;
7025    }
7026    return;
7027}
7028
7029static void
7030compare_tests(struct ipv4_service_contents * * old_test,
7031	      struct ipv4_service_contents * * new_test)
7032{
7033    IPv4RouteListRef	new_routes;
7034    IPv4RouteListRef	old_routes;
7035    compare_context_t	context;
7036
7037    old_routes = make_IPv4RouteList(old_test);
7038    new_routes = make_IPv4RouteList(new_test);
7039
7040    if (old_routes == NULL) {
7041	printf("No Old Routes\n");
7042    }
7043    else {
7044	printf("Old Routes = ");
7045	IPv4RouteListPrint(old_routes);
7046    }
7047    if (new_routes == NULL) {
7048	printf("No New Routes\n");
7049    }
7050    else {
7051	printf("New Routes = ");
7052	IPv4RouteListPrint(new_routes);
7053    }
7054    context.old = old_routes;
7055    context.new = new_routes;
7056    IPv4RouteListApply(old_routes, new_routes, compare_callback, &context);
7057    if (old_routes != NULL) {
7058	free(old_routes);
7059    }
7060    if (new_routes != NULL) {
7061	free(new_routes);
7062    }
7063
7064    return;
7065}
7066
7067int
7068main(int argc, char **argv)
7069{
7070    _sc_log     = FALSE;
7071    _sc_verbose = (argc > 1) ? TRUE : FALSE;
7072
7073    S_IPMonitor_debug = kDebugFlag1 | kDebugFlag2 | kDebugFlag4;
7074    if (argc > 1) {
7075	S_IPMonitor_debug = strtoul(argv[1], NULL, 0);
7076    }
7077
7078    if (run_test("test1", test1) == FALSE) {
7079	fprintf(stderr, "test1 failed\n");
7080	exit(1);
7081    }
7082    if (run_test("test2", test2) == FALSE) {
7083	fprintf(stderr, "test2 failed\n");
7084	exit(1);
7085    }
7086    if (run_test("test3", test4) == FALSE) {
7087	fprintf(stderr, "test3 failed\n");
7088	exit(1);
7089    }
7090    if (run_test("test4", test4) == FALSE) {
7091	fprintf(stderr, "test4 failed\n");
7092	exit(1);
7093    }
7094    if (run_test("test5", test5) == FALSE) {
7095	fprintf(stderr, "test5 failed\n");
7096	exit(1);
7097    }
7098    if (run_test("test15", test15) == FALSE) {
7099	fprintf(stderr, "test15 failed\n");
7100	exit(1);
7101    }
7102
7103
7104    printf("\nCompare 1 to 2:\n");
7105    compare_tests(test1, test2);
7106
7107    printf("\nCompare 2 to 1:\n");
7108    compare_tests(test2, test1);
7109
7110    printf("\nCompare 1 to 1:\n");
7111    compare_tests(test1, test1);
7112
7113    printf("\nCompare 1 to 3:\n");
7114    compare_tests(test1, test3);
7115
7116    printf("\nCompare 3 to 1:\n");
7117    compare_tests(test3, test1);
7118
7119    printf("\nCompare 2 to 3:\n");
7120    compare_tests(test2, test3);
7121
7122    printf("\nCompare 3 to 2:\n");
7123    compare_tests(test3, test2);
7124
7125    printf("\nCompare 3 to 4:\n");
7126    compare_tests(test3, test4);
7127
7128    printf("\nCompare 5 to 4:\n");
7129    compare_tests(test5, test4);
7130
7131    printf("\nCompare 6 to 7:\n");
7132    compare_tests(test6, test7);
7133
7134    printf("\nCompare 7 to 6:\n");
7135    compare_tests(test7, test6);
7136
7137    printf("\nCompare 8 to 9:\n");
7138    compare_tests(test8, test9);
7139
7140    printf("\nCompare 8 to 10:\n");
7141    compare_tests(test8, test10);
7142
7143    printf("\nCompare 8 to 11:\n");
7144    compare_tests(test8, test11);
7145
7146    printf("\nCompare 12 to 13:\n");
7147    compare_tests(test12, test13);
7148
7149    printf("\nCompare 13 to 14:\n");
7150    compare_tests(test13, test14);
7151
7152    printf("\nChecking for leaks\n");
7153    char    cmd[128];
7154    sprintf(cmd, "leaks %d 2>&1", getpid());
7155    fflush(stdout);
7156    (void)system(cmd);
7157
7158    exit(0);
7159    return (0);
7160}
7161
7162#endif /* TEST_IPV4_ROUTELIST */
7163
7164