1/*
2 * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * ipconfigd.c
26 * - daemon that configures interfaces using manual settings,
27 *   manual with DHCP INFORM, BOOTP, or DHCP
28 */
29/*
30 * Modification History
31 *
32 * September, 1999 	Dieter Siegmund (dieter@apple.com)
33 * - initial revision
34 *
35 * May 8, 2000		Dieter Siegmund (dieter@apple.com)
36 * - re-architected to be event-driven to satisfy mobility
37 *   requirements
38 * - converted to use a single main configuration thread
39 *   instead of a thread per interface
40 * - removed dependency on objective C
41 *
42 * June 12, 2000	Dieter Siegmund (dieter@apple.com)
43 * - converted to use CFRunLoop
44 * - added ability to change the configuration on the fly, either by
45 *   a configuration data change, or having the user send a command
46 *   via ipconfig
47 *
48 * July 5, 2000		Dieter Siegmund (dieter@apple.com)
49 * - added code to publish information with configd
50 * - wrote common IP config module to read information from the cache,
51 *   and update the default route, DNS, and netinfo parent(s) on the fly
52 *
53 * August 20, 2000 	Dieter Siegmund (dieter@apple.com)
54 * - moved common IP config module to configd_plugins/IPMonitor.bproj
55 * - added code to handle information from setup/cache only
56 *
57 * October 4, 2000	Dieter Siegmund (dieter@apple.com)
58 * - added code to handle error cases and force the interface
59 *   state to be ready to avoid hanging the system startup in case
60 *   there is bad data in the cache
61 *
62 * November 20, 2000	Dieter Siegmund (dieter@apple.com)
63 * - changed to use new preferences keys and service-based configuration
64 *
65 * March 27, 2001	Dieter Siegmund (dieter@apple.com)
66 * - turned ipconfigd into the IPConfiguration bundle
67 *
68 * May 17, 2001		Dieter Siegmund (dieter@apple.com)
69 * - publish information in service state instead of interface state
70 *
71 * June 14, 2001	Dieter Siegmund (dieter@apple.com)
72 * - publish DHCP options in dynamic store, and allow third party
73 *   applications to request additional options using a DHCPClient
74 *   preference
75 * - add notification handler to automatically force the DHCP client
76 *   to renew its lease
77 *
78 * July 12, 2001	Dieter Siegmund (dieter@apple.com)
79 * - don't bother reporting arp collisions with our own interfaces
80 *
81 * July 19, 2001	Dieter Siegmund (dieter@apple.com)
82 * - port to use public SystemConfiguration APIs
83 *
84 * August 28, 2001	Dieter Siegmund (dieter@apple.com)
85 * - when multiple interfaces are configured to be on the same subnet,
86 *   keep the subnet route correct and have it follow the service/interface
87 *   with the highest priority
88 * - this also eliminates problems with the subnet route getting lost
89 *   when an interface is de-configured, yet another interface is on
90 *   the same subnet
91 *
92 * September 10, 2001	Dieter Siegmund (dieter@apple.com)
93 * - added multiple service per interface support
94 * - separated ad-hoc/link-local address configuration into its own service
95 *
96 * January 4, 2002	Dieter Siegmund (dieter@apple.com)
97 * - always configure the link-local service on the service with the
98 *   highest priority service that's active
99 * - modified link-local service to optionally allocate an IP address;
100 *   if we don't allocate an IP, we just set the link-local subnet
101 * - allow a previously failed DHCP service to acquire a link-local IP
102 *   address if it later becomes the primary service
103 * - a link-local address will only be allocated when a DHCP service fails,
104 *   and it is the primary service, and the G_dhcp_failure_configures_linklocal
105 *   is TRUE
106 *
107 * February 1, 2002	Dieter Siegmund (dieter@apple.com)
108 * - make IPConfiguration netboot-aware:
109 *   + grab the DHCP information from the packet in the device tree
110 *
111 * May 20, 2002		Dieter Siegmund (dieter@apple.com)
112 * - allocate a link-local address more quickly, after the first
113 *   DHCP request fails
114 * - re-structured the automatic link-local service allocation to do
115 *   most of the work from a run-loop observer instead of within the
116 *   context of the caller; this avoids unnecessary re-entrancy issues
117 *   and complexity
118 *
119 * December 3, 2002	Dieter Siegmund (dieter@apple.com)
120 * - add support to detect ARP collisions after we have already
121 *   assigned ourselves the address
122 *
123 * June 16, 2003	Dieter Siegmund (dieter@apple.com)
124 * - added support for firewire (IFT_IEEE1394)
125 *
126 * November 19, 2003	Dieter Siegmund (dieter@apple.com)
127 * - added support for VLAN's (IFT_L2VLAN)
128 *
129 * April 13, 2005	Dieter Siegmund (dieter@apple.com)
130 * - added support for multiple link-local services, one per interface
131 * - changed logic to favor a successful service (i.e. one with an IP address)
132 *   over simply the highest ranked service per interface
133 * - maintain the link-local subnet on the interface with the highest ranked
134 *   active service
135 *
136 * October 20, 2006	Dieter Siegmund (dieter@apple.com)
137 * - resolve the router's MAC address using ARP, and publish that
138 *   information to the NetworkSignature in the IPv4 dict
139 *
140 * December 19, 2007	Dieter Siegmund (dieter@apple.com)
141 * - removed subnet route maintenance code since IPMonitor now takes
142 *   care of that
143 *
144 * September 4, 2009	Dieter Siegmund (dieter@apple.com)
145 * - added support for IPv6 configuration
146 */
147
148#include <stdlib.h>
149#include <unistd.h>
150#include <string.h>
151#include <stdio.h>
152#include <sys/types.h>
153#include <sys/param.h>
154#include <sys/sysctl.h>
155#include <sys/wait.h>
156#include <sys/errno.h>
157#include <sys/socket.h>
158#define KERNEL_PRIVATE
159#include <sys/ioctl.h>
160#undef KERNEL_PRIVATE
161#include <ctype.h>
162#include <net/if.h>
163#include <net/ethernet.h>
164#include <net/firewire.h>
165#include <netinet/in.h>
166#include <netinet/udp.h>
167#include <netinet/ip.h>
168#include <netinet/bootp.h>
169#include <arpa/inet.h>
170#include <fcntl.h>
171#include <paths.h>
172#include <syslog.h>
173#include <net/if_types.h>
174#include <mach/boolean.h>
175
176#include <SystemConfiguration/SystemConfiguration.h>
177#include <SystemConfiguration/SCPrivate.h>
178#include <SystemConfiguration/SCValidation.h>
179#include <SystemConfiguration/SCDPlugin.h>
180#include <IOKit/IOMessage.h>
181#include <IOKit/IOKitLib.h>
182#include <IOKit/pwr_mgt/IOPMLib.h>
183#include <IOKit/IOHibernatePrivate.h>
184#include <TargetConditionals.h>
185#include <Availability.h>
186#if ! TARGET_OS_EMBEDDED
187#include <CoreFoundation/CFUserNotification.h>
188#include <CoreFoundation/CFUserNotificationPriv.h>
189#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
190#endif /* ! TARGET_OS_EMBEDDED */
191
192#include "rfc_options.h"
193#include "dhcp_options.h"
194#include "interfaces.h"
195#include "util.h"
196
197#include "dhcplib.h"
198#include "ioregpath.h"
199
200#include "ipconfig_types.h"
201#include "ipconfigd.h"
202#include "server.h"
203#include "timer.h"
204
205#include "ipconfigd_globals.h"
206#include "ipconfigd_threads.h"
207#include "FDSet.h"
208
209#include "symbol_scope.h"
210#include "cfutil.h"
211#include "sysconfig.h"
212#include "ifutil.h"
213#include "rtutil.h"
214#include "DHCPv6Client.h"
215#include "DHCPv6Socket.h"
216#include "IPConfigurationServiceInternal.h"
217#include "IPConfigurationControlPrefs.h"
218#include "CGA.h"
219#include "IPv6Socket.h"
220
221#define RIFLAGS_IADDR_VALID	((uint32_t)0x1)
222#define RIFLAGS_HWADDR_VALID	((uint32_t)0x2)
223#define RIFLAGS_ARP_VERIFIED	((uint32_t)0x4)
224#define RIFLAGS_ALL_VALID	(RIFLAGS_IADDR_VALID | RIFLAGS_HWADDR_VALID | RIFLAGS_ARP_VERIFIED)
225
226#define kARPResolvedIPAddress		CFSTR("ARPResolvedIPAddress")
227#define kARPResolvedHardwareAddress	CFSTR("ARPResolvedHardwareAddress")
228
229typedef struct {
230    uint32_t			flags;
231    struct in_addr		iaddr;
232    uint8_t			hwaddr[MAX_LINK_ADDR_LEN];
233} router_info_t;
234
235typedef struct IFState * IFStateRef;
236
237typedef struct {
238    struct in_addr	addr;
239    struct in_addr	mask;
240} ip_addr_mask_t;
241
242typedef struct {
243    ip_addr_mask_t	requested_ip;
244    inet_addrinfo_t	info;
245    router_info_t	router;
246    absolute_time_t	ip_assigned_time;
247    absolute_time_t	ip_conflict_time;
248    int			ip_conflict_count;
249} ServiceIPv4, * ServiceIPv4Ref;
250
251typedef struct {
252    struct in6_addr		addr;
253    int				prefix_length;
254} inet6_addr_prefix_t;
255
256typedef struct {
257    inet6_addr_prefix_t		requested_ip;
258} ServiceIPv6, * ServiceIPv6Ref;
259
260struct ServiceInfo {
261    CFStringRef			serviceID;
262    IFStateRef			ifstate;
263    ipconfig_method_t		method;
264    ipconfig_status_t		status;
265    boolean_t			is_dynamic;
266    boolean_t			no_publish;
267    boolean_t			ready;
268    CFStringRef			parent_serviceID;
269    CFStringRef			child_serviceID;
270    dispatch_source_t		pid_source;
271    void * 			private;
272#if ! TARGET_OS_EMBEDDED
273    CFUserNotificationRef	user_notification;
274    CFRunLoopSourceRef		user_rls;
275#endif /* ! TARGET_OS_EMBEDDED */
276    union {
277	ServiceIPv4		v4;
278	ServiceIPv6		v6;
279    } u;
280};
281
282typedef struct ActiveDuringSleepInfo {
283    boolean_t			needs_attention;
284    boolean_t			requested;
285} ActiveDuringSleepInfo, * ActiveDuringSleepInfoRef;
286
287struct IFState {
288    interface_t *		if_p;
289    CFStringRef			ifname;
290    dynarray_t			services;
291    dynarray_t			services_v6;
292    ServiceRef			linklocal_service_p;
293    boolean_t			startup_ready;
294    boolean_t			netboot;
295    CFStringRef			ssid;
296    struct ether_addr		bssid;
297    timer_callout_t *		timer;
298    struct in_addr		v4_link_local;
299    uint32_t			wake_generation;
300    boolean_t			disable_perform_nud;
301    boolean_t			link_timer_suppressed;
302    CFMutableArrayRef		neighbor_advert_list;
303    ActiveDuringSleepInfo	active_during_sleep;
304};
305
306typedef dynarray_t	IFStateList_t;
307
308#define IS_IPV4		TRUE
309#define IS_IPV6		FALSE
310
311#ifndef kSCEntNetRefreshConfiguration
312#define kSCEntNetRefreshConfiguration	CFSTR("RefreshConfiguration")
313#endif /* kSCEntNetRefreshConfiguration */
314
315#ifndef kSCEntNetIPv4ARPCollision
316#define kSCEntNetIPv4ARPCollision	CFSTR("IPv4ARPCollision")
317#endif /* kSCEntNetIPv4ARPCollision */
318
319#ifndef kSCEntNetDHCPv6
320#define kSCEntNetDHCPv6			CFSTR("DHCPv6")
321#endif /* kSCEntNetDHCPv6 */
322
323#ifndef kSCValNetIPv4ConfigMethodFailover
324static const CFStringRef kIPConfigurationConfigMethodFailover = CFSTR("Failover");
325#define kSCValNetIPv4ConfigMethodFailover kIPConfigurationConfigMethodFailover
326#endif /* kSCValNetIPv4ConfigMethodFailover */
327
328#ifndef kSCValNetIPv6ConfigMethodLinkLocal
329static const CFStringRef kIPConfigurationIPv6ConfigMethodLinkLocal = CFSTR("LinkLocal");
330#define kSCValNetIPv6ConfigMethodLinkLocal kIPConfigurationIPv6ConfigMethodLinkLocal
331#endif /* kSCValNetIPv6ConfigMethodLinkLocal */
332
333
334#ifndef kSCPropNetIPv4FailoverAddressTimeout
335static const CFStringRef kIPConfigurationFailoverAddressTimeout = CFSTR("FailoverAddressTimeout");
336#define kSCPropNetIPv4FailoverAddressTimeout	kIPConfigurationFailoverAddressTimeout
337#endif /* kSCPropNetIPv4FailoverAddressTimeout */
338
339#ifndef kSCPropNetIgnoreLinkStatus
340static const CFStringRef kIPConfigurationIgnoreLinkStatus = CFSTR("IgnoreLinkStatus");
341#define kSCPropNetIgnoreLinkStatus	kIPConfigurationIgnoreLinkStatus
342#endif /* kSCPropNetIgnoreLinkStatus */
343
344#ifndef kSCPropNetIPv66to4Relay
345static const CFStringRef kSCPropNetIPv66to4Relay = CFSTR("6to4Relay");
346#endif /* kSCPropNetIPv66to4Relay */
347
348#ifndef kSCEntNetInterfaceActiveDuringSleepRequested
349#define kSCEntNetInterfaceActiveDuringSleepRequested	CFSTR("ActiveDuringSleepRequested")
350#endif /* kSCEntNetInterfaceActiveDuringSleepRequested */
351
352#ifndef kSCEntNetInterfaceActiveDuringSleepSupported
353#define kSCEntNetInterfaceActiveDuringSleepSupported	CFSTR("ActiveDuringSleepSupported")
354#endif /* kSCEntNetInterfaceActiveDuringSleepSupported */
355
356#define kDHCPClientPreferencesID	CFSTR("DHCPClient.plist")
357#define kDHCPClientApplicationPref	CFSTR("Application")
358#define kDHCPRequestedParameterList	CFSTR("DHCPRequestedParameterList")
359
360#define kDHCPv6RequestedOptions		CFSTR("DHCPv6RequestedOptions")
361
362/* default values */
363#define MAX_RETRIES				9
364#define INITIAL_WAIT_SECS			1
365#define MAX_WAIT_SECS				8
366#define GATHER_SECS				1
367#define LINK_INACTIVE_WAIT_SECS			0
368#define LINK_INACTIVE_WAIT_USECS		(100 * 1000)
369#define DHCP_INIT_REBOOT_RETRY_COUNT		2
370#define DHCP_SELECT_RETRY_COUNT			3
371#define DHCP_ALLOCATE_LINKLOCAL_AT_RETRY_COUNT	4
372#define DHCP_ROUTER_ARP_AT_RETRY_COUNT		7
373#define DHCP_FAILURE_CONFIGURES_LINKLOCAL	TRUE
374#define DHCP_SUCCESS_DECONFIGURES_LINKLOCAL	TRUE
375#define DHCP_LOCAL_HOSTNAME_LENGTH_MAX		15
376#define DISCOVER_ROUTER_MAC_ADDRESS_SECS	60
377#define DEFEND_IP_ADDRESS_INTERVAL_SECS		10
378#define DEFEND_IP_ADDRESS_COUNT			5
379#define DHCP_LEASE_WRITE_T1_THRESHOLD_SECS	3600
380#define MANUAL_CONFLICT_RETRY_INTERVAL_SECS	300
381
382#define USER_ERROR			1
383#define UNEXPECTED_ERROR 		2
384#define TIMEOUT_ERROR			3
385
386/* global variables */
387PRIVATE_EXTERN uint16_t 	G_client_port = IPPORT_BOOTPC;
388PRIVATE_EXTERN boolean_t 	G_dhcp_accepts_bootp = FALSE;
389PRIVATE_EXTERN boolean_t	G_dhcp_failure_configures_linklocal
390				    = DHCP_FAILURE_CONFIGURES_LINKLOCAL;
391PRIVATE_EXTERN boolean_t	G_dhcp_success_deconfigures_linklocal
392				    = DHCP_SUCCESS_DECONFIGURES_LINKLOCAL;
393PRIVATE_EXTERN int		G_dhcp_allocate_linklocal_at_retry_count
394				    = DHCP_ALLOCATE_LINKLOCAL_AT_RETRY_COUNT;
395PRIVATE_EXTERN int		G_dhcp_router_arp_at_retry_count
396				    = DHCP_ROUTER_ARP_AT_RETRY_COUNT;
397PRIVATE_EXTERN int		G_dhcp_init_reboot_retry_count
398				    = DHCP_INIT_REBOOT_RETRY_COUNT;
399PRIVATE_EXTERN int		G_dhcp_select_retry_count
400				    = DHCP_SELECT_RETRY_COUNT;
401PRIVATE_EXTERN int		G_dhcp_lease_write_t1_threshold_secs
402				    = DHCP_LEASE_WRITE_T1_THRESHOLD_SECS;
403PRIVATE_EXTERN uint16_t 	G_server_port = IPPORT_BOOTPS;
404PRIVATE_EXTERN int		G_manual_conflict_retry_interval_secs
405				    = MANUAL_CONFLICT_RETRY_INTERVAL_SECS;
406
407PRIVATE_EXTERN boolean_t	G_is_netboot = FALSE;
408
409/*
410 * Static: S_link_inactive_secs
411 * Purpose:
412 *   Time to wait after the link goes inactive before unpublishing
413 *   the interface state information
414 */
415static struct timeval		S_link_inactive_secs = {
416    LINK_INACTIVE_WAIT_SECS,
417    LINK_INACTIVE_WAIT_USECS,
418};
419
420/*
421 * Global: G_gather_secs
422 * Purpose:
423 *   Time to wait for the ideal packet after receiving
424 *   the first acceptable packet.
425 */
426int				G_gather_secs = GATHER_SECS;
427
428/*
429 * Global: G_initial_wait_secs
430 * Purpose:
431 *   First timeout interval in seconds.
432 */
433int				G_initial_wait_secs = INITIAL_WAIT_SECS;
434
435/*
436 * Global: G_max_retries
437 * Purpose:
438 *   Number of times to retry sending the packet.
439 */
440int				G_max_retries = MAX_RETRIES;
441
442/*
443 * Global: G_max_wait_secs
444 * Purpose:
445 *   Maximum timeout interval.  Timeouts timeout[i] are chosen as:
446 *   i = 0:
447 *     timeout[0] = G_initial_wait_secs;
448 *   i > 0:
449 *     timeout[i] = min(timeout[i - 1] * 2, G_max_wait_secs);
450 *   If G_initial_wait_secs = 4, G_max_wait_secs = 16, the sequence is:
451 *     4, 8, 16, 16, ...
452 */
453int				G_max_wait_secs = MAX_WAIT_SECS;
454
455boolean_t 			G_must_broadcast;
456Boolean				G_IPConfiguration_verbose;
457bootp_session_t *		G_bootp_session = NULL;
458arp_session_t * 		G_arp_session = NULL;
459boolean_t			G_router_arp = TRUE;
460int				G_router_arp_wifi_lease_start_threshold_secs = (3600 * 24); /* 1 day */
461boolean_t			G_discover_and_publish_router_mac_address = TRUE;
462
463#define DHCPv6_ENABLED		TRUE
464#define DHCPv6_STATEFUL_ENABLED	TRUE
465
466boolean_t			G_dhcpv6_enabled = DHCPv6_ENABLED;
467boolean_t			G_dhcpv6_stateful_enabled = DHCPv6_STATEFUL_ENABLED;
468int				G_dhcp_duid_type = kDHCPDUIDTypeLLT;
469
470const unsigned char		G_rfc_magic[4] = RFC_OPTIONS_MAGIC;
471const struct in_addr		G_ip_broadcast = { INADDR_BROADCAST };
472const struct in_addr		G_ip_zeroes = { 0 };
473
474#define MIN_SHORT_WAKE_INTERVAL_SECS	60
475PRIVATE_EXTERN int		G_min_short_wake_interval_secs = MIN_SHORT_WAKE_INTERVAL_SECS;
476#define MIN_WAKE_INTERVAL_SECS	(60 * 15)
477PRIVATE_EXTERN int		G_min_wake_interval_secs = MIN_WAKE_INTERVAL_SECS;
478#define WAKE_SKEW_SECS		30
479PRIVATE_EXTERN int		G_wake_skew_secs = WAKE_SKEW_SECS;
480
481/* local variables */
482static interface_list_t *	S_interfaces = NULL;
483static CFBundleRef		S_bundle = NULL;
484static CFRunLoopObserverRef	S_observer = NULL;
485static boolean_t		S_linklocal_needs_attention = FALSE;
486static IFStateList_t		S_ifstate_list;
487static io_connect_t 		S_power_connection;
488static SCDynamicStoreRef	S_scd_session = NULL;
489static CFStringRef		S_setup_service_prefix = NULL;
490static CFStringRef		S_state_interface_prefix = NULL;
491static char * 			S_computer_name = NULL;
492static CFStringRef		S_computer_name_key = NULL;
493static CFStringRef		S_hostnames_key = NULL;
494static int			S_arp_probe_count = ARP_PROBE_COUNT;
495static int			S_arp_gratuitous_count = ARP_GRATUITOUS_COUNT;
496static struct timeval		S_arp_retry = {
497  ARP_RETRY_SECS,
498  ARP_RETRY_USECS
499};
500static int			S_arp_detect_count = ARP_DETECT_COUNT;
501static struct timeval		S_arp_detect_retry = {
502    ARP_DETECT_RETRY_SECS,
503    ARP_DETECT_RETRY_USECS
504};
505static struct timeval		S_arp_resolve_retry = {
506    ARP_RESOLVE_RETRY_SECS,
507    ARP_RESOLVE_RETRY_USECS
508};
509static int			S_discover_router_mac_address_secs
510					= DISCOVER_ROUTER_MAC_ADDRESS_SECS;
511
512static int			S_arp_conflict_retry = ARP_CONFLICT_RETRY_COUNT;
513static struct timeval		S_arp_conflict_delay = {
514    ARP_CONFLICT_RETRY_DELAY_SECS,
515    ARP_CONFLICT_RETRY_DELAY_USECS
516};
517
518static int			S_defend_ip_address_interval_secs
519				    = DEFEND_IP_ADDRESS_INTERVAL_SECS;
520static int			S_defend_ip_address_count
521				    = DEFEND_IP_ADDRESS_COUNT;
522
523
524static int S_dhcp_local_hostname_length_max = DHCP_LOCAL_HOSTNAME_LENGTH_MAX;
525
526static struct in_addr		S_netboot_ip;
527static struct in_addr		S_netboot_server_ip;
528static char			S_netboot_ifname[IFNAMSIZ + 1];
529
530static boolean_t		S_awake = TRUE;
531#if ! TARGET_OS_EMBEDDED
532static boolean_t		S_use_maintenance_wake = TRUE;
533static boolean_t		S_wake_event_sent;
534#endif /* ! TARGET_OS_EMBEDDED */
535static uint32_t			S_wake_generation;
536static absolute_time_t		S_wake_time;
537static boolean_t		S_woke_from_hibernation;
538
539static boolean_t		S_configure_ipv6 = TRUE;
540
541STATIC boolean_t		S_active_during_sleep_needs_attention;
542
543#define PROP_SERVICEID		CFSTR("ServiceID")
544
545/*
546 * forward declarations
547 */
548static void
549S_add_dhcp_parameters(SCPreferencesRef prefs);
550
551static void
552configuration_changed(SCDynamicStoreRef session);
553
554static ipconfig_status_t
555config_method_event(ServiceRef service_p, IFEventID_t event, void * data);
556
557static ipconfig_status_t
558config_method_start(ServiceRef service_p, ipconfig_method_t method,
559		    ipconfig_method_data_t * method_data);
560
561
562static ipconfig_status_t
563config_method_change(ServiceRef service_p, ipconfig_method_t method,
564		     ipconfig_method_data_t * method_data,
565		     boolean_t * needs_stop);
566
567
568static ipconfig_status_t
569config_method_stop(ServiceRef service_p);
570
571static ipconfig_status_t
572config_method_media(ServiceRef service_p, void * network_changed);
573
574static ipconfig_status_t
575config_method_bssid_changed(ServiceRef service_p);
576
577static ipconfig_status_t
578config_method_renew(ServiceRef service_p);
579
580static void
581service_publish_clear(ServiceRef service_p);
582
583static boolean_t
584all_services_ready();
585
586static void
587S_linklocal_elect(CFArrayRef service_order);
588
589static CFArrayRef
590S_get_service_order(SCDynamicStoreRef session);
591
592static __inline__ IFStateRef
593service_ifstate(ServiceRef service_p)
594{
595    return (service_p->ifstate);
596}
597
598static boolean_t
599S_get_plist_boolean_quiet(CFDictionaryRef plist, CFStringRef key,
600			  boolean_t def);
601static int
602S_get_plist_int_quiet(CFDictionaryRef plist, CFStringRef key,
603		      int def);
604
605static unsigned int
606S_get_service_rank(CFArrayRef arr, ServiceRef service_p);
607
608static IFStateRef
609IFStateList_ifstate_with_name(IFStateList_t * list, const char * ifname,
610			      int * where);
611
612static IFStateRef
613IFStateListGetIFState(IFStateList_t * list, CFStringRef ifname,
614		      int * where);
615static void
616IFStateFreeService(IFStateRef ifstate, ServiceRef service_p);
617
618static ServiceRef
619IFState_service_with_ip(IFStateRef ifstate, struct in_addr iaddr);
620
621static void
622IFState_set_ssid_bssid(IFStateRef ifstate, CFStringRef ssid,
623		       const struct ether_addr * bssid);
624
625STATIC void
626IFStateSetActiveDuringSleepRequested(IFStateRef ifstate, boolean_t requested);
627
628STATIC void
629IFStateProcessActiveDuringSleep(IFStateRef ifstate);
630
631static void
632S_linklocal_start(ServiceRef parent_service_p, boolean_t allocate);
633
634static CFStringRef
635S_copy_ssid_bssid(CFStringRef ifname, struct ether_addr * ap_mac);
636
637static int
638S_remove_ip_address(const char * ifname, struct in_addr this_ip);
639
640STATIC ipconfig_status_t
641S_remove_service_with_id_str(const char * ifname, CFStringRef serviceID);
642
643static ipconfig_status_t
644method_info_from_dict(CFDictionaryRef dict,
645		      ipconfig_method_t * ret_method,
646		      ipconfig_method_data_t * * ret_method_data);
647static ipconfig_status_t
648method_info_from_ipv6_dict(CFDictionaryRef dict,
649			   ipconfig_method_t * ret_method,
650			   ipconfig_method_data_t * * ret_method_data);
651
652STATIC CFDictionaryRef
653ServiceIPv4CopyMergedDNS(ServiceRef service_p, dhcp_info_t * info_p);
654
655STATIC CFDictionaryRef
656ServiceIPv6CopyMergedDNS(ServiceRef service_p, dhcpv6_info_t * info_v6_p);
657
658STATIC void
659process_link_timer_expired(IFStateRef ifstate);
660
661STATIC bool
662my_CFStringToIPv6Address(CFStringRef str, struct in6_addr * ret_ip);
663
664static void
665service_list_event(dynarray_t * services_p, IFEventID_t event, void * data);
666
667PRIVATE_EXTERN const char *
668ipconfig_method_string(ipconfig_method_t m)
669{
670    const char *	str = "<unknown>";
671
672    switch (m) {
673    case ipconfig_method_none_e:
674	str = "NONE";
675	break;
676    case ipconfig_method_none_v4_e:
677	str = "NONE-V4";
678	break;
679    case ipconfig_method_none_v6_e:
680	str = "NONE-V6";
681	break;
682    case ipconfig_method_manual_e:
683	str = "MANUAL";
684	break;
685    case ipconfig_method_bootp_e:
686	str = "BOOTP";
687	break;
688    case ipconfig_method_dhcp_e:
689	str = "DHCP";
690	break;
691    case ipconfig_method_inform_e:
692	str = "INFORM";
693	break;
694    case ipconfig_method_linklocal_e:
695	str = "LINKLOCAL";
696	break;
697    case ipconfig_method_failover_e:
698	str = "FAILOVER";
699	break;
700    case ipconfig_method_manual_v6_e:
701	str = "MANUAL-V6";
702	break;
703    case ipconfig_method_automatic_v6_e:
704	str = "AUTOMATIC-V6";
705	break;
706    case ipconfig_method_rtadv_e:
707	str = "RTADV";
708	break;
709    case ipconfig_method_stf_e:
710	str = "6TO4";
711	break;
712    case ipconfig_method_linklocal_v6_e:
713	str = "LINKLOCAL-V6";
714	break;
715    }
716    return (str);
717}
718
719/*
720 * Function: S_is_our_hardware_address
721 *
722 * Purpose:
723 *   Returns whether the given hardware address is that of any of
724 *   our attached network interfaces.
725 */
726static boolean_t
727S_is_our_hardware_address(interface_t * ignored,
728			  int hwtype, void * hwaddr, int hwlen)
729{
730    int 	i;
731
732    for (i = 0; i < ifl_count(S_interfaces); i++) {
733	interface_t *	if_p = ifl_at_index(S_interfaces, i);
734	int		link_length = if_link_length(if_p);
735
736	if (hwlen != link_length) {
737	    continue;
738	}
739	if (hwtype != if_link_arptype(if_p)) {
740	    continue;
741	}
742	if (bcmp(hwaddr, if_link_address(if_p), link_length) == 0) {
743	    return (TRUE);
744	}
745    }
746    return (FALSE);
747}
748
749#define kBSPAddress			CFSTR("BonjourSleepProxyAddress")
750#define kBSPMACAddress			kSCPropMACAddress
751#define kBSPRegisteredAddresses		CFSTR("RegisteredAddresses")
752
753/*
754 * Function: S_copy_sleep_proxy_info
755 * Purpose:
756 *   Copy the Bonjour Sleep Proxy Address information.
757 */
758STATIC CFDictionaryRef
759S_copy_sleep_proxy_info(SCDynamicStoreRef store, CFStringRef ifname)
760{
761    CFDictionaryRef		info;
762    CFStringRef			key;
763
764    key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
765							kSCDynamicStoreDomainState,
766							ifname,
767							kBSPAddress);
768    info = SCDynamicStoreCopyValue(store, key);
769    CFRelease(key);
770    if (isA_CFDictionary(info) == NULL) {
771	my_CFRelease(&info);
772    }
773    return (info);
774}
775
776STATIC boolean_t
777S_is_sleep_proxy_hardware_address(SCDynamicStoreRef session,
778                                  IFStateRef ifstate,
779				  void * hwaddr, int hw_len)
780{
781    /* check if the address belongs to any of the known sleep proxies */
782    boolean_t		found = FALSE;
783    CFDictionaryRef	info;
784
785    info = S_copy_sleep_proxy_info(session, ifstate->ifname);
786    if (info != NULL) {
787	CFStringRef 	sp_mac_cf;
788
789	sp_mac_cf = CFDictionaryGetValue(info, kBSPMACAddress);
790	if (isA_CFString(sp_mac_cf) != NULL) {
791	    int		len;
792	    void *	sp_mac;
793
794	    sp_mac = bytesFromColonHexString(sp_mac_cf, &len);
795	    if (sp_mac != NULL) {
796		if (len == hw_len) {
797		    if (bcmp(sp_mac, hwaddr, len) == 0) {
798			found = TRUE;
799		    }
800		}
801		free(sp_mac);
802	    }
803	}
804	CFRelease(info);
805    }
806    return (found);
807}
808
809/*
810 * Function: S_process_neighbor_adverts
811 * Purpose:
812 *   Process the list of IPv6 addresses we registered with a sleep proxy.
813 *   For each address, check whether it is in the list of addresses
814 *   currently assigned to the interface.  If it is, then send a
815 *   Neighbor Advertisement packet.
816 *
817 *   The list of IPv6 addresses is copied at wake, and tossed at sleep.
818 *   If the list is more than 60 seconds old, also toss it to avoid needing
819 *   to traverse it if we happen to end up with an address in there that's
820 *   never going to get assigned (e.g. temporary address, or we switched
821 *   networks).
822 */
823STATIC void
824S_process_neighbor_adverts(IFStateRef ifstate, inet6_addrlist_t * addr_list_p)
825{
826    CFIndex		count;
827    absolute_time_t	current_time;
828    int			i;
829    interface_t *	if_p;
830    int			sockfd = -1;
831
832    if (ifstate->neighbor_advert_list == NULL) {
833	return;
834    }
835
836#define kProcessNeighborAdvertExpirationTimeSecs	60
837    current_time = timer_current_secs();
838    if ((current_time - S_wake_time)
839	> kProcessNeighborAdvertExpirationTimeSecs) {
840	my_log(LOG_DEBUG,
841	       "%@: tossing neighbor advert list (%ld - %ld) > (%d)",
842	       ifstate->ifname,
843	       current_time,
844	       S_wake_time,
845	       kProcessNeighborAdvertExpirationTimeSecs);
846	/* information is stale, toss it */
847	my_CFRelease(&ifstate->neighbor_advert_list);
848	return;
849    }
850    if_p = ifstate->if_p;
851    count = CFArrayGetCount(ifstate->neighbor_advert_list);
852    for (i = 0; i < count; ) {
853	struct in6_addr	address;
854	CFStringRef	address_cf;
855	int		error;
856	boolean_t	remove = FALSE;
857
858	address_cf = (CFStringRef)
859	    CFArrayGetValueAtIndex(ifstate->neighbor_advert_list, i);
860	if (my_CFStringToIPv6Address(address_cf, &address) == FALSE) {
861	    /* failed to convert, remove bogus value */
862	    my_log(LOG_ERR, "%@: bogus address value %@",
863		   ifstate->ifname, address_cf);
864	    remove = TRUE;
865	}
866	else if (inet6_addrlist_in6_addr_is_ready(addr_list_p,
867						  &address) == FALSE) {
868	    /* address not assigned or is not ready */
869	    my_log(LOG_ERR, "%@: address %@ not present/ready",
870		   ifstate->ifname, address_cf);
871	    remove = FALSE;
872	}
873	else {
874	    /* address ready, send neighbor advert and remove from list */
875	    remove = TRUE;
876	    if (sockfd < 0) {
877		sockfd = ICMPv6SocketOpen(FALSE);
878		if (sockfd < 0) {
879		    my_log_fl(LOG_ERR, "can't open socket, %s",
880			      strerror(errno));
881		    break;
882		}
883	    }
884	    error
885		= ICMPv6SocketSendNeighborAdvertisement(sockfd,
886							if_link_index(if_p),
887							if_link_address(if_p),
888							if_link_length(if_p),
889							&address);
890	    error = 0;
891	    if (error != 0) {
892		my_log(LOG_ERR,
893		       "%s: failed to send neighbor advertisement, %s",
894		       if_name(if_p), strerror(error));
895	    }
896	    else if (G_IPConfiguration_verbose) {
897		char 	ntopbuf[INET6_ADDRSTRLEN];
898
899		my_log(LOG_DEBUG,
900		       "%s: sent neighbor advertisement for %s",
901		       if_name(if_p),
902		       inet_ntop(AF_INET6, &address, ntopbuf, sizeof(ntopbuf)));
903	    }
904	}
905	if (remove) {
906	    CFArrayRemoveValueAtIndex(ifstate->neighbor_advert_list, i);
907	    count--;
908	}
909	else {
910	    /* skip to next value */
911	    i++;
912	}
913    }
914    if (CFArrayGetCount(ifstate->neighbor_advert_list) == 0) {
915	/* list is empty, toss it */
916	my_CFRelease(&ifstate->neighbor_advert_list);
917    }
918
919    if (sockfd >= 0) {
920	close(sockfd);
921    }
922    return;
923}
924
925STATIC CFMutableArrayRef
926S_copy_neighbor_advert_list(SCDynamicStoreRef store, CFStringRef ifname)
927{
928    CFDictionaryRef		info;
929    CFArrayRef			list;
930    CFMutableArrayRef		ret = NULL;
931
932    info = S_copy_sleep_proxy_info(store, ifname);
933    if (info != NULL) {
934	list = CFDictionaryGetValue(info, kBSPRegisteredAddresses);
935	if (isA_CFArray(list) != NULL) {
936	    if (G_IPConfiguration_verbose) {
937		my_log(LOG_DEBUG, "%@: Sleep Proxy Addresses = %@",
938		       ifname, list);
939	    }
940	    ret = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(list), list);
941	}
942	CFRelease(info);
943    }
944    return (ret);
945}
946
947/**
948 ** Computer Name handling routines
949 **/
950
951PRIVATE_EXTERN char *
952computer_name()
953{
954    return (S_computer_name);
955}
956
957/*
958 *	Try to shorten the length of the string by replacing certain
959 *	product names.  If still not short enough, eliminate -'s.
960 *	If still not short enough, remove enough of the middle part of
961 *	the remaining string to make it 'desired_length'.
962 */
963static CFStringRef
964myCFStringCopyShortenedString(CFStringRef computer_name, int desired_length)
965{
966    CFMutableStringRef short_name;
967    CFIndex len, delete_len;
968
969#define MINIMUM_SHORTENED_STRING_LENGTH 3 //  Min 3 chars <first part>-<last part>
970
971
972    if (computer_name == NULL
973	|| desired_length < MINIMUM_SHORTENED_STRING_LENGTH) {
974	return NULL;
975    }
976    short_name  = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, computer_name);
977
978    /*
979     * shorten commonly used long names like MacBook-Pro, MacBook-Air
980     */
981    CFStringFindAndReplace(short_name, CFSTR("macbook-air"), CFSTR("Air"), CFRangeMake(0, CFStringGetLength(computer_name)), kCFCompareCaseInsensitive);
982    CFStringFindAndReplace(short_name, CFSTR("macbook-pro"), CFSTR("MBP"), CFRangeMake(0, CFStringGetLength(computer_name)), kCFCompareCaseInsensitive);
983    CFStringFindAndReplace(short_name, CFSTR("mac-mini"), CFSTR("Mini"), CFRangeMake(0, CFStringGetLength(computer_name)), kCFCompareCaseInsensitive);
984    CFStringFindAndReplace(short_name, CFSTR("mac-pro"), CFSTR("Pro"), CFRangeMake(0, CFStringGetLength(computer_name)), kCFCompareCaseInsensitive);
985
986    // recompute the new string length
987    len = CFStringGetLength(short_name);
988	if (len <= desired_length) {
989		/* shrinking long names worked */
990		return short_name;
991	}
992
993    CFStringFindAndReplace(short_name, CFSTR("-"), CFSTR(""), CFRangeMake(0, len-1), kCFCompareCaseInsensitive);
994	// recompute the new string length
995    len = CFStringGetLength(short_name);
996
997    if (len <= desired_length) {
998	/* computer name already short due to removal of '-'*/
999	return short_name;
1000    }
1001
1002    delete_len = len - desired_length;
1003    // last option: eliminate the middle string, keep first and last part of the string
1004    CFStringDelete(short_name,
1005		   CFRangeMake(desired_length / 2, delete_len));
1006
1007    return short_name;
1008
1009}
1010
1011static void
1012computer_name_update(SCDynamicStoreRef session)
1013{
1014    char		buf[256];
1015    CFStringEncoding	encoding;
1016    CFStringRef 	name;
1017
1018    if (session == NULL)
1019	return;
1020
1021    if (S_computer_name) {
1022	free(S_computer_name);
1023	S_computer_name = NULL;
1024    }
1025
1026    name = SCDynamicStoreCopyComputerName(session, &encoding);
1027    if (name == NULL) {
1028	goto done;
1029    }
1030    if (_SC_CFStringIsValidDNSName(name) == FALSE) {
1031	my_CFRelease(&name);
1032	name = SCDynamicStoreCopyLocalHostName(session);
1033	if (name == NULL) {
1034	    goto done;
1035	}
1036	if (_SC_CFStringIsValidDNSName(name) == FALSE) {
1037	    goto done;
1038	}
1039	if (CFStringGetLength(name) > S_dhcp_local_hostname_length_max) {
1040	    /* don't exceed the maximum */
1041	    CFStringRef short_name = myCFStringCopyShortenedString(name, S_dhcp_local_hostname_length_max);
1042	    if (short_name == NULL) {
1043		goto done;
1044	    }
1045	    my_CFRelease(&name);
1046    	    name = short_name;
1047	}
1048    }
1049    if (CFStringGetCString(name, buf, sizeof(buf),
1050			   kCFStringEncodingASCII) == FALSE) {
1051	goto done;
1052    }
1053    S_computer_name = strdup(buf);
1054
1055 done:
1056    my_CFRelease(&name);
1057    return;
1058}
1059
1060/**
1061 ** ARP routine
1062 **/
1063
1064static void
1065service_resolve_router_complete(void * arg1, void * arg2,
1066				const arp_result_t * result)
1067{
1068    service_resolve_router_callback_t *	callback_func;
1069    interface_t *			if_p;
1070    ServiceRef				service_p;
1071    router_arp_status_t			status;
1072
1073    service_p = (ServiceRef)arg1;
1074    callback_func = (service_resolve_router_callback_t *)arg2;
1075    if_p = service_interface(service_p);
1076    if (result->error) {
1077	my_log(LOG_ERR, "service_resolve_router_complete %s: ARP failed, %s",
1078	       if_name(if_p),
1079	       arp_client_errmsg(result->client));
1080	status = router_arp_status_failed_e;
1081    }
1082    else if (result->in_use) {
1083	/* grab the latest router hardware address */
1084	bcopy(result->addr.target_hardware, service_p->u.v4.router.hwaddr,
1085	      if_link_length(if_p));
1086	service_router_set_all_valid(service_p);
1087	my_log(LOG_DEBUG, "service_resolve_router_complete %s: ARP "
1088	       IP_FORMAT ": response received", if_name(if_p),
1089	       IP_LIST(&service_p->u.v4.router.iaddr));
1090	status = router_arp_status_success_e;
1091    }
1092    else {
1093	status = router_arp_status_no_response_e;
1094	my_log(LOG_DEBUG, "service_resolve_router_complete %s: ARP router "
1095	       IP_FORMAT ": no response", if_name(if_p),
1096	       IP_LIST(&service_p->u.v4.router.iaddr));
1097    }
1098    (*callback_func)(service_p, status);
1099    return;
1100}
1101
1102PRIVATE_EXTERN boolean_t
1103service_resolve_router(ServiceRef service_p, arp_client_t * arp,
1104		       service_resolve_router_callback_t * callback_func,
1105		       struct in_addr our_ip)
1106{
1107    interface_t *	if_p = service_interface(service_p);
1108    struct in_addr	router_ip;
1109
1110    if (G_discover_and_publish_router_mac_address == FALSE) {
1111	/* don't bother */
1112	return (FALSE);
1113    }
1114
1115    service_router_clear_arp_verified(service_p);
1116    if (service_router_is_iaddr_valid(service_p) == 0) {
1117	my_log(LOG_NOTICE,
1118	       "service_resolve_router %s: IP address missing", if_name(if_p));
1119	return (FALSE);
1120    }
1121    router_ip = service_router_iaddr(service_p);
1122    my_log(LOG_DEBUG, "service_resolve_router %s: sender " IP_FORMAT
1123	   " target " IP_FORMAT " started",
1124	   if_name(if_p), IP_LIST(&our_ip), IP_LIST(&router_ip));
1125    arp_client_resolve(arp, service_resolve_router_complete,
1126		       service_p, callback_func, our_ip, router_ip,
1127		       S_discover_router_mac_address_secs);
1128    return (TRUE);
1129}
1130
1131PRIVATE_EXTERN boolean_t
1132service_populate_router_arpinfo(ServiceRef service_p,
1133				arp_address_info_t * info_p)
1134{
1135    interface_t *       	if_p = service_interface(service_p);
1136    struct in_addr      	router_ip;
1137
1138    if (G_discover_and_publish_router_mac_address == FALSE) {
1139	/* don't bother */
1140	return (FALSE);
1141    }
1142
1143    service_router_clear_arp_verified(service_p);
1144
1145    if (service_router_is_iaddr_valid(service_p) == 0) {
1146	my_log(LOG_DEBUG,
1147	       "service_populate_router_arpinfo %s: "
1148	       "Router IP address missing",
1149	       if_name(if_p));
1150	return (FALSE);
1151    }
1152
1153    router_ip = service_router_iaddr(service_p);
1154
1155    my_log(LOG_DEBUG, "service_populate_router_arpinfo %s: "
1156	   "found gateway " IP_FORMAT,
1157	   if_name(if_p), IP_LIST(&router_ip));
1158
1159
1160    info_p->target_ip = router_ip;
1161    bcopy(service_router_hwaddr(service_p), info_p->target_hardware,
1162	  service_router_hwaddr_size(service_p));
1163
1164    return (TRUE);
1165}
1166
1167
1168PRIVATE_EXTERN boolean_t
1169service_update_router_address(ServiceRef service_p,
1170			      dhcpol_t * options, struct in_addr our_ip)
1171{
1172    struct in_addr *		router_p;
1173
1174    router_p = dhcp_get_router_from_options(options, our_ip);
1175    if (router_p == NULL) {
1176	goto failed;
1177    }
1178    if (service_router_all_valid(service_p)
1179	&& router_p->s_addr == service_router_iaddr(service_p).s_addr) {
1180	/* router is the same, no need to update */
1181	return (FALSE);
1182    }
1183    service_router_clear(service_p);
1184    service_router_set_iaddr(service_p, *router_p);
1185    service_router_set_iaddr_valid(service_p);
1186    return (TRUE);
1187
1188 failed:
1189    service_router_clear(service_p);
1190    return (FALSE);
1191}
1192
1193#define STARTUP_KEY	CFSTR("Plugin:IPConfiguration")
1194
1195static __inline__ void
1196unblock_startup(SCDynamicStoreRef session)
1197{
1198    (void)SCDynamicStoreSetValue(session, STARTUP_KEY, STARTUP_KEY);
1199}
1200
1201/**
1202 ** Active During Sleep (ADS) routines
1203 **/
1204INLINE CFStringRef
1205ActiveDuringSleepRequestedKeyCopy(CFStringRef ifn_cf)
1206{
1207    return (SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
1208							  kSCDynamicStoreDomainState,
1209							  ifn_cf,
1210							  kSCEntNetInterfaceActiveDuringSleepRequested));
1211}
1212
1213STATIC void
1214ActiveDuringSleepRequestedKeyChanged(SCDynamicStoreRef store,
1215				     CFStringRef cache_key)
1216{
1217    boolean_t		ads_requested;
1218    CFDictionaryRef	dict;
1219    CFStringRef		ifname;
1220    IFStateRef   	ifstate;
1221
1222    if (CFStringHasPrefix(cache_key, S_state_interface_prefix) == FALSE) {
1223	return;
1224    }
1225
1226    /* State:/Network/Interface/<ifname>/ActiveDuringSleepRequested */
1227    ifname = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
1228    if (ifname == NULL) {
1229	return;
1230    }
1231    ifstate = IFStateListGetIFState(&S_ifstate_list, ifname, NULL);
1232    my_CFRelease(&ifname);
1233    if (ifstate != NULL) {
1234	dict = SCDynamicStoreCopyValue(store, cache_key);
1235	if (dict != NULL) {
1236	    ads_requested = TRUE;
1237	    CFRelease(dict);
1238	}
1239	else {
1240	    ads_requested = FALSE;
1241	}
1242	IFStateSetActiveDuringSleepRequested(ifstate, ads_requested);
1243    }
1244    return;
1245}
1246
1247STATIC void
1248ActiveDuringSleepProcess(IFStateList_t * list)
1249{
1250    int 		i;
1251    int			if_count;
1252
1253    if_count = dynarray_count(list);
1254    for (i = 0; i < if_count; i++) {
1255	IFStateRef		ifstate = dynarray_element(list, i);
1256
1257	IFStateProcessActiveDuringSleep(ifstate);
1258    }
1259    return;
1260}
1261
1262#define WAKE_ID_PREFIX		"com.apple.networking.IPConfiguration"
1263
1264STATIC void
1265CleanupWakeEvents(void)
1266{
1267    CFArrayRef		events;
1268    CFIndex		count;
1269    int			i;
1270
1271    events = IOPMCopyScheduledPowerEvents();
1272    if (events == NULL) {
1273	return;
1274    }
1275    count = CFArrayGetCount(events);
1276    for (i = 0; i < count; i++) {
1277	CFDictionaryRef	event = CFArrayGetValueAtIndex(events, i);
1278	CFStringRef	wake_id;
1279
1280	wake_id = CFDictionaryGetValue(event, CFSTR(kIOPMPowerEventAppNameKey));
1281	if (CFStringHasPrefix(wake_id, CFSTR(WAKE_ID_PREFIX))) {
1282	    CFDateRef	wake_time;
1283
1284	    my_log_fl(LOG_DEBUG, "IOPMCancelScheduledPowerEvent(%@)", wake_id);
1285	    wake_time
1286		= CFDictionaryGetValue(event, CFSTR(kIOPMPowerEventTimeKey));
1287	    IOPMCancelScheduledPowerEvent(wake_time, wake_id,
1288					  CFSTR(kIOPMAutoWake));
1289	}
1290    }
1291    CFRelease(events);
1292    return;
1293}
1294
1295/**
1296 ** Service, IFState routines
1297 **/
1298static void
1299ServiceFree(void * arg)
1300{
1301    IFStateRef		ifstate;
1302    ServiceRef		service_p = (ServiceRef)arg;
1303
1304    if (G_IPConfiguration_verbose) {
1305	my_log(LOG_DEBUG, "ServiceFree(%@) %s",
1306	       service_p->serviceID, ipconfig_method_string(service_p->method));
1307    }
1308    ifstate = service_ifstate(service_p);
1309    if (ifstate != NULL && ifstate->linklocal_service_p == service_p) {
1310	ifstate->linklocal_service_p = NULL;
1311    }
1312    config_method_stop(service_p);
1313    service_publish_clear(service_p);
1314#if ! TARGET_OS_EMBEDDED
1315    ServiceRemoveAddressConflict(service_p);
1316#endif /* ! TARGET_OS_EMBEDDED */
1317    my_CFRelease(&service_p->serviceID);
1318    my_CFRelease(&service_p->parent_serviceID);
1319    my_CFRelease(&service_p->child_serviceID);
1320    if (service_p->pid_source != NULL) {
1321	CFStringRef	serviceID;
1322
1323	serviceID = (CFStringRef)dispatch_get_context(service_p->pid_source);
1324	CFRelease(serviceID);
1325	dispatch_source_cancel(service_p->pid_source);
1326	dispatch_release(service_p->pid_source);
1327	service_p->pid_source = NULL;
1328    }
1329    free(service_p);
1330    return;
1331}
1332
1333static ServiceRef
1334ServiceCreate(IFStateRef ifstate, CFStringRef serviceID,
1335	      ipconfig_method_t method,
1336	      ipconfig_method_data_t * method_data,
1337	      ServiceRef parent_service_p, ipconfig_status_t * status_p)
1338{
1339    ServiceRef		service_p;
1340    ipconfig_status_t	status = ipconfig_status_success_e;
1341
1342    if (method == ipconfig_method_linklocal_e
1343	&& ifstate->linklocal_service_p != NULL) {
1344	IFStateFreeService(ifstate,
1345			   ifstate->linklocal_service_p);
1346	/* side-effect: ifstate->linklocal_service_p = NULL */
1347    }
1348    service_p = (ServiceRef)malloc(sizeof(*service_p));
1349    if (service_p == NULL) {
1350	status = ipconfig_status_allocation_failed_e;
1351	goto failed;
1352    }
1353    bzero(service_p, sizeof(*service_p));
1354    service_p->method = method;
1355    service_p->ifstate = ifstate;
1356    if (serviceID != NULL) {
1357	service_p->serviceID = (void *)CFRetain(serviceID);
1358    }
1359    else {
1360	service_p->serviceID = (void *)
1361	    CFStringCreateWithFormat(NULL, NULL,
1362				     CFSTR("%s-%s"),
1363				     ipconfig_method_string(method),
1364				     if_name(ifstate->if_p));
1365    }
1366    if (parent_service_p != NULL) {
1367	service_p->parent_serviceID
1368	    = (void *)CFRetain(parent_service_p->serviceID);
1369    }
1370    status = config_method_start(service_p, method, method_data);
1371    if (status != ipconfig_status_success_e) {
1372	goto failed;
1373    }
1374    if (parent_service_p != NULL) {
1375	my_CFRelease(&parent_service_p->child_serviceID);
1376	parent_service_p->child_serviceID
1377	    = (void *)CFRetain(service_p->serviceID);
1378    }
1379
1380    /* keep track of which service is the linklocal service */
1381    if (service_p->method == ipconfig_method_linklocal_e) {
1382	ifstate->linklocal_service_p = service_p;
1383    }
1384    *status_p = status;
1385    return (service_p);
1386
1387 failed:
1388    if (service_p != NULL) {
1389	my_CFRelease(&service_p->serviceID);
1390	my_CFRelease(&service_p->parent_serviceID);
1391	free(service_p);
1392    }
1393    *status_p = status;
1394    return (NULL);
1395}
1396
1397static void
1398ServiceHandleProcessExit(dispatch_source_t source)
1399{
1400    pid_t		pid;
1401    CFStringRef		serviceID;
1402    ipconfig_status_t	status;
1403
1404    pid = (pid_t)dispatch_source_get_handle(source);
1405    my_log(LOG_DEBUG, "IPConfiguration: pid %d exited", pid);
1406    serviceID = (CFStringRef)dispatch_get_context(source);
1407    status = S_remove_service_with_id_str(NULL, serviceID);
1408    if (status != ipconfig_status_success_e) {
1409	my_log(LOG_ERR,
1410	       "IPConfiguration: failed to stop service %@, %s",
1411	       serviceID, ipconfig_status_string(status));
1412    }
1413    return;
1414}
1415
1416static CFRunLoopRef 	S_plugin_runloop;
1417
1418static void
1419ServiceProcessExited(dispatch_source_t source)
1420{
1421    /* handle the source on our runloop to avoid locking issues */
1422    CFRunLoopPerformBlock(S_plugin_runloop,
1423			  kCFRunLoopDefaultMode,
1424			  ^{ ServiceHandleProcessExit(source); });
1425    CFRunLoopWakeUp(S_plugin_runloop);
1426    return;
1427}
1428
1429static void
1430ServiceMonitorPID(ServiceRef service_p, pid_t pid)
1431{
1432    dispatch_source_t		source;
1433
1434    if (S_plugin_runloop == NULL) {
1435	S_plugin_runloop = CFRunLoopGetCurrent();
1436    }
1437    source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid,
1438				    DISPATCH_PROC_EXIT,
1439				    dispatch_get_main_queue());
1440    if (source == NULL) {
1441	my_log(LOG_ERR, "IPConfiguration: dispatch_source_create failed");
1442	return;
1443    }
1444    CFRetain(service_p->serviceID);
1445    dispatch_set_context(source, (void *)service_p->serviceID);
1446    dispatch_source_set_event_handler(source,
1447				      ^{ ServiceProcessExited(source); });
1448    dispatch_resume(source);
1449    service_p->pid_source = source;
1450    return;
1451}
1452
1453static ServiceRef
1454IFStateGetServiceWithID(IFStateRef ifstate, CFStringRef serviceID,
1455			boolean_t is_ipv4)
1456{
1457    int			i;
1458    dynarray_t *	list;
1459
1460    if (is_ipv4) {
1461	list = &ifstate->services;
1462    }
1463    else {
1464	list = &ifstate->services_v6;
1465    }
1466    for (i = 0; i < dynarray_count(list); i++) {
1467	ServiceRef	service_p = dynarray_element(list, i);
1468
1469	if (CFEqual(serviceID, service_p->serviceID)) {
1470	    return (service_p);
1471	}
1472    }
1473    return (NULL);
1474}
1475
1476
1477static ServiceRef
1478IFState_service_with_ip(IFStateRef ifstate, struct in_addr iaddr)
1479{
1480    int		j;
1481
1482    for (j = 0; j < dynarray_count(&ifstate->services); j++) {
1483	ServiceRef	service_p = dynarray_element(&ifstate->services, j);
1484
1485	if (service_p->u.v4.info.addr.s_addr == iaddr.s_addr) {
1486	    return (service_p);
1487	}
1488    }
1489    return (NULL);
1490}
1491
1492/*
1493 * Function: IFStateGetServiceMatchingIPv4Method
1494 * Purpose:
1495 *   Find a service that "matches" the given requested IPv4 method.  A service
1496 *   "matches" if the method types are not manual (i.e. BOOTP, DHCP),
1497 *   or the method types are manual (Manual, Inform, Failover), and the
1498 *   requested IP address matches.
1499 *
1500 *   If the parameter "just_dynamic" is TRUE, only match any dynamic
1501 *   service, otherwise match all services.
1502 *
1503 */
1504static ServiceRef
1505IFStateGetServiceMatchingIPv4Method(IFStateRef ifstate,
1506				     ipconfig_method_t method,
1507				     ipconfig_method_data_t * method_data,
1508				     boolean_t just_dynamic)
1509{
1510    int			i;
1511    boolean_t		is_dhcp_or_bootp = FALSE;
1512    boolean_t		is_manual;
1513
1514    is_manual = ipconfig_method_is_manual(method);
1515    if (is_manual == FALSE) {
1516	is_dhcp_or_bootp = ipconfig_method_is_dhcp_or_bootp(method);
1517    }
1518    for (i = 0; i < dynarray_count(&ifstate->services); i++) {
1519	ServiceRef	service_p = dynarray_element(&ifstate->services, i);
1520
1521	if (just_dynamic && service_p->is_dynamic == FALSE) {
1522	    continue;
1523	}
1524	if (is_manual) {
1525	    if (ipconfig_method_is_manual(service_p->method)
1526		&& (method_data->manual.addr.s_addr
1527		    == service_requested_ip_addr(service_p).s_addr)) {
1528		return (service_p);
1529	    }
1530	}
1531	else if (is_dhcp_or_bootp
1532		 && ipconfig_method_is_dhcp_or_bootp(service_p->method)) {
1533	    return (service_p);
1534	}
1535	else if (service_p->method == method) {
1536	    return (service_p);
1537	}
1538    }
1539    return (NULL);
1540}
1541
1542/*
1543 * Function: IFStateGetServiceMatchingIPv6Method
1544 * Purpose:
1545 *   Find a service that "matches" the given requested method.  A service
1546 *   "matches" if the method types are the same, and for manual method,
1547 *   the IPv6 addresses are the same.
1548 *
1549 *   If the parameter "just_dynamic" is TRUE, only match any dynamic
1550 *   service, otherwise match all services.
1551 *
1552 */
1553static ServiceRef
1554IFStateGetServiceMatchingIPv6Method(IFStateRef ifstate,
1555				    ipconfig_method_t method,
1556				    ipconfig_method_data_t * method_data,
1557				    boolean_t just_dynamic)
1558{
1559    int			i;
1560
1561    for (i = 0; i < dynarray_count(&ifstate->services_v6); i++) {
1562	ServiceRef	service_p = dynarray_element(&ifstate->services_v6, i);
1563	ServiceIPv6Ref	v6_p;
1564
1565	if (just_dynamic && service_p->is_dynamic == FALSE) {
1566	    continue;
1567	}
1568	if (service_p->method != method) {
1569	    continue;
1570	}
1571	if (method != ipconfig_method_manual_v6_e) {
1572	    return (service_p);
1573	}
1574	v6_p = (ServiceIPv6Ref)service_p;
1575	if (IN6_ARE_ADDR_EQUAL(&method_data->manual_v6.addr,
1576			       &v6_p->requested_ip.addr)) {
1577	    return (service_p);
1578	}
1579    }
1580    return (NULL);
1581}
1582
1583/*
1584 * Function: IFStateGetServiceMatchingMethod
1585 * Purpose:
1586 *   Find a service that "matches" the given requested method.
1587 */
1588static ServiceRef
1589IFStateGetServiceMatchingMethod(IFStateRef ifstate,
1590				ipconfig_method_t method,
1591				ipconfig_method_data_t * method_data,
1592				boolean_t just_dynamic)
1593{
1594    if (ipconfig_method_is_v4(method)) {
1595	return (IFStateGetServiceMatchingIPv4Method(ifstate, method,
1596						     method_data,
1597						     just_dynamic));
1598    }
1599    return (IFStateGetServiceMatchingIPv6Method(ifstate, method,
1600						method_data,
1601						just_dynamic));
1602}
1603
1604static ServiceRef
1605IFStateGetServiceWithIPv4Method(IFStateRef ifstate,
1606				ipconfig_method_t method,
1607				ipconfig_method_data_t * method_data,
1608				boolean_t just_dynamic)
1609{
1610    int			j;
1611    boolean_t		is_manual;
1612
1613    is_manual = ipconfig_method_is_manual(method);
1614    for (j = 0; j < dynarray_count(&ifstate->services); j++) {
1615	ServiceRef	service_p = dynarray_element(&ifstate->services, j);
1616
1617	if (just_dynamic && service_p->is_dynamic == FALSE) {
1618	    continue;
1619	}
1620	if (method == service_p->method) {
1621	    if (is_manual == FALSE
1622		|| (method_data->manual.addr.s_addr
1623		    == service_requested_ip_addr(service_p).s_addr)) {
1624		return (service_p);
1625	    }
1626	}
1627    }
1628    return (NULL);
1629}
1630
1631static __inline__ ServiceRef
1632IFStateGetServiceWithIPv6Method(IFStateRef ifstate,
1633				ipconfig_method_t method,
1634				ipconfig_method_data_t * method_data,
1635				boolean_t just_dynamic)
1636{
1637    /* IFStateGetServiceMatchingIPv6Method is already an exact match */
1638    return (IFStateGetServiceMatchingIPv6Method(ifstate, method,
1639						method_data,
1640						just_dynamic));
1641}
1642
1643/*
1644 * Function: IFStateGetServiceWithMethod
1645 * Purpose:
1646 *   Find a service with the given method and method args.
1647 *
1648 *   If the parameter "just_dynamic" is TRUE, only match any dynamic
1649 *   service, otherwise match all services.
1650 *
1651 */
1652static ServiceRef
1653IFStateGetServiceWithMethod(IFStateRef ifstate,
1654			    ipconfig_method_t method,
1655			    ipconfig_method_data_t * method_data,
1656			    boolean_t just_dynamic)
1657{
1658    if (ipconfig_method_is_v4(method)) {
1659	return (IFStateGetServiceWithIPv4Method(ifstate, method,
1660						 method_data,
1661						 just_dynamic));
1662    }
1663    return (IFStateGetServiceWithIPv6Method(ifstate, method,
1664					    method_data,
1665					    just_dynamic));
1666}
1667
1668static void
1669S_FreeNonDynamicServices(dynarray_t * services_p)
1670{
1671    int	count;
1672    int	i;
1673
1674    count = dynarray_count(services_p);
1675    for (i = 0; i < count; ) {
1676	ServiceRef	service_p = dynarray_element(services_p, i);
1677
1678	if (service_p->is_dynamic) {
1679	    i++;
1680	    continue;
1681	}
1682	dynarray_free_element(services_p, i);
1683	count--;
1684    }
1685    return;
1686}
1687
1688static void
1689IFStateFreeIPv4Services(IFStateRef ifstate, boolean_t all)
1690{
1691    if (all) {
1692	dynarray_free(&ifstate->services);
1693    }
1694    else {
1695	S_FreeNonDynamicServices(&ifstate->services);
1696    }
1697    ifstate->startup_ready = TRUE;
1698    if (dynarray_count(&ifstate->services) == 0
1699	&& if_ift_type(ifstate->if_p) != IFT_STF) {
1700	inet_detach_interface(if_name(ifstate->if_p));
1701    }
1702    return;
1703}
1704
1705static void
1706IFStateFreeIPv6Services(IFStateRef ifstate, boolean_t all)
1707{
1708    if (all) {
1709	dynarray_free(&ifstate->services_v6);
1710    }
1711    else {
1712	S_FreeNonDynamicServices(&ifstate->services_v6);
1713    }
1714    if (dynarray_count(&ifstate->services_v6) == 0) {
1715	(void)inet6_linklocal_stop(if_name(ifstate->if_p));
1716	inet6_detach_interface(if_name(ifstate->if_p));
1717    }
1718    return;
1719}
1720
1721static void
1722IFStateFreeServiceWithID(IFStateRef ifstate, CFStringRef serviceID,
1723			 boolean_t is_ipv4)
1724{
1725    int			i;
1726    dynarray_t *	list;
1727
1728    if (is_ipv4) {
1729	list = &ifstate->services;
1730    }
1731    else {
1732	list = &ifstate->services_v6;
1733    }
1734    for (i = 0; i < dynarray_count(list); i++) {
1735	ServiceRef	service_p = dynarray_element(list, i);
1736
1737	if (CFEqual(serviceID, service_p->serviceID)) {
1738	    dynarray_free_element(list, i);
1739	    return;
1740	}
1741    }
1742    return;
1743}
1744
1745static void
1746IFStateFreeService(IFStateRef ifstate, ServiceRef service_p)
1747{
1748    IFStateFreeServiceWithID(ifstate, service_p->serviceID,
1749			     ipconfig_method_is_v4(service_p->method));
1750    return;
1751}
1752
1753static ipconfig_status_t
1754IFState_service_add(IFStateRef ifstate, CFStringRef serviceID,
1755		    ipconfig_method_t method, void * method_data,
1756		    ServiceRef parent_service_p, ServiceRef * ret_service_p)
1757{
1758    interface_t *	if_p = ifstate->if_p;
1759    ServiceRef		service_p = NULL;
1760    ipconfig_status_t	status = ipconfig_status_success_e;
1761    boolean_t		started_v6_linklocal = FALSE;
1762
1763    if (ipconfig_method_is_v4(method)) {
1764	/* attach IP */
1765	inet_attach_interface(if_name(if_p));
1766    }
1767    else {
1768	int	ift_type = if_ift_type(if_p);
1769
1770	/* attach IPv6 */
1771	inet6_attach_interface(if_name(if_p));
1772
1773	if (ift_type != IFT_LOOP && ift_type != IFT_STF) {
1774	    link_status_t	link = if_get_link_status(if_p);
1775
1776	    /* make sure ND6_IFF_PERFORMNUD is set correctly */
1777	    (void)inet6_set_perform_nud(if_name(if_p),
1778					!ifstate->disable_perform_nud);
1779
1780	    /* start IPv6 Link Local */
1781	    if (link.valid == FALSE || link.active) {
1782		(void)inet6_linklocal_start(if_name(if_p), !if_is_awdl(if_p));
1783		started_v6_linklocal = TRUE;
1784	    }
1785	}
1786    }
1787    /* try to configure the service */
1788    service_p = ServiceCreate(ifstate, serviceID, method,
1789			      method_data,
1790			      parent_service_p, &status);
1791    if (service_p == NULL) {
1792	my_log(LOG_DEBUG, "status from %s was %s",
1793	       ipconfig_method_string(method),
1794	       ipconfig_status_string(status));
1795	if (ipconfig_method_is_v4(method)) {
1796	    if (dynarray_count(&ifstate->services) == 0) {
1797		/* no services configured, detach IP again */
1798		ifstate->startup_ready = TRUE;
1799		inet_detach_interface(if_name(if_p));
1800	    }
1801	}
1802	else {
1803	    if (dynarray_count(&ifstate->services_v6) == 0) {
1804		if (started_v6_linklocal) {
1805		    (void)inet6_linklocal_stop(if_name(if_p));
1806		}
1807		inet6_detach_interface(if_name(if_p));
1808	    }
1809	}
1810	all_services_ready();
1811    }
1812    else if (ipconfig_method_is_v4(method)) {
1813	dynarray_add(&ifstate->services, service_p);
1814    }
1815    else {
1816	dynarray_add(&ifstate->services_v6, service_p);
1817    }
1818
1819    if (ret_service_p) {
1820	*ret_service_p = service_p;
1821    }
1822    return (status);
1823}
1824
1825static void
1826IFState_update_media_status(IFStateRef ifstate)
1827{
1828    const char * 	ifname = if_name(ifstate->if_p);
1829    link_status_t	link;
1830
1831    link = if_link_status_update(ifstate->if_p);
1832    if (link.valid == FALSE) {
1833	my_log(LOG_DEBUG, "%s link is unknown", ifname);
1834    }
1835    else {
1836	my_log(LOG_DEBUG, "%s link is %s", ifname, link.active ? "up" : "down");
1837    }
1838    if (if_is_wireless(ifstate->if_p)) {
1839	struct ether_addr	bssid;
1840	CFStringRef		ssid;
1841
1842	ssid = S_copy_ssid_bssid(ifstate->ifname, &bssid);
1843	if (G_IPConfiguration_verbose) {
1844	    if (ssid != NULL) {
1845		my_log(LOG_DEBUG, "%s: SSID %@ BSSID %s",
1846		       if_name(ifstate->if_p), ssid, ether_ntoa(&bssid));
1847	    }
1848	    else {
1849		my_log(LOG_DEBUG, "%s: no SSID",
1850		       if_name(ifstate->if_p));
1851	    }
1852	}
1853	/* remember the ssid */
1854	IFState_set_ssid_bssid(ifstate, ssid, &bssid);
1855	my_CFRelease(&ssid);
1856    }
1857    return;
1858}
1859
1860STATIC void
1861IFState_active_during_sleep_init(IFStateRef ifstate)
1862{
1863    CFDictionaryRef	dict;
1864    CFStringRef		key;
1865
1866    key = ActiveDuringSleepRequestedKeyCopy(ifstate->ifname);
1867    dict = SCDynamicStoreCopyValue(S_scd_session, key);
1868    CFRelease(key);
1869    if (dict != NULL) {
1870	ifstate->active_during_sleep.requested = TRUE;
1871	CFRelease(dict);
1872    }
1873    return;
1874}
1875
1876static IFStateRef
1877IFState_init(interface_t * if_p)
1878{
1879    IFStateRef ifstate;
1880
1881    ifstate = malloc(sizeof(*ifstate));
1882    if (ifstate == NULL) {
1883	my_log(LOG_ERR, "IFState_init: malloc ifstate failed");
1884	return (NULL);
1885    }
1886    bzero(ifstate, sizeof(*ifstate));
1887    ifstate->if_p = if_dup(if_p);
1888    ifstate->ifname = CFStringCreateWithCString(NULL, if_name(if_p),
1889						kCFStringEncodingASCII);
1890    IFState_update_media_status(ifstate);
1891    IFState_active_during_sleep_init(ifstate);
1892    ifstate->timer = timer_callout_init();
1893    dynarray_init(&ifstate->services, ServiceFree, NULL);
1894    dynarray_init(&ifstate->services_v6, ServiceFree, NULL);
1895    return (ifstate);
1896}
1897
1898static boolean_t
1899IFState_wireless_did_roam(IFStateRef ifstate, CFStringRef ssid,
1900			  const struct ether_addr * bssid)
1901{
1902    if (ssid != NULL
1903	&& my_CFEqual(ssid, ifstate->ssid)
1904	&& bcmp(bssid, &ifstate->bssid, sizeof(ifstate->bssid)) != 0) {
1905	return (TRUE);
1906    }
1907    return (FALSE);
1908}
1909
1910static void
1911IFState_set_bssid(IFStateRef ifstate, const struct ether_addr * bssid)
1912{
1913    if (bssid != NULL) {
1914	ifstate->bssid = *bssid;
1915    }
1916    else {
1917	bzero(&ifstate->bssid, sizeof(ifstate->bssid));
1918    }
1919    return;
1920}
1921
1922static void
1923IFState_set_ssid_bssid(IFStateRef ifstate, CFStringRef ssid,
1924		       const struct ether_addr * bssid)
1925{
1926    if (ssid != NULL) {
1927	CFRetain(ssid);
1928    }
1929    if (ifstate->ssid != NULL) {
1930	CFRelease(ifstate->ssid);
1931    }
1932    ifstate->ssid = ssid;
1933    IFState_set_bssid(ifstate, bssid);
1934    return;
1935}
1936
1937static void
1938IFState_free(void * arg)
1939{
1940    IFStateRef		ifstate = (IFStateRef)arg;
1941
1942    if (G_IPConfiguration_verbose) {
1943	my_log(LOG_DEBUG, "IFState_free(%s)", if_name(ifstate->if_p));
1944    }
1945    IFStateFreeIPv4Services(ifstate, TRUE);
1946    IFStateFreeIPv6Services(ifstate, TRUE);
1947    my_CFRelease(&ifstate->ifname);
1948    my_CFRelease(&ifstate->neighbor_advert_list);
1949    IFState_set_ssid_bssid(ifstate, NULL, NULL);
1950    if_free(&ifstate->if_p);
1951    timer_callout_free(&ifstate->timer);
1952    free(ifstate);
1953    return;
1954}
1955
1956STATIC void
1957IFStateSetActiveDuringSleepNeedsAttention(IFStateRef ifstate)
1958{
1959    S_active_during_sleep_needs_attention = TRUE;
1960    ifstate->active_during_sleep.needs_attention = TRUE;
1961    return;
1962}
1963
1964STATIC void
1965IFStateSetActiveDuringSleepRequested(IFStateRef ifstate,
1966				     boolean_t ads_requested)
1967{
1968    if (ads_requested != ifstate->active_during_sleep.requested) {
1969	/* active during sleep request changed */
1970	my_log(LOG_DEBUG, "%s: active during sleep %srequested",
1971	       if_name(ifstate->if_p), ads_requested ? "" : "not ");
1972	ifstate->active_during_sleep.requested = ads_requested;
1973	IFStateSetActiveDuringSleepNeedsAttention(ifstate);
1974    }
1975    return;
1976}
1977
1978STATIC void
1979IFStateProcessActiveDuringSleep(IFStateRef ifstate)
1980{
1981    active_during_sleep_t	active_during_sleep;
1982    CFDictionaryRef		dict;
1983
1984    if (ifstate->active_during_sleep.needs_attention == FALSE) {
1985	return;
1986    }
1987    ifstate->active_during_sleep.needs_attention = FALSE;
1988    bzero(&active_during_sleep, sizeof(active_during_sleep));
1989    active_during_sleep.requested
1990	= ifstate->active_during_sleep.requested;
1991    active_during_sleep.supported = TRUE;
1992
1993    /* v4 services */
1994    service_list_event(&ifstate->services, IFEventID_active_during_sleep_e,
1995		       &active_during_sleep);
1996#if 0
1997    /* v6 services */
1998    service_list_event(&ifstate->services_v6, IFEventID_active_during_sleep_e,
1999		       &active_during_sleep);
2000#endif
2001    if (ifstate->active_during_sleep.requested
2002	&& active_during_sleep.supported) {
2003	/* indicate that sleep is supported */
2004	dict = CFDictionaryCreate(NULL,
2005				  NULL, NULL, 0,
2006				  &kCFTypeDictionaryKeyCallBacks,
2007				  &kCFTypeDictionaryValueCallBacks);
2008    }
2009    else {
2010	/* indicate that sleep is not supported */
2011	dict = NULL;
2012    }
2013    my_SCDynamicStoreSetInterface(S_scd_session,
2014				  ifstate->ifname,
2015				  kSCEntNetInterfaceActiveDuringSleepSupported,
2016				  dict);
2017    my_CFRelease(&dict);
2018    return;
2019}
2020
2021static IFStateRef
2022IFStateList_ifstate_with_name(IFStateList_t * list, const char * ifname,
2023			      int * where)
2024{
2025    int i;
2026
2027    for (i = 0; i < dynarray_count(list); i++) {
2028	IFStateRef	element = dynarray_element(list, i);
2029	if (strcmp(if_name(element->if_p), ifname) == 0) {
2030	    if (where != NULL) {
2031		*where = i;
2032	    }
2033	    return (element);
2034	}
2035    }
2036    return (NULL);
2037}
2038
2039static IFStateRef
2040IFStateListGetIFState(IFStateList_t * list, CFStringRef ifname,
2041		      int * where)
2042{
2043    int i;
2044
2045    for (i = 0; i < dynarray_count(list); i++) {
2046	IFStateRef	element = dynarray_element(list, i);
2047
2048	if (CFEqual(ifname, element->ifname)) {
2049	    if (where != NULL) {
2050		*where = i;
2051	    }
2052	    return (element);
2053	}
2054    }
2055    return (NULL);
2056}
2057
2058static IFStateRef
2059IFStateList_ifstate_create(IFStateList_t * list, interface_t * if_p)
2060{
2061    IFStateRef   	ifstate;
2062
2063    ifstate = IFStateList_ifstate_with_name(list, if_name(if_p), NULL);
2064    if (ifstate == NULL) {
2065	ifstate = IFState_init(if_p);
2066	if (ifstate) {
2067	    dynarray_add(list, ifstate);
2068	}
2069    }
2070    return (ifstate);
2071}
2072
2073static void
2074IFStateList_ifstate_free(IFStateList_t * list, const char * ifname)
2075{
2076    IFStateRef	ifstate;
2077    int		where = -1;
2078
2079    ifstate = IFStateList_ifstate_with_name(list, ifname, &where);
2080    if (ifstate == NULL) {
2081	return;
2082    }
2083    dynarray_free_element(list, where);
2084    return;
2085}
2086
2087#ifdef DEBUG
2088static void
2089IFStateList_print(IFStateList_t * list)
2090{
2091    int i;
2092
2093    printf("-------start--------\n");
2094    for (i = 0; i < dynarray_count(list); i++) {
2095	IFStateRef	ifstate = dynarray_element(list, i);
2096	int		j;
2097
2098	printf("%s:", if_name(ifstate->if_p));
2099	for (j = 0; j < dynarray_count(&ifstate->services); j++) {
2100	    ServiceRef	service_p = dynarray_element(&ifstate->services, j);
2101
2102	    printf("%s%s", (j == 0) ? "" : ", ",
2103		   ipconfig_method_string(service_p->method));
2104	}
2105	printf("\n");
2106    }
2107    printf("-------end--------\n");
2108    return;
2109}
2110#endif /* DEBUG */
2111
2112static IFStateRef
2113IFStateListGetServiceWithID(IFStateList_t * list, CFStringRef serviceID,
2114			    ServiceRef * ret_service, boolean_t is_ipv4)
2115{
2116    int 	i;
2117
2118    for (i = 0; i < dynarray_count(list); i++) {
2119	IFStateRef	ifstate = dynarray_element(list, i);
2120	ServiceRef	service_p;
2121
2122	service_p = IFStateGetServiceWithID(ifstate, serviceID, is_ipv4);
2123	if (service_p) {
2124	    if (ret_service) {
2125		*ret_service = service_p;
2126	    }
2127	    return (ifstate);
2128	}
2129    }
2130    if (ret_service) {
2131	*ret_service = NULL;
2132    }
2133    return (NULL);
2134}
2135
2136PRIVATE_EXTERN boolean_t
2137service_is_using_ip(ServiceRef exclude_service_p, struct in_addr iaddr)
2138{
2139    int         i;
2140
2141    for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
2142	IFStateRef	ifstate = dynarray_element(&S_ifstate_list, i);
2143	int		j;
2144
2145	for (j = 0; j < dynarray_count(&ifstate->services); j++) {
2146	    ServiceRef service_p = dynarray_element(&ifstate->services, j);
2147
2148	    if (service_p == exclude_service_p) {
2149		continue;
2150	    }
2151
2152	    if (service_p->u.v4.info.addr.s_addr == iaddr.s_addr) {
2153		return (TRUE);
2154            }
2155	}
2156    }
2157    return (FALSE);
2158
2159}
2160
2161/**
2162 ** netboot-specific routines
2163 **/
2164PRIVATE_EXTERN void
2165netboot_addresses(struct in_addr * ip, struct in_addr * server_ip)
2166{
2167    if (ip)
2168	*ip = S_netboot_ip;
2169    if (server_ip)
2170	*server_ip = S_netboot_server_ip;
2171}
2172
2173
2174#ifndef KERN_NETBOOT
2175#define KERN_NETBOOT            40      /* int: are we netbooted? 1=yes,0=no */
2176#endif /* KERN_NETBOOT */
2177
2178static boolean_t
2179S_netboot_root()
2180{
2181    int mib[2];
2182    size_t len;
2183    int netboot = 0;
2184
2185    mib[0] = CTL_KERN;
2186    mib[1] = KERN_NETBOOT;
2187    len = sizeof(netboot);
2188    sysctl(mib, 2, &netboot, &len, NULL, 0);
2189    return (netboot);
2190}
2191
2192static boolean_t
2193S_netboot_init()
2194{
2195    CFDictionaryRef	chosen = NULL;
2196    struct dhcp *	dhcp;
2197    struct in_addr *	iaddr_p;
2198    interface_t *	if_p;
2199    IFStateRef		ifstate;
2200    boolean_t		is_dhcp = TRUE;
2201    int			length;
2202    CFDataRef		response = NULL;
2203
2204    if (S_netboot_root() == FALSE) {
2205	goto done;
2206    }
2207
2208    chosen = myIORegistryEntryCopyValue("IODeviceTree:/chosen");
2209    if (chosen == NULL) {
2210	goto done;
2211    }
2212    response = CFDictionaryGetValue(chosen, CFSTR("dhcp-response"));
2213    if (isA_CFData(response) == NULL) {
2214	response = CFDictionaryGetValue(chosen, CFSTR("bootp-response"));
2215	if (isA_CFData(response) == NULL) {
2216	    goto done;
2217	}
2218	is_dhcp = FALSE;
2219    }
2220    /* ALIGN: CFDataGetBytePtr should be at least sizeof(uint64_t) */
2221    dhcp = (struct dhcp *)(void *)CFDataGetBytePtr(response);
2222    length = (int)CFDataGetLength(response);
2223    if (dhcp->dp_yiaddr.s_addr != 0) {
2224	S_netboot_ip = dhcp->dp_yiaddr;
2225    }
2226    else if (dhcp->dp_ciaddr.s_addr != 0) {
2227	S_netboot_ip = dhcp->dp_ciaddr;
2228    }
2229    else {
2230	goto done;
2231    }
2232    S_netboot_server_ip = dhcp->dp_siaddr;
2233    if_p = ifl_find_ip(S_interfaces, S_netboot_ip);
2234    if (if_p == NULL) {
2235	/* not netbooting: some interface (en0) must have the assigned IP */
2236	goto done;
2237    }
2238    ifstate = IFStateList_ifstate_create(&S_ifstate_list, if_p);
2239    ifstate->netboot = TRUE;
2240    if (is_dhcp == TRUE) {
2241	dhcpol_t		options;
2242
2243	(void)dhcpol_parse_packet(&options, dhcp, length, NULL);
2244	iaddr_p = (struct in_addr *)
2245	    dhcpol_find_with_length(&options,
2246				    dhcptag_server_identifier_e,
2247				    sizeof(*iaddr_p));
2248	if (iaddr_p != NULL) {
2249	    S_netboot_server_ip = *iaddr_p;
2250	}
2251	dhcpol_free(&options);
2252    }
2253    strlcpy(S_netboot_ifname, if_name(if_p), sizeof(S_netboot_ifname));
2254    G_is_netboot = TRUE;
2255
2256 done:
2257    my_CFRelease(&chosen);
2258    return (G_is_netboot);
2259}
2260
2261static void
2262set_entity_value(CFStringRef * entities,
2263		 CFDictionaryRef * values, int size,
2264		 CFStringRef entity, CFDictionaryRef value,
2265		 int * count_p)
2266{
2267    int		i;
2268
2269    i = *count_p;
2270    if (i >= size) {
2271	my_log(LOG_ERR, "IPConfiguration: set_entity_value %d >= %d",
2272	       i, size);
2273	return;
2274    }
2275    entities[i] = entity;
2276    values[i] = value;
2277    (*count_p)++;
2278    return;
2279}
2280
2281PRIVATE_EXTERN const char *
2282ServiceGetMethodString(ServiceRef service_p)
2283{
2284    return (ipconfig_method_string(service_p->method));
2285}
2286
2287static void
2288service_clear(ServiceRef service_p)
2289{
2290    service_p->ready = FALSE;
2291    service_p->status = ipconfig_status_success_e;
2292    return;
2293}
2294
2295#define N_PUBLISH_ENTITIES	5
2296
2297static void
2298service_publish_clear(ServiceRef service_p)
2299{
2300    CFDictionaryRef	dns_dict = NULL;
2301    CFStringRef		entities[N_PUBLISH_ENTITIES];
2302    int			entity_count;
2303    CFDictionaryRef	values[N_PUBLISH_ENTITIES];
2304
2305    service_clear(service_p);
2306    if (S_scd_session == NULL) {
2307	return;
2308    }
2309    if (ServiceIsIPv4(service_p)) {
2310	/* IPv4 */
2311	entity_count = 0;
2312	set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2313			 kSCEntNetIPv4, NULL, &entity_count);
2314	dns_dict = ServiceIPv4CopyMergedDNS(service_p, NULL);
2315	set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2316			 kSCEntNetDNS, dns_dict, &entity_count);
2317	set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2318			 kSCEntNetDHCP, NULL, &entity_count);
2319#if ! TARGET_OS_EMBEDDED
2320	set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2321			 kSCEntNetSMB, NULL, &entity_count);
2322#endif /* ! TARGET_OS_EMBEDDED */
2323	ServiceSetActiveDuringSleepNeedsAttention(service_p);
2324    }
2325    else {
2326	/* IPv6 */
2327	entity_count = 0;
2328	set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2329			 kSCEntNetIPv6, NULL, &entity_count);
2330	set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2331			 kSCEntNetDHCPv6, NULL, &entity_count);
2332	dns_dict = ServiceIPv6CopyMergedDNS(service_p, NULL);
2333	set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2334			 kSCEntNetDNS, dns_dict, &entity_count);
2335    }
2336    my_SCDynamicStoreSetService(S_scd_session,
2337				service_p->serviceID,
2338				entities, values, entity_count,
2339				service_p->no_publish);
2340    my_CFRelease(&dns_dict);
2341    return;
2342}
2343
2344static boolean_t
2345all_services_ready()
2346{
2347    int 		i;
2348
2349    for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
2350	int		j;
2351	IFStateRef	ifstate = dynarray_element(&S_ifstate_list, i);
2352
2353	if (dynarray_count(&ifstate->services) == 0
2354	    && ifstate->startup_ready == FALSE) {
2355	    return (FALSE);
2356	}
2357	for (j = 0; j < dynarray_count(&ifstate->services); j++) {
2358	    ServiceRef	service_p = dynarray_element(&ifstate->services, j);
2359
2360	    if (service_p->ready == FALSE) {
2361		return (FALSE);
2362	    }
2363	}
2364    }
2365    unblock_startup(S_scd_session);
2366    return (TRUE);
2367}
2368
2369static void
2370dict_insert_router_info(ServiceRef service_p, CFMutableDictionaryRef dict)
2371{
2372    interface_t *		if_p = service_interface(service_p);
2373    char			link_addr[MAX_LINK_ADDR_LEN * 3];
2374    CFStringRef			link_addr_cf;
2375    CFStringRef			router_ip;
2376    CFStringRef			sig_str;
2377
2378    if (service_router_all_valid(service_p) == FALSE) {
2379	return;
2380    }
2381
2382    /* router IP address */
2383    router_ip
2384	= CFStringCreateWithFormat(NULL, NULL,
2385				   CFSTR(IP_FORMAT),
2386				   IP_LIST(&service_p->u.v4.router.iaddr));
2387    /* router link address */
2388    link_addr_to_string(link_addr, sizeof(link_addr),
2389			service_p->u.v4.router.hwaddr,
2390			if_link_length(if_p));
2391    link_addr_cf = CFStringCreateWithCString(NULL,
2392					     link_addr,
2393					     kCFStringEncodingASCII);
2394
2395    /* signature */
2396    sig_str
2397	= CFStringCreateWithFormat(NULL, NULL,
2398				   CFSTR("IPv4.Router=%@;IPv4.RouterHardwareAddress=%s"),
2399				   router_ip,
2400				   link_addr);
2401    CFDictionarySetValue(dict, kNetworkSignature, sig_str);
2402    CFDictionarySetValue(dict, kARPResolvedIPAddress, router_ip);
2403    CFDictionarySetValue(dict, kARPResolvedHardwareAddress, link_addr_cf);
2404    CFRelease(sig_str);
2405    CFRelease(router_ip);
2406    CFRelease(link_addr_cf);
2407    return;
2408}
2409
2410STATIC CFDictionaryRef
2411ServiceIPv4CopyMergedDNS(ServiceRef service_p, dhcp_info_t * info_p)
2412{
2413    dhcpv6_info_t	info_v6;
2414    ServiceRef		ipv6_service_p;
2415
2416    ipv6_service_p = IFStateGetServiceWithID(service_p->ifstate,
2417					     service_p->serviceID,
2418					     IS_IPV6);
2419    bzero(&info_v6, sizeof(info_v6));
2420    if (ipv6_service_p != NULL) {
2421	(void)config_method_event(ipv6_service_p, IFEventID_get_dhcpv6_info_e,
2422				  &info_v6);
2423    }
2424    return (DNSEntityCreateWithDHCPv4AndDHCPv6Info(info_p, &info_v6));
2425}
2426
2427#ifndef kSCPropConfirmedInterfaceName
2428#define kSCPropConfirmedInterfaceName CFSTR("ConfirmedInterfaceName")
2429#endif /* kSCPropConfirmedInterfaceName */
2430
2431STATIC CFDictionaryRef
2432route_dict_create(const struct in_addr * dest, const struct in_addr * mask,
2433		  const struct in_addr * gate)
2434{
2435    int			count = 0;
2436    CFDictionaryRef	dict;
2437    int			i;
2438#define N_KEYS		3
2439    const void *	keys[N_KEYS];
2440    CFStringRef		values[N_KEYS];
2441
2442    if (dest != NULL) {
2443	keys[count] = kSCPropNetIPv4RouteDestinationAddress;
2444	values[count] = my_CFStringCreateWithIPAddress(*dest);
2445	count++;
2446    }
2447    if (mask != NULL) {
2448	keys[count] = kSCPropNetIPv4RouteSubnetMask;
2449	values[count] = my_CFStringCreateWithIPAddress(*mask);
2450	count++;
2451    }
2452    if (gate != NULL) {
2453	keys[count] = kSCPropNetIPv4RouteGatewayAddress;
2454	values[count] = my_CFStringCreateWithIPAddress(*gate);
2455	count++;
2456    }
2457    if (count == 0) {
2458	return (NULL);
2459    }
2460    dict = CFDictionaryCreate(NULL, keys, (const void * *)values,
2461			      count,
2462			      &kCFTypeDictionaryKeyCallBacks,
2463			      &kCFTypeDictionaryValueCallBacks);
2464    for (i = 0; i < count; i++) {
2465	CFRelease(values[i]);
2466    }
2467    return (dict);
2468}
2469
2470STATIC void
2471dict_insert_additional_routes(CFMutableDictionaryRef dict,
2472			      struct in_addr addr)
2473{
2474    struct in_addr	linklocal_mask = { htonl(IN_CLASSB_NET) };
2475    struct in_addr	linklocal_network = { htonl(IN_LINKLOCALNETNUM) };
2476    CFDictionaryRef	route_dict[2];
2477    CFArrayRef		routes;
2478
2479    route_dict[0] = route_dict_create(&addr, &G_ip_broadcast, NULL);
2480    route_dict[1] = route_dict_create(&linklocal_network, &linklocal_mask,
2481				      NULL);
2482    routes = CFArrayCreate(NULL, (const void * *)route_dict,
2483			   sizeof(route_dict) / sizeof(route_dict[0]),
2484			   &kCFTypeArrayCallBacks);
2485    CFRelease(route_dict[0]);
2486    CFRelease(route_dict[1]);
2487    CFDictionarySetValue(dict, kSCPropNetIPv4AdditionalRoutes, routes);
2488    CFRelease(routes);
2489    return;
2490}
2491
2492PRIVATE_EXTERN void
2493ServicePublishSuccessIPv4(ServiceRef service_p, dhcp_info_t * dhcp_info_p)
2494{
2495    CFDictionaryRef		dhcp_dict = NULL;
2496    CFDictionaryRef		dns_dict = NULL;
2497    CFStringRef			entities[N_PUBLISH_ENTITIES];
2498    int				entity_count;
2499    interface_t *		if_p = service_interface(service_p);
2500    inet_addrinfo_t *		info_p;
2501    CFMutableDictionaryRef	ipv4_dict = NULL;
2502    dhcpol_t *			options = NULL;
2503    ServiceRef			parent_service_p = NULL;
2504    CFStringRef			serviceID;
2505#if ! TARGET_OS_EMBEDDED
2506    CFMutableDictionaryRef	smb_dict = NULL;
2507    const uint8_t *		smb_nodetype = NULL;
2508    int				smb_nodetype_len = 0;
2509    struct in_addr *		smb_server = NULL;
2510    int				smb_server_len = 0;
2511#endif /* ! TARGET_OS_EMBEDDED */
2512    CFDictionaryRef		values[N_PUBLISH_ENTITIES];
2513
2514    if (service_p->serviceID == NULL) {
2515	return;
2516    }
2517    info_p = &service_p->u.v4.info;
2518    service_p->ready = TRUE;
2519    service_p->status = ipconfig_status_success_e;
2520
2521    if (S_scd_session == NULL) {
2522	/* configd is not running */
2523	return;
2524    }
2525    if (dhcp_info_p != NULL) {
2526	options = dhcp_info_p->options;
2527    }
2528    if (service_p->parent_serviceID != NULL) {
2529	parent_service_p
2530	    = IFStateGetServiceWithID(service_ifstate(service_p),
2531				      service_p->parent_serviceID,
2532				      IS_IPV4);
2533	if (parent_service_p == NULL
2534	    || parent_service_p->u.v4.info.addr.s_addr != 0) {
2535	    return;
2536	}
2537	serviceID = service_p->parent_serviceID;
2538    }
2539    else {
2540	serviceID = service_p->serviceID;
2541    }
2542
2543    /* IPv4 */
2544    ipv4_dict = CFDictionaryCreateMutable(NULL, 0,
2545					  &kCFTypeDictionaryKeyCallBacks,
2546					  &kCFTypeDictionaryValueCallBacks);
2547    /* Addresses */
2548    my_CFDictionarySetIPAddressAsArrayValue(ipv4_dict,
2549					    kSCPropNetIPv4Addresses,
2550					    info_p->addr);
2551    /* SubnetMasks */
2552    my_CFDictionarySetIPAddressAsArrayValue(ipv4_dict,
2553					    kSCPropNetIPv4SubnetMasks,
2554					    info_p->mask);
2555
2556    /* InterfaceName */
2557    CFDictionarySetValue(ipv4_dict, kSCPropInterfaceName,
2558			 service_ifstate(service_p)->ifname);
2559
2560    if (service_ifstate(service_p)->netboot
2561	&& service_p->parent_serviceID == NULL) {
2562	CFNumberRef	primary;
2563	int		enabled = 1;
2564
2565	/* ensure that we're the primary service */
2566	primary = CFNumberCreate(NULL, kCFNumberIntType, &enabled);
2567	CFDictionarySetValue(ipv4_dict, kSCPropNetOverridePrimary,
2568			     primary);
2569	CFRelease(primary);
2570    }
2571
2572    if (options != NULL) {
2573	char *		host_name = NULL;
2574	int		host_name_len = 0;
2575
2576	if (service_p->method == ipconfig_method_bootp_e
2577	    || dhcp_parameter_is_ok(dhcptag_host_name_e)) {
2578	    host_name = (char *)
2579		dhcpol_find(options,
2580			    dhcptag_host_name_e,
2581			    &host_name_len, NULL);
2582	    /* set the hostname */
2583	    if (host_name && host_name_len > 0) {
2584		CFStringRef		str;
2585		str = CFStringCreateWithBytes(NULL, (UInt8 *)host_name,
2586					      host_name_len,
2587					      kCFStringEncodingUTF8,
2588					      FALSE);
2589		if (str != NULL) {
2590		    CFDictionarySetValue(ipv4_dict, CFSTR("Hostname"), str);
2591		    CFRelease(str);
2592		}
2593	    }
2594	}
2595	if (dhcp_parameter_is_ok(dhcptag_router_e)) {
2596	    struct in_addr *		router = NULL;
2597
2598	    router = (struct in_addr *)
2599		dhcpol_find_with_length(options,
2600					dhcptag_router_e,
2601					sizeof(*router));
2602	    /* set the router */
2603	    if (router != NULL) {
2604		CFStringRef		str;
2605
2606		str = my_CFStringCreateWithIPAddress(*router);
2607		CFDictionarySetValue(ipv4_dict, kSCPropNetIPv4Router, str);
2608		CFRelease(str);
2609	    }
2610	}
2611    }
2612
2613    if ((if_flags(if_p) & IFF_LOOPBACK) == 0) {
2614	/* insert the signature */
2615	dict_insert_router_info(service_p, ipv4_dict);
2616
2617	/* AdditionalRoutes */
2618	dict_insert_additional_routes(ipv4_dict, info_p->addr);
2619
2620	/* ConfirmedInterfaceName */
2621	CFDictionarySetValue(ipv4_dict, kSCPropConfirmedInterfaceName,
2622			     service_ifstate(service_p)->ifname);
2623    }
2624
2625    /*
2626     * Entity values can be NULL or not NULL.  The values are accumulated in
2627     * the "values" array.
2628     */
2629    entity_count = 0;
2630
2631    /* IPv4 */
2632    set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2633		     kSCEntNetIPv4, ipv4_dict, &entity_count);
2634
2635    /* DNS */
2636    if (parent_service_p != NULL) {
2637	dns_dict = ServiceIPv4CopyMergedDNS(parent_service_p, dhcp_info_p);
2638    }
2639    else {
2640	dns_dict = ServiceIPv4CopyMergedDNS(service_p, dhcp_info_p);
2641    }
2642    set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2643		     kSCEntNetDNS, dns_dict, &entity_count);
2644
2645#if ! TARGET_OS_EMBEDDED
2646    /* SMB */
2647    if (options != NULL) {
2648	if (dhcp_parameter_is_ok(dhcptag_nb_over_tcpip_name_server_e)) {
2649	    smb_server = (struct in_addr *)
2650		dhcpol_find(options,
2651			    dhcptag_nb_over_tcpip_name_server_e,
2652			    &smb_server_len, NULL);
2653	}
2654	if (dhcp_parameter_is_ok(dhcptag_nb_over_tcpip_node_type_e)) {
2655	    smb_nodetype = (uint8_t *)
2656		dhcpol_find(options,
2657			    dhcptag_nb_over_tcpip_node_type_e,
2658			    &smb_nodetype_len, NULL);
2659	}
2660    }
2661    if ((smb_server && smb_server_len >= sizeof(struct in_addr))
2662	|| (smb_nodetype && smb_nodetype_len == sizeof(uint8_t))) {
2663	smb_dict
2664	    = CFDictionaryCreateMutable(NULL, 0,
2665					&kCFTypeDictionaryKeyCallBacks,
2666					&kCFTypeDictionaryValueCallBacks);
2667	if (smb_server && smb_server_len >= sizeof(struct in_addr)) {
2668	    CFMutableArrayRef	array = NULL;
2669	    int			i;
2670
2671	    array = CFArrayCreateMutable(NULL,
2672					 smb_server_len / sizeof(struct in_addr),
2673					 &kCFTypeArrayCallBacks);
2674	    for (i = 0; i < (smb_server_len / sizeof(struct in_addr)); i++) {
2675		CFStringRef		str;
2676		str = my_CFStringCreateWithIPAddress(smb_server[i]);
2677		CFArrayAppendValue(array, str);
2678		CFRelease(str);
2679	    }
2680	    CFDictionarySetValue(smb_dict, kSCPropNetSMBWINSAddresses, array);
2681	    CFRelease(array);
2682	}
2683	if (smb_nodetype && smb_nodetype_len == sizeof(uint8_t)) {
2684	    switch (smb_nodetype[0]) {
2685	    case 1 :
2686		CFDictionarySetValue(smb_dict, kSCPropNetSMBNetBIOSNodeType,
2687				     kSCValNetSMBNetBIOSNodeTypeBroadcast);
2688		break;
2689	    case 2 :
2690		CFDictionarySetValue(smb_dict, kSCPropNetSMBNetBIOSNodeType,
2691				     kSCValNetSMBNetBIOSNodeTypePeer);
2692		break;
2693	    case 4:
2694		CFDictionarySetValue(smb_dict, kSCPropNetSMBNetBIOSNodeType,
2695				     kSCValNetSMBNetBIOSNodeTypeMixed);
2696		break;
2697	    case 8 :
2698		CFDictionarySetValue(smb_dict, kSCPropNetSMBNetBIOSNodeType,
2699				     kSCValNetSMBNetBIOSNodeTypeHybrid);
2700		break;
2701	    }
2702	}
2703    }
2704    set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2705		     kSCEntNetSMB, smb_dict, &entity_count);
2706#endif /* ! TARGET_OS_EMBEDDED */
2707
2708    /* DHCP */
2709    if (dhcp_info_p != NULL && dhcp_info_p->pkt_size != 0) {
2710	dhcp_dict = DHCPInfoDictionaryCreate(service_p->method,
2711					     dhcp_info_p->options,
2712					     dhcp_info_p->lease_start,
2713					     dhcp_info_p->lease_expiration);
2714    }
2715    set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2716		     kSCEntNetDHCP, dhcp_dict, &entity_count);
2717
2718    my_SCDynamicStoreSetService(S_scd_session,
2719				serviceID,
2720				entities, values, entity_count,
2721				service_p->no_publish);
2722    my_CFRelease(&ipv4_dict);
2723    my_CFRelease(&dns_dict);
2724    my_CFRelease(&dhcp_dict);
2725#if ! TARGET_OS_EMBEDDED
2726    my_CFRelease(&smb_dict);
2727#endif /* ! TARGET_OS_EMBEDDED */
2728    all_services_ready();
2729    ServiceSetActiveDuringSleepNeedsAttention(service_p);
2730    return;
2731}
2732
2733PRIVATE_EXTERN boolean_t
2734ServiceDefendIPv4Address(ServiceRef service_p, arp_collision_data_t * arpc)
2735{
2736    absolute_time_t 		current_time;
2737    boolean_t			defended = FALSE;
2738    ServiceIPv4Ref		v4_p = &service_p->u.v4;
2739
2740    current_time = timer_current_secs();
2741    if (arpc->is_sleep_proxy
2742	|| ((current_time - v4_p->ip_assigned_time) >
2743	    S_defend_ip_address_interval_secs)) {
2744	if (v4_p->ip_conflict_count > 0
2745	    && ((current_time - v4_p->ip_conflict_time)
2746		> S_defend_ip_address_interval_secs)) {
2747	    /*
2748	     * if it's been awhile since we last had to defend
2749	     * our IP address, assume we defended it successfully
2750	     * and start the conflict counter over again
2751	     */
2752	    v4_p->ip_conflict_count = 0;
2753	}
2754	v4_p->ip_conflict_time = current_time;
2755	v4_p->ip_conflict_count++;
2756	if (v4_p->ip_conflict_count > S_defend_ip_address_count) {
2757	    /* too many conflicts */
2758	}
2759	else {
2760	    arp_client_t *	arp;
2761	    interface_t *	if_p = service_interface(service_p);
2762
2763	    arp = arp_client_init(G_arp_session, if_p);
2764	    if (arp == NULL) {
2765		my_log(LOG_ERR,
2766		       "IPConfiguration: "
2767		       "ServiceDefendIPv4Address arp_client_init failed");
2768	    }
2769	    else {
2770		defended = arp_client_defend(arp, v4_p->info.addr);
2771		arp_client_free(&arp);
2772		my_log(LOG_NOTICE, "%s %s: defending IP "
2773		       IP_FORMAT
2774		       " against %s"
2775		       EA_FORMAT
2776		       " %d (of %d)",
2777		       ServiceGetMethodString(service_p), if_name(if_p),
2778		       IP_LIST(&v4_p->info.addr),
2779		       arpc->is_sleep_proxy ? "BonjourSleepProxy " : "",
2780		       EA_LIST(arpc->hwaddr),
2781		       v4_p->ip_conflict_count, S_defend_ip_address_count);
2782
2783	    }
2784	}
2785    }
2786    return (defended);
2787}
2788
2789static void
2790my_CFDictionarySetIPv6AddressAsString(CFMutableDictionaryRef dict,
2791				      CFStringRef prop,
2792				      struct in6_addr * ip6_addr)
2793{
2794    CFStringRef		str;
2795
2796    str = my_CFStringCreateWithIPv6Address(ip6_addr);
2797    CFDictionarySetValue(dict, prop, str);
2798    CFRelease(str);
2799    return;
2800}
2801
2802static void
2803dict_set_inet6_info(CFMutableDictionaryRef dict,
2804		    inet6_addrinfo_t * addr, int addr_count)
2805{
2806    CFMutableArrayRef	address_list;
2807    int			i;
2808    CFMutableArrayRef	prefix_list;
2809
2810    address_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2811    prefix_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2812    for (i = 0; i < addr_count; i++) {
2813	CFStringRef	str;
2814	CFNumberRef	num;
2815	int		val;
2816
2817	str = my_CFStringCreateWithIPv6Address(&(addr[i].addr));
2818	CFArrayAppendValue(address_list, str);
2819	CFRelease(str);
2820	val = addr[i].prefix_length;
2821	num = CFNumberCreate(NULL, kCFNumberIntType, &val);
2822	CFArrayAppendValue(prefix_list, num);
2823	CFRelease(num);
2824    }
2825    CFDictionarySetValue(dict, kSCPropNetIPv6Addresses, address_list);
2826    CFRelease(address_list);
2827    CFDictionarySetValue(dict, kSCPropNetIPv6PrefixLength, prefix_list);
2828    CFRelease(prefix_list);
2829    return;
2830}
2831
2832STATIC CFDictionaryRef
2833ServiceIPv6CopyMergedDNS(ServiceRef service_p, dhcpv6_info_t * info_v6_p)
2834{
2835    dhcp_info_t		info;
2836    ServiceRef		ipv4_service_p;
2837
2838    ipv4_service_p = IFStateGetServiceWithID(service_p->ifstate,
2839					     service_p->serviceID,
2840					     IS_IPV4);
2841    bzero(&info, sizeof(info));
2842    if (ipv4_service_p != NULL) {
2843	(void)config_method_event(ipv4_service_p, IFEventID_get_dhcp_info_e,
2844				  &info);
2845    }
2846    return (DNSEntityCreateWithDHCPv4AndDHCPv6Info(&info, info_v6_p));
2847}
2848
2849PRIVATE_EXTERN void
2850ServicePublishSuccessIPv6(ServiceRef service_p,
2851			  inet6_addrinfo_t * addresses, int addresses_count,
2852			  struct in6_addr * router, int router_count,
2853			  dhcpv6_info_t * dhcp_info_p,
2854			  CFStringRef signature)
2855{
2856    CFStringRef			entities[N_PUBLISH_ENTITIES];
2857    int				entity_count;
2858    CFDictionaryRef		dhcp_dict = NULL;
2859    CFDictionaryRef		dns_dict = NULL;
2860    interface_t *		if_p = service_interface(service_p);
2861    CFMutableDictionaryRef	ipv6_dict = NULL;
2862    DHCPv6OptionListRef		options = NULL;
2863    CFDictionaryRef		values[N_PUBLISH_ENTITIES];
2864
2865    if (service_p->serviceID == NULL) {
2866	return;
2867    }
2868    if (addresses == NULL || addresses_count == 0) {
2869	return;
2870    }
2871    service_p->ready = TRUE;
2872    service_p->status = ipconfig_status_success_e;
2873
2874    if (S_scd_session == NULL) {
2875	/* configd is not running */
2876	return;
2877    }
2878
2879    if (dhcp_info_p != NULL) {
2880	options = dhcp_info_p->options;
2881    }
2882
2883    /* IPv6 */
2884    ipv6_dict = CFDictionaryCreateMutable(NULL, 0,
2885					  &kCFTypeDictionaryKeyCallBacks,
2886					  &kCFTypeDictionaryValueCallBacks);
2887
2888    /* Addresses, PrefixLength */
2889    dict_set_inet6_info(ipv6_dict, addresses, addresses_count);
2890
2891    /* Router */
2892    if (router != NULL) {
2893	my_CFDictionarySetIPv6AddressAsString(ipv6_dict,
2894					      kSCPropNetIPv6Router,
2895					      router);
2896    }
2897    /* InterfaceName */
2898    CFDictionarySetValue(ipv6_dict, kSCPropInterfaceName,
2899			 service_ifstate(service_p)->ifname);
2900
2901    if ((if_flags(if_p) & IFF_LOOPBACK) == 0) {
2902	/* ConfirmedInterfaceName */
2903	CFDictionarySetValue(ipv6_dict, kSCPropConfirmedInterfaceName,
2904			     service_ifstate(service_p)->ifname);
2905	/* NetworkSignature */
2906	if (signature != NULL) {
2907	    CFDictionarySetValue(ipv6_dict, kNetworkSignature,
2908				 signature);
2909	}
2910    }
2911
2912    /* DNS */
2913    dns_dict = ServiceIPv6CopyMergedDNS(service_p, dhcp_info_p);
2914
2915    /* DHCPv6 */
2916    if (options != NULL) {
2917	dhcp_dict = DHCPv6InfoDictionaryCreate(options);
2918    }
2919
2920    /*
2921     * Entity values can be NULL or not NULL.  The values are accumulated in
2922     * the "values" array.
2923     */
2924    entity_count = 0;
2925    set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2926		     kSCEntNetIPv6, ipv6_dict, &entity_count);
2927    set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2928		     kSCEntNetDNS, dns_dict, &entity_count);
2929    set_entity_value(entities, values, N_PUBLISH_ENTITIES,
2930		     kSCEntNetDHCPv6, dhcp_dict, &entity_count);
2931    my_SCDynamicStoreSetService(S_scd_session,
2932				service_p->serviceID,
2933				entities, values, entity_count,
2934				service_p->no_publish);
2935    my_CFRelease(&ipv6_dict);
2936    my_CFRelease(&dns_dict);
2937    my_CFRelease(&dhcp_dict);
2938    return;
2939}
2940
2941PRIVATE_EXTERN void
2942service_publish_failure_sync(ServiceRef service_p, ipconfig_status_t status,
2943			     boolean_t sync)
2944{
2945    if (ipconfig_method_is_v4(service_p->method)) {
2946	ServiceRef	child_service_p = NULL;
2947	ServiceRef	parent_service_p = NULL;
2948
2949	if (service_p->child_serviceID != NULL) {
2950	    child_service_p
2951		= IFStateGetServiceWithID(service_ifstate(service_p),
2952					  service_p->child_serviceID,
2953					  IS_IPV4);
2954	}
2955	if (service_p->parent_serviceID != NULL) {
2956	    parent_service_p
2957		= IFStateGetServiceWithID(service_ifstate(service_p),
2958					  service_p->parent_serviceID,
2959					  IS_IPV4);
2960	}
2961	if (child_service_p != NULL
2962	    && child_service_p->u.v4.info.addr.s_addr != 0) {
2963	    ServicePublishSuccessIPv4(child_service_p, NULL);
2964	    service_clear(service_p);
2965	}
2966	else if (parent_service_p != NULL
2967		 && parent_service_p->u.v4.info.addr.s_addr == 0) {
2968	    ipconfig_status_t status;
2969
2970	    /* clear the information in the DynamicStore, but not the status */
2971	    status = parent_service_p->status;
2972	    service_publish_clear(parent_service_p);
2973	    parent_service_p->status = status;
2974	}
2975	else {
2976	    service_publish_clear(service_p);
2977	}
2978    }
2979    else {
2980	service_publish_clear(service_p);
2981    }
2982    service_p->ready = TRUE;
2983    service_p->status = status;
2984    my_log(LOG_DEBUG, "%s %s: status = '%s'",
2985	   ServiceGetMethodString(service_p),
2986	   if_name(service_interface(service_p)),
2987	   ipconfig_status_string(status));
2988    if (sync == TRUE) {
2989	all_services_ready();
2990    }
2991    return;
2992}
2993
2994PRIVATE_EXTERN void
2995service_publish_failure(ServiceRef service_p, ipconfig_status_t status)
2996{
2997    service_publish_failure_sync(service_p, status, TRUE);
2998    return;
2999}
3000
3001PRIVATE_EXTERN int
3002service_enable_autoaddr(ServiceRef service_p)
3003{
3004    return (inet_set_autoaddr(if_name(service_interface(service_p)), 1));
3005}
3006
3007PRIVATE_EXTERN int
3008service_disable_autoaddr(ServiceRef service_p)
3009{
3010    flush_routes(if_link_index(service_interface(service_p)),
3011		 G_ip_zeroes, G_ip_zeroes);
3012    return (inet_set_autoaddr(if_name(service_interface(service_p)), 0));
3013}
3014
3015#define RANK_LOWEST	(1024 * 1024)
3016#define RANK_NONE	(RANK_LOWEST + 1)
3017
3018static unsigned int
3019S_get_service_rank(CFArrayRef arr, ServiceRef service_p)
3020{
3021    int i;
3022    CFStringRef serviceID = service_p->serviceID;
3023
3024    if (service_ifstate(service_p)->netboot
3025	&& service_p->method == ipconfig_method_dhcp_e) {
3026	/* the netboot service is the best service */
3027	return (0);
3028    }
3029    if (serviceID != NULL && arr != NULL) {
3030	CFIndex count = CFArrayGetCount(arr);
3031
3032	for (i = 0; i < count; i++) {
3033	    CFStringRef s = isA_CFString(CFArrayGetValueAtIndex(arr, i));
3034
3035	    if (s == NULL) {
3036		continue;
3037	    }
3038	    if (CFEqual(serviceID, s)) {
3039		return (i);
3040	    }
3041	}
3042    }
3043    return (RANK_LOWEST);
3044}
3045
3046static CFArrayRef
3047S_get_service_order(SCDynamicStoreRef session)
3048{
3049    CFArrayRef	 		order = NULL;
3050    CFStringRef 		ipv4_key = NULL;
3051    CFDictionaryRef 		ipv4_dict = NULL;
3052
3053    if (session == NULL)
3054	goto done;
3055
3056    ipv4_key
3057	= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
3058						     kSCDynamicStoreDomainSetup,
3059						     kSCEntNetIPv4);
3060    if (ipv4_key == NULL) {
3061	goto done;
3062    }
3063    ipv4_dict = my_SCDynamicStoreCopyDictionary(session, ipv4_key);
3064    if (ipv4_dict != NULL) {
3065	order = CFDictionaryGetValue(ipv4_dict, kSCPropNetServiceOrder);
3066	order = isA_CFArray(order);
3067	if (order) {
3068	    CFRetain(order);
3069	}
3070    }
3071
3072 done:
3073    my_CFRelease(&ipv4_key);
3074    my_CFRelease(&ipv4_dict);
3075    return (order);
3076}
3077
3078/*
3079 * Function: service_parent_service
3080 * Purpose:
3081 *   Return the parent service pointer of the given service, if the
3082 *   parent is valid.
3083 */
3084PRIVATE_EXTERN ServiceRef
3085service_parent_service(ServiceRef service_p)
3086{
3087    ipconfig_method_t		method;
3088
3089    if (service_p == NULL || service_p->parent_serviceID == NULL) {
3090	return (NULL);
3091    }
3092    method = service_p->method;
3093    return (IFStateGetServiceWithID(service_ifstate(service_p),
3094				    service_p->parent_serviceID,
3095				    ipconfig_method_is_v4(method)));
3096}
3097
3098/*
3099 * Function: linklocal_service_change
3100 *
3101 * Purpose:
3102 *   If we're the parent of the link-local service,
3103 *   send a change message to the link-local service, asking it to
3104 *   either allocate or not allocate an IP.
3105 */
3106PRIVATE_EXTERN void
3107linklocal_service_change(ServiceRef parent_service_p, boolean_t allocate)
3108{
3109    ipconfig_method_data_t	method_data;
3110    IFStateRef			ifstate = service_ifstate(parent_service_p);
3111    ServiceRef			ll_service_p;
3112    ServiceRef			ll_parent_p = NULL;
3113    boolean_t			needs_stop;
3114
3115    /* if the interface has a user-configured service, ignore this request */
3116    ll_service_p = ifstate->linklocal_service_p;
3117    if (ll_service_p != NULL) {
3118	if (ll_service_p->parent_serviceID == NULL) {
3119	    /* don't touch user-configured link-local service */
3120	    return;
3121	}
3122	ll_parent_p = IFStateGetServiceWithID(ifstate,
3123					      ll_service_p->parent_serviceID,
3124					      IS_IPV4);
3125    }
3126    if (ll_parent_p == NULL) {
3127	linklocal_set_needs_attention();
3128	return;
3129    }
3130    if (parent_service_p != ll_parent_p) {
3131	/* we're not the one that triggered the link-local service */
3132	linklocal_set_needs_attention();
3133	return;
3134    }
3135    bzero(&method_data, sizeof(method_data));
3136    method_data.linklocal.allocate = allocate;
3137    (void)config_method_change(ll_service_p,
3138			       ipconfig_method_linklocal_e,
3139			       &method_data, &needs_stop);
3140    return;
3141}
3142
3143PRIVATE_EXTERN void
3144linklocal_set_needs_attention()
3145{
3146    S_linklocal_needs_attention = TRUE;
3147    return;
3148}
3149
3150PRIVATE_EXTERN void
3151linklocal_set_address(ServiceRef ll_service_p, struct in_addr ll_addr)
3152{
3153    IFStateRef		ifstate = service_ifstate(ll_service_p);
3154
3155    ifstate->v4_link_local = ll_addr;
3156    return;
3157}
3158
3159PRIVATE_EXTERN struct in_addr
3160linklocal_get_address(ServiceRef ll_service_p)
3161{
3162    IFStateRef		ifstate = service_ifstate(ll_service_p);
3163
3164    return (ifstate->v4_link_local);
3165}
3166
3167/*
3168 * Function: S_linklocal_start
3169 * Purpose:
3170 *   Start a child link-local service for the given parent service.
3171 */
3172static void
3173S_linklocal_start(ServiceRef parent_service_p, boolean_t allocate)
3174
3175{
3176    ipconfig_method_data_t	method_data;
3177    IFStateRef			ifstate = service_ifstate(parent_service_p);
3178    ServiceRef			service_p;
3179    ipconfig_status_t		status;
3180
3181    bzero(&method_data, sizeof(method_data));
3182    method_data.linklocal.allocate = allocate;
3183    status = IFState_service_add(ifstate, NULL, ipconfig_method_linklocal_e,
3184				 &method_data, parent_service_p,
3185				 &service_p);
3186    if (status != ipconfig_status_success_e) {
3187	my_log(LOG_ERR,
3188	       "IPConfiguration: failed to start link-local service on %s, %s",
3189	       if_name(ifstate->if_p),
3190	       ipconfig_status_string(status));
3191    }
3192    return;
3193}
3194
3195/*
3196 * Function: S_linklocal_elect
3197 * Purpose:
3198 */
3199static void
3200S_linklocal_elect(CFArrayRef service_order)
3201{
3202    int 		i;
3203
3204    for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
3205	unsigned int	best_rank = RANK_NONE;
3206	ServiceRef	best_service_p = NULL;
3207	boolean_t	election_required = TRUE;
3208	IFStateRef	ifstate = dynarray_element(&S_ifstate_list, i);
3209	int		j;
3210	ServiceRef 	ll_parent_p = NULL;
3211	ServiceRef	ll_service_p;
3212	unsigned int	rank;
3213
3214	if (if_ift_type(ifstate->if_p) == IFT_LOOP) {
3215	    /* skip loopback interface */
3216	    continue;
3217	}
3218	ll_service_p = ifstate->linklocal_service_p;
3219	if (ll_service_p != NULL) {
3220	    if (ll_service_p->parent_serviceID == NULL) {
3221		election_required = FALSE;
3222		if (ll_service_p->u.v4.info.addr.s_addr != 0) {
3223		    best_service_p = ll_service_p;
3224		    best_rank = S_get_service_rank(service_order, ll_service_p);
3225		}
3226	    }
3227	    else {
3228		/* check whether linklocal parent service is still there */
3229		ll_parent_p
3230		    = IFStateGetServiceWithID(ifstate,
3231					      ll_service_p->parent_serviceID,
3232					      IS_IPV4);
3233		if (ll_parent_p == NULL) {
3234		    /* parent of link-local service is gone, child goes too */
3235		    IFStateFreeService(ifstate, ll_service_p);
3236		    ll_service_p = NULL;
3237		    /* side-effect: ifstate->linklocal_service_p = NULL */
3238		}
3239	    }
3240	}
3241	if (election_required) {
3242	    /* find the best parent service for the linklocal service */
3243	    for (j = 0; j < dynarray_count(&ifstate->services); j++) {
3244		ServiceRef 			service_p;
3245		inet_addrinfo_t *		info_p;
3246
3247		service_p = dynarray_element(&ifstate->services, j);
3248		if (service_p->method == ipconfig_method_linklocal_e) {
3249		    /* skip existing child linklocal service */
3250		    continue;
3251		}
3252		info_p = &service_p->u.v4.info;
3253		if (info_p->addr.s_addr == 0) {
3254		    if (service_p->method != ipconfig_method_dhcp_e
3255			|| G_dhcp_failure_configures_linklocal == FALSE
3256			|| (service_p->status != ipconfig_status_no_server_e)) {
3257			/* service isn't ready to be a parent */
3258			continue;
3259		    }
3260		}
3261		rank = S_get_service_rank(service_order, service_p);
3262		if (best_service_p == NULL
3263		    || rank < best_rank
3264		    || (best_service_p->u.v4.info.addr.s_addr == 0
3265			&& info_p->addr.s_addr != 0)) {
3266		    best_service_p = service_p;
3267		    best_rank = rank;
3268		}
3269	    }
3270	    if (ll_parent_p != best_service_p) {
3271		/* best parent service changed */
3272		if (ll_parent_p != NULL) {
3273		    my_CFRelease(&ll_parent_p->child_serviceID);
3274		    IFStateFreeService(ifstate, ll_service_p);
3275		}
3276		if (best_service_p != NULL) {
3277		    boolean_t	allocate = LINKLOCAL_NO_ALLOCATE;
3278
3279		    if (best_service_p->u.v4.info.addr.s_addr == 0) {
3280			/* service has no IP address, allocate a linklocal IP */
3281			allocate = LINKLOCAL_ALLOCATE;
3282		    }
3283		    S_linklocal_start(best_service_p, allocate);
3284		}
3285	    }
3286	}
3287    }
3288    return;
3289}
3290
3291PRIVATE_EXTERN int
3292service_set_address(ServiceRef service_p,
3293		    struct in_addr addr,
3294		    struct in_addr mask,
3295		    struct in_addr broadcast)
3296{
3297    interface_t *	if_p = service_interface(service_p);
3298    int			ret = 0;
3299    struct in_addr	netaddr = { 0 };
3300    int 		s = inet_dgram_socket();
3301
3302    if (mask.s_addr == 0) {
3303	u_int32_t ipval = ntohl(addr.s_addr);
3304
3305	if (IN_CLASSA(ipval)) {
3306	    mask.s_addr = htonl(IN_CLASSA_NET);
3307	}
3308	else if (IN_CLASSB(ipval)) {
3309	    mask.s_addr = htonl(IN_CLASSB_NET);
3310	}
3311	else {
3312	    mask.s_addr = htonl(IN_CLASSC_NET);
3313	}
3314    }
3315    if (broadcast.s_addr == 0) {
3316	broadcast = hltoip(iptohl(addr) | ~iptohl(mask));
3317    }
3318    netaddr = hltoip(iptohl(addr) & iptohl(mask));
3319
3320    if (G_IPConfiguration_verbose) {
3321	my_log(LOG_DEBUG,
3322	       "%s %s: setting " IP_FORMAT " netmask " IP_FORMAT
3323	       " broadcast " IP_FORMAT,
3324	       ServiceGetMethodString(service_p),
3325	       if_name(if_p),
3326	       IP_LIST(&addr), IP_LIST(&mask), IP_LIST(&broadcast));
3327    }
3328    if (s < 0) {
3329	ret = errno;
3330	my_log(LOG_ERR,
3331	       "service_set_address(%s): socket() failed, %s (%d)",
3332	       if_name(if_p), strerror(errno), errno);
3333    }
3334    else {
3335	inet_addrinfo_t *	info_p = &service_p->u.v4.info;
3336
3337	if (inet_aifaddr(s, if_name(if_p), addr, &mask, &broadcast) < 0) {
3338	    ret = errno;
3339	    my_log(LOG_DEBUG, "service_set_address(%s) "
3340		   IP_FORMAT " inet_aifaddr() failed, %s (%d)", if_name(if_p),
3341		   IP_LIST(&addr), strerror(errno), errno);
3342	}
3343	bzero(info_p, sizeof(*info_p));
3344	info_p->addr = addr;
3345	info_p->mask = mask;
3346	info_p->netaddr = netaddr;
3347	info_p->broadcast = broadcast;
3348	close(s);
3349    }
3350    service_p->u.v4.ip_assigned_time = timer_current_secs();
3351    service_p->u.v4.ip_conflict_count = 0;
3352
3353    flush_routes(if_link_index(if_p), G_ip_zeroes, broadcast);
3354    linklocal_set_needs_attention();
3355    return (ret);
3356}
3357
3358static int
3359S_remove_ip_address(const char * ifname, struct in_addr this_ip)
3360{
3361    int			ret = 0;
3362    int 		s;
3363
3364    s = inet_dgram_socket();
3365    if (s < 0) {
3366	ret = errno;
3367	my_log(LOG_DEBUG,
3368	       "S_remove_ip_address(%s) socket() failed, %s (%d)",
3369	       ifname, strerror(errno), errno);
3370    }
3371    else {
3372	if (inet_difaddr(s, ifname, this_ip) < 0) {
3373	    ret = errno;
3374	    my_log(LOG_DEBUG, "S_remove_ip_address(%s) "
3375		   IP_FORMAT " failed, %s (%d)", ifname,
3376		   IP_LIST(&this_ip), strerror(errno), errno);
3377	}
3378	close(s);
3379    }
3380    return (ret);
3381}
3382
3383int
3384service_remove_address(ServiceRef service_p)
3385{
3386    interface_t *	if_p = service_interface(service_p);
3387    inet_addrinfo_t *	info_p = &service_p->u.v4.info;
3388    int			ret = 0;
3389
3390    if (info_p->addr.s_addr != 0) {
3391	inet_addrinfo_t		saved_info;
3392
3393	/* copy IP info then clear it so that it won't be elected */
3394	saved_info = service_p->u.v4.info;
3395	bzero(info_p, sizeof(*info_p));
3396
3397	/* if no service on this interface refers to this IP, remove the IP */
3398	if (IFState_service_with_ip(service_ifstate(service_p),
3399				    saved_info.addr) == NULL) {
3400	    /*
3401	     * This can only happen if there's a manual/inform service
3402	     * and a BOOTP/DHCP service with the same IP.  Duplicate
3403	     * manual/inform services are prevented when created.
3404	     */
3405	    if (G_IPConfiguration_verbose) {
3406		my_log(LOG_DEBUG, "%s %s: removing " IP_FORMAT,
3407		       ServiceGetMethodString(service_p),
3408		       if_name(if_p), IP_LIST(&saved_info.addr));
3409	    }
3410	    ret = S_remove_ip_address(if_name(if_p), saved_info.addr);
3411	}
3412	flush_routes(if_link_index(if_p),
3413		     saved_info.addr, saved_info.broadcast);
3414    }
3415    linklocal_set_needs_attention();
3416    return (ret);
3417}
3418
3419/**
3420 ** ServiceRef accessor routines
3421 **/
3422
3423PRIVATE_EXTERN interface_t *
3424service_interface(ServiceRef service_p)
3425{
3426    return (service_p->ifstate->if_p);
3427}
3428
3429PRIVATE_EXTERN link_status_t
3430service_link_status(ServiceRef service_p)
3431{
3432    return (if_get_link_status(service_interface(service_p)));
3433}
3434
3435
3436PRIVATE_EXTERN bool
3437service_is_address_set(ServiceRef service_p)
3438{
3439    if (ServiceIsIPv4(service_p)) {
3440	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3441
3442	return (v4_p->info.addr.s_addr == v4_p->requested_ip.addr.s_addr);
3443    }
3444    return (FALSE);
3445}
3446
3447PRIVATE_EXTERN void
3448service_set_requested_ip_addr(ServiceRef service_p, struct in_addr ip)
3449{
3450    if (ServiceIsIPv4(service_p)) {
3451	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3452
3453	v4_p->requested_ip.addr = ip;
3454    }
3455    return;
3456}
3457
3458PRIVATE_EXTERN struct in_addr
3459service_requested_ip_addr(ServiceRef service_p)
3460{
3461    if (ServiceIsIPv4(service_p)) {
3462	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3463
3464	return (v4_p->requested_ip.addr);
3465    }
3466    return (G_ip_zeroes);
3467}
3468
3469PRIVATE_EXTERN void
3470service_set_requested_ip_mask(ServiceRef service_p, struct in_addr mask)
3471{
3472    if (ServiceIsIPv4(service_p)) {
3473	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3474
3475	v4_p->requested_ip.mask = mask;
3476    }
3477    return;
3478}
3479
3480PRIVATE_EXTERN struct in_addr
3481service_requested_ip_mask(ServiceRef service_p)
3482{
3483    if (ServiceIsIPv4(service_p)) {
3484	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3485
3486	return (v4_p->requested_ip.mask);
3487    }
3488    return (G_ip_zeroes);
3489}
3490
3491PRIVATE_EXTERN boolean_t
3492service_router_is_hwaddr_valid(ServiceRef service_p)
3493{
3494    if (ServiceIsIPv4(service_p)) {
3495	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3496
3497	return ((v4_p->router.flags & RIFLAGS_HWADDR_VALID) != 0);
3498    }
3499    return (FALSE);
3500}
3501
3502PRIVATE_EXTERN void
3503service_router_set_hwaddr_valid(ServiceRef service_p)
3504{
3505    if (ServiceIsIPv4(service_p)) {
3506	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3507
3508	v4_p->router.flags |= RIFLAGS_HWADDR_VALID;
3509    }
3510    return;
3511}
3512
3513PRIVATE_EXTERN void
3514service_router_clear_hwaddr_valid(ServiceRef service_p)
3515{
3516    if (ServiceIsIPv4(service_p)) {
3517	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3518
3519	v4_p->router.flags &= ~RIFLAGS_HWADDR_VALID;
3520    }
3521    return;
3522}
3523
3524PRIVATE_EXTERN boolean_t
3525service_router_is_iaddr_valid(ServiceRef service_p)
3526{
3527    if (ServiceIsIPv4(service_p)) {
3528	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3529
3530	return ((v4_p->router.flags & RIFLAGS_IADDR_VALID) != 0);
3531    }
3532    return (FALSE);
3533}
3534
3535PRIVATE_EXTERN void
3536service_router_set_iaddr_valid(ServiceRef service_p)
3537{
3538    if (ServiceIsIPv4(service_p)) {
3539	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3540
3541	v4_p->router.flags |= RIFLAGS_IADDR_VALID;
3542    }
3543    return;
3544}
3545
3546PRIVATE_EXTERN void
3547service_router_clear_iaddr_valid(ServiceRef service_p)
3548{
3549    if (ServiceIsIPv4(service_p)) {
3550	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3551
3552	v4_p->router.flags &= ~RIFLAGS_IADDR_VALID;
3553    }
3554    return;
3555}
3556
3557PRIVATE_EXTERN boolean_t
3558service_router_is_arp_verified(ServiceRef service_p)
3559{
3560    if (ServiceIsIPv4(service_p)) {
3561	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3562	return ((v4_p->router.flags & RIFLAGS_ARP_VERIFIED) != 0);
3563    }
3564    return (FALSE);
3565}
3566
3567PRIVATE_EXTERN void
3568service_router_set_arp_verified(ServiceRef service_p)
3569{
3570    if (ServiceIsIPv4(service_p)) {
3571	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3572
3573	v4_p->router.flags |= RIFLAGS_ARP_VERIFIED;
3574    }
3575    return;
3576}
3577
3578PRIVATE_EXTERN void
3579service_router_clear_arp_verified(ServiceRef service_p)
3580{
3581    if (ServiceIsIPv4(service_p)) {
3582	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3583
3584	v4_p->router.flags &= ~RIFLAGS_ARP_VERIFIED;
3585    }
3586    return;
3587}
3588
3589PRIVATE_EXTERN void
3590service_router_clear(ServiceRef service_p)
3591{
3592    if (ServiceIsIPv4(service_p)) {
3593	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3594
3595	v4_p->router.flags = 0;
3596    }
3597    return;
3598}
3599
3600PRIVATE_EXTERN uint8_t *
3601service_router_hwaddr(ServiceRef service_p)
3602{
3603    if (ServiceIsIPv4(service_p)) {
3604	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3605	return (v4_p->router.hwaddr);
3606    }
3607    return (NULL);
3608}
3609
3610PRIVATE_EXTERN int
3611service_router_hwaddr_size(ServiceRef service_p)
3612{
3613    if (ServiceIsIPv4(service_p)) {
3614	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3615	return (sizeof(v4_p->router.hwaddr));
3616    }
3617    return (0);
3618}
3619
3620PRIVATE_EXTERN struct in_addr
3621service_router_iaddr(ServiceRef service_p)
3622{
3623    if (ServiceIsIPv4(service_p)) {
3624	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3625
3626	return (v4_p->router.iaddr);
3627    }
3628    return (G_ip_zeroes);
3629}
3630
3631PRIVATE_EXTERN void
3632service_router_set_iaddr(ServiceRef service_p, struct in_addr iaddr)
3633{
3634    if (ServiceIsIPv4(service_p)) {
3635	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3636
3637	v4_p->router.iaddr = iaddr;
3638    }
3639    return;
3640}
3641
3642PRIVATE_EXTERN boolean_t
3643service_router_all_valid(ServiceRef service_p)
3644{
3645    if (ServiceIsIPv4(service_p)) {
3646	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3647
3648	return ((v4_p->router.flags & RIFLAGS_ALL_VALID) == RIFLAGS_ALL_VALID);
3649    }
3650    return (FALSE);
3651}
3652
3653PRIVATE_EXTERN void
3654service_router_set_all_valid(ServiceRef service_p)
3655{
3656    if (ServiceIsIPv4(service_p)) {
3657	ServiceIPv4Ref	v4_p = &service_p->u.v4;
3658
3659	v4_p->router.flags = RIFLAGS_ALL_VALID;
3660    }
3661    return;
3662}
3663
3664PRIVATE_EXTERN boolean_t
3665ServiceIsIPv4(ServiceRef service_p)
3666{
3667    return (ipconfig_method_is_v4(service_p->method));
3668}
3669
3670PRIVATE_EXTERN boolean_t
3671ServiceIsIPv6(ServiceRef service_p)
3672{
3673    return (ipconfig_method_is_v6(service_p->method));
3674}
3675
3676PRIVATE_EXTERN boolean_t
3677ServiceIsNetBoot(ServiceRef service_p)
3678{
3679    return (service_p->ifstate->netboot);
3680}
3681
3682PRIVATE_EXTERN void *
3683ServiceGetPrivate(ServiceRef service_p)
3684{
3685    return (service_p->private);
3686}
3687
3688PRIVATE_EXTERN void
3689ServiceSetPrivate(ServiceRef service_p, void * private)
3690{
3691    service_p->private = private;
3692    return;
3693}
3694
3695PRIVATE_EXTERN struct in_addr
3696ServiceGetActiveIPAddress(ServiceRef service_p)
3697{
3698    if (ServiceIsIPv4(service_p)) {
3699	return (service_p->u.v4.info.addr);
3700    }
3701    return (G_ip_zeroes);
3702}
3703
3704PRIVATE_EXTERN struct in_addr
3705ServiceGetActiveSubnetMask(ServiceRef service_p)
3706{
3707    if (ServiceIsIPv4(service_p)) {
3708	return (service_p->u.v4.info.mask);
3709    }
3710    return (G_ip_zeroes);
3711}
3712
3713PRIVATE_EXTERN void
3714ServiceSetStatus(ServiceRef service_p, ipconfig_status_t status)
3715{
3716    service_p->status = status;
3717    return;
3718}
3719
3720PRIVATE_EXTERN void
3721ServiceSetRequestedIPv6Address(ServiceRef service_p,
3722			       const struct in6_addr * addr_p,
3723			       int prefix_length)
3724{
3725    if (ServiceIsIPv6(service_p) == FALSE) {
3726	return;
3727    }
3728    service_p->u.v6.requested_ip.addr = *addr_p;
3729    service_p->u.v6.requested_ip.prefix_length = prefix_length;
3730    return;
3731}
3732
3733PRIVATE_EXTERN void
3734ServiceGetRequestedIPv6Address(ServiceRef service_p,
3735			       struct in6_addr * addr_p,
3736			       int * prefix_length)
3737{
3738    if (ServiceIsIPv6(service_p) == FALSE) {
3739	return;
3740    }
3741    *addr_p = service_p->u.v6.requested_ip.addr;
3742    *prefix_length = service_p->u.v6.requested_ip.prefix_length;
3743    return;
3744}
3745
3746PRIVATE_EXTERN int
3747ServiceSetIPv6Address(ServiceRef service_p, const struct in6_addr * addr_p,
3748		      int prefix_length,
3749		      u_int32_t flags,
3750		      u_int32_t valid_lifetime,
3751		      u_int32_t preferred_lifetime)
3752{
3753    interface_t *	if_p = service_interface(service_p);
3754    int			ret = 0;
3755    int			s;
3756
3757    if (ServiceIsIPv6(service_p) == FALSE) {
3758	return (EINVAL);
3759    }
3760    if (G_IPConfiguration_verbose) {
3761	char 	ntopbuf[INET6_ADDRSTRLEN];
3762
3763	my_log(LOG_DEBUG, "%s %s: setting %s/%d",
3764	       ServiceGetMethodString(service_p),
3765	       if_name(if_p),
3766	       inet_ntop(AF_INET6, addr_p, ntopbuf, sizeof(ntopbuf)),
3767	       prefix_length);
3768    }
3769    s = inet6_dgram_socket();
3770    if (s < 0) {
3771	ret = errno;
3772	my_log(LOG_ERR,
3773	       "ServiceSetIPv6Address(%s): socket() failed, %s (%d)",
3774	       if_name(if_p), strerror(errno), errno);
3775    }
3776    else {
3777	if (inet6_aifaddr(s, if_name(if_p), addr_p, NULL, prefix_length, flags,
3778			  valid_lifetime, preferred_lifetime) < 0) {
3779	    ret = errno;
3780	    my_log(LOG_DEBUG,
3781		   "ServiceSetIPv6Address(%s): socket() failed, %s (%d)",
3782		   if_name(if_p), strerror(errno), errno);
3783	}
3784	close(s);
3785    }
3786    return (ret);
3787}
3788
3789PRIVATE_EXTERN void
3790ServiceRemoveIPv6Address(ServiceRef service_p,
3791			 const struct in6_addr * addr_p, int prefix_length)
3792{
3793    interface_t *	if_p = service_interface(service_p);
3794    int			s;
3795
3796    if (ServiceIsIPv6(service_p) == FALSE) {
3797	return;
3798    }
3799    if (IN6_IS_ADDR_UNSPECIFIED(addr_p)) {
3800	/* no address assigned */
3801	return;
3802    }
3803    if (G_IPConfiguration_verbose) {
3804	char 	ntopbuf[INET6_ADDRSTRLEN];
3805
3806	my_log(LOG_DEBUG,
3807	       "%s %s: removing %s/%d",
3808	       ServiceGetMethodString(service_p),
3809	       if_name(if_p),
3810	       inet_ntop(AF_INET6, addr_p, ntopbuf, sizeof(ntopbuf)),
3811	       prefix_length);
3812    }
3813    s = inet6_dgram_socket();
3814    if (s < 0) {
3815	my_log(LOG_ERR,
3816	       "ServiceRemoveIPv6Address(%s): socket() failed, %s (%d)",
3817	       if_name(if_p), strerror(errno), errno);
3818    }
3819    else {
3820	inet6_difaddr(s, if_name(if_p), addr_p);
3821	close(s);
3822    }
3823    return;
3824}
3825
3826PRIVATE_EXTERN CFStringRef
3827ServiceGetSSID(ServiceRef service_p)
3828{
3829    return (service_p->ifstate->ssid);
3830}
3831
3832PRIVATE_EXTERN void
3833ServiceSetActiveDuringSleepNeedsAttention(ServiceRef service_p)
3834{
3835    IFStateSetActiveDuringSleepNeedsAttention(service_ifstate(service_p));
3836    return;
3837}
3838
3839/**
3840 ** other
3841 **/
3842static void
3843set_loopback()
3844{
3845    struct in_addr	loopback;
3846    struct in_addr	loopback_net;
3847    struct in_addr	loopback_mask;
3848    int 		s = inet_dgram_socket();
3849
3850#ifndef INADDR_LOOPBACK_NET
3851#define	INADDR_LOOPBACK_NET		(u_int32_t)0x7f000000
3852#endif /* INADDR_LOOPBACK_NET */
3853
3854    loopback.s_addr = htonl(INADDR_LOOPBACK);
3855    loopback_mask.s_addr = htonl(IN_CLASSA_NET);
3856    loopback_net.s_addr = htonl(INADDR_LOOPBACK_NET);
3857
3858    if (s < 0) {
3859	my_log(LOG_ERR,
3860	       "set_loopback(): socket() failed, %s (%d)",
3861	       strerror(errno), errno);
3862	return;
3863    }
3864    if (inet_aifaddr(s, "lo0", loopback, &loopback_mask, NULL) < 0) {
3865	my_log(LOG_DEBUG, "set_loopback: inet_aifaddr() failed, %s (%d)",
3866	       strerror(errno), errno);
3867    }
3868    close(s);
3869
3870    /* add 127/8 route */
3871    if (subnet_route_add(loopback, loopback_net, loopback_mask, "lo0")
3872	== FALSE) {
3873	my_log(LOG_DEBUG, "set_loopback: subnet_route_add() failed, %s (%d)",
3874	       strerror(errno), errno);
3875    }
3876    return;
3877}
3878
3879void
3880remove_unused_ip(const char * ifname, struct in_addr ip)
3881{
3882    IFStateRef 	ifstate;
3883
3884    /* if no service on this interface refers to this IP, remove the IP */
3885    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifname, NULL);
3886    if (ifstate != NULL
3887	&& IFState_service_with_ip(ifstate, ip) == NULL) {
3888	if (G_IPConfiguration_verbose) {
3889	    my_log(LOG_DEBUG, "IPConfiguration %s: removing " IP_FORMAT,
3890		   ifname, IP_LIST(&ip));
3891	}
3892	S_remove_ip_address(if_name(ifstate->if_p), ip);
3893    }
3894    return;
3895}
3896
3897
3898/**
3899 ** Routines for MiG interface
3900 **/
3901
3902extern ipconfig_status_t
3903ipconfig_method_info_from_plist(CFPropertyListRef plist,
3904				ipconfig_method_t * method_p,
3905				ipconfig_method_data_t * * method_data_p)
3906{
3907    CFDictionaryRef	dict;
3908    boolean_t		is_ipv4;
3909    ipconfig_status_t	status = ipconfig_status_invalid_parameter_e;
3910
3911    *method_p = ipconfig_method_none_e;
3912    *method_data_p = NULL;
3913
3914    if (plist == NULL) {
3915	/* NULL means no config method i.e. ipconfig_method_none_e */
3916	status = ipconfig_status_success_e;
3917	goto done;
3918    }
3919    if (isA_CFDictionary(plist) == NULL) {
3920	/* if specified, plist must be a dictionary */
3921	goto done;
3922    }
3923    /* if dictionary contains IPv4 dict, use that */
3924    dict = CFDictionaryGetValue((CFDictionaryRef)plist, kSCEntNetIPv4);
3925    if (dict != NULL) {
3926	is_ipv4 = TRUE;
3927    }
3928    else {
3929	dict = CFDictionaryGetValue((CFDictionaryRef)plist, kSCEntNetIPv6);
3930	if (dict != NULL) {
3931	    is_ipv4 = FALSE;
3932	}
3933	else {
3934	    dict = (CFDictionaryRef)plist;
3935	    is_ipv4 = TRUE;
3936	}
3937    }
3938    if (isA_CFDictionary(dict) == NULL) {
3939	my_log(LOG_ERR, "IPConfiguration: invalid IPv%c entity",
3940	       is_ipv4 ? '4' : '6');
3941	goto done;
3942    }
3943    if (CFDictionaryGetCount(dict) == 0) {
3944	*method_p = (is_ipv4)
3945	    ? ipconfig_method_none_v4_e
3946	    : ipconfig_method_none_v6_e;
3947	status = ipconfig_status_success_e;
3948	goto done;
3949    }
3950    if (is_ipv4) {
3951	status = method_info_from_dict(dict, method_p, method_data_p);
3952    }
3953    else {
3954	status = method_info_from_ipv6_dict(dict, method_p, method_data_p);
3955    }
3956
3957 done:
3958    return (status);
3959}
3960
3961static boolean_t
3962service_get_option(ServiceRef service_p, int option_code, void * option_data,
3963		   unsigned int * option_dataCnt)
3964{
3965    boolean_t ret = FALSE;
3966
3967    switch (service_p->method) {
3968      case ipconfig_method_inform_e:
3969      case ipconfig_method_dhcp_e:
3970      case ipconfig_method_bootp_e: {
3971	  void * 	data;
3972	  dhcp_info_t	dhcp_info;
3973	  int	 	len;
3974
3975	  if (service_p->ready == FALSE) {
3976	      break;
3977	  }
3978	  bzero(&dhcp_info, sizeof(dhcp_info));
3979	  (void)config_method_event(service_p, IFEventID_get_dhcp_info_e,
3980				    &dhcp_info);
3981	  if (dhcp_info.pkt_size == 0) {
3982	      break; /* out of switch */
3983	  }
3984	  data = dhcpol_find(dhcp_info.options, option_code,
3985			     &len, NULL);
3986	  if (data) {
3987	      if (len > *option_dataCnt) {
3988		  break; /* out of switch */
3989	      }
3990	      *option_dataCnt = len;
3991	      bcopy(data, option_data, *option_dataCnt);
3992	      ret = TRUE;
3993	  }
3994	  break;
3995      }
3996    default:
3997	break;
3998    } /* switch */
3999    return (ret);
4000}
4001
4002int
4003get_if_count()
4004{
4005    return (dynarray_count(&S_ifstate_list));
4006}
4007
4008ipconfig_status_t
4009get_if_addr(const char * name, u_int32_t * addr)
4010{
4011    IFStateRef 	ifstate;
4012    int			j;
4013
4014    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
4015    if (ifstate == NULL) {
4016	return (ipconfig_status_interface_does_not_exist_e);
4017    }
4018    for (j = 0; j < dynarray_count(&ifstate->services); j++) {
4019	ServiceRef service_p = dynarray_element(&ifstate->services, j);
4020
4021	if (service_p->u.v4.info.addr.s_addr != 0) {
4022	    *addr = service_p->u.v4.info.addr.s_addr;
4023	    return (ipconfig_status_success_e);
4024	}
4025    }
4026    return (ipconfig_status_not_found_e);
4027}
4028
4029ipconfig_status_t
4030get_if_option(const char * name, int option_code, void * option_data,
4031	      unsigned int * option_dataCnt)
4032{
4033    int 		i;
4034    boolean_t		name_match;
4035
4036    for (i = 0, name_match = FALSE;
4037	 i < dynarray_count(&S_ifstate_list) && name_match == FALSE;
4038	 i++) {
4039	IFStateRef 	ifstate = dynarray_element(&S_ifstate_list, i);
4040	int		j;
4041
4042	if (name[0] != '\0') {
4043	    if (strcmp(if_name(ifstate->if_p), name) != 0) {
4044		continue;
4045	    }
4046	    name_match = TRUE;
4047	}
4048	for (j = 0; j < dynarray_count(&ifstate->services); j++) {
4049	    ServiceRef service_p = dynarray_element(&ifstate->services, j);
4050
4051	    if (service_get_option(service_p, option_code, option_data,
4052				   option_dataCnt)) {
4053		return (ipconfig_status_success_e);
4054	    }
4055	}
4056    }
4057    if (name_match == FALSE) {
4058	return (ipconfig_status_interface_does_not_exist_e);
4059    }
4060    return (ipconfig_status_not_found_e);
4061}
4062
4063ipconfig_status_t
4064get_if_packet(const char * name, void * packet_data,
4065	      unsigned int * packet_dataCnt)
4066{
4067    dhcp_info_t		dhcp_info;
4068    IFStateRef 		ifstate;
4069    int			j;
4070
4071    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
4072    if (ifstate == NULL) {
4073	return (ipconfig_status_interface_does_not_exist_e);
4074    }
4075    for (j = 0; j < dynarray_count(&ifstate->services); j++) {
4076	ServiceRef service_p = dynarray_element(&ifstate->services, j);
4077
4078	switch (service_p->method) {
4079	case ipconfig_method_inform_e:
4080	case ipconfig_method_dhcp_e:
4081	case ipconfig_method_bootp_e:
4082	    if (service_p->ready == FALSE) {
4083		break;
4084	    }
4085	    bzero(&dhcp_info, sizeof(dhcp_info));
4086	    (void)config_method_event(service_p, IFEventID_get_dhcp_info_e,
4087				      &dhcp_info);
4088	    if (dhcp_info.pkt_size == 0
4089		|| dhcp_info.pkt_size > *packet_dataCnt) {
4090		break; /* out of switch */
4091	    }
4092	    *packet_dataCnt = dhcp_info.pkt_size;
4093	    bcopy(dhcp_info.pkt, packet_data, *packet_dataCnt);
4094	    return (ipconfig_status_success_e);
4095	default:
4096	    break;
4097	} /* switch */
4098    } /* for */
4099    return (ipconfig_status_not_found_e);
4100}
4101
4102ipconfig_status_t
4103get_if_v6_packet(const char * name, void * packet_data,
4104		 unsigned int * packet_dataCnt)
4105{
4106    dhcpv6_info_t	dhcp_info;
4107    IFStateRef 		ifstate;
4108    int			j;
4109
4110    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
4111    if (ifstate == NULL) {
4112	return (ipconfig_status_interface_does_not_exist_e);
4113    }
4114    for (j = 0; j < dynarray_count(&ifstate->services_v6); j++) {
4115	ServiceRef service_p = dynarray_element(&ifstate->services_v6, j);
4116
4117	switch (service_p->method) {
4118	case ipconfig_method_automatic_v6_e:
4119	case ipconfig_method_rtadv_e:
4120	    if (service_p->ready == FALSE) {
4121		break;
4122	    }
4123	    bzero(&dhcp_info, sizeof(dhcp_info));
4124	    (void)config_method_event(service_p, IFEventID_get_dhcpv6_info_e,
4125				      &dhcp_info);
4126	    if (dhcp_info.pkt_len == 0
4127		|| dhcp_info.pkt_len > *packet_dataCnt) {
4128		break; /* out of switch */
4129	    }
4130	    *packet_dataCnt = dhcp_info.pkt_len;
4131	    bcopy(dhcp_info.pkt, packet_data, *packet_dataCnt);
4132	    return (ipconfig_status_success_e);
4133	default:
4134	    break;
4135	} /* switch */
4136    } /* for */
4137    return (ipconfig_status_not_found_e);
4138}
4139
4140static IPConfigFuncRef
4141lookup_func(ipconfig_method_t method)
4142{
4143    IPConfigFuncRef	func = NULL;
4144
4145    switch (method) {
4146    case ipconfig_method_linklocal_e:
4147	func =  linklocal_thread;
4148	break;
4149    case ipconfig_method_inform_e:
4150	func =  inform_thread;
4151	break;
4152    case ipconfig_method_manual_e:
4153	func =  manual_thread;
4154	break;
4155    case ipconfig_method_dhcp_e:
4156	func =  dhcp_thread;
4157	break;
4158    case ipconfig_method_bootp_e:
4159	func =  bootp_thread;
4160	break;
4161    case ipconfig_method_failover_e:
4162	func =  failover_thread;
4163	break;
4164    case ipconfig_method_rtadv_e:
4165    case ipconfig_method_automatic_v6_e:
4166	if (S_configure_ipv6) {
4167	    func = rtadv_thread;
4168	}
4169	break;
4170    case ipconfig_method_stf_e:
4171	if (S_configure_ipv6) {
4172	    func = stf_thread;
4173	}
4174	break;
4175    case ipconfig_method_manual_v6_e:
4176	if (S_configure_ipv6) {
4177	    func = manual_v6_thread;
4178	}
4179	break;
4180    case ipconfig_method_linklocal_v6_e:
4181	if (S_configure_ipv6) {
4182	    func = linklocal_v6_thread;
4183	}
4184	break;
4185    default:
4186	break;
4187    }
4188    return (func);
4189}
4190
4191static ipconfig_status_t
4192config_method_start(ServiceRef service_p, ipconfig_method_t method,
4193		    ipconfig_method_data_t * method_data)
4194{
4195    IPConfigFuncRef		func;
4196    interface_t * 		if_p = service_interface(service_p);
4197    int				type = if_link_type(if_p);
4198
4199    if (method == ipconfig_method_stf_e && type != IFT_STF) {
4200	/* can't do 6to4 over anything but IFT_STF */
4201	return (ipconfig_status_invalid_operation_e);
4202    }
4203    switch (type) {
4204    case IFT_STF:
4205	if (method != ipconfig_method_stf_e) {
4206	    /* stf interface only does 6to4 */
4207	    return (ipconfig_status_invalid_operation_e);
4208	}
4209	break;
4210    case IFT_IEEE1394:
4211	if (method == ipconfig_method_bootp_e) {
4212	    /* can't do BOOTP over firewire */
4213	    return (ipconfig_status_invalid_operation_e);
4214	}
4215	break;
4216    case IFT_ETHER:
4217	break;
4218    case IFT_LOOP:
4219	    if (method != ipconfig_method_manual_e
4220		&& method != ipconfig_method_manual_v6_e) {
4221		/* loopback interface only does MANUAL */
4222		return (ipconfig_status_invalid_operation_e);
4223	    }
4224	    break;
4225    default:
4226	switch (method) {
4227	case ipconfig_method_linklocal_e:
4228	case ipconfig_method_inform_e:
4229	case ipconfig_method_dhcp_e:
4230	case ipconfig_method_bootp_e:
4231	    /* can't do ARP over anything but Ethernet and FireWire */
4232	    return (ipconfig_status_invalid_operation_e);
4233	default:
4234	    break;
4235	}
4236    }
4237    func = lookup_func(method);
4238    if (func == NULL) {
4239	return (ipconfig_status_operation_not_supported_e);
4240    }
4241    return (*func)(service_p, IFEventID_start_e, method_data);
4242}
4243
4244static ipconfig_status_t
4245config_method_change(ServiceRef service_p,
4246		     ipconfig_method_t method,
4247		     ipconfig_method_data_t * method_data,
4248		     boolean_t * needs_stop)
4249{
4250    change_event_data_t		change_event;
4251    IPConfigFuncRef		func;
4252    ipconfig_status_t		status;
4253
4254    *needs_stop = FALSE;
4255    func = lookup_func(method);
4256    if (func == NULL) {
4257	return (ipconfig_status_operation_not_supported_e);
4258    }
4259    change_event.method_data = method_data;
4260    change_event.needs_stop = FALSE;
4261    status = (*func)(service_p, IFEventID_change_e, &change_event);
4262    *needs_stop = change_event.needs_stop;
4263    return (status);
4264}
4265
4266static ipconfig_status_t
4267config_method_event(ServiceRef service_p, IFEventID_t event, void * data)
4268{
4269    ipconfig_status_t	status = ipconfig_status_success_e;
4270    IPConfigFuncRef	func;
4271    ipconfig_method_t	method = service_p->method;
4272
4273    func = lookup_func(method);
4274    if (func == NULL) {
4275	my_log(LOG_ERR,
4276	       "config_method_event(%s): lookup_func(%d) failed",
4277	       IFEventID_names(event), method);
4278	status = ipconfig_status_internal_error_e;
4279	goto done;
4280    }
4281    (*func)(service_p, event, data);
4282
4283 done:
4284    return (status);
4285
4286}
4287
4288static ipconfig_status_t
4289config_method_stop(ServiceRef service_p)
4290{
4291    return (config_method_event(service_p, IFEventID_stop_e, NULL));
4292}
4293
4294static ipconfig_status_t
4295config_method_media(ServiceRef service_p, void * network_changed)
4296{
4297    /* if there's a media event, we need to re-ARP */
4298    service_router_clear_arp_verified(service_p);
4299    return (config_method_event(service_p, IFEventID_link_status_changed_e,
4300				network_changed));
4301}
4302
4303static ipconfig_status_t
4304config_method_bssid_changed(ServiceRef service_p)
4305{
4306   /* if there is a bssid change, we need to re-ARP */
4307   service_router_clear_arp_verified(service_p);
4308   return (config_method_event(service_p, IFEventID_bssid_changed_e,
4309			       NULL));
4310
4311}
4312
4313static ipconfig_status_t
4314config_method_renew(ServiceRef service_p)
4315{
4316    /* renew forces a re-ARP too */
4317    service_router_clear_arp_verified(service_p);
4318    return (config_method_event(service_p, IFEventID_renew_e, NULL));
4319}
4320
4321static void
4322service_list_event(dynarray_t * services_p, IFEventID_t event, void * data)
4323{
4324    int		i;
4325
4326    for (i = 0; i < dynarray_count(services_p); i++) {
4327	ServiceRef	service_p = dynarray_element(services_p, i);
4328
4329	config_method_event(service_p, event, data);
4330    }
4331}
4332
4333static void
4334IFStateList_all_services_event(IFStateList_t * list,
4335			       IFEventID_t event, void * evdata)
4336{
4337    int 		i;
4338    int			if_count = dynarray_count(list);
4339
4340    for (i = 0; i < if_count; i++) {
4341	IFStateRef		ifstate = dynarray_element(list, i);
4342
4343	service_list_event(&ifstate->services, event, evdata);
4344	service_list_event(&ifstate->services_v6, event, evdata);
4345    }
4346    return;
4347}
4348
4349static void
4350IFStateList_all_services_sleep(IFStateList_t * list)
4351{
4352    int 		i;
4353    int			if_count = dynarray_count(list);
4354
4355    for (i = 0; i < if_count; i++) {
4356	IFStateRef	ifstate = dynarray_element(list, i);
4357
4358	/* v4 services */
4359	service_list_event(&ifstate->services, IFEventID_sleep_e, NULL);
4360
4361	/* v6 services */
4362	service_list_event(&ifstate->services_v6, IFEventID_sleep_e, NULL);
4363
4364	my_CFRelease(&ifstate->neighbor_advert_list);
4365    }
4366    return;
4367}
4368
4369ipconfig_status_t
4370set_if(const char * name, ipconfig_method_t method,
4371       ipconfig_method_data_t * method_data)
4372{
4373    interface_t * 	if_p = ifl_find_name(S_interfaces, name);
4374    IFStateRef   	ifstate;
4375
4376    if (G_IPConfiguration_verbose) {
4377	my_log(LOG_DEBUG, "set %s %s", name, ipconfig_method_string(method));
4378    }
4379    if (if_p == NULL) {
4380	return (ipconfig_status_interface_does_not_exist_e);
4381    }
4382    ifstate = IFStateList_ifstate_create(&S_ifstate_list, if_p);
4383    if (ifstate == NULL) {
4384	return (ipconfig_status_allocation_failed_e);
4385    }
4386    /* stop existing services */
4387    if (method == ipconfig_method_none_e
4388	|| method == ipconfig_method_none_v4_e
4389	|| ipconfig_method_is_v4(method)) {
4390	IFStateFreeIPv4Services(ifstate, TRUE);
4391    }
4392    else {
4393	IFStateFreeIPv6Services(ifstate, TRUE);
4394    }
4395    switch (method) {
4396    case ipconfig_method_none_e:
4397    case ipconfig_method_none_v4_e:
4398    case ipconfig_method_none_v6_e:
4399	return (ipconfig_status_success_e);
4400    default:
4401	break;
4402    }
4403
4404    /* add a new service */
4405    return (IFState_service_add(ifstate, NULL, method, method_data,
4406				NULL, NULL));
4407}
4408
4409static CFStringRef
4410myCFUUIDStringCreate(CFAllocatorRef alloc)
4411{
4412    CFUUIDRef 	uuid;
4413    CFStringRef	uuid_str;
4414
4415    uuid = CFUUIDCreate(alloc);
4416    uuid_str = CFUUIDCreateString(alloc, uuid);
4417    CFRelease(uuid);
4418    return (uuid_str);
4419}
4420
4421static ipconfig_status_t
4422add_or_set_service(const char * name, ipconfig_method_t method,
4423		   bool add_only,
4424		   ipconfig_method_data_t * method_data,
4425		   void * service_id, unsigned int * service_id_len,
4426		   CFDictionaryRef plist, pid_t pid)
4427{
4428    interface_t * 	if_p = ifl_find_name(S_interfaces, name);
4429    IFStateRef   	ifstate;
4430    unsigned int	in_length;
4431    pid_t		monitor_pid = -1;
4432    int			mtu = -1;
4433    boolean_t		no_publish = FALSE;
4434    boolean_t		perform_nud = TRUE;
4435    ServiceRef		service_p;
4436    CFStringRef		serviceID;
4437    ipconfig_status_t	status;
4438
4439    in_length = *service_id_len;
4440    *service_id_len = 0;
4441    switch (method) {
4442    case ipconfig_method_none_e:
4443    case ipconfig_method_none_v4_e:
4444    case ipconfig_method_none_v6_e:
4445	return (ipconfig_status_invalid_parameter_e);
4446    default:
4447	break;
4448    }
4449    if (if_p == NULL) {
4450	return (ipconfig_status_interface_does_not_exist_e);
4451    }
4452    ifstate = IFStateList_ifstate_create(&S_ifstate_list, if_p);
4453    if (ifstate == NULL) {
4454	return (ipconfig_status_allocation_failed_e);
4455    }
4456    service_p = IFStateGetServiceMatchingMethod(ifstate, method,
4457						method_data,
4458						FALSE);
4459    if (service_p != NULL) {
4460	boolean_t	needs_stop = FALSE;
4461
4462	if (add_only) {
4463	    return (ipconfig_status_duplicate_service_e);
4464	}
4465	status = config_method_change(service_p, method,
4466				      method_data,
4467				      &needs_stop);
4468	if (status == ipconfig_status_success_e
4469	    && needs_stop == FALSE) {
4470	    return (ipconfig_status_success_e);
4471	}
4472	IFStateFreeService(ifstate, service_p);
4473    }
4474    serviceID = myCFUUIDStringCreate(NULL);
4475    if (serviceID == NULL) {
4476	return (ipconfig_status_allocation_failed_e);
4477    }
4478
4479    /* get service options */
4480    if (plist != NULL) {
4481	CFDictionaryRef		options_dict;
4482
4483	options_dict = CFDictionaryGetValue(plist,
4484					    kIPConfigurationServiceOptions);
4485	if (isA_CFDictionary(options_dict) != NULL) {
4486	    if (S_get_plist_boolean_quiet(options_dict,
4487					  _kIPConfigurationServiceOptionMonitorPID,
4488					  FALSE)) {
4489		monitor_pid = pid;
4490	    }
4491	    no_publish
4492		= S_get_plist_boolean_quiet(options_dict,
4493					    _kIPConfigurationServiceOptionNoPublish,
4494					    FALSE);
4495	    mtu = S_get_plist_int_quiet(options_dict,
4496					_kIPConfigurationServiceOptionMTU,
4497					-1);
4498	    perform_nud
4499		= S_get_plist_boolean_quiet(options_dict,
4500					    _kIPConfigurationServiceOptionPerformNUD,
4501					    TRUE);
4502	}
4503    }
4504
4505    /* add a new service */
4506    if (G_IPConfiguration_verbose) {
4507	my_log(LOG_DEBUG, "%s %s %s", add_only ? "add_service" : "set_service",
4508	       name, ipconfig_method_string(method));
4509    }
4510    if (mtu > 0) {
4511	/* set the mtu */
4512	my_log(LOG_DEBUG, "set interface %s mtu to %d", name, mtu);
4513	interface_set_mtu(name, mtu);
4514    }
4515    ifstate->disable_perform_nud = !perform_nud;
4516    status = IFState_service_add(ifstate, serviceID, method, method_data,
4517				 NULL, &service_p);
4518    if (status == ipconfig_status_success_e) {
4519	CFIndex		len;
4520
4521	service_p->is_dynamic = TRUE;
4522	service_p->no_publish = no_publish;
4523	if (monitor_pid != -1) {
4524	    ServiceMonitorPID(service_p, monitor_pid);
4525	}
4526	(void)CFStringGetBytes(serviceID,
4527			       CFRangeMake(0, CFStringGetLength(serviceID)),
4528			       kCFStringEncodingASCII,
4529			       0, FALSE, service_id, in_length,
4530			       &len);
4531	*service_id_len = (int)len;
4532    }
4533    CFRelease(serviceID);
4534    return (status);
4535}
4536
4537PRIVATE_EXTERN ipconfig_status_t
4538add_service(const char * name, ipconfig_method_t method,
4539	    ipconfig_method_data_t * method_data,
4540	    void * service_id, unsigned int * service_id_len,
4541	    CFDictionaryRef plist, pid_t pid)
4542{
4543    return (add_or_set_service(name, method, TRUE, method_data,
4544			       service_id, service_id_len, plist, pid));
4545}
4546
4547PRIVATE_EXTERN ipconfig_status_t
4548set_service(const char * name, ipconfig_method_t method,
4549	    ipconfig_method_data_t * method_data,
4550	    void * service_id, unsigned int * service_id_len)
4551{
4552    return (add_or_set_service(name, method, FALSE, method_data,
4553			       service_id, service_id_len, NULL, -1));
4554}
4555
4556STATIC ipconfig_status_t
4557S_remove_service(IFStateRef ifstate, ServiceRef service_p)
4558{
4559    boolean_t		is_ipv4;
4560
4561    is_ipv4 = ipconfig_method_is_v4(service_p->method);
4562    if (service_p->is_dynamic == FALSE) {
4563	return (ipconfig_status_invalid_operation_e);
4564    }
4565    if (G_IPConfiguration_verbose) {
4566	my_log(LOG_DEBUG, "remove_service %s %s", if_name(ifstate->if_p),
4567	       ServiceGetMethodString(service_p));
4568    }
4569
4570    /* remove the service */
4571    IFStateFreeService(ifstate, service_p);
4572    if (is_ipv4 == FALSE
4573	&& dynarray_count(&ifstate->services_v6) == 0) {
4574	(void)inet6_linklocal_stop(if_name(ifstate->if_p));
4575	inet6_detach_interface(if_name(ifstate->if_p));
4576    }
4577    return (ipconfig_status_success_e);
4578}
4579
4580STATIC IFStateRef
4581S_find_service_with_id(const char * ifname, CFStringRef serviceID,
4582		       ServiceRef * ret_service_p)
4583{
4584    IFStateRef   	ifstate;
4585    ServiceRef		service_p = NULL;
4586
4587    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifname, NULL);
4588    if (ifstate != NULL) {
4589	service_p = IFStateGetServiceWithID(ifstate, serviceID, IS_IPV6);
4590	if (service_p == NULL) {
4591	    service_p = IFStateGetServiceWithID(ifstate, serviceID, IS_IPV4);
4592	}
4593	if (service_p == NULL) {
4594	    ifstate = NULL;
4595	}
4596    }
4597    *ret_service_p = service_p;
4598    return (ifstate);
4599}
4600
4601STATIC ipconfig_status_t
4602S_remove_service_with_id_str(const char * ifname, CFStringRef serviceID)
4603{
4604    IFStateRef   	ifstate;
4605    ServiceRef		service_p;
4606    ipconfig_status_t	status;
4607
4608    if (ifname != NULL) {
4609	ifstate = S_find_service_with_id(ifname, serviceID, &service_p);
4610    }
4611    else {
4612	ifstate = IFStateListGetServiceWithID(&S_ifstate_list,
4613					      serviceID, &service_p,
4614					      IS_IPV6);
4615	if (ifstate == NULL) {
4616	    ifstate = IFStateListGetServiceWithID(&S_ifstate_list,
4617						  serviceID, &service_p,
4618						  IS_IPV4);
4619	}
4620    }
4621    if (ifstate == NULL) {
4622	status = ipconfig_status_no_such_service_e;
4623    }
4624    else {
4625	status = S_remove_service(ifstate, service_p);
4626    }
4627    return (status);
4628}
4629
4630PRIVATE_EXTERN ipconfig_status_t
4631remove_service_with_id(const char * ifname,
4632		       void * service_id, unsigned int service_id_len)
4633{
4634    CFStringRef		serviceID;
4635    ipconfig_status_t	status;
4636
4637    serviceID = CFStringCreateWithBytes(NULL, service_id, service_id_len,
4638					kCFStringEncodingASCII, FALSE);
4639    if (serviceID == NULL) {
4640	return (ipconfig_status_allocation_failed_e);
4641    }
4642    status = S_remove_service_with_id_str(ifname, serviceID);
4643    CFRelease(serviceID);
4644    return (status);
4645}
4646
4647PRIVATE_EXTERN ipconfig_status_t
4648find_service(const char * name, boolean_t exact,
4649	     ipconfig_method_t method,
4650	     ipconfig_method_data_t * method_data,
4651	     void * service_id, unsigned int * service_id_len)
4652{
4653    IFStateRef   	ifstate;
4654    unsigned int	in_length;
4655    CFIndex		len = 0;
4656    ServiceRef		service_p;
4657
4658    in_length = *service_id_len;
4659    *service_id_len = 0;
4660    switch (method) {
4661    case ipconfig_method_none_e:
4662    case ipconfig_method_none_v4_e:
4663    case ipconfig_method_none_v6_e:
4664	return (ipconfig_status_invalid_parameter_e);
4665    default:
4666	break;
4667    }
4668    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
4669    if (ifstate == NULL) {
4670	return (ipconfig_status_interface_does_not_exist_e);
4671    }
4672    if (exact) {
4673	service_p
4674	    = IFStateGetServiceWithMethod(ifstate, method,
4675					  method_data,
4676					  FALSE);
4677    }
4678    else {
4679	service_p
4680	    = IFStateGetServiceMatchingMethod(ifstate, method,
4681					      method_data,
4682					      FALSE);
4683    }
4684    if (service_p == NULL) {
4685	return (ipconfig_status_no_such_service_e);
4686    }
4687    (void)CFStringGetBytes(service_p->serviceID,
4688			   CFRangeMake(0,
4689				       CFStringGetLength(service_p->serviceID)),
4690			   kCFStringEncodingASCII,
4691			   0, FALSE, service_id, in_length,
4692			   &len);
4693    *service_id_len = (int)len;
4694    return (ipconfig_status_success_e);
4695}
4696
4697PRIVATE_EXTERN ipconfig_status_t
4698remove_service(const char * name, ipconfig_method_t method,
4699	       ipconfig_method_data_t * method_data)
4700{
4701    IFStateRef   	ifstate;
4702    ServiceRef		service_p;
4703
4704    switch (method) {
4705    case ipconfig_method_none_e:
4706    case ipconfig_method_none_v4_e:
4707    case ipconfig_method_none_v6_e:
4708	return (ipconfig_status_invalid_parameter_e);
4709    default:
4710	break;
4711    }
4712    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, name, NULL);
4713    if (ifstate == NULL) {
4714	return (ipconfig_status_interface_does_not_exist_e);
4715    }
4716    service_p = IFStateGetServiceWithMethod(ifstate, method, method_data,
4717					    FALSE);
4718    if (service_p == NULL) {
4719	return (ipconfig_status_no_such_service_e);
4720    }
4721    return (S_remove_service(ifstate, service_p));
4722}
4723
4724PRIVATE_EXTERN ipconfig_status_t
4725refresh_service(const char * ifname,
4726		void * service_id, unsigned int service_id_len)
4727{
4728    IFStateRef		ifstate;
4729    CFStringRef		serviceID;
4730    ServiceRef		service_p;
4731    ipconfig_status_t	status;
4732
4733    serviceID = CFStringCreateWithBytes(NULL, service_id, service_id_len,
4734					kCFStringEncodingASCII, FALSE);
4735    if (serviceID == NULL) {
4736	return (ipconfig_status_allocation_failed_e);
4737    }
4738    ifstate = S_find_service_with_id(ifname, serviceID, &service_p);
4739    if (ifstate == NULL) {
4740	status = ipconfig_status_no_such_service_e;
4741    }
4742    else {
4743	if (G_IPConfiguration_verbose) {
4744	    my_log(LOG_DEBUG, "%s %s: refresh",
4745		   if_name(ifstate->if_p),
4746		   ServiceGetMethodString(service_p));
4747	}
4748	status = config_method_event(service_p, IFEventID_renew_e, NULL);
4749    }
4750    CFRelease(serviceID);
4751    return (status);
4752}
4753
4754static boolean_t
4755ipconfig_method_from_cfstring(CFStringRef m, ipconfig_method_t * method)
4756{
4757    if (isA_CFString(m) == NULL) {
4758	return (FALSE);
4759    }
4760    if (CFEqual(m, kSCValNetIPv4ConfigMethodBOOTP)) {
4761	*method = ipconfig_method_bootp_e;
4762    }
4763    else if (CFEqual(m, kSCValNetIPv4ConfigMethodDHCP)) {
4764	*method = ipconfig_method_dhcp_e;
4765    }
4766    else if (CFEqual(m, kSCValNetIPv4ConfigMethodManual)) {
4767	*method = ipconfig_method_manual_e;
4768    }
4769    else if (CFEqual(m, kSCValNetIPv4ConfigMethodINFORM)) {
4770	*method = ipconfig_method_inform_e;
4771    }
4772    else if (CFEqual(m, kSCValNetIPv4ConfigMethodLinkLocal)) {
4773	*method = ipconfig_method_linklocal_e;
4774    }
4775    else if (CFEqual(m, kSCValNetIPv4ConfigMethodFailover)) {
4776	*method = ipconfig_method_failover_e;
4777    }
4778    else {
4779	return (FALSE);
4780    }
4781    return (TRUE);
4782}
4783
4784static ipconfig_status_t
4785method_info_from_dict(CFDictionaryRef dict,
4786		      ipconfig_method_t * ret_method,
4787		      ipconfig_method_data_t * * ret_method_data)
4788{
4789    ipconfig_method_t		method = ipconfig_method_none_e;
4790    CFStringRef			method_cf;
4791    ipconfig_method_data_t *	method_data = NULL;
4792    int				method_data_len = 0;
4793    boolean_t			status = ipconfig_status_invalid_parameter_e;
4794
4795    method_cf = CFDictionaryGetValue(dict,
4796				     kSCPropNetIPv4ConfigMethod);
4797    if (ipconfig_method_from_cfstring(method_cf, &method) == FALSE) {
4798	my_log(LOG_ERR,
4799	       "IPConfiguration: IPv4 ConfigMethod is missing/invalid");
4800	goto done;
4801    }
4802    if (ipconfig_method_is_manual(method)) {
4803	struct in_addr		address;
4804	CFArrayRef		addresses;
4805	CFStringRef		address_cf;
4806	CFIndex			count = 0;
4807	CFArrayRef		masks;
4808	CFStringRef		mask_cf = NULL;
4809	struct in_addr		mask = { 0 };
4810
4811	addresses = isA_CFArray(CFDictionaryGetValue(dict,
4812						     kSCPropNetIPv4Addresses));
4813	masks = isA_CFArray(CFDictionaryGetValue(dict,
4814						 kSCPropNetIPv4SubnetMasks));
4815	if (addresses != NULL) {
4816	    count = CFArrayGetCount(addresses);
4817	}
4818	if (count == 0) {
4819	    my_log(LOG_ERR,
4820		   "IPConfiguration: %s Addresses missing/invalid\n",
4821		   ipconfig_method_string(method));
4822	    goto done;
4823	}
4824	address_cf = CFArrayGetValueAtIndex(addresses, 0);
4825	if (my_CFStringToIPAddress(address_cf, &address) == FALSE) {
4826	    my_log(LOG_ERR,
4827		   "IPConfiguration: %s Addresses invalid",
4828		   ipconfig_method_string(method));
4829	    goto done;
4830	}
4831	if (masks != NULL) {
4832	    if (count != CFArrayGetCount(masks)) {
4833		my_log(LOG_ERR,
4834		       "IPConfiguration: "
4835		       "%s Addresses/SubnetMasks are different sizes",
4836		       ipconfig_method_string(method));
4837		goto done;
4838	    }
4839	    mask_cf = CFArrayGetValueAtIndex(masks, 0);
4840	    if (my_CFStringToIPAddress(mask_cf, &mask) == FALSE) {
4841		my_log(LOG_ERR,
4842		       "IPConfiguration: %s SubnetMask invalid",
4843		       ipconfig_method_string(method));
4844		goto done;
4845	    }
4846	}
4847	if (count > 1) {
4848	    my_log(LOG_NOTICE,
4849		   "IPConfiguration: %s "
4850		   "multiple addresses specified - ignoring all but first",
4851		   ipconfig_method_string(method));
4852	}
4853	method_data_len = sizeof(ipconfig_method_data_manual_t);
4854	method_data = (ipconfig_method_data_t *)malloc(method_data_len);
4855	if (method_data == NULL) {
4856	    my_log(LOG_ERR, "IPConfiguration: malloc method_data failed");
4857	    status = ipconfig_status_allocation_failed_e;
4858	    goto done;
4859	}
4860	bzero(method_data, method_data_len);
4861	method_data->manual.addr = address;
4862	method_data->manual.mask = mask;
4863	if (method == ipconfig_method_manual_e) {
4864	    CFBooleanRef	b;
4865	    CFStringRef		router = NULL;
4866
4867	    b = isA_CFBoolean(CFDictionaryGetValue(dict,
4868						   kSCPropNetIgnoreLinkStatus));
4869	    method_data->manual.ignore_link_status
4870		= (b != NULL) ? CFBooleanGetValue(b) : FALSE;
4871	    router = CFDictionaryGetValue(dict, kSCPropNetIPv4Router);
4872	    if (router != NULL
4873		&& my_CFStringToIPAddress(router, &method_data->manual.router)
4874		== FALSE) {
4875		my_log(LOG_NOTICE,
4876		       "IPConfiguration: %s Router invalid",
4877		       ipconfig_method_string(method));
4878	    }
4879	}
4880	else if (method == ipconfig_method_failover_e) {
4881	    CFNumberRef	num;
4882
4883	    num = CFDictionaryGetValue(dict,
4884				       kSCPropNetIPv4FailoverAddressTimeout);
4885	    if (num != NULL
4886		&& (isA_CFNumber(num) == NULL
4887		    || (CFNumberGetValue(num, kCFNumberSInt32Type,
4888					 &method_data->manual.failover_timeout)
4889			== FALSE))) {
4890		my_log(LOG_NOTICE,
4891		       "IPConfiguration: FailoverAddressTimeout invalid");
4892	    }
4893	}
4894    }
4895    else if (method == ipconfig_method_dhcp_e) {
4896	char			cid[255];
4897	int			cid_len = 0;
4898	CFStringRef		client_id = NULL;
4899
4900	client_id = CFDictionaryGetValue(dict, kSCPropNetIPv4DHCPClientID);
4901	if (isA_CFString(client_id) != NULL) {
4902	    cid_len = my_CFStringToCStringAndLength(client_id, cid, sizeof(cid));
4903	}
4904	if (cid_len != 0) {
4905	    cid_len--; /* we don't want the trailing nul character */
4906	    method_data_len = offsetof(ipconfig_method_data_dhcp_t, client_id)
4907		+ cid_len;
4908	}
4909	if (method_data_len > 0) {
4910	    method_data = (ipconfig_method_data_t *)malloc(method_data_len);
4911	    if (method_data == NULL) {
4912		my_log(LOG_ERR, "IPConfiguration: malloc DHCPClientID failed");
4913		status = ipconfig_status_allocation_failed_e;
4914		goto done;
4915	    }
4916	    method_data->dhcp.client_id_len = cid_len;
4917	    bcopy(cid, method_data->dhcp.client_id, cid_len);
4918	}
4919    }
4920    status = ipconfig_status_success_e;
4921
4922 done:
4923    *ret_method_data = method_data;
4924    *ret_method = method;
4925    return (status);
4926}
4927
4928static boolean_t
4929ipconfig_method_from_cfstring_ipv6(CFStringRef m, ipconfig_method_t * method)
4930{
4931    if (isA_CFString(m) == NULL) {
4932	return (FALSE);
4933    }
4934    if (CFEqual(m, kSCValNetIPv6ConfigMethodManual)) {
4935	*method = ipconfig_method_manual_v6_e;
4936    }
4937    else if (CFEqual(m, kSCValNetIPv6ConfigMethodAutomatic)) {
4938	*method = ipconfig_method_automatic_v6_e;
4939    }
4940    else if (CFEqual(m, kSCValNetIPv6ConfigMethodRouterAdvertisement)) {
4941	*method = ipconfig_method_rtadv_e;
4942    }
4943    else if (CFEqual(m, kSCValNetIPv6ConfigMethod6to4)) {
4944	*method = ipconfig_method_stf_e;
4945    }
4946    else if (CFEqual(m, kSCValNetIPv6ConfigMethodLinkLocal)) {
4947	*method = ipconfig_method_linklocal_v6_e;
4948    }
4949    else {
4950	return (FALSE);
4951    }
4952    return (TRUE);
4953}
4954
4955STATIC bool
4956my_CFStringToIPv6Address(CFStringRef str, struct in6_addr * ret_ip)
4957{
4958    char		buf[64];
4959
4960    if (isA_CFString(str) == NULL) {
4961	return (FALSE);
4962    }
4963    if (CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingASCII)
4964	== FALSE) {
4965	return (FALSE);
4966    }
4967    if (inet_pton(AF_INET6, buf, ret_ip) == 1) {
4968	return (TRUE);
4969    }
4970    return (FALSE);
4971}
4972
4973static ipconfig_status_t
4974method_info_from_ipv6_dict(CFDictionaryRef dict,
4975			   ipconfig_method_t * ret_method,
4976			   ipconfig_method_data_t * * ret_method_data)
4977{
4978    ipconfig_method_t		method = ipconfig_method_none_v6_e;
4979    CFStringRef			method_cf;
4980    ipconfig_method_data_t *	method_data = NULL;
4981    int				method_data_len = 0;
4982    boolean_t			status = ipconfig_status_invalid_parameter_e;
4983
4984    method_cf = CFDictionaryGetValue(dict,
4985				     kSCPropNetIPv6ConfigMethod);
4986    if (ipconfig_method_from_cfstring_ipv6(method_cf, &method) == FALSE) {
4987	my_log(LOG_ERR,
4988	       "IPConfiguration: IPv6 ConfigMethod is missing/invalid");
4989	goto done;
4990    }
4991    if (method == ipconfig_method_manual_v6_e) {
4992	struct in6_addr		address;
4993	CFArrayRef		addresses;
4994	CFStringRef		address_cf;
4995	CFIndex			count = 0;
4996	CFArrayRef		prefixes;
4997	CFNumberRef		prefix_cf = NULL;
4998	int			prefix = 0;
4999
5000	addresses
5001	    = isA_CFArray(CFDictionaryGetValue(dict, kSCPropNetIPv6Addresses));
5002	prefixes
5003	    = isA_CFArray(CFDictionaryGetValue(dict,
5004					       kSCPropNetIPv6PrefixLength));
5005	if (addresses != NULL) {
5006	    count = CFArrayGetCount(addresses);
5007	}
5008	if (count == 0) {
5009	    my_log(LOG_ERR,
5010		   "IPConfiguration: %s Addresses missing/invalid\n",
5011		   ipconfig_method_string(method));
5012	    goto done;
5013	}
5014	address_cf = CFArrayGetValueAtIndex(addresses, 0);
5015	if (my_CFStringToIPv6Address(address_cf, &address) == FALSE) {
5016	    my_log(LOG_ERR,
5017		   "IPConfiguration: %s Addresses invalid",
5018		   ipconfig_method_string(method));
5019	    goto done;
5020	}
5021	if (IN6_IS_ADDR_LINKLOCAL(&address)) {
5022	    my_log(LOG_ERR,
5023		   "IPConfiguration: %s cannot configure IPv6 Link Local address",
5024		   ipconfig_method_string(method));
5025	    goto done;
5026	}
5027	if (prefixes != NULL) {
5028	    if (count != CFArrayGetCount(prefixes)) {
5029		my_log(LOG_ERR,
5030		       "IPConfiguration: "
5031		       "%s Addresses/PrefixLength are different sizes",
5032		       ipconfig_method_string(method));
5033		goto done;
5034	    }
5035	    prefix_cf = CFArrayGetValueAtIndex(prefixes, 0);
5036	    if (isA_CFNumber(prefix_cf) == NULL
5037		|| (CFNumberGetValue(prefix_cf, kCFNumberIntType, &prefix)
5038		    == FALSE)) {
5039		my_log(LOG_ERR, "IPConfiguration: %s PrefixLength invalid",
5040		       ipconfig_method_string(method));
5041		goto done;
5042	    }
5043	}
5044	if (count > 1) {
5045	    my_log(LOG_NOTICE,
5046		   "IPConfiguration: %s "
5047		   "multiple addresses specified - ignoring all but first",
5048		   ipconfig_method_string(method));
5049	}
5050	method_data_len = sizeof(ipconfig_method_data_manual_v6_t);
5051	method_data = (ipconfig_method_data_t *)malloc(method_data_len);
5052	if (method_data == NULL) {
5053	    my_log(LOG_ERR, "IPConfiguration: malloc method_data failed");
5054	    status = ipconfig_status_allocation_failed_e;
5055	    goto done;
5056	}
5057	bzero(method_data, method_data_len);
5058	method_data->manual_v6.addr = address;
5059	method_data->manual_v6.prefix_length = prefix;
5060    }
5061    else if (method == ipconfig_method_stf_e) {
5062	CFStringRef	relay_cf;
5063
5064	relay_cf = CFDictionaryGetValue(dict, kSCPropNetIPv66to4Relay);
5065	if (relay_cf != NULL) {
5066	    char		buf[256];
5067	    int			len;
5068	    address_type_t	relay_addr_type;
5069	    struct in_addr	relay_ip;
5070	    struct in6_addr	relay_ipv6;
5071
5072	    if (isA_CFString(relay_cf) == NULL) {
5073		my_log(LOG_ERR, "IPConfiguration: %s 6to4 Relay invalid",
5074		       ipconfig_method_string(method));
5075		goto done;
5076	    }
5077	    len = my_CFStringToCStringAndLength(relay_cf, buf, sizeof(buf));
5078	    if (len == 0) {
5079		my_log(LOG_ERR, "IPConfiguration: %s 6to4 Relay empty",
5080		       ipconfig_method_string(method));
5081		goto done;
5082	    }
5083	    if (inet_aton(buf, &relay_ip) == 1) {
5084		relay_addr_type = address_type_ipv4_e;
5085		method_data_len = sizeof(ipconfig_method_data_stf_t);
5086	    }
5087	    else if (inet_pton(AF_INET6, buf, &relay_ipv6) == 1) {
5088		relay_addr_type = address_type_ipv6_e;
5089		method_data_len = sizeof(ipconfig_method_data_stf_t);
5090	    }
5091	    else {
5092		relay_addr_type = address_type_dns_e;
5093		method_data_len = offsetof(ipconfig_method_data_stf_t,
5094					   relay_addr.dns) + len + 1;
5095	    }
5096	    method_data = (ipconfig_method_data_t *)malloc(method_data_len);
5097	    if (method_data == NULL) {
5098		my_log(LOG_ERR, "IPConfiguration: malloc method_data failed");
5099		status = ipconfig_status_allocation_failed_e;
5100		goto done;
5101	    }
5102	    method_data->stf.relay_addr_type = relay_addr_type;
5103	    switch (relay_addr_type) {
5104	    case address_type_ipv4_e:
5105		method_data->stf.relay_addr.v4 = relay_ip;
5106		break;
5107	    case address_type_ipv6_e:
5108		method_data->stf.relay_addr.v6 = relay_ipv6;
5109		break;
5110	    case address_type_dns_e:
5111	    default:
5112		bcopy(buf, method_data->stf.relay_addr.dns, len);
5113		method_data->stf.relay_addr.dns[len] = '\0';
5114		break;
5115	    }
5116	}
5117    }
5118    status = ipconfig_status_success_e;
5119
5120 done:
5121    *ret_method_data = method_data;
5122    *ret_method = method;
5123    return (status);
5124}
5125
5126static CFArrayRef
5127get_order_array_from_values(CFDictionaryRef values, CFStringRef order_key)
5128{
5129    CFDictionaryRef	dict;
5130    CFArrayRef		order_array = NULL;
5131
5132    dict = isA_CFDictionary(CFDictionaryGetValue(values, order_key));
5133    if (dict) {
5134	order_array = CFDictionaryGetValue(dict,
5135					   kSCPropNetServiceOrder);
5136	order_array = isA_CFArray(order_array);
5137	if (order_array && CFArrayGetCount(order_array) == 0) {
5138	    order_array = NULL;
5139	}
5140    }
5141    return (order_array);
5142}
5143
5144#define ARBITRARILY_LARGE_NUMBER	(1000 * 1000)
5145
5146static int
5147lookup_order(CFArrayRef order, CFStringRef serviceID)
5148{
5149    CFIndex 	count;
5150    int		i;
5151
5152    if (order == NULL)
5153	goto done;
5154
5155    count = CFArrayGetCount(order);
5156    for (i = 0; i < count; i++) {
5157	CFStringRef	sid = CFArrayGetValueAtIndex(order, i);
5158
5159	if (CFEqual(sid, serviceID))
5160	    return (i);
5161    }
5162 done:
5163    return (ARBITRARILY_LARGE_NUMBER);
5164}
5165
5166static CFComparisonResult
5167compare_serviceIDs(const void *val1, const void *val2, void *context)
5168{
5169    CFArrayRef		order_array = (CFArrayRef)context;
5170    int			rank1;
5171    int			rank2;
5172
5173    rank1 = lookup_order(order_array, (CFStringRef)val1);
5174    rank2 = lookup_order(order_array, (CFStringRef)val2);
5175    if (rank1 == rank2)
5176	return (kCFCompareEqualTo);
5177    if (rank1 < rank2)
5178	return (kCFCompareLessThan);
5179    return (kCFCompareGreaterThan);
5180}
5181
5182static CFDictionaryRef
5183copy_ipv4_service_dict(CFDictionaryRef values, CFStringRef serviceID,
5184		       CFStringRef type, CFStringRef ifn_cf)
5185{
5186    CFDictionaryRef		dict;
5187    CFStringRef			key;
5188    CFMutableDictionaryRef	service_dict;
5189
5190    if (CFEqual(type, kSCValNetInterfaceTypeEthernet) == FALSE
5191	&& CFEqual(type, kSCValNetInterfaceTypeFireWire) == FALSE
5192	&& CFEqual(type, kSCValNetInterfaceTypeLoopback) == FALSE) {
5193	/* we only configure ethernet/firewire/loopback interfaces currently */
5194	return (NULL);
5195    }
5196    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5197						      kSCDynamicStoreDomainSetup,
5198						      serviceID,
5199						      kSCEntNetIPv4);
5200    dict = CFDictionaryGetValue(values, key);
5201    CFRelease(key);
5202    if (isA_CFDictionary(dict) == NULL) {
5203	return (NULL);
5204    }
5205    /* return IPv4 dict annotated with interface name and serviceID */
5206    service_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
5207    CFDictionarySetValue(service_dict, kSCPropNetInterfaceDeviceName,
5208			 ifn_cf);
5209    CFDictionarySetValue(service_dict, PROP_SERVICEID, serviceID);
5210    return (service_dict);
5211}
5212
5213static CFDictionaryRef
5214copy_ipv6_service_dict(CFDictionaryRef values, CFStringRef serviceID,
5215		       CFStringRef type, CFStringRef ifn_cf)
5216{
5217    CFDictionaryRef		dict;
5218    CFStringRef			key;
5219    CFMutableDictionaryRef	service_dict;
5220
5221    if (CFEqual(type, kSCValNetInterfaceTypeEthernet) == FALSE
5222	&& CFEqual(type, kSCValNetInterfaceTypeFireWire) == FALSE
5223	&& CFEqual(type, kSCValNetInterfaceType6to4) == FALSE
5224	&& CFEqual(type, kSCValNetInterfaceTypeLoopback) == FALSE) {
5225	/* we only configure ethernet/firewire/6to4/loopback interfaces currently */
5226	return (NULL);
5227    }
5228    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5229						      kSCDynamicStoreDomainSetup,
5230						      serviceID,
5231						      kSCEntNetIPv6);
5232    dict = CFDictionaryGetValue(values, key);
5233    CFRelease(key);
5234
5235    if (isA_CFDictionary(dict) == NULL) {
5236	return (NULL);
5237    }
5238    /* return IPv6 dict annotated with interface name, serviceID, 6to4Relay */
5239    service_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
5240    CFDictionarySetValue(service_dict, kSCPropNetInterfaceDeviceName,
5241			 ifn_cf);
5242    CFDictionarySetValue(service_dict, PROP_SERVICEID, serviceID);
5243
5244    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5245						      kSCDynamicStoreDomainSetup,
5246						      serviceID,
5247						      kSCEntNet6to4);
5248    dict = CFDictionaryGetValue(values, key);
5249    CFRelease(key);
5250    if (isA_CFDictionary(dict) != NULL) {
5251	CFStringRef		stf_relay;
5252
5253	stf_relay = CFDictionaryGetValue(dict, kSCPropNet6to4Relay);
5254	if (stf_relay != NULL) {
5255	    CFDictionarySetValue(service_dict,
5256				 kSCPropNetIPv66to4Relay,
5257				 stf_relay);
5258	}
5259    }
5260    return (service_dict);
5261}
5262
5263static CFArrayRef
5264copy_serviceIDs_from_values(CFDictionaryRef values, CFArrayRef order_array)
5265{
5266    CFIndex		count;
5267    int			i;
5268    const void * *	keys;
5269    CFMutableArrayRef	list = NULL;
5270    CFIndex		list_count;
5271
5272    /* if there are no values, we're done */
5273    count = CFDictionaryGetCount(values);
5274    if (count == 0) {
5275	return (NULL);
5276    }
5277    list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5278    keys = (const void * *)malloc(sizeof(*keys) * count);
5279    CFDictionaryGetKeysAndValues(values, (const void * *)keys, NULL);
5280    for (i = 0; i < count; i++) {
5281	CFStringRef		serviceID;
5282
5283	if (CFStringHasPrefix(keys[i], S_setup_service_prefix) == FALSE) {
5284	    continue;
5285	}
5286	/* Setup:/Network/Service/<serviceID>/{IPv4,[IPv6,]Interface} */
5287	serviceID = my_CFStringCopyComponent(keys[i], CFSTR("/"), 3);
5288	if (serviceID == NULL) {
5289	    continue;
5290	}
5291	my_CFArrayAppendUniqueValue(list, serviceID);
5292	CFRelease(serviceID);
5293    }
5294    free(keys);
5295    list_count = CFArrayGetCount(list);
5296    if (list_count == 0) {
5297	my_CFRelease(&list);
5298    }
5299    else if (order_array != NULL) {
5300	/* sort the list according to the defined service order */
5301	CFArraySortValues(list, CFRangeMake(0, list_count),
5302			  compare_serviceIDs, (void *)order_array);
5303    }
5304    return (list);
5305}
5306
5307static CFArrayRef
5308entity_all(SCDynamicStoreRef session, CFArrayRef * ret_ipv6_services)
5309{
5310    CFMutableArrayRef		all_services = NULL;
5311    CFMutableArrayRef		all_v6_services = NULL;
5312    CFMutableArrayRef		get_keys = NULL;
5313    CFMutableArrayRef		get_patterns = NULL;
5314    int				i;
5315    CFStringRef			key = NULL;
5316    CFArrayRef			service_IDs = NULL;
5317    CFIndex			service_IDs_count;
5318    CFStringRef			order_key = NULL;
5319    CFArrayRef			order_array = NULL;
5320    CFDictionaryRef		values = NULL;
5321
5322    get_keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5323    get_patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5324    all_services = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5325    all_v6_services = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5326
5327    /* get Setup:/Network/Service/any/{IPv4,[ IPv6,] Interface} */
5328    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5329						      kSCDynamicStoreDomainSetup,
5330						      kSCCompAnyRegex,
5331						      kSCEntNetIPv4);
5332    CFArrayAppendValue(get_patterns, key);
5333    CFRelease(key);
5334
5335    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5336						      kSCDynamicStoreDomainSetup,
5337						      kSCCompAnyRegex,
5338						      kSCEntNetIPv6);
5339    CFArrayAppendValue(get_patterns, key);
5340    CFRelease(key);
5341    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5342						      kSCDynamicStoreDomainSetup,
5343						      kSCCompAnyRegex,
5344						      kSCEntNet6to4);
5345    CFArrayAppendValue(get_patterns, key);
5346    CFRelease(key);
5347
5348    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5349						      kSCDynamicStoreDomainSetup,
5350						      kSCCompAnyRegex,
5351						      kSCEntNetInterface);
5352    CFArrayAppendValue(get_patterns, key);
5353    CFRelease(key);
5354
5355    /* populate keys array to get Setup:/Network/Global/IPv4 */
5356    order_key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
5357							   kSCDynamicStoreDomainSetup,
5358							   kSCEntNetIPv4);
5359    CFArrayAppendValue(get_keys, order_key);
5360
5361    /* get keys and values atomically */
5362    values = SCDynamicStoreCopyMultiple(session, get_keys, get_patterns);
5363    if (values == NULL) {
5364	goto done;
5365    }
5366
5367    /* grab the service order array */
5368    order_array = get_order_array_from_values(values, order_key);
5369
5370    /* build a list of configured service ID's */
5371    service_IDs = copy_serviceIDs_from_values(values, order_array);
5372    if (service_IDs == NULL) {
5373	/* if there are no serviceIDs, we're done */
5374	goto done;
5375    }
5376
5377    /* populate all_services array with annotated IPv4[/IPv6] dict's */
5378    service_IDs_count = CFArrayGetCount(service_IDs);
5379    for (i = 0; i < service_IDs_count; i++) {
5380	CFStringRef 		key;
5381	CFDictionaryRef		if_dict;
5382	CFStringRef 		ifname;
5383	CFDictionaryRef		service_dict = NULL;
5384	CFStringRef		serviceID;
5385	CFStringRef		type;
5386
5387	serviceID = CFArrayGetValueAtIndex(service_IDs, i);
5388	key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5389							  kSCDynamicStoreDomainSetup,
5390							  serviceID,
5391							  kSCEntNetInterface);
5392	if_dict = CFDictionaryGetValue(values, key);
5393	CFRelease(key);
5394	if_dict = isA_CFDictionary(if_dict);
5395	if (if_dict == NULL) {
5396	    continue;
5397	}
5398	type = CFDictionaryGetValue(if_dict, kSCPropNetInterfaceType);
5399	if (isA_CFString(type) == NULL) {
5400	    my_log(LOG_NOTICE,
5401		   "IPConfiguration: Interface Type missing/invalid"
5402		   "\nInterface = %@", if_dict);
5403	    continue;
5404	}
5405	ifname = CFDictionaryGetValue(if_dict, kSCPropNetInterfaceDeviceName);
5406	if (isA_CFString(ifname) == NULL) {
5407	    continue;
5408	}
5409
5410	/* get IPv4 service configuration */
5411	service_dict = copy_ipv4_service_dict(values, serviceID,
5412					      type, ifname);
5413	if (service_dict != NULL) {
5414	    CFArrayAppendValue(all_services, service_dict);
5415	    CFRelease(service_dict);
5416	}
5417	/* get IPv6 service configuration */
5418	service_dict = copy_ipv6_service_dict(values, serviceID,
5419					      type, ifname);
5420	if (service_dict != NULL) {
5421	    CFArrayAppendValue(all_v6_services, service_dict);
5422	    CFRelease(service_dict);
5423	}
5424    }
5425
5426 done:
5427    my_CFRelease(&values);
5428    my_CFRelease(&order_key);
5429    my_CFRelease(&get_keys);
5430    my_CFRelease(&get_patterns);
5431    my_CFRelease(&service_IDs);
5432    if (all_services != NULL && CFArrayGetCount(all_services) == 0) {
5433	my_CFRelease(&all_services);
5434    }
5435    if (all_v6_services != NULL && CFArrayGetCount(all_v6_services) == 0) {
5436	my_CFRelease(&all_v6_services);
5437    }
5438    *ret_ipv6_services = all_v6_services;
5439    return (all_services);
5440}
5441
5442
5443static CFDictionaryRef
5444lookup_entity(CFArrayRef all, CFStringRef ifn_cf)
5445{
5446    CFIndex		count;
5447    int 		i;
5448
5449    if (all == NULL)
5450	return (NULL);
5451
5452    count = CFArrayGetCount(all);
5453    for (i = 0; i < count; i++) {
5454	CFDictionaryRef	item = CFArrayGetValueAtIndex(all, i);
5455	CFStringRef	name;
5456
5457	name = CFDictionaryGetValue(item, kSCPropNetInterfaceDeviceName);
5458	if (CFEqual(name, ifn_cf)) {
5459	    return (item);
5460	}
5461    }
5462    return (NULL);
5463}
5464
5465static CFArrayRef
5466interface_services_copy(CFArrayRef all, CFStringRef ifn_cf)
5467{
5468    CFIndex		count;
5469    int 		i;
5470    CFMutableArrayRef	list = NULL;
5471
5472    if (all == NULL) {
5473	return (NULL);
5474    }
5475
5476    list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5477    if (list == NULL) {
5478	return (NULL);
5479    }
5480    count = CFArrayGetCount(all);
5481    for (i = 0; i < count; i++) {
5482	CFDictionaryRef	item = CFArrayGetValueAtIndex(all, i);
5483	CFStringRef	name;
5484
5485	name = CFDictionaryGetValue(item, kSCPropNetInterfaceDeviceName);
5486	if (CFEqual(name, ifn_cf)) {
5487	    CFArrayAppendValue(list, item);
5488	}
5489    }
5490    if (CFArrayGetCount(list) == 0) {
5491	my_CFRelease(&list);
5492    }
5493    return (list);
5494}
5495
5496typedef struct {
5497    CFStringRef			serviceID;
5498    ipconfig_method_t		method;
5499    ipconfig_method_data_t *	method_data;
5500} ServiceConfig, * ServiceConfigRef;
5501
5502typedef struct {
5503    ServiceConfigRef		list;
5504    int				count;
5505    boolean_t			is_ipv4;
5506} ServiceConfigList, * ServiceConfigListRef;
5507
5508static void
5509ServiceConfigListFree(ServiceConfigListRef scl)
5510{
5511    int 		i;
5512    ServiceConfigRef	scan;
5513
5514    if (scl->list == NULL) {
5515	return;
5516    }
5517    for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
5518	my_CFRelease(&scan->serviceID);
5519	if (scan->method_data != NULL) {
5520	    free(scan->method_data);
5521	}
5522    }
5523    free(scl->list);
5524    scl->list = NULL;
5525    return;
5526}
5527
5528#ifdef DEBUG
5529void
5530ServiceConfigListPrint(ServiceConfigListRef scl)
5531{
5532    int			i;
5533    ServiceConfigRef	scan;
5534
5535    my_log(LOG_DEBUG,
5536	   "%d %s configs\n", scl->count, scl->is_ipv4 ? "IPv4" : "IPv6");
5537    for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
5538	my_log(LOG_DEBUG, "serviceID %@ %s Data %p",
5539	       scan->serviceID,
5540	       ipconfig_method_string(scan->method),
5541	       scan->method_data);
5542	if (scan->method_data != NULL
5543	    && scan->method == ipconfig_method_stf_e) {
5544	    char 		ntopbuf[INET6_ADDRSTRLEN];
5545    	    ipconfig_method_data_stf_t * stf;
5546
5547	    stf = &scan->method_data->stf;
5548	    switch (stf->relay_addr_type) {
5549	    case address_type_ipv4_e:
5550		my_log(LOG_DEBUG, "IPv4 relay " IP_FORMAT,
5551		       IP_LIST(&stf->relay_addr.v4));
5552		break;
5553	    case address_type_ipv6_e:
5554		my_log(LOG_DEBUG, "IPv6 relay %s",
5555		       inet_ntop(AF_INET6, &stf->relay_addr.v6,
5556				 ntopbuf, sizeof(ntopbuf)));
5557		break;
5558	    case address_type_dns_e:
5559		my_log(LOG_DEBUG, "DNS relay %s", stf->relay_addr.dns);
5560		break;
5561	    default:
5562		my_log(LOG_DEBUG, "Bogus relay type %d", stf->relay_addr_type);
5563		break;
5564	    }
5565	}
5566    }
5567}
5568#endif /* DEBUG */
5569
5570static ServiceConfigRef
5571ServiceConfigListLookupMethod(ServiceConfigListRef scl,
5572			      ipconfig_method_t method,
5573			      ipconfig_method_data_t * method_data)
5574{
5575    int 		i;
5576    ServiceConfigRef	scan;
5577
5578    switch (method) {
5579    case ipconfig_method_stf_e:
5580    case ipconfig_method_linklocal_e:
5581	for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
5582	    if (method == scan->method) {
5583		return (scan);
5584	    }
5585	}
5586	break;
5587    case ipconfig_method_rtadv_e:
5588    case ipconfig_method_automatic_v6_e:
5589	for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
5590	    switch (scan->method) {
5591	    case ipconfig_method_rtadv_e:
5592	    case ipconfig_method_automatic_v6_e:
5593		return (scan);
5594	    default:
5595		break;
5596	    }
5597	}
5598	break;
5599    case ipconfig_method_dhcp_e:
5600    case ipconfig_method_bootp_e:
5601	for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
5602	    if (ipconfig_method_is_dhcp_or_bootp(scan->method))
5603		return (scan);
5604	}
5605	break;
5606    case ipconfig_method_failover_e:
5607    case ipconfig_method_manual_e:
5608    case ipconfig_method_inform_e:
5609	for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
5610	    if (ipconfig_method_is_manual(scan->method)
5611		&& (method_data->manual.addr.s_addr
5612		    == scan->method_data->manual.addr.s_addr)) {
5613		return (scan);
5614	    }
5615	}
5616	break;
5617    case ipconfig_method_manual_v6_e:
5618	for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
5619	    if (scan->method != ipconfig_method_manual_v6_e) {
5620		continue;
5621	    }
5622	    if (IN6_ARE_ADDR_EQUAL(&method_data->manual_v6.addr,
5623				   &scan->method_data->manual_v6.addr)) {
5624		return (scan);
5625	    }
5626	}
5627	break;
5628    default:
5629	break;
5630    }
5631    return (NULL);
5632}
5633
5634static ServiceConfigRef
5635ServiceConfigListLookupService(ServiceConfigListRef scl, CFStringRef serviceID)
5636{
5637    int 		i;
5638    ServiceConfigRef	scan;
5639
5640    for (i = 0, scan = scl->list; i < scl->count; i++, scan++) {
5641	if (CFEqual(serviceID, scan->serviceID)) {
5642	    return (scan);
5643	}
5644    }
5645    return (NULL);
5646}
5647
5648static ServiceRef
5649find_dynamic_service(const char * ifname, ipconfig_method_t method,
5650		     ipconfig_method_data_t * method_data)
5651{
5652    interface_t * 	if_p = ifl_find_name(S_interfaces, ifname);
5653    IFStateRef		ifstate = NULL;
5654
5655    if (if_p == NULL) {
5656	return (NULL);
5657    }
5658    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifname, NULL);
5659    if (ifstate == NULL) {
5660	return (NULL);
5661    }
5662    return (IFStateGetServiceMatchingMethod(ifstate, method,
5663					    method_data, TRUE));
5664}
5665
5666static boolean_t
5667ServiceConfigListInit(ServiceConfigListRef scl, boolean_t is_ipv4,
5668		      CFArrayRef all_services, const char * ifname)
5669{
5670    int			i;
5671    CFArrayRef 		if_service_list;
5672    CFIndex		if_service_count;
5673    CFStringRef		ifn_cf = NULL;
5674    boolean_t		ret;
5675
5676    bzero(scl, sizeof(*scl));
5677    scl->is_ipv4 = is_ipv4;
5678    ifn_cf = CFStringCreateWithCString(NULL, ifname,
5679				       kCFStringEncodingASCII);
5680    if_service_list = interface_services_copy(all_services, ifn_cf);
5681    if (if_service_list == NULL) {
5682	goto done;
5683    }
5684    if_service_count = CFArrayGetCount(if_service_list);
5685    scl->list = (ServiceConfigRef)malloc(if_service_count * sizeof(*scl->list));
5686    if (scl->list == NULL) {
5687	goto done;
5688    }
5689    for (i = 0; i < if_service_count; i++) {
5690	boolean_t		duplicate_config = FALSE;
5691	boolean_t		duplicate_dynamic = FALSE;
5692	ipconfig_method_t	method;
5693	ipconfig_method_data_t *method_data = NULL;
5694	CFDictionaryRef		service_dict;
5695	CFStringRef		serviceID;
5696
5697	service_dict = CFArrayGetValueAtIndex(if_service_list, i);
5698	if (is_ipv4) {
5699	    if (method_info_from_dict(service_dict, &method, &method_data)
5700		!= ipconfig_status_success_e) {
5701		continue;
5702	    }
5703	}
5704	else {
5705	    if (method_info_from_ipv6_dict(service_dict, &method, &method_data)
5706		!= ipconfig_status_success_e) {
5707		continue;
5708	    }
5709	}
5710	duplicate_config
5711	    = (ServiceConfigListLookupMethod(scl, method, method_data) != NULL);
5712	if (duplicate_config == FALSE) {
5713	    duplicate_dynamic = (find_dynamic_service(ifname, method,
5714						      method_data) != NULL);
5715	}
5716	if (duplicate_config || duplicate_dynamic) {
5717	    my_log(LOG_NOTICE, "%s: %s %s",
5718		   ifname, ipconfig_method_string(method),
5719		   duplicate_config
5720		   ? "duplicate configured service"
5721		   : "configured service conflicts with dynamic service");
5722	    free(method_data);
5723	    continue;
5724	}
5725	serviceID = CFDictionaryGetValue(service_dict, PROP_SERVICEID);
5726	scl->list[scl->count].serviceID = CFRetain(serviceID);
5727	scl->list[scl->count].method = method;
5728	scl->list[scl->count].method_data = method_data;
5729	scl->count++;
5730    }
5731 done:
5732    if (scl->count == 0) {
5733	ServiceConfigListFree(scl);
5734	ret = FALSE;
5735    }
5736    else {
5737	ret = TRUE;
5738    }
5739    my_CFRelease(&ifn_cf);
5740    my_CFRelease(&if_service_list);
5741    return (ret);
5742}
5743
5744static void
5745ServiceConfigListFreeInactiveServices(ServiceConfigListRef scl,
5746				      const char * ifname)
5747{
5748    CFMutableArrayRef	inactive_list = NULL;
5749    CFIndex		inactive_list_count;
5750    int			i;
5751    IFStateRef		ifstate;
5752    dynarray_t *	list;
5753
5754    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifname, NULL);
5755    if (ifstate == NULL) {
5756	return;
5757    }
5758    if (scl->is_ipv4) {
5759	list = &ifstate->services;
5760    }
5761    else {
5762	list = &ifstate->services_v6;
5763    }
5764    inactive_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5765    if (inactive_list == NULL) {
5766	return;
5767    }
5768    for (i = 0; i < dynarray_count(list); i++) {
5769	ServiceRef	service_p = dynarray_element(list, i);
5770	CFStringRef 	serviceID = service_p->serviceID;
5771
5772	if (service_p->is_dynamic) {
5773	    /* dynamically created services survive configuration changes */
5774	    continue;
5775	}
5776	if (service_p->parent_serviceID != NULL) {
5777	    /* this service gets cleaned up on its own */
5778	    continue;
5779	}
5780	if (ServiceConfigListLookupService(scl, serviceID) == NULL) {
5781	    CFArrayAppendValue(inactive_list, serviceID);
5782	}
5783    }
5784    inactive_list_count = CFArrayGetCount(inactive_list);
5785    for (i = 0; i < inactive_list_count; i++) {
5786	CFStringRef serviceID = CFArrayGetValueAtIndex(inactive_list, i);
5787
5788	IFStateFreeServiceWithID(ifstate, serviceID, scl->is_ipv4);
5789    }
5790    my_CFRelease(&inactive_list);
5791    return;
5792}
5793
5794static ipconfig_status_t
5795S_set_service(IFStateRef ifstate, ServiceConfigRef config, boolean_t is_ipv4)
5796{
5797    CFStringRef		serviceID = config->serviceID;
5798    ServiceRef		service_p;
5799    IFStateRef		this_ifstate = NULL;
5800
5801    service_p = IFStateGetServiceWithID(ifstate, serviceID, is_ipv4);
5802    if (service_p != NULL) {
5803	boolean_t		needs_stop = FALSE;
5804	ipconfig_status_t	status;
5805
5806	if (service_p->method == config->method) {
5807	    status = config_method_change(service_p, config->method,
5808					  config->method_data,
5809					  &needs_stop);
5810	    if (status == ipconfig_status_success_e
5811		&& needs_stop == FALSE) {
5812		return (ipconfig_status_success_e);
5813	    }
5814	}
5815	IFStateFreeService(ifstate, service_p);
5816    }
5817    else {
5818	this_ifstate = IFStateListGetServiceWithID(&S_ifstate_list,
5819						   serviceID,
5820						   &service_p,
5821						   is_ipv4);
5822	if (this_ifstate) {
5823	    /* service is on other interface, stop it now */
5824	    IFStateFreeService(this_ifstate, service_p);
5825	}
5826    }
5827    return (IFState_service_add(ifstate, serviceID, config->method,
5828				config->method_data,
5829				NULL, NULL));
5830}
5831
5832static void
5833interface_configuration_changed(interface_t * if_p, CFArrayRef all,
5834				boolean_t is_ipv4)
5835{
5836    IFStateRef		ifstate;
5837    ServiceConfigList	scl;
5838
5839    /* if no services are defined, remove them all */
5840    if (ServiceConfigListInit(&scl, is_ipv4, all, if_name(if_p)) == FALSE) {
5841	ifstate = IFStateList_ifstate_with_name(&S_ifstate_list,
5842						if_name(if_p), NULL);
5843	if (ifstate != NULL) {
5844	    if (is_ipv4) {
5845		IFStateFreeIPv4Services(ifstate, FALSE);
5846	    }
5847	    else {
5848		IFStateFreeIPv6Services(ifstate, FALSE);
5849	    }
5850	}
5851	return;
5852    }
5853
5854    /* stop services that are no longer active */
5855    ServiceConfigListFreeInactiveServices(&scl, if_name(if_p));
5856
5857    /* update services that are still defined */
5858    ifstate = IFStateList_ifstate_create(&S_ifstate_list, if_p);
5859    if (ifstate != NULL) {
5860	int			i;
5861	ServiceConfigRef	config;
5862
5863	/* update each of the services that are configured */
5864	for (i = 0, config = scl.list; i < scl.count; i++, config++) {
5865	    (void)S_set_service(ifstate, config, scl.is_ipv4);
5866	}
5867    }
5868    ServiceConfigListFree(&scl);
5869    return;
5870}
5871
5872static void
5873handle_configuration_changed(SCDynamicStoreRef session,
5874			     CFArrayRef all_ipv4, CFArrayRef all_ipv6)
5875{
5876    int i;
5877
5878    for (i = 0; i < ifl_count(S_interfaces); i++) {
5879	interface_t *		if_p = ifl_at_index(S_interfaces, i);
5880
5881	interface_configuration_changed(if_p, all_ipv4, IS_IPV4);
5882	interface_configuration_changed(if_p, all_ipv6, IS_IPV6);
5883    }
5884    return;
5885}
5886
5887static void
5888configuration_changed(SCDynamicStoreRef session)
5889{
5890    CFArrayRef		all_ipv4 = NULL;
5891    CFArrayRef		all_ipv6 = NULL;
5892
5893    all_ipv4 = entity_all(session, &all_ipv6);
5894    handle_configuration_changed(session, all_ipv4, all_ipv6);
5895    my_CFRelease(&all_ipv4);
5896    my_CFRelease(&all_ipv6);
5897    return;
5898}
5899
5900static void
5901configure_from_cache(SCDynamicStoreRef session)
5902{
5903    CFArrayRef		all_ipv4 = NULL;
5904    CFArrayRef		all_ipv6 = NULL;
5905    int			count = 0;
5906    int 		i;
5907
5908    all_ipv4 = entity_all(session, &all_ipv6);
5909    if (all_ipv4 == NULL) {
5910	goto done;
5911    }
5912
5913    /*
5914     * Go through the list of interfaces and find those that have a
5915     * configuration.  If an interface is present, pre-allocate an ifstate
5916     * entry so that the system startup will wait for that interface to
5917     * complete its initialization.
5918     */
5919    for (i = 0; i < ifl_count(S_interfaces); i++) {
5920	CFDictionaryRef		dict;
5921	interface_t *		if_p = ifl_at_index(S_interfaces, i);
5922	CFStringRef		ifn_cf = NULL;
5923
5924	ifn_cf = CFStringCreateWithCString(NULL,
5925					   if_name(if_p),
5926					   kCFStringEncodingASCII);
5927	if (ifn_cf == NULL) {
5928	    continue;
5929	}
5930	dict = lookup_entity(all_ipv4, ifn_cf);
5931	if (dict != NULL) {
5932	    (void)IFStateList_ifstate_create(&S_ifstate_list, if_p);
5933	    count++;
5934	}
5935	CFRelease(ifn_cf);
5936    }
5937
5938 done:
5939    if (count == 0) {
5940	unblock_startup(session);
5941    }
5942    else {
5943	handle_configuration_changed(session, all_ipv4, all_ipv6);
5944    }
5945    my_CFRelease(&all_ipv4);
5946    my_CFRelease(&all_ipv6);
5947
5948    return;
5949}
5950
5951static void
5952notifier_init(SCDynamicStoreRef session)
5953{
5954    CFMutableArrayRef	keys = NULL;
5955    CFStringRef		key;
5956    CFMutableStringRef	pattern;
5957    CFMutableArrayRef	patterns = NULL;
5958    CFRunLoopSourceRef	rls;
5959
5960    if (session == NULL) {
5961	return;
5962    }
5963    keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5964    patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
5965
5966    /* notify when IPv4 config of any service changes */
5967    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5968						      kSCDynamicStoreDomainSetup,
5969						      kSCCompAnyRegex,
5970						      kSCEntNetIPv4);
5971    CFArrayAppendValue(patterns, key);
5972    CFRelease(key);
5973    /* notify when IPv6 config of any service changes */
5974    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5975						      kSCDynamicStoreDomainSetup,
5976						      kSCCompAnyRegex,
5977						      kSCEntNetIPv6);
5978    CFArrayAppendValue(patterns, key);
5979    CFRelease(key);
5980    /* notify when 6to4 config of any service changes */
5981    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5982						      kSCDynamicStoreDomainSetup,
5983						      kSCCompAnyRegex,
5984						      kSCEntNet6to4);
5985    CFArrayAppendValue(patterns, key);
5986    CFRelease(key);
5987    /* notify when IPv6 address changes on any interface */
5988    key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
5989							kSCDynamicStoreDomainState,
5990							kSCCompAnyRegex,
5991							kSCEntNetIPv6);
5992    CFArrayAppendValue(patterns, key);
5993    CFRelease(key);
5994    /* notify when Interface service <-> interface binding changes */
5995    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
5996						      kSCDynamicStoreDomainSetup,
5997						      kSCCompAnyRegex,
5998						      kSCEntNetInterface);
5999    CFArrayAppendValue(patterns, key);
6000    CFRelease(key);
6001
6002    /* notify when the link status of any interface changes */
6003    key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
6004							kSCDynamicStoreDomainState,
6005							kSCCompAnyRegex,
6006							kSCEntNetLink);
6007    CFArrayAppendValue(patterns, key);
6008    CFRelease(key);
6009
6010    /* notify when the bssid key of any interface changes */
6011    key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
6012							kSCDynamicStoreDomainState,
6013							kSCCompAnyRegex,
6014							kSCEntNetAirPort);
6015    CFArrayAppendValue(patterns, key);
6016    CFRelease(key);
6017
6018    /* notify for a refresh configuration request */
6019    key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
6020							kSCDynamicStoreDomainState,
6021							kSCCompAnyRegex,
6022							kSCEntNetRefreshConfiguration);
6023    CFArrayAppendValue(patterns, key);
6024    CFRelease(key);
6025
6026    /* notify when there's an ARP collision on any interface */
6027    key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
6028							kSCDynamicStoreDomainState,
6029							kSCCompAnyRegex,
6030							kSCEntNetIPv4ARPCollision);
6031    pattern = CFStringCreateMutableCopy(NULL, 0, key);
6032    CFStringAppend(pattern, CFSTR(".*"));
6033    CFRelease(key);
6034    CFArrayAppendValue(patterns, pattern);
6035    CFRelease(pattern);
6036
6037    /* notify when ActiveDuringSleepRequested changes */
6038    key = ActiveDuringSleepRequestedKeyCopy(kSCCompAnyRegex);
6039    CFArrayAppendValue(patterns, key);
6040    CFRelease(key);
6041
6042    /* notify when list of interfaces changes */
6043    key = SCDynamicStoreKeyCreateNetworkInterface(NULL,
6044						  kSCDynamicStoreDomainState);
6045    CFArrayAppendValue(keys, key);
6046    CFRelease(key);
6047
6048    /* notify when the service order changes */
6049    key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6050						     kSCDynamicStoreDomainSetup,
6051						     kSCEntNetIPv4);
6052    CFArrayAppendValue(keys, key);
6053    CFRelease(key);
6054
6055    /* notify when ComputerName/LocalHostName changes */
6056    S_computer_name_key = SCDynamicStoreKeyCreateComputerName(NULL);
6057    CFArrayAppendValue(keys, S_computer_name_key);
6058    S_hostnames_key = SCDynamicStoreKeyCreateHostNames(NULL);
6059    CFArrayAppendValue(keys, S_hostnames_key);
6060
6061    SCDynamicStoreSetNotificationKeys(session, keys, patterns);
6062    CFRelease(keys);
6063    CFRelease(patterns);
6064
6065    rls = SCDynamicStoreCreateRunLoopSource(NULL, session, 0);
6066    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
6067    CFRelease(rls);
6068
6069    /* initialize the computer name */
6070    computer_name_update(session);
6071    return;
6072}
6073
6074static boolean_t
6075update_interface_list()
6076{
6077    interface_list_t *	new_interfaces = NULL;
6078
6079    new_interfaces = ifl_init();
6080    if (new_interfaces == NULL) {
6081	my_log(LOG_ERR, "IPConfiguration: ifl_init failed");
6082	return (FALSE);
6083    }
6084    if (S_interfaces) {
6085	ifl_free(&S_interfaces);
6086    }
6087    S_interfaces = new_interfaces;
6088
6089    return (TRUE);
6090}
6091
6092interface_list_t *
6093get_interface_list(void)
6094{
6095    if (S_interfaces == NULL) {
6096	S_interfaces = ifl_init();
6097    }
6098    return (S_interfaces);
6099}
6100
6101
6102/*
6103 * Function: check_for_detached_interfaces
6104 * Purpose:
6105 *   Remove interface state for any interface that has been removed.
6106 *   Create a temporary list to store the name of each interface that
6107 *   has been removed.  Iterate through that list to remove individual
6108 *   interface state records.  This is done to avoid problems with
6109 *   iterating over a list while it is modified.
6110 */
6111static void
6112check_for_detached_interfaces()
6113{
6114    int			count = dynarray_count(&S_ifstate_list);
6115    const char * *	names = NULL;
6116    int			names_count = 0;
6117    int 		i;
6118
6119
6120    if (count == 0) {
6121	return;
6122    }
6123
6124    /* allocate worst case scenario in which each ifstate needs to be removed */
6125    names = (const char * *)malloc(sizeof(char *) * count);
6126    if (names == NULL) {
6127	return;
6128    }
6129    for (i = 0; i < count; i++) {
6130	IFStateRef	ifstate = dynarray_element(&S_ifstate_list, i);
6131
6132	if (ifl_find_name(S_interfaces, if_name(ifstate->if_p)) == NULL) {
6133	    names[names_count++] = if_name(ifstate->if_p);
6134	}
6135    }
6136    for (i = 0; i < names_count; i++) {
6137	IFStateList_ifstate_free(&S_ifstate_list, names[i]);
6138    }
6139    free(names);
6140    return;
6141}
6142
6143static void
6144runloop_observer(CFRunLoopObserverRef observer,
6145		 CFRunLoopActivity activity, void *info)
6146{
6147    if (S_scd_session == NULL) {
6148	return;
6149    }
6150    if (S_linklocal_needs_attention) {
6151	CFArrayRef		service_order = NULL;
6152
6153	S_linklocal_needs_attention = FALSE;
6154	service_order = S_get_service_order(S_scd_session);
6155	my_log(LOG_DEBUG, "runloop_observer: calling S_linklocal_elect");
6156	S_linklocal_elect(service_order);
6157	my_CFRelease(&service_order);
6158    }
6159    if (S_active_during_sleep_needs_attention) {
6160	S_active_during_sleep_needs_attention = FALSE;
6161	ActiveDuringSleepProcess(&S_ifstate_list);
6162    }
6163    my_SCDynamicStorePublish(S_scd_session);
6164    return;
6165}
6166
6167/*
6168 * Function: woke_from_hibernation
6169 *
6170 * Purpose:
6171 *   When we wake from sleep, check whether we woke from a hibernation
6172 *   image or a regular wake from sleep.
6173 */
6174
6175#define IO_PATH_PM_ROOT_DOMAIN kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain"
6176STATIC boolean_t
6177woke_from_hibernation(void)
6178{
6179    CFDataRef		hib_prop;
6180    uint32_t		hibernate_state;
6181
6182    hibernate_state = kIOHibernateStateInactive;
6183    hib_prop = myIORegistryEntryCopyProperty(IO_PATH_PM_ROOT_DOMAIN,
6184					     CFSTR(kIOHibernateStateKey));
6185    if (isA_CFData(hib_prop) != NULL
6186	&& CFDataGetLength(hib_prop) == sizeof(hibernate_state)) {
6187	hibernate_state = *((uint32_t *)(void *)CFDataGetBytePtr(hib_prop));
6188    }
6189    my_CFRelease(&hib_prop);
6190    return (hibernate_state == kIOHibernateStateWakingFromHibernate);
6191}
6192
6193STATIC void
6194S_ifstate_process_wake(IFStateRef ifstate)
6195{
6196    wake_data_t		event_data;
6197    interface_t	*	if_p = ifstate->if_p;
6198    link_status_t	link_status;
6199
6200    link_status = if_get_link_status(if_p);
6201    event_data.flags = 0;
6202    if (S_woke_from_hibernation) {
6203	event_data.flags |= kWakeFlagsFromHibernation;
6204    }
6205    else if (link_status.valid
6206	     && link_status.active == FALSE
6207	     && link_status.wake_on_same_network) {
6208	my_log(LOG_DEBUG, "%s: wake on same network (link inactive)",
6209	       if_name(if_p));
6210	return;
6211    }
6212    if (ifstate->link_timer_suppressed) {
6213	ifstate->link_timer_suppressed = FALSE;
6214	my_log(LOG_DEBUG, "%s: processing link timer expired at wake",
6215	       if_name(if_p));
6216	process_link_timer_expired(ifstate);
6217    }
6218    ifstate->wake_generation = S_wake_generation;
6219    my_log(LOG_DEBUG, "%s: Wake", if_name(if_p));
6220    if (if_is_wireless(if_p)) {
6221	CFStringRef		ssid;
6222	struct ether_addr	bssid;
6223
6224	ssid = S_copy_ssid_bssid(ifstate->ifname, &bssid);
6225	if (ssid != NULL
6226	    && ifstate->ssid != NULL) {
6227	    if (!CFEqual(ssid, ifstate->ssid)) {
6228		event_data.flags |= kWakeFlagsSSIDChanged;
6229	    }
6230	    else if (bcmp(&bssid, &ifstate->bssid, sizeof(bssid))) {
6231		event_data.flags |= kWakeFlagsBSSIDChanged;
6232	    }
6233	    IFState_set_ssid_bssid(ifstate, ssid, &bssid);
6234	}
6235	my_CFRelease(&ssid);
6236    }
6237    if (dynarray_count(&ifstate->services) > 0) {
6238	/* attach IPv4 in case the interface went away during sleep */
6239	inet_attach_interface(if_name(if_p));
6240	service_list_event(&ifstate->services, IFEventID_wake_e,
6241			   (void *)&event_data);
6242    }
6243    if (dynarray_count(&ifstate->services_v6) > 0) {
6244	/* attach IPv6 in case the interface went away during sleep */
6245	inet6_attach_interface(if_name(if_p));
6246
6247	/* make sure IPv6 link-local is started */
6248	if (link_status.valid == FALSE
6249	    || link_status.active) {
6250	    (void)inet6_linklocal_start(if_name(if_p), !if_is_awdl(if_p));
6251	}
6252	/* update our neighbor advert list */
6253	if (ifstate->neighbor_advert_list != NULL) {
6254	    CFRelease(ifstate->neighbor_advert_list);
6255	}
6256	ifstate->neighbor_advert_list
6257	    = S_copy_neighbor_advert_list(S_scd_session, ifstate->ifname);
6258	service_list_event(&ifstate->services_v6, IFEventID_wake_e,
6259			   (void *)&event_data);
6260    }
6261    return;
6262}
6263
6264STATIC void
6265S_deliver_wake_event(void)
6266{
6267    int 		i;
6268    int			if_count = dynarray_count(&S_ifstate_list);
6269
6270    for (i = 0; i < if_count; i++) {
6271	IFStateRef		ifstate = dynarray_element(&S_ifstate_list, i);
6272
6273	if (ifstate->wake_generation == S_wake_generation) {
6274	    /* we've already seen this wake via link status event */
6275	    my_log(LOG_DEBUG, "%s: ignoring wake (already processed)",
6276		   if_name(ifstate->if_p));
6277	    continue;
6278	}
6279	S_ifstate_process_wake(ifstate);
6280    }
6281    return;
6282}
6283
6284static void
6285power_changed(void * refcon, io_service_t service, natural_t msg_type,
6286	      void * msg)
6287{
6288    boolean_t	ack_msg = TRUE;
6289
6290    switch (msg_type) {
6291    case kIOMessageSystemWillPowerOff:
6292    case kIOMessageSystemWillRestart:
6293	/*
6294	 * Note: we never see these messages because we get killed
6295	 * off before that would happen (SIGTERM, SIGKILL).
6296	 */
6297	break;
6298
6299    case kIOMessageSystemWillNotSleep:
6300    case kIOMessageSystemWillNotPowerOff:
6301	ack_msg = FALSE;
6302	break;
6303
6304    case kIOMessageSystemWillSleep:
6305	/* sleep */
6306	if (S_awake == FALSE) {
6307	    /* already asleep (should not happen) */
6308	    break;
6309	}
6310	my_log(LOG_DEBUG, "IPConfiguration: Sleep");
6311	S_awake = FALSE;
6312	IFStateList_all_services_sleep(&S_ifstate_list);
6313	break;
6314
6315    case kIOMessageSystemWillPowerOn:
6316	if (S_awake) {
6317	    /* already awake (should not happen) */
6318	    break;
6319	}
6320	my_log(LOG_DEBUG, "IPConfiguration: Wake");
6321	S_awake = TRUE;
6322	S_wake_time = timer_current_secs();
6323	S_wake_generation++;
6324	S_woke_from_hibernation = woke_from_hibernation();
6325	S_deliver_wake_event();
6326	break;
6327    case kIOMessageSystemHasPoweredOn:
6328	/* wake */
6329	ack_msg = FALSE;
6330	break;
6331
6332    default:
6333	break;
6334    }
6335    if (ack_msg) {
6336	IOAllowPowerChange(S_power_connection, (long)msg);
6337    }
6338    return;
6339}
6340
6341
6342static io_connect_t
6343power_notification_init()
6344{
6345    io_object_t 		obj;
6346    CFRunLoopSourceRef 		rls;
6347    IONotificationPortRef 	port;
6348    io_connect_t 		power_connection;
6349
6350    power_connection = IORegisterForSystemPower(NULL, &port,
6351						power_changed, &obj);
6352    if (power_connection != 0) {
6353        rls = IONotificationPortGetRunLoopSource(port);
6354        CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
6355    }
6356    return (power_connection);
6357}
6358
6359#if ! TARGET_OS_EMBEDDED
6360#define POWER_INTEREST	(kIOPMEarlyWakeNotification	\
6361			 | kIOPMCapabilityNetwork	\
6362			 | kIOPMCapabilityCPU)
6363
6364void
6365new_power_changed(void * param,
6366		  IOPMConnection connection,
6367		  IOPMConnectionMessageToken token,
6368		  IOPMSystemPowerStateCapabilities capabilities)
6369{
6370    IOReturn                ret;
6371
6372    if ((capabilities & kIOPMCapabilityCPU) != 0) {
6373	if (S_awake == FALSE) {
6374	    S_woke_from_hibernation = woke_from_hibernation();
6375	    S_wake_generation++;
6376	    S_wake_time = timer_current_secs();
6377	}
6378	S_awake = TRUE;
6379	if ((capabilities & kIOPMEarlyWakeNotification) != 0) {
6380	    my_log(LOG_DEBUG, "IPConfiguration: Early Wake");
6381	}
6382	else if (S_wake_event_sent == FALSE) {
6383	    my_log(LOG_DEBUG, "IPConfiguration: Wake");
6384	    S_deliver_wake_event();
6385	    S_wake_event_sent = TRUE;
6386	}
6387    }
6388    else {
6389	/* sleep */
6390	S_awake = FALSE;
6391	S_wake_event_sent = FALSE;
6392	my_log(LOG_DEBUG, "IPConfiguration: Sleep");
6393	IFStateList_all_services_sleep(&S_ifstate_list);
6394
6395    }
6396    ret = IOPMConnectionAcknowledgeEvent(connection, token);
6397    if (ret != kIOReturnSuccess) {
6398	my_log(LOG_NOTICE, "IPConfiguration: "
6399	       "IOPMConnectionAcknowledgeEvent failed, 0x%08x", ret);
6400    }
6401    return;
6402}
6403
6404static void
6405new_power_notification_init(void)
6406{
6407    IOPMConnection      connection = NULL;
6408    IOReturn            ret;
6409
6410    ret = IOPMConnectionCreate(CFSTR("IPConfiguration"),
6411			       POWER_INTEREST,
6412			       &connection);
6413    if (ret != kIOReturnSuccess) {
6414	my_log(LOG_ERR,
6415	       "IPConfiguration: IOPMConnectionCreate failed, 0x%08x", ret);
6416	goto failed;
6417    }
6418    ret = IOPMConnectionSetNotification(connection, NULL,
6419					new_power_changed);
6420
6421    if (ret != kIOReturnSuccess) {
6422	my_log(LOG_ERR, "IPConfiguration:"
6423	       "IOPMConnectionSetNotification failed, 0x%08x", ret);
6424	goto failed;
6425    }
6426
6427    ret = IOPMConnectionScheduleWithRunLoop(connection,
6428					    CFRunLoopGetCurrent(),
6429					    kCFRunLoopDefaultMode);
6430    if (ret != kIOReturnSuccess) {
6431	my_log(LOG_ERR, "IPConfiguration:"
6432	       "IOPMConnectionScheduleWithRunloop failed, 0x%08x", ret);
6433	goto failed;
6434    }
6435    return;
6436
6437 failed:
6438    if (connection != NULL) {
6439	IOPMConnectionRelease(connection);
6440    }
6441    return;
6442}
6443
6444#endif /* ! TARGET_OS_EMBEDDED */
6445
6446static boolean_t
6447start_initialization(SCDynamicStoreRef session)
6448{
6449    S_observer = CFRunLoopObserverCreate(NULL,
6450					 kCFRunLoopAllActivities,
6451					 TRUE, 0, runloop_observer, NULL);
6452    if (S_observer != NULL) {
6453	CFRunLoopAddObserver(CFRunLoopGetCurrent(), S_observer,
6454			     kCFRunLoopDefaultMode);
6455    }
6456    else {
6457	my_log(LOG_NOTICE,
6458	       "start_initialization: CFRunLoopObserverCreate failed!");
6459    }
6460    S_setup_service_prefix = SCDynamicStoreKeyCreate(NULL,
6461						     CFSTR("%@/%@/%@/"),
6462						     kSCDynamicStoreDomainSetup,
6463						     kSCCompNetwork,
6464						     kSCCompService);
6465
6466    S_state_interface_prefix = SCDynamicStoreKeyCreate(NULL,
6467						       CFSTR("%@/%@/%@/"),
6468						       kSCDynamicStoreDomainState,
6469						       kSCCompNetwork,
6470						       kSCCompInterface);
6471
6472    /* install run-time notifiers */
6473    notifier_init(session);
6474
6475    (void)update_interface_list();
6476
6477    (void)S_netboot_init();
6478
6479    configure_from_cache(session);
6480
6481    /* register for sleep/wake */
6482#if ! TARGET_OS_EMBEDDED
6483    if (S_use_maintenance_wake) {
6484	new_power_notification_init();
6485    }
6486    else {
6487	S_power_connection = power_notification_init();
6488    }
6489#else /* ! TARGET_OS_EMBEDDED */
6490    S_power_connection = power_notification_init();
6491#endif /* ! TARGET_OS_EMBEDDED */
6492    return (TRUE);
6493}
6494
6495static void
6496link_refresh(SCDynamicStoreRef session, CFStringRef cache_key)
6497{
6498    CFStringRef			ifn_cf = NULL;
6499    IFStateRef   		ifstate;
6500    int 			j;
6501
6502    if (CFStringHasPrefix(cache_key, S_state_interface_prefix) == FALSE) {
6503	return;
6504    }
6505    /* State:/Network/Interface/<ifname>/RefreshConfiguration */
6506    ifn_cf = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
6507    if (ifn_cf == NULL) {
6508	return;
6509    }
6510    ifstate = IFStateListGetIFState(&S_ifstate_list, ifn_cf, NULL);
6511    if (ifstate == NULL || ifstate->netboot) {
6512	/* don't propagate media status events for netboot interface */
6513	goto done;
6514    }
6515    for (j = 0; j < dynarray_count(&ifstate->services); j++) {
6516	ServiceRef	service_p = dynarray_element(&ifstate->services, j);
6517
6518	config_method_renew(service_p);
6519    }
6520    service_list_event(&ifstate->services_v6, IFEventID_renew_e, NULL);
6521
6522 done:
6523    my_CFRelease(&ifn_cf);
6524    return;
6525}
6526
6527static void
6528ipv6_interface_address_changed(SCDynamicStoreRef session,
6529			       CFStringRef cache_key)
6530{
6531    inet6_addrlist_t 	addr_list;
6532    CFStringRef		ifn_cf;
6533    IFStateRef   	ifstate;
6534
6535    if (CFStringHasPrefix(cache_key, S_state_interface_prefix) == FALSE) {
6536	return;
6537    }
6538
6539    /* figure out which interface this belongs to and deliver the event */
6540
6541    /* State:/Network/Interface/<ifname>/IPv6 */
6542    ifn_cf = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
6543    if (ifn_cf == NULL) {
6544	return;
6545    }
6546    ifstate = IFStateListGetIFState(&S_ifstate_list, ifn_cf, NULL);
6547    if (ifstate == NULL) {
6548	/* not tracking this event */
6549	goto done;
6550    }
6551    /* get the addresses from the interface and deliver the event */
6552    inet6_addrlist_copy(&addr_list, if_link_index(ifstate->if_p));
6553    if (G_IPConfiguration_verbose) {
6554	CFStringRef		str;
6555
6556	str = inet6_addrlist_copy_description(&addr_list);
6557	my_log(~LOG_DEBUG, "%@: IPv6 address list = %@", ifn_cf, str);
6558	CFRelease(str);
6559    }
6560    service_list_event(&ifstate->services_v6,
6561		       IFEventID_ipv6_address_changed_e, &addr_list);
6562
6563    /* send neighbor advertisements if necessary */
6564    S_process_neighbor_adverts(ifstate, &addr_list);
6565
6566    inet6_addrlist_free(&addr_list);
6567
6568 done:
6569    my_CFRelease(&ifn_cf);
6570    return;
6571}
6572
6573
6574#include "my_darwin.h"
6575
6576#ifndef NO_WIRELESS
6577
6578#include <Apple80211/Apple80211API.h>
6579#include <Kernel/IOKit/apple80211/apple80211_ioctl.h>
6580
6581static CFStringRef
6582S_copy_ssid_bssid(CFStringRef ifname, struct ether_addr * ap_mac)
6583{
6584    Apple80211Err	error;
6585    CFMutableDataRef	ssid;
6586    CFStringRef		ssid_str = NULL;
6587    Apple80211Ref	wref;
6588
6589    error = Apple80211Open(&wref);
6590    if (error != kA11NoErr) {
6591	my_log(LOG_DEBUG, "Apple80211Open failed, 0x%x");
6592	return (NULL);
6593    }
6594    error = Apple80211BindToInterface(wref, ifname);
6595    if (error != kA11NoErr) {
6596	goto done;
6597    }
6598    ssid = CFDataCreateMutable(kCFAllocatorDefault, 0);
6599    if (Apple80211Get((Apple80211Ref)wref, APPLE80211_IOC_SSID, 0,
6600		      ssid, 0) == kA11NoErr) {
6601	ssid_str = CFStringCreateWithBytes(NULL,
6602					   CFDataGetBytePtr(ssid),
6603					   CFDataGetLength(ssid),
6604					   kCFStringEncodingUTF8,
6605					   FALSE);
6606	if (ssid_str == NULL) {
6607	    ssid_str = CFStringCreateWithBytes(NULL,
6608					       CFDataGetBytePtr(ssid),
6609					       CFDataGetLength(ssid),
6610					       kCFStringEncodingMacRoman,
6611					       FALSE);
6612	}
6613    }
6614    CFRelease(ssid);
6615    if (ap_mac != NULL) {
6616	(void)Apple80211Get((Apple80211Ref)wref, APPLE80211_IOC_BSSID, 0,
6617			    ap_mac, sizeof(*ap_mac));
6618    }
6619
6620 done:
6621    Apple80211Close(wref);
6622    return (ssid_str);
6623}
6624
6625#else /* NO_WIRELESS */
6626
6627static CFStringRef
6628S_copy_ssid_bssid(CFStringRef ifname, struct ether_addr * ap_mac)
6629{
6630    return (NULL);
6631}
6632
6633#endif /* NO_WIRELESS */
6634
6635STATIC void
6636process_link_timer_expired(IFStateRef ifstate)
6637{
6638    my_log(LOG_DEBUG, "%s: link inactive timer fired",
6639	   if_name(ifstate->if_p));
6640    service_list_event(&ifstate->services,
6641		       IFEventID_link_timer_expired_e, NULL);
6642    service_list_event(&ifstate->services_v6,
6643		       IFEventID_link_timer_expired_e, NULL);
6644    if (dynarray_count(&ifstate->services_v6) != 0) {
6645	(void)inet6_linklocal_stop(if_name(ifstate->if_p));
6646    }
6647    return;
6648}
6649
6650static void
6651link_timer_expired(void * arg0, void * arg1, void * arg2)
6652{
6653    process_link_timer_expired(arg0);
6654    return;
6655}
6656
6657static void
6658ap_key_changed(SCDynamicStoreRef session, CFStringRef cache_key)
6659{
6660    struct ether_addr		bssid;
6661    interface_t *               if_p;
6662    CFStringRef                 ifn_cf;
6663    IFStateRef                  ifstate;
6664    link_status_t               link;
6665    uint32_t			j;
6666    CFStringRef 		ssid;
6667    boolean_t			wireless_did_roam;
6668
6669    if (CFStringHasPrefix(cache_key, S_state_interface_prefix) == FALSE) {
6670	return;
6671    }
6672
6673    /* State:/Network/Interface/<ifname>/Airport */
6674    ifn_cf = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
6675    if (ifn_cf == NULL) {
6676	return;
6677    }
6678    ifstate = IFStateListGetIFState(&S_ifstate_list, ifn_cf, NULL);
6679    my_CFRelease(&ifn_cf);
6680    if (ifstate == NULL || ifstate->netboot) {
6681	/* don't propagate media status events for netboot interface */
6682	goto done;
6683    }
6684    if_p = ifl_find_name(S_interfaces, if_name(ifstate->if_p));
6685    if (if_p == NULL) {
6686	/* interface doesn't exist */
6687	goto done;
6688    }
6689    link = if_link_status_update(if_p);
6690    if (if_is_wireless(ifstate->if_p) == FALSE) {
6691	goto done;
6692    }
6693    if ((link.valid && !link.active)
6694	|| (ifstate->if_p->link_status.valid
6695	    && !ifstate->if_p->link_status.active)) {
6696	/*
6697	 * Link is/was down, no need to handle it now.  This will be handled
6698	 * when the next link up event comes in.
6699	 */
6700	goto done;
6701    }
6702    ssid = S_copy_ssid_bssid(ifstate->ifname, &bssid);
6703    if (G_IPConfiguration_verbose) {
6704	if (ssid != NULL) {
6705	    my_log(LOG_DEBUG, "%s: SSID %@ BSSID %s",
6706		   if_name(if_p), ssid, ether_ntoa(&bssid));
6707	}
6708	else {
6709	    my_log(LOG_DEBUG, "%s: no SSID", if_name(if_p));
6710	}
6711    }
6712
6713    /* check whether just the bssid has changed i.e. we roamed */
6714    wireless_did_roam = IFState_wireless_did_roam(ifstate, ssid, &bssid);
6715    my_CFRelease(&ssid);
6716    if (!wireless_did_roam) {
6717	goto done;
6718    }
6719
6720    /* update just the BSSID */
6721    IFState_set_bssid(ifstate, &bssid);
6722
6723    /* Notify v4 services */
6724    for (j = 0; j < dynarray_count(&ifstate->services); j++) {
6725	ServiceRef      service_p = dynarray_element(&ifstate->services, j);
6726
6727	config_method_bssid_changed(service_p);
6728    }
6729
6730    /* Notify v6 services */
6731    service_list_event(&ifstate->services_v6, IFEventID_bssid_changed_e,
6732		       NULL);
6733
6734 done:
6735    return;
6736}
6737
6738static void
6739link_key_changed(SCDynamicStoreRef session, CFStringRef cache_key)
6740{
6741    CFDictionaryRef		dict = NULL;
6742    interface_t *		if_p;
6743    CFStringRef			ifn_cf;
6744    char			ifn[IFNAMSIZ + 1];
6745    IFStateRef   		ifstate;
6746    int 			j;
6747    link_status_t		link_status;
6748    boolean_t			link_address_changed = FALSE;
6749    void *			network_changed = NULL;
6750
6751    if (CFStringHasPrefix(cache_key, S_state_interface_prefix) == FALSE) {
6752	return;
6753    }
6754    /* State:/Network/Interface/<ifname>/Link */
6755    ifn_cf = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
6756    if (ifn_cf == NULL) {
6757	return;
6758    }
6759    my_CFStringToCStringAndLength(ifn_cf, ifn, sizeof(ifn));
6760    my_CFRelease(&ifn_cf);
6761    ifstate = IFStateList_ifstate_with_name(&S_ifstate_list, ifn, NULL);
6762    dict = my_SCDynamicStoreCopyDictionary(session, cache_key);
6763    if (dict != NULL) {
6764	if (CFDictionaryContainsKey(dict, kSCPropNetLinkDetaching)) {
6765	    if (ifstate != NULL) {
6766		IFStateFreeIPv4Services(ifstate, TRUE);
6767		IFStateFreeIPv6Services(ifstate, TRUE);
6768	    }
6769	    goto done;
6770	}
6771    }
6772    if_p = ifl_find_name(S_interfaces, ifn);
6773    if (if_p == NULL) {
6774	/* interface doesn't exist */
6775	goto done;
6776    }
6777    link_status = if_link_status_update(if_p);
6778    if (link_status.valid) {
6779	/* make sure address information is up to date */
6780	link_address_changed = if_link_update(if_p);
6781    }
6782    if (ifstate == NULL || ifstate->netboot) {
6783	/* don't propagate media status events for netboot interface */
6784	goto done;
6785    }
6786    if_link_copy(ifstate->if_p, if_p);
6787    if (G_IPConfiguration_verbose) {
6788	if (link_status.valid == FALSE) {
6789	    my_log(LOG_DEBUG, "%s link is unknown", ifn);
6790	}
6791	else {
6792	    my_log(LOG_DEBUG, "%s link %s%s%s",
6793		   ifn, link_status.active ? "ACTIVE" : "INACTIVE",
6794		   link_address_changed ? " [link address changed]" : "",
6795		   link_status.wake_on_same_network
6796		    ? " [wake on same network]" : "");
6797	}
6798    }
6799    if (link_address_changed == FALSE
6800	&& S_wake_generation != ifstate->wake_generation) {
6801	my_log(LOG_DEBUG,
6802	       "%s: link status changed at wake", ifn);
6803	S_ifstate_process_wake(ifstate);
6804	if (link_status.wake_on_same_network) {
6805	    goto done;
6806	}
6807    }
6808    if (if_is_wireless(ifstate->if_p)) {
6809	struct ether_addr	bssid;
6810	CFStringRef		ssid;
6811
6812	ssid = S_copy_ssid_bssid(ifstate->ifname, &bssid);
6813	if (G_IPConfiguration_verbose) {
6814	    if (ssid != NULL) {
6815		my_log(LOG_DEBUG, "%s: SSID %@ BSSID %s",
6816		       ifn, ssid, ether_ntoa(&bssid));
6817	    }
6818	    else {
6819		my_log(LOG_DEBUG, "%s: no SSID", ifn);
6820	    }
6821	}
6822	if (ssid != NULL) {
6823	    if (ifstate->ssid != NULL
6824		&& !CFEqual(ssid, ifstate->ssid)) {
6825		network_changed = (void *)1;
6826	    }
6827	    /* remember the last ssid */
6828	    IFState_set_ssid_bssid(ifstate, ssid, &bssid);
6829	    CFRelease(ssid);
6830	}
6831    }
6832    if (link_status.valid && link_status.active == FALSE) {
6833	if (S_awake == FALSE) {
6834	    if (G_IPConfiguration_verbose) {
6835		my_log(LOG_DEBUG,
6836		       "%s: suppressing link inactive timer (going to sleep)",
6837		       ifn);
6838	    }
6839	    timer_cancel(ifstate->timer);
6840	    ifstate->link_timer_suppressed = TRUE;
6841	}
6842	else {
6843	    ifstate->link_timer_suppressed = FALSE;
6844	    if (G_IPConfiguration_verbose) {
6845		my_log(LOG_DEBUG,
6846		       "%s: scheduling link inactive timer for %d.%06d secs",
6847		       ifn,
6848		       S_link_inactive_secs.tv_sec,
6849		       S_link_inactive_secs.tv_usec);
6850	    }
6851	    timer_set_relative(ifstate->timer,
6852			       S_link_inactive_secs, link_timer_expired,
6853			       ifstate, NULL, NULL);
6854	}
6855    }
6856    else {
6857	ifstate->link_timer_suppressed = FALSE;
6858	timer_cancel(ifstate->timer);
6859    }
6860    for (j = 0; j < dynarray_count(&ifstate->services); j++) {
6861	ServiceRef	service_p = dynarray_element(&ifstate->services, j);
6862
6863	config_method_media(service_p, network_changed);
6864    }
6865    if (if_ift_type(ifstate->if_p) != IFT_STF) {
6866	if (link_address_changed) {
6867	    /* first stop IPv6 link-local */
6868	    my_log(LOG_DEBUG, "%s: link address changed", if_name(if_p));
6869	    (void)inet6_linklocal_stop(if_name(if_p));
6870	}
6871	if ((link_status.valid == FALSE || link_status.active)
6872	    && dynarray_count(&ifstate->services_v6) != 0) {
6873	    /* start IPv6 link-local */
6874	    (void)inet6_linklocal_start(if_name(if_p), !if_is_awdl(if_p));
6875	}
6876    }
6877    service_list_event(&ifstate->services_v6, IFEventID_link_status_changed_e,
6878		       (void *)network_changed);
6879 done:
6880    my_CFRelease(&dict);
6881    return;
6882}
6883
6884static void
6885arp_collision(SCDynamicStoreRef session, CFStringRef cache_key)
6886{
6887    arp_collision_data_t	evdata;
6888    void *			hwaddr = NULL;
6889    int				hwlen;
6890    CFStringRef			ifn_cf = NULL;
6891    struct in_addr		ip_addr;
6892    IFStateRef   		ifstate;
6893
6894    ifn_cf = IPv4ARPCollisionKeyParse(cache_key, &ip_addr, &hwaddr, &hwlen);
6895    if (ifn_cf == NULL || hwaddr == NULL) {
6896	goto done;
6897    }
6898    ifstate = IFStateListGetIFState(&S_ifstate_list, ifn_cf, NULL);
6899    if (ifstate == NULL || ifstate->netboot) {
6900	/* don't propogate collision events for netboot interface */
6901	goto done;
6902    }
6903    if (S_is_our_hardware_address(NULL, if_link_arptype(ifstate->if_p),
6904				  hwaddr, hwlen)) {
6905	goto done;
6906    }
6907    evdata.ip_addr = ip_addr;
6908    evdata.hwaddr = hwaddr;
6909    evdata.hwlen = hwlen;
6910    evdata.is_sleep_proxy
6911	= S_is_sleep_proxy_hardware_address(session, ifstate, hwaddr, hwlen);
6912    service_list_event(&ifstate->services, IFEventID_arp_collision_e, &evdata);
6913
6914 done:
6915    if (hwaddr != NULL) {
6916	free(hwaddr);
6917    }
6918    my_CFRelease(&ifn_cf);
6919    return;
6920}
6921
6922static void
6923dhcp_preferences_changed(SCPreferencesRef prefs,
6924			 SCPreferencesNotification type,
6925			 void * info)
6926{
6927    int i;
6928
6929    if ((type & kSCPreferencesNotificationApply) == 0) {
6930	return;
6931    }
6932
6933    /* merge in the new requested parameters */
6934    S_add_dhcp_parameters(prefs);
6935    SCPreferencesSynchronize(prefs);
6936
6937    for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
6938	IFStateRef	ifstate = dynarray_element(&S_ifstate_list, i);
6939	int		j;
6940
6941	/* ask each service to renew immediately to pick up new options */
6942	for (j = 0; j < dynarray_count(&ifstate->services); j++) {
6943	    ServiceRef	service_p = dynarray_element(&ifstate->services, j);
6944
6945	    config_method_renew(service_p);
6946	}
6947    }
6948    return;
6949}
6950
6951static void
6952handle_change(SCDynamicStoreRef session, CFArrayRef changes, void * arg)
6953{
6954    boolean_t		config_changed = FALSE;
6955    CFIndex		count;
6956    CFIndex		i;
6957    boolean_t		iflist_changed = FALSE;
6958    boolean_t		name_changed = FALSE;
6959    boolean_t		order_changed = FALSE;
6960    CFStringRef		setup_global_ipv4_key = NULL;
6961    CFMutableArrayRef	state_changes = NULL;
6962
6963    count = CFArrayGetCount(changes);
6964    if (count == 0) {
6965	goto done;
6966    }
6967    if (G_IPConfiguration_verbose) {
6968	my_log(LOG_DEBUG, "Changes: %@ (%d)", changes, count);
6969    }
6970    for (i = 0; i < count; i++) {
6971	CFStringRef	cache_key = CFArrayGetValueAtIndex(changes, i);
6972
6973	if (CFEqual(cache_key, S_computer_name_key)
6974	    || CFEqual(cache_key, S_hostnames_key)) {
6975	    name_changed = TRUE;
6976	}
6977        else if (CFStringHasPrefix(cache_key, kSCDynamicStoreDomainSetup)) {
6978	    if (setup_global_ipv4_key == NULL) {
6979		setup_global_ipv4_key
6980		    = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
6981								 kSCDynamicStoreDomainSetup,
6982								 kSCEntNetIPv4);
6983	    }
6984	    if (CFEqual(cache_key, setup_global_ipv4_key)) {
6985		/* service order may have changed */
6986		order_changed = TRUE;
6987	    }
6988	    /* IPv4 configuration changed */
6989	    config_changed = TRUE;
6990	}
6991	else if (CFStringHasSuffix(cache_key, kSCCompInterface)) {
6992	    /* list of interfaces changed */
6993	    iflist_changed = TRUE;
6994	}
6995	else {
6996	    if (state_changes == NULL) {
6997		state_changes
6998		    = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
6999	    }
7000	    CFArrayAppendValue(state_changes, cache_key);
7001	}
7002    }
7003    /* the computer name changed */
7004    if (name_changed) {
7005	computer_name_update(session);
7006    }
7007    /* an interface was added/removed */
7008    if (iflist_changed) {
7009	if (update_interface_list()) {
7010	    config_changed = TRUE;
7011	    check_for_detached_interfaces();
7012	}
7013    }
7014    /* configuration changed */
7015    if (config_changed) {
7016	configuration_changed(session);
7017    }
7018    /* look through remaining State: key changes */
7019    if (state_changes != NULL) {
7020	count = CFArrayGetCount(state_changes);
7021    }
7022    else {
7023	count = 0;
7024    }
7025    for (i = 0; i < count; i++) {
7026	CFStringRef	cache_key = CFArrayGetValueAtIndex(state_changes, i);
7027
7028	if (CFStringHasSuffix(cache_key, kSCEntNetLink)) {
7029	    link_key_changed(session, cache_key);
7030	}
7031	else if (CFStringHasSuffix(cache_key, kSCEntNetAirPort)) {
7032	    ap_key_changed(session, cache_key);
7033	}
7034	else if (CFStringHasSuffix(cache_key, kSCEntNetRefreshConfiguration)) {
7035	    link_refresh(session, cache_key);
7036	}
7037	else if (CFStringHasSuffix(cache_key, kSCEntNetIPv6)) {
7038	    ipv6_interface_address_changed(session, cache_key);
7039	}
7040	else if (CFStringHasSuffix(cache_key,
7041				   kSCEntNetInterfaceActiveDuringSleepRequested)) {
7042	    ActiveDuringSleepRequestedKeyChanged(session, cache_key);
7043	}
7044
7045	else {
7046	    CFRange 	range = CFRangeMake(0, CFStringGetLength(cache_key));
7047
7048	    if (CFStringFindWithOptions(cache_key, kSCEntNetIPv4ARPCollision,
7049					range, 0, NULL)) {
7050		arp_collision(session, cache_key);
7051	    }
7052	}
7053    }
7054
7055    /* service order may have changed */
7056    if (order_changed) {
7057	linklocal_set_needs_attention();
7058    }
7059
7060 done:
7061    my_CFRelease(&setup_global_ipv4_key);
7062    my_CFRelease(&state_changes);
7063    return;
7064}
7065
7066#if ! TARGET_OS_EMBEDDED
7067
7068static void
7069user_confirm(CFUserNotificationRef userNotification,
7070	     CFOptionFlags response_flags)
7071{
7072    int 	i;
7073
7074    /* clean-up the notification */
7075    for (i = 0; i < dynarray_count(&S_ifstate_list); i++) {
7076	IFStateRef	ifstate = dynarray_element(&S_ifstate_list, i);
7077	int		j;
7078
7079	for (j = 0; j < dynarray_count(&ifstate->services); j++) {
7080	    ServiceRef service_p = dynarray_element(&ifstate->services, j);
7081	    if (service_p->user_notification == userNotification) {
7082		CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
7083				      service_p->user_rls,
7084				      kCFRunLoopDefaultMode);
7085		my_CFRelease(&service_p->user_rls);
7086		my_CFRelease(&service_p->user_notification);
7087		return;
7088	    }
7089	}
7090    }
7091    return;
7092}
7093
7094static CFURLRef
7095copy_icon_url(CFStringRef icon)
7096{
7097    CFBundleRef		np_bundle;
7098    CFURLRef		np_url;
7099    CFURLRef		url = NULL;
7100
7101#define kNetworkPrefPanePath	"/System/Library/PreferencePanes/Network.prefPane"
7102    np_url = CFURLCreateWithFileSystemPath(NULL,
7103					   CFSTR(kNetworkPrefPanePath),
7104					   kCFURLPOSIXPathStyle, FALSE);
7105    if (np_url != NULL) {
7106	np_bundle = CFBundleCreate(NULL, np_url);
7107	if (np_bundle != NULL) {
7108	    url = CFBundleCopyResourceURL(np_bundle, icon,
7109					  CFSTR("icns"), NULL);
7110	    CFRelease(np_bundle);
7111	}
7112	CFRelease(np_url);
7113    }
7114    return (url);
7115}
7116
7117void
7118ServiceRemoveAddressConflict(ServiceRef service_p)
7119{
7120    if (service_p->user_rls != NULL) {
7121	CFRunLoopRemoveSource(CFRunLoopGetCurrent(), service_p->user_rls,
7122			      kCFRunLoopDefaultMode);
7123	my_CFRelease(&service_p->user_rls);
7124    }
7125    if (service_p->user_notification != NULL) {
7126	CFUserNotificationCancel(service_p->user_notification);
7127	my_CFRelease(&service_p->user_notification);
7128    }
7129    return;
7130}
7131
7132static void
7133service_notify_user(ServiceRef service_p, CFArrayRef header,
7134		    CFStringRef message)
7135{
7136    CFMutableDictionaryRef	dict;
7137    SInt32			error;
7138    CFURLRef			icon_url;
7139    CFUserNotificationRef 	notify;
7140    CFRunLoopSourceRef		rls;
7141    CFURLRef			url;
7142
7143    dict = CFDictionaryCreateMutable(NULL, 0,
7144				     &kCFTypeDictionaryKeyCallBacks,
7145				     &kCFTypeDictionaryValueCallBacks);
7146    CFDictionarySetValue(dict, kCFUserNotificationAlertHeaderKey,
7147			 header);
7148    CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey,
7149			 message);
7150    url = CFBundleCopyBundleURL(S_bundle);
7151    CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey,
7152			 url);
7153    CFRelease(url);
7154    icon_url = copy_icon_url(CFSTR("Network"));
7155    if (icon_url != NULL) {
7156	CFDictionarySetValue(dict, kCFUserNotificationIconURLKey,
7157			     icon_url);
7158	CFRelease(icon_url);
7159    }
7160    ServiceRemoveAddressConflict(service_p);
7161    CFDictionaryAddValue(dict,
7162			 kCFUserNotificationHelpAnchorKey,
7163			 CFSTR("mh27606"));
7164    CFDictionaryAddValue(dict,
7165			 kCFUserNotificationHelpBookKey,
7166			 CFSTR("com.apple.machelp"));
7167    CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey,
7168			 CFSTR("OK"));
7169    notify = CFUserNotificationCreate(NULL, 0, 0, &error, dict);
7170    CFRelease(dict);
7171    if (notify == NULL) {
7172	my_log(LOG_ERR, "CFUserNotificationCreate() failed, %d",
7173	       error);
7174	return;
7175    }
7176    rls = CFUserNotificationCreateRunLoopSource(NULL, notify,
7177						user_confirm, 0);
7178    if (rls == NULL) {
7179	my_log(LOG_ERR, "CFUserNotificationCreateRunLoopSource() failed");
7180	my_CFRelease(&notify);
7181    }
7182    else {
7183	CFRunLoopAddSource(CFRunLoopGetCurrent(), rls,
7184			   kCFRunLoopDefaultMode);
7185	service_p->user_rls = rls;
7186	service_p->user_notification = notify;
7187    }
7188    return;
7189}
7190
7191static void
7192service_report_conflict(ServiceRef service_p, CFStringRef ip_str)
7193{
7194    CFArrayRef		array = NULL;
7195    const void *	values[3];
7196
7197    /*
7198     * CONFLICT_HEADER_BEFORE_IP, CONFLICT_HEADER_AFTER_IP
7199     *
7200     * Unfortunately, we can't just use a format string with %@ because
7201     * the entity that displays the CFUserNotification (CFUN) needs to be able
7202     * to localize the alert strings based on the logged in user's localization.
7203     * If we localize the string here with variable data (the IP address),
7204     * there's no way for the CFUN to localize the string.
7205     *
7206     * The ugly work-around is to break the string into localizable pieces,
7207     * in this case, the string before the IP address, and the string after
7208     * the IP address.
7209     *
7210     * We pass the three pieces { BEFORE_IP, ip_str, AFTER_IP } as an array
7211     * to the CFUN.  It will only be able to localize BEFORE_IP and AFTER_IP.
7212     */
7213    values[0] = CFSTR("CONFLICT_HEADER_BEFORE_IP");
7214    values[1] = ip_str;
7215    values[2] = CFSTR("CONFLICT_HEADER_AFTER_IP");
7216    array = CFArrayCreate(NULL, (const void **)values, 3,
7217			  &kCFTypeArrayCallBacks);
7218    service_notify_user(service_p, array, CFSTR("CONFLICT_MESSAGE"));
7219    CFRelease(array);
7220    return;
7221}
7222
7223void
7224ServiceReportIPv4AddressConflict(ServiceRef service_p, struct in_addr addr)
7225{
7226    CFStringRef         ip_str = NULL;
7227
7228    ip_str = my_CFStringCreateWithIPAddress(addr);
7229    service_report_conflict(service_p, ip_str);
7230    CFRelease(ip_str);
7231    return;
7232}
7233
7234void
7235ServiceReportIPv6AddressConflict(ServiceRef service_p,
7236				 const struct in6_addr * addr_p)
7237{
7238    CFStringRef         ip_str = NULL;
7239
7240    ip_str = my_CFStringCreateWithIPv6Address(addr_p);
7241    service_report_conflict(service_p, ip_str);
7242    CFRelease(ip_str);
7243    return;
7244}
7245
7246#endif /* TARGET_OS_EMBEDDED */
7247
7248PRIVATE_EXTERN CFStringRef
7249ServiceCopyWakeID(ServiceRef service_p)
7250{
7251    IFStateRef		ifstate = service_ifstate(service_p);
7252    const char *	method_string;
7253
7254#define WAKE_ID_FORMAT		WAKE_ID_PREFIX ".%s.%s"
7255    method_string = ipconfig_method_string(service_p->method);
7256    return (CFStringCreateWithFormat(NULL, NULL, CFSTR(WAKE_ID_FORMAT),
7257				     if_name(ifstate->if_p),
7258				     method_string));
7259}
7260
7261
7262/**
7263 ** Routines to read configuration and convert from CF types to
7264 ** simple types.
7265 **/
7266static boolean_t
7267S_get_plist_boolean_quiet(CFDictionaryRef plist, CFStringRef key,
7268			  boolean_t def)
7269{
7270    CFBooleanRef 	b;
7271    boolean_t		ret = def;
7272
7273    b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
7274    if (b) {
7275	ret = CFBooleanGetValue(b);
7276    }
7277    return (ret);
7278}
7279
7280static boolean_t
7281S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key,
7282		    boolean_t def)
7283{
7284    boolean_t	ret;
7285    ret = S_get_plist_boolean_quiet(plist, key, def);
7286    if (G_IPConfiguration_verbose) {
7287	my_log(LOG_DEBUG,
7288	       "%@ = %s", key, ret == TRUE ? "true" : "false");
7289    }
7290    return (ret);
7291}
7292
7293static int
7294S_get_plist_int_quiet(CFDictionaryRef plist, CFStringRef key,
7295		      int def)
7296{
7297    CFNumberRef 	n;
7298    int			ret = def;
7299
7300    n = isA_CFNumber(CFDictionaryGetValue(plist, key));
7301    if (n) {
7302	if (CFNumberGetValue(n, kCFNumberIntType, &ret) == FALSE) {
7303	    ret = def;
7304	}
7305    }
7306    return (ret);
7307}
7308
7309static int
7310S_get_plist_int(CFDictionaryRef plist, CFStringRef key,
7311		int def)
7312{
7313    int		ret;
7314
7315    ret = S_get_plist_int_quiet(plist, key, def);
7316    if (G_IPConfiguration_verbose) {
7317	my_log(LOG_DEBUG, "%@ = %d", key, ret);
7318    }
7319    return (ret);
7320}
7321
7322
7323#include <math.h>
7324static struct timeval
7325S_get_plist_timeval(CFDictionaryRef plist, CFStringRef key,
7326		    struct timeval def)
7327{
7328    CFNumberRef 	n;
7329    struct timeval	ret = def;
7330
7331    n = CFDictionaryGetValue(plist, key);
7332    if (n) {
7333	double	f;
7334
7335	if (CFNumberGetValue(n, kCFNumberDoubleType, &f) == TRUE) {
7336	    ret.tv_sec = (int)floor(f);
7337	    ret.tv_usec = (int)((f - floor(f)) * 1000000.0);
7338	}
7339    }
7340    if (G_IPConfiguration_verbose) {
7341	my_log(LOG_DEBUG,
7342	       "%@ = %d.%06d", key, ret.tv_sec,
7343	       ret.tv_usec);
7344    }
7345    return (ret);
7346}
7347
7348static void *
7349S_get_number_array(CFArrayRef arr, int num_size, int * ret_count)
7350{
7351    void *	buf = NULL;
7352    CFIndex	count;
7353    int 	i;
7354    int 	real_count = 0;
7355    void *	scan;
7356
7357    switch (num_size) {
7358    case 1:
7359    case 2:
7360    case 4:
7361	break;
7362    default:
7363	goto done;
7364    }
7365    count = CFArrayGetCount(arr);
7366    if (count == 0) {
7367	goto done;
7368    }
7369    buf = malloc(count * num_size);
7370    if (buf == NULL) {
7371	goto done;
7372    }
7373    for (i = 0, real_count = 0, scan = buf; i < count; i++) {
7374	CFNumberRef	n = isA_CFNumber(CFArrayGetValueAtIndex(arr, i));
7375	int		val;
7376
7377	if (n == NULL
7378	    || CFNumberGetValue(n, kCFNumberIntType, &val) == FALSE) {
7379	    continue;
7380	}
7381	switch (num_size) {
7382	case 1:
7383	    *((uint8_t *)scan) = val;
7384	    break;
7385	case 2:
7386	    *((uint16_t *)scan) = val;
7387	    break;
7388	case 4:
7389	    *((uint32_t *)scan) = val;
7390	    break;
7391	default:
7392	    break;
7393	}
7394	real_count++;
7395	scan += num_size;
7396    }
7397 done:
7398    *ret_count = real_count;
7399    if (real_count == 0 && buf != NULL) {
7400	free(buf);
7401	buf = NULL;
7402    }
7403    return (buf);
7404}
7405
7406static void *
7407S_get_plist_number_array(CFDictionaryRef plist, CFStringRef key,
7408			 int num_size, int * ret_count)
7409{
7410    CFArrayRef	a;
7411
7412    a = isA_CFArray(CFDictionaryGetValue(plist, key));
7413    if (a == NULL) {
7414	return (NULL);
7415    }
7416    return (S_get_number_array(a, num_size, ret_count));
7417}
7418
7419static uint8_t *
7420S_get_plist_uint8_array(CFDictionaryRef plist, CFStringRef key,
7421			int * ret_count)
7422{
7423    return (S_get_plist_number_array(plist, key, sizeof(uint8_t), ret_count));
7424}
7425
7426static uint16_t *
7427S_get_plist_uint16_array(CFDictionaryRef plist, CFStringRef key,
7428			 int * ret_count)
7429{
7430    return (S_get_plist_number_array(plist, key, sizeof(uint16_t), ret_count));
7431}
7432
7433static void
7434my_CFNumberAddUniqueToArray(CFNumberRef number, CFMutableArrayRef merge)
7435{
7436    number = isA_CFNumber(number);
7437    if (number == NULL) {
7438	return;
7439    }
7440    my_CFArrayAppendUniqueValue(merge, number);
7441}
7442
7443static void
7444merge_arrays(const void *key, const void *value, void *context)
7445{
7446    CFArrayRef	        arr;
7447    CFDictionaryRef	dict;
7448    CFMutableArrayRef	merge = (CFMutableArrayRef)context;
7449
7450    dict = isA_CFDictionary(value);
7451    if (dict == NULL) {
7452	return;
7453    }
7454    arr = CFDictionaryGetValue(dict,
7455			       kDHCPRequestedParameterList);
7456    if (isA_CFArray(arr) == NULL) {
7457	return;
7458    }
7459    CFArrayApplyFunction(arr, CFRangeMake(0, CFArrayGetCount(arr)),
7460			 (CFArrayApplierFunction)my_CFNumberAddUniqueToArray,
7461			 merge);
7462    return;
7463}
7464
7465static CFArrayRef
7466applicationRequestedParametersCopy(SCPreferencesRef prefs)
7467{
7468    CFPropertyListRef data = NULL;
7469    CFMutableArrayRef array = NULL;
7470
7471    data = SCPreferencesGetValue(prefs, kDHCPClientApplicationPref);
7472    if (isA_CFDictionary(data) == NULL) {
7473	goto done;
7474    }
7475    if (G_IPConfiguration_verbose) {
7476	my_log(LOG_DEBUG, "dictionary is %@", data);
7477    }
7478    array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
7479    if (array == NULL) {
7480	goto done;
7481    }
7482    CFDictionaryApplyFunction(data, merge_arrays, array);
7483    if (CFArrayGetCount(array) == 0) {
7484	CFRelease(array);
7485	array = NULL;
7486    }
7487
7488 done:
7489    return (array);
7490}
7491
7492static void
7493S_set_globals(void)
7494{
7495    uint8_t *		dhcp_params = NULL;
7496    CFDictionaryRef 	info_dict;
7497    int			n_dhcp_params = 0;
7498    CFDictionaryRef	plist;
7499
7500    if (S_bundle == NULL) {
7501	return;
7502    }
7503    info_dict = CFBundleGetInfoDictionary(S_bundle);
7504    if (info_dict == NULL) {
7505	return;
7506    }
7507    plist = CFDictionaryGetValue(info_dict, CFSTR("IPConfiguration"));
7508    if (isA_CFDictionary(plist) == NULL) {
7509	return;
7510    }
7511    G_must_broadcast
7512	= S_get_plist_boolean(plist, CFSTR("MustBroadcast"), FALSE);
7513    G_max_retries = S_get_plist_int(plist, CFSTR("RetryCount"),
7514				    MAX_RETRIES);
7515    G_gather_secs = S_get_plist_int(plist, CFSTR("GatherTimeSeconds"),
7516				    GATHER_SECS);
7517    S_link_inactive_secs
7518	= S_get_plist_timeval(plist, CFSTR("LinkInactiveWaitTimeSeconds"),
7519			      S_link_inactive_secs);
7520    G_initial_wait_secs
7521	= S_get_plist_int(plist, CFSTR("InitialRetryTimeSeconds"),
7522			  INITIAL_WAIT_SECS);
7523    G_max_wait_secs
7524	= S_get_plist_int(plist, CFSTR("MaximumRetryTimeSeconds"),
7525			  MAX_WAIT_SECS);
7526    S_arp_probe_count
7527	= S_get_plist_int(plist, CFSTR("ARPProbeCount"),
7528			  ARP_PROBE_COUNT);
7529    S_arp_gratuitous_count
7530	= S_get_plist_int(plist, CFSTR("ARPGratuitousCount"),
7531			  ARP_GRATUITOUS_COUNT);
7532    S_arp_retry
7533	= S_get_plist_timeval(plist, CFSTR("ARPRetryTimeSeconds"),
7534			      S_arp_retry);
7535    S_arp_detect_count
7536	= S_get_plist_int(plist, CFSTR("ARPDetectCount"),
7537			  ARP_DETECT_COUNT);
7538    S_arp_detect_retry
7539	= S_get_plist_timeval(plist, CFSTR("ARPDetectRetryTimeSeconds"),
7540			      S_arp_detect_retry);
7541    S_arp_resolve_retry
7542	= S_get_plist_timeval(plist, CFSTR("ARPResolveRetryTimeSeconds"),
7543			      S_arp_resolve_retry);
7544    G_dhcp_accepts_bootp
7545	= S_get_plist_boolean(plist, CFSTR("DHCPAcceptsBOOTP"), FALSE);
7546    G_dhcp_failure_configures_linklocal
7547	= S_get_plist_boolean(plist,
7548			      CFSTR("DHCPFailureConfiguresLinkLocal"),
7549			      DHCP_FAILURE_CONFIGURES_LINKLOCAL);
7550    G_dhcp_success_deconfigures_linklocal
7551	= S_get_plist_boolean(plist,
7552			      CFSTR("DHCPSuccessDeconfiguresLinkLocal"),
7553			      DHCP_SUCCESS_DECONFIGURES_LINKLOCAL);
7554    G_dhcp_init_reboot_retry_count
7555	= S_get_plist_int(plist, CFSTR("DHCPInitRebootRetryCount"),
7556			  DHCP_INIT_REBOOT_RETRY_COUNT);
7557    G_dhcp_select_retry_count
7558	= S_get_plist_int(plist, CFSTR("DHCPSelectRetryCount"),
7559			  DHCP_SELECT_RETRY_COUNT);
7560    G_dhcp_allocate_linklocal_at_retry_count
7561	= S_get_plist_int(plist, CFSTR("DHCPAllocateLinkLocalAtRetryCount"),
7562			  DHCP_ALLOCATE_LINKLOCAL_AT_RETRY_COUNT);
7563    G_dhcp_router_arp_at_retry_count
7564	= S_get_plist_int(plist, CFSTR("DHCPRouterARPAtRetryCount"),
7565			  DHCP_ROUTER_ARP_AT_RETRY_COUNT);
7566    dhcp_params
7567	= S_get_plist_uint8_array(plist,
7568				  kDHCPRequestedParameterList,
7569				  &n_dhcp_params);
7570    dhcp_set_default_parameters(dhcp_params, n_dhcp_params);
7571    G_router_arp
7572	= S_get_plist_boolean(plist, CFSTR("RouterARPEnabled"), TRUE);
7573
7574    G_router_arp_wifi_lease_start_threshold_secs
7575	= S_get_plist_int(plist,
7576			  CFSTR("RouterARPWiFiLeaseStartThresholdSeconds"),
7577			  G_router_arp_wifi_lease_start_threshold_secs);
7578
7579    S_dhcp_local_hostname_length_max
7580	= S_get_plist_int(plist, CFSTR("DHCPLocalHostNameLengthMax"),
7581			  DHCP_LOCAL_HOSTNAME_LENGTH_MAX);
7582    G_discover_and_publish_router_mac_address
7583	= S_get_plist_boolean(plist,
7584			      CFSTR("DiscoverAndPublishRouterMACAddress"),
7585			      TRUE);
7586    S_discover_router_mac_address_secs
7587	= S_get_plist_int(plist,
7588			  CFSTR("DiscoverRouterMACAddressTimeSeconds"),
7589			  DISCOVER_ROUTER_MAC_ADDRESS_SECS);
7590    S_defend_ip_address_interval_secs
7591	= S_get_plist_int(plist,
7592			  CFSTR("DefendIPAddressIntervalSeconds"),
7593			  DEFEND_IP_ADDRESS_INTERVAL_SECS);
7594    S_defend_ip_address_count
7595	= S_get_plist_int(plist,
7596			  CFSTR("DefendIPAddressCount"),
7597			  DEFEND_IP_ADDRESS_COUNT);
7598    G_dhcp_lease_write_t1_threshold_secs
7599	= S_get_plist_int(plist,
7600			  CFSTR("DHCPLeaseWriteT1ThresholdSeconds"),
7601			  DHCP_LEASE_WRITE_T1_THRESHOLD_SECS);
7602    S_arp_conflict_retry
7603	= S_get_plist_int(plist,
7604			  CFSTR("ARPConflictRetryCount"),
7605			  ARP_CONFLICT_RETRY_COUNT);
7606    S_arp_conflict_delay
7607	= S_get_plist_timeval(plist, CFSTR("ARPConflictRetryDelaySeconds"),
7608			      S_arp_conflict_delay);
7609    G_manual_conflict_retry_interval_secs
7610	= S_get_plist_int(plist,
7611			  CFSTR("ManualConflictRetryIntervalSeconds"),
7612			  MANUAL_CONFLICT_RETRY_INTERVAL_SECS);
7613    G_min_short_wake_interval_secs
7614	= S_get_plist_int(plist,
7615			  CFSTR("MinimumShortWakeIntervalSeconds"),
7616			  MIN_SHORT_WAKE_INTERVAL_SECS);
7617    G_min_wake_interval_secs
7618	= S_get_plist_int(plist,
7619			  CFSTR("MinimumWakeIntervalSeconds"),
7620			  MIN_WAKE_INTERVAL_SECS);
7621    G_wake_skew_secs
7622	= S_get_plist_int(plist,
7623			  CFSTR("WakeSkewSeconds"),
7624			  WAKE_SKEW_SECS);
7625#if ! TARGET_OS_EMBEDDED
7626    S_use_maintenance_wake
7627	= S_get_plist_boolean(plist,
7628			      CFSTR("UseMaintenanceWake"),
7629			      TRUE);
7630#endif /* ! TARGET_OS_EMBEDDED */
7631    S_configure_ipv6 = S_get_plist_boolean(plist,
7632					   CFSTR("ConfigureIPv6"),
7633					   TRUE);
7634    if (S_configure_ipv6) {
7635	uint16_t *	dhcpv6_options;
7636	int		dhcpv6_options_count;
7637
7638	G_dhcpv6_enabled = S_get_plist_boolean(plist,
7639					       CFSTR("DHCPv6Enabled"),
7640					       DHCPv6_ENABLED);
7641	dhcpv6_options = S_get_plist_uint16_array(plist,
7642						  kDHCPv6RequestedOptions,
7643						  &dhcpv6_options_count);
7644	DHCPv6ClientSetRequestedOptions(dhcpv6_options,
7645					dhcpv6_options_count);
7646	G_dhcpv6_stateful_enabled = S_get_plist_boolean(plist,
7647							CFSTR("DHCPv6StatefulEnabled"),
7648							DHCPv6_STATEFUL_ENABLED);
7649	G_dhcp_duid_type = S_get_plist_int(plist,
7650					   CFSTR("DHCPDUIDType"),
7651					   kDHCPDUIDTypeLLT);
7652	switch (G_dhcp_duid_type) {
7653	case kDHCPDUIDTypeLLT:
7654	case kDHCPDUIDTypeLL:
7655	    /* supported */
7656	    break;
7657	default:
7658	    /* unsupported, use default (LLT) */
7659	    G_dhcp_duid_type = kDHCPDUIDTypeLLT;
7660	    break;
7661	}
7662    }
7663    return;
7664}
7665
7666static void
7667S_add_dhcp_parameters(SCPreferencesRef prefs)
7668{
7669    uint8_t *	dhcp_params = NULL;
7670    int		n_dhcp_params = 0;
7671    CFArrayRef	rp = applicationRequestedParametersCopy(prefs);
7672
7673    if (rp != NULL) {
7674	dhcp_params = S_get_number_array(rp, sizeof(*dhcp_params),
7675					 &n_dhcp_params);
7676	my_CFRelease(&rp);
7677    }
7678    dhcp_set_additional_parameters(dhcp_params, n_dhcp_params);
7679    return;
7680}
7681
7682STATIC void
7683check_verbose(SCPreferencesRef prefs)
7684{
7685    Boolean		verbose;
7686
7687    verbose = IPConfigurationControlPrefsGetVerbose();
7688    if (G_IPConfiguration_verbose != verbose) {
7689	G_IPConfiguration_verbose = verbose;
7690	if (verbose) {
7691	    IPConfigurationLogSetVerbose(verbose);
7692	    my_log(LOG_NOTICE, "IPConfiguration: verbose mode enabled");
7693	}
7694	else {
7695	    my_log(LOG_NOTICE, "IPConfiguration: verbose mode disabled");
7696	    IPConfigurationLogSetVerbose(verbose);
7697	}
7698	bootp_session_set_verbose(verbose);
7699	DHCPv6SocketSetVerbose(verbose);
7700    }
7701    IPConfigurationControlPrefsSynchronize();
7702    return;
7703}
7704
7705void
7706load(CFBundleRef bundle, Boolean bundleVerbose)
7707{
7708    S_bundle = (CFBundleRef)CFRetain(bundle);
7709    return;
7710}
7711
7712void
7713start(const char *bundleName, const char *bundleDir)
7714{
7715    arp_session_values_t	arp_values;
7716    SCPreferencesRef 		prefs = NULL;
7717
7718    /* register for verbose logging changes, check current verbose state */
7719    check_verbose(IPConfigurationControlPrefsInit(CFRunLoopGetCurrent(),
7720						  check_verbose));
7721    /* create paths */
7722    ipconfigd_create_paths();
7723
7724    /* initialize CGA */
7725    CGAInit();
7726
7727    /* set globals */
7728    S_set_globals();
7729    prefs = SCPreferencesCreate(NULL, CFSTR("IPConfiguration.DHCPClient"),
7730				kDHCPClientPreferencesID);
7731    if (prefs == NULL) {
7732	my_log(LOG_ERR,
7733	       "IPConfiguration: SCPreferencesCreate failed: %s",
7734	       SCErrorString(SCError()));
7735	return;
7736    }
7737    if (SCPreferencesSetCallback(prefs,
7738				 dhcp_preferences_changed,
7739				 NULL) == FALSE
7740	|| SCPreferencesScheduleWithRunLoop(prefs,
7741					    CFRunLoopGetCurrent(),
7742					    kCFRunLoopDefaultMode) == FALSE) {
7743	my_log(LOG_ERR, "IPConfigurationSCPreferencesSetCallback failed: %s",
7744	       SCErrorString(SCError()));
7745	my_CFRelease(&prefs);
7746	return;
7747    }
7748    S_add_dhcp_parameters(prefs);
7749    SCPreferencesSynchronize(prefs);
7750
7751    S_scd_session = SCDynamicStoreCreate(NULL,
7752					 CFSTR("IPConfiguration"),
7753					 handle_change, NULL);
7754    if (S_scd_session == NULL) {
7755	S_scd_session = NULL;
7756	my_log(LOG_ERR, "SCDynamicStoreCreate failed: %s",
7757	       SCErrorString(SCError()));
7758    }
7759
7760    G_bootp_session = bootp_session_init(G_client_port);
7761    if (G_bootp_session == NULL) {
7762	my_log(LOG_DEBUG, "bootp_session_init() failed");
7763	return;
7764    }
7765
7766    /* initialize the default values structure */
7767    bzero(&arp_values, sizeof(arp_values));
7768    arp_values.probe_count = &S_arp_probe_count;
7769    arp_values.probe_gratuitous_count = &S_arp_gratuitous_count;
7770    arp_values.probe_interval = &S_arp_retry;
7771    arp_values.detect_count = &S_arp_detect_count;
7772    arp_values.detect_interval = &S_arp_detect_retry;
7773    arp_values.conflict_retry_count = &S_arp_conflict_retry;
7774    arp_values.conflict_delay_interval = &S_arp_conflict_delay;
7775    arp_values.resolve_interval = &S_arp_resolve_retry;
7776    G_arp_session = arp_session_init(S_is_our_hardware_address,
7777				     &arp_values);
7778    if (G_arp_session == NULL) {
7779	my_log(LOG_DEBUG, "arp_session_init() failed");
7780	return;
7781    }
7782    dynarray_init(&S_ifstate_list, IFState_free, NULL);
7783
7784    CleanupWakeEvents();
7785
7786    /* set the loopback interface address */
7787    set_loopback();
7788    return;
7789}
7790
7791void
7792prime()
7793{
7794    if (G_bootp_session == NULL) {
7795	return;
7796    }
7797    if (S_scd_session == NULL) {
7798	update_interface_list();
7799    }
7800    else {
7801	/* begin interface initialization */
7802	start_initialization(S_scd_session);
7803    }
7804
7805    /* initialize the MiG server */
7806    server_init();
7807}
7808
7809void
7810stop(CFRunLoopSourceRef	stopRls)
7811{
7812    if (G_bootp_session != NULL) {
7813	IFStateList_all_services_event(&S_ifstate_list,
7814				       IFEventID_power_off_e, NULL);
7815    }
7816    CFRunLoopSourceSignal(stopRls);
7817}
7818