1/*
2 * Copyright (c) 2001-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * Modification History
26 *
27 * May 20, 2006			Joe Liu <joe.liu@apple.com>
28 *				Allan Nathanson <ajn@apple.com>
29 * - register interface by entryID (and not path)
30 *
31 * November 6, 2006		Allan Nathanson <ajn@apple.com>
32 *				Dan Markarian <markarian@apple.com>
33 *				Dieter Siegmund <dieter@apple.com>
34 * - updated code to name interfaces quicker (without need for
35 *   calling IOKitWaitQuiet).
36 *
37 * October 3, 2003		Allan Nathanson <ajn@apple.com>
38 * - sort new interfaces by IOKit path (rather than MAC address) to
39 *   help facilitate a more predictable interface-->name mapping for
40 *   like hardware configurations.
41 *
42 * June 23, 2001		Allan Nathanson <ajn@apple.com>
43 * - update to public SystemConfiguration.framework APIs
44 *
45 * January 23, 2001		Dieter Siegmund <dieter@apple.com>
46 * - initial revision
47 */
48
49/*
50 * ifnamer.c
51 * - module that receives IOKit Network Interface messages
52 *   and names any interface that currently does not have a name
53 * - uses Interface Type and MACAddress as the unique identifying
54 *   keys; any interface that doesn't contain both of these properties
55 *   is ignored and not processed
56 * - stores the Interface Type, MACAddress, and Unit in permanent storage
57 *   to give persistent interface names
58 */
59
60#include <ctype.h>
61#include <stdlib.h>
62#include <stdio.h>
63#include <unistd.h>
64#include <fcntl.h>
65#include <sys/ioctl.h>
66#include <sys/stat.h>
67#include <sys/sysctl.h>
68#include <sys/param.h>
69#include <mach/mach.h>
70#include <net/ethernet.h>
71#include <net/if_types.h>
72#include <pthread.h>
73#include <vproc.h>
74
75#include <CommonCrypto/CommonDigest.h>
76
77#include <CoreFoundation/CoreFoundation.h>
78
79#include <SystemConfiguration/SystemConfiguration.h>
80#include <SystemConfiguration/SCDPlugin.h>
81#include <SystemConfiguration/SCPrivate.h>	// for SCLog(), SCPrint()
82#include <SystemConfiguration/SCValidation.h>
83
84#include <IOKit/IOKitLib.h>
85#include <IOKit/IOKitLibPrivate.h>
86#include <IOKit/IOBSD.h>
87#include <IOKit/IOMessage.h>
88#include <IOKit/network/IONetworkController.h>
89#include <IOKit/network/IONetworkInterface.h>
90#include <IOKit/network/IONetworkStack.h>
91#include <IOKit/usb/USB.h>
92
93#ifdef kIONetworkStackUserCommandKey
94#define	USE_REGISTRY_ENTRY_ID
95#endif
96
97#ifndef	USE_REGISTRY_ENTRY_ID
98// from <IOKit/network/IONetworkStack.h>
99#define kIONetworkStackUserCommandKey	"IONetworkStackUserCommand"
100enum {
101    kRegisterInterfaceWithFixedUnit = 0,
102    kRegisterInterface,
103    kRegisterAllInterfaces
104};
105#endif	// !USE_REGISTRY_ENTRY_ID
106
107#define	kSCNetworkInterfaceInfo		"SCNetworkInterfaceInfo"
108#define	kSCNetworkInterfaceType		"SCNetworkInterfaceType"
109#define	kSCNetworkInterfaceActive	"Active"
110
111#define MY_PLUGIN_NAME			"InterfaceNamer"
112#define	MY_PLUGIN_ID			CFSTR("com.apple.SystemConfiguration." MY_PLUGIN_NAME)
113
114#define WAIT_STACK_TIMEOUT_KEY		"WaitStackTimeout"
115#define WAIT_STACK_TIMEOUT_DEFAULT	300.0
116
117#define WAIT_QUIET_TIMEOUT_KEY		"WaitQuietTimeout"
118#define WAIT_QUIET_TIMEOUT_DEFAULT	60.0
119
120/*
121 * S_connect
122 *   "IONetworkStack" connect object used to "name" an interface.
123 */
124static io_connect_t		S_connect		= MACH_PORT_NULL;
125
126/*
127 * S_dblist
128 *   An array of CFDictionary's representing the interfaces
129 *   that have been identified and [need to be] named.
130 */
131static CFMutableArrayRef	S_dblist		= NULL;
132
133/*
134 * S_debug
135 *   A boolean that enables additional logging.
136 */
137static boolean_t		S_debug			= FALSE;
138
139/*
140 * S_iflist
141 *   An array of SCNetworkInterface's representing the
142 *   interfaces that have been identified.
143 */
144static CFMutableArrayRef	S_iflist		= NULL;
145
146/*
147 * S_iter
148 *   IOServiceAddMatchingNotification object used to watch for
149 *   new network interfaces.
150 */
151static io_iterator_t		S_iter			= MACH_PORT_NULL;
152
153/*
154 * S_notify
155 *   notification object for receiving IOKit notifications of
156 *   new devices or state changes.
157 */
158static IONotificationPortRef	S_notify		= NULL;
159
160/* S_prev_active_list
161 *   An array of CFDictionary's representing the previously
162 *   named interfaces.
163 */
164static CFMutableArrayRef	S_prev_active_list	= NULL;
165
166/*
167 * S_quiet
168 *   IOServiceAddInterestNotification object used to watch for
169 *   IOKit matching to quiesce.
170 */
171static io_object_t		S_quiet			= MACH_PORT_NULL;
172
173/*
174 * S_stack
175 *   IOServiceAddMatchingNotification object used to watch for
176 *   the availability of the "IONetworkStack" object.
177 */
178static io_iterator_t		S_stack			= MACH_PORT_NULL;
179
180/*
181 * S_state
182 *   A dictionary containing Information about each network
183 *   interface.  For now, the key is the BSD name and the
184 *   value is a CFNumber noting how long (in milliseconds)
185 *   it took for the interface to be recognized/named.
186 */
187static CFMutableDictionaryRef	S_state			= NULL;
188
189/*
190 * S_timer
191 *   CFRunLoopTimer tracking how long we are willing to wait
192 *   for IOKit matching to quiesce (IOKitWaitQuiet).
193 *
194 * S_stack_timeout
195 *   time to wait for the IONetworkStack object to appear before timeout
196 *
197 * S_quiet_timeout
198 *   time to wait for the IOKit to quiesce (after the IONetworkStack is
199 *   has appeared.
200 */
201static CFRunLoopTimerRef	S_timer			= NULL;
202static double			S_stack_timeout		= WAIT_STACK_TIMEOUT_DEFAULT;
203static double			S_quiet_timeout		= WAIT_QUIET_TIMEOUT_DEFAULT;
204
205#if	!TARGET_OS_EMBEDDED
206/*
207 * S_vproc_transaction
208 *   The vproc transaction used to keep launchd from sending us
209 *   a SIGKILL before we've had a chance to set the platform UUID
210 */
211vproc_transaction_t		S_vproc_transaction	= NULL;
212#endif	// !TARGET_OS_EMBEDDED
213
214/*
215 * Virtual network interface configuration
216 *   S_prefs   : SCPreferences to configuration
217 *   S_bonds   : most recently actived Bond configuration
218 *   S_bridges : most recently actived Bridge configuration
219 *   S_vlans   : most recently actived VLAN configuration
220 */
221static SCPreferencesRef		S_prefs			= NULL;
222static CFArrayRef		S_bonds			= NULL;
223static CFArrayRef		S_bridges		= NULL;
224static CFArrayRef		S_vlans			= NULL;
225
226static void
227addTimestamp(CFMutableDictionaryRef dict, CFStringRef key)
228{
229    CFAbsoluteTime	now;
230    CFNumberRef		val;
231
232    now = CFAbsoluteTimeGetCurrent();
233    val = CFNumberCreate(NULL, kCFNumberDoubleType, &now);
234    CFDictionaryAddValue(dict, key, val);
235    CFRelease(val);
236    return;
237}
238
239#define	INTERFACES			CFSTR("Interfaces")
240#define	NETWORK_INTERFACES_PREFS	CFSTR("NetworkInterfaces.plist")
241
242static CFComparisonResult
243if_unit_compare(const void *val1, const void *val2, void *context)
244{
245    CFComparisonResult	res;
246    CFNumberRef		type1;
247    CFNumberRef		type2;
248    CFNumberRef		unit1;
249    CFNumberRef		unit2;
250
251    type1 = CFDictionaryGetValue((CFDictionaryRef)val1,
252				 CFSTR(kIOInterfaceType));
253    type2 = CFDictionaryGetValue((CFDictionaryRef)val2,
254				 CFSTR(kIOInterfaceType));
255    res = CFNumberCompare(type1, type2, NULL);
256    if (res != kCFCompareEqualTo) {
257	return (res);
258    }
259    unit1 = CFDictionaryGetValue((CFDictionaryRef)val1,
260				 CFSTR(kIOInterfaceUnit));
261    unit2 = CFDictionaryGetValue((CFDictionaryRef)val2,
262				 CFSTR(kIOInterfaceUnit));
263    return (CFNumberCompare(unit1, unit2, NULL));
264}
265
266static void
267reportIssue(const char *signature, CFStringRef issue)
268{
269    aslmsg  m;
270
271    m = asl_new(ASL_TYPE_MSG);
272    asl_set(m, "com.apple.message.domain", "com.apple.SystemConfiguration." MY_PLUGIN_NAME);
273    asl_set(m, "com.apple.message.signature", signature);
274    asl_set(m, "com.apple.message.result", "failure");
275    SCLOG(NULL, m, ~ASL_LEVEL_ERR, CFSTR("%s\n%@"), signature, issue);
276    asl_free(m);
277
278    return;
279}
280
281static void
282writeInterfaceList(CFArrayRef if_list)
283{
284    CFArrayRef		cur_list;
285    CFStringRef		new_model;
286    CFStringRef		old_model;
287    SCPreferencesRef	prefs;
288
289    if (isA_CFArray(if_list) == NULL) {
290	return;
291    }
292
293    prefs = SCPreferencesCreate(NULL, MY_PLUGIN_ID, NETWORK_INTERFACES_PREFS);
294    if (prefs == NULL) {
295	SCLog(TRUE, LOG_ERR,
296	      CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate failed, %s"),
297	      SCErrorString(SCError()));
298	return;
299    }
300
301    cur_list = SCPreferencesGetValue(prefs, INTERFACES);
302    if (_SC_CFEqual(cur_list, if_list)) {
303	goto done;
304    }
305
306    old_model = SCPreferencesGetValue(prefs, MODEL);
307    new_model = _SC_hw_model();
308    if ((new_model != NULL) && !_SC_CFEqual(old_model, new_model)) {
309	// if new hardware
310	if ((old_model != NULL) && (cur_list != NULL)) {
311	    CFStringRef history;
312	    CFStringRef issue;
313
314	    // if interface list was created on other hardware
315	    history = CFStringCreateWithFormat(NULL, NULL,
316					       CFSTR("%@:%@"),
317					       INTERFACES,
318					       old_model);
319	    SCPreferencesSetValue(prefs, history, cur_list);
320	    CFRelease(history);
321
322	    SCLog(TRUE, LOG_ERR,
323		  CFSTR(MY_PLUGIN_NAME ": Hardware model changed\n"
324			MY_PLUGIN_NAME ":   created on \"%@\"\n"
325			MY_PLUGIN_NAME ":   now on     \"%@\""),
326		  old_model,
327		  new_model);
328
329	    issue = CFStringCreateWithFormat(NULL, NULL,
330					     CFSTR("%@ --> %@"),
331					     old_model,
332					     new_model);
333	    reportIssue("Hardware model changed", issue);
334	    CFRelease(issue);
335	}
336
337	if (!SCPreferencesSetValue(prefs, MODEL, new_model)) {
338	    SCLog(TRUE, LOG_ERR,
339		  CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetValue failed, %s"),
340		  SCErrorString(SCError()));
341	    goto done;
342	}
343    }
344
345    if (!SCPreferencesSetValue(prefs, INTERFACES, if_list)) {
346	SCLog(TRUE, LOG_ERR,
347	      CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetValue failed, %s"),
348	      SCErrorString(SCError()));
349	goto done;
350    }
351
352    if (!SCPreferencesCommitChanges(prefs)) {
353	SCLog((SCError() != EROFS), LOG_ERR,
354	      CFSTR(MY_PLUGIN_NAME ": SCPreferencesCommitChanges failed, %s"),
355	      SCErrorString(SCError()));
356	goto done;
357    }
358
359done:
360
361    CFRelease(prefs);
362    return;
363}
364
365static CF_RETURNS_RETAINED CFMutableArrayRef
366readInterfaceList()
367{
368    CFArrayRef		if_list;
369    CFStringRef		old_model;
370    CFMutableArrayRef 	plist	= NULL;
371    SCPreferencesRef	prefs	= NULL;
372
373    prefs = SCPreferencesCreate(NULL, MY_PLUGIN_ID, NETWORK_INTERFACES_PREFS);
374    if (!prefs) {
375	SCLog(TRUE, LOG_ERR,
376	      CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate failed, %s"),
377	      SCErrorString(SCError()));
378	return (NULL);
379    }
380
381    if_list = SCPreferencesGetValue(prefs, INTERFACES);
382    if_list = isA_CFArray(if_list);
383
384    old_model = SCPreferencesGetValue(prefs, MODEL);
385    if (old_model != NULL) {
386	CFStringRef new_model;
387
388	new_model = _SC_hw_model();
389	if (!_SC_CFEqual(old_model, new_model)) {
390	    // if interface list was created on other hardware
391	    if_list = NULL;
392	}
393    }
394
395    if (if_list != NULL) {
396	CFIndex	i;
397	CFIndex	n	= CFArrayGetCount(if_list);
398
399	plist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
400	for (i = 0; i < n; i++) {
401	    CFDictionaryRef	dict;
402
403	    dict = CFArrayGetValueAtIndex(if_list, i);
404	    if (isA_CFDictionary(dict) &&
405		CFDictionaryContainsKey(dict, CFSTR(kIOInterfaceType)) &&
406		CFDictionaryContainsKey(dict, CFSTR(kIOInterfaceUnit)) &&
407		CFDictionaryContainsKey(dict, CFSTR(kIOMACAddress))) {
408		    CFArrayAppendValue(plist, dict);
409	    }
410	}
411    }
412
413    if (prefs != NULL) {
414	CFRelease(prefs);
415    }
416    return (plist);
417}
418
419static CF_RETURNS_RETAINED CFMutableArrayRef
420previouslyActiveInterfaces()
421{
422    CFMutableArrayRef	active;
423    CFIndex		i;
424    CFIndex		n;
425
426    if (S_dblist == NULL) {
427	return NULL;
428    }
429
430    active = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
431
432    n = CFArrayGetCount(S_dblist);
433    for (i = 0; i < n; i++) {
434	CFDictionaryRef	if_dict;
435
436	if_dict = CFArrayGetValueAtIndex(S_dblist, i);
437	if (CFDictionaryContainsKey(if_dict, CFSTR(kSCNetworkInterfaceActive))) {
438	    CFMutableDictionaryRef	new_dict;
439
440	    new_dict = CFDictionaryCreateMutableCopy(NULL, 0, if_dict);
441	    CFDictionaryRemoveValue(new_dict, CFSTR(kSCNetworkInterfaceActive));
442	    CFArraySetValueAtIndex(S_dblist, i, new_dict);
443	    CFArrayAppendValue(active, new_dict);
444	    CFRelease(new_dict);
445	}
446    }
447
448    return active;
449}
450
451static void
452updateStore(void)
453{
454    CFStringRef		key;
455
456    key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@" MY_PLUGIN_NAME),
457				  kSCDynamicStoreDomainPlugin);
458    (void)SCDynamicStoreSetValue(NULL, key, S_state);
459    CFRelease(key);
460
461    return;
462}
463
464#if	!TARGET_OS_IPHONE
465static void
466updateBondInterfaceConfiguration(SCPreferencesRef prefs)
467{
468    CFArrayRef	interfaces;
469
470    interfaces = SCBondInterfaceCopyAll(prefs);
471    if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) {
472	CFRelease(interfaces);
473	interfaces = NULL;
474    }
475
476    if (_SC_CFEqual(S_bonds, interfaces)) {
477	// if no change
478	if (interfaces != NULL) CFRelease(interfaces);
479	return;
480    }
481
482    if (S_bonds != NULL) CFRelease(S_bonds);
483    S_bonds = interfaces;
484
485    if (!_SCBondInterfaceUpdateConfiguration(prefs)) {
486	SCLog(TRUE, LOG_ERR,
487	      CFSTR(MY_PLUGIN_NAME ": _SCBondInterfaceUpdateConfiguration failed, %s"),
488	      SCErrorString(SCError()));
489    }
490
491    return;
492}
493#endif	// !TARGET_OS_IPHONE
494
495static void
496updateBridgeInterfaceConfiguration(SCPreferencesRef prefs)
497{
498    CFArrayRef	interfaces;
499
500    interfaces = SCBridgeInterfaceCopyAll(prefs);
501    if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) {
502	CFRelease(interfaces);
503	interfaces = NULL;
504    }
505
506    if (_SC_CFEqual(S_bridges, interfaces)) {
507	// if no change
508	if (interfaces != NULL) CFRelease(interfaces);
509	return;
510    }
511
512    if (S_bridges != NULL) CFRelease(S_bridges);
513    S_bridges = interfaces;
514
515    if (!_SCBridgeInterfaceUpdateConfiguration(prefs)) {
516	SCLog(TRUE, LOG_ERR,
517	      CFSTR(MY_PLUGIN_NAME ": _SCBridgeInterfaceUpdateConfiguration failed, %s"),
518	      SCErrorString(SCError()));
519    }
520
521    return;
522}
523
524static void
525updateVLANInterfaceConfiguration(SCPreferencesRef prefs)
526{
527    CFArrayRef	interfaces;
528
529    interfaces = SCVLANInterfaceCopyAll(prefs);
530    if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) {
531	CFRelease(interfaces);
532	interfaces = NULL;
533    }
534
535    if (_SC_CFEqual(S_vlans, interfaces)) {
536	// if no change
537	if (interfaces != NULL) CFRelease(interfaces);
538	return;
539    }
540
541    if (S_vlans != NULL) CFRelease(S_vlans);
542    S_vlans = interfaces;
543
544    if (!_SCVLANInterfaceUpdateConfiguration(prefs)) {
545	SCLog(TRUE, LOG_ERR,
546	      CFSTR(MY_PLUGIN_NAME ": _SCVLANInterfaceUpdateConfiguration failed, %s"),
547	      SCErrorString(SCError()));
548    }
549
550    return;
551}
552
553static void
554updateVirtualNetworkInterfaceConfiguration(SCPreferencesRef		prefs,
555					   SCPreferencesNotification   notificationType,
556					   void				*info)
557{
558    if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
559	return;
560    }
561
562    if (prefs == NULL) {
563	// if a new interface has been "named"
564	prefs = S_prefs;
565	if (S_bonds != NULL) {
566	    CFRelease(S_bonds);
567	    S_bonds = NULL;
568	}
569	if (S_bridges != NULL) {
570	    CFRelease(S_bridges);
571	    S_bridges = NULL;
572	}
573	if (S_vlans != NULL) {
574	    CFRelease(S_vlans);
575	    S_vlans = NULL;
576	}
577    }
578
579#if	!TARGET_OS_IPHONE
580    updateBondInterfaceConfiguration  (prefs);
581#endif	// !TARGET_OS_IPHONE
582    updateBridgeInterfaceConfiguration(prefs);
583    updateVLANInterfaceConfiguration  (prefs);
584
585    // we are finished with current prefs, wait for changes
586    SCPreferencesSynchronize(prefs);
587    return;
588}
589
590#if	!TARGET_OS_EMBEDDED
591
592#define	BT_PAN_NAME "Bluetooth PAN"
593
594static void
595updateBTPANInformation(const void *value, void *context)
596{   CFDictionaryRef dict    = (CFDictionaryRef)value;
597    CFStringRef	    if_name;
598    CFDictionaryRef info;
599    CFStringRef	    name;
600
601    if_name = CFDictionaryGetValue(dict, CFSTR(kIOBSDNameKey));
602    if (!isA_CFString(if_name)) {
603	// if no BSD name
604	return;
605    }
606
607    info = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceInfo));
608    if (!isA_CFDictionary(info)) {
609	// if no SCNetworkInterface info
610	return;
611    }
612
613    name = CFDictionaryGetValue(info, kSCPropUserDefinedName);
614    if (!isA_CFString(name) || !CFEqual(name, CFSTR(BT_PAN_NAME))) {
615	// if not BT-PAN interface
616	return;
617    }
618
619    CFDictionaryAddValue(S_state, CFSTR("_" BT_PAN_NAME "_"), if_name);
620    return;
621}
622#endif	// !TARGET_OS_EMBEDDED
623
624static CFDictionaryRef
625createInterfaceDict(SCNetworkInterfaceRef interface)
626{
627    CFMutableDictionaryRef	new_if;
628    CFTypeRef			val;
629
630    new_if = CFDictionaryCreateMutable(NULL,
631				       0,
632				       &kCFTypeDictionaryKeyCallBacks,
633				       &kCFTypeDictionaryValueCallBacks);
634
635    val = _SCNetworkInterfaceCopyInterfaceInfo(interface);
636    if (val != NULL) {
637	CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceInfo), val);
638	CFRelease(val);
639    }
640
641    val = _SCNetworkInterfaceGetIOPath(interface);
642    if (val != NULL) {
643	CFDictionarySetValue(new_if, CFSTR(kIOPathMatchKey), val);
644    }
645
646    val = _SCNetworkInterfaceGetIOInterfaceNamePrefix(interface);
647    if (val != NULL) {
648	CFDictionarySetValue(new_if, CFSTR(kIOInterfaceNamePrefix), val);
649    }
650
651    val = _SCNetworkInterfaceGetIOInterfaceType(interface);
652    if (val != NULL) {
653	CFDictionarySetValue(new_if, CFSTR(kIOInterfaceType), val);
654    }
655
656    val = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
657    if (val != NULL) {
658	CFDictionarySetValue(new_if, CFSTR(kIOInterfaceUnit), val);
659    }
660
661    val = _SCNetworkInterfaceGetHardwareAddress(interface);
662    if (val != NULL) {
663	CFDictionarySetValue(new_if, CFSTR(kIOMACAddress), val);
664    }
665
666    val = SCNetworkInterfaceGetBSDName(interface);
667    if (val != NULL) {
668	CFDictionarySetValue(new_if, CFSTR(kIOBSDNameKey), val);
669    }
670
671    val = SCNetworkInterfaceGetInterfaceType(interface);
672    if (val != NULL) {
673	CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceType), val);
674    }
675
676    CFDictionarySetValue(new_if,
677			 CFSTR(kIOBuiltin),
678			 _SCNetworkInterfaceIsBuiltin(interface) ? kCFBooleanTrue : kCFBooleanFalse);
679
680    CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceActive), kCFBooleanTrue);
681
682    return new_if;
683}
684
685static CFDictionaryRef
686lookupInterfaceByAddress(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
687{
688    CFDataRef	addr;
689    CFIndex	i;
690    CFIndex	n;
691    CFNumberRef	type;
692
693    if (db_list == NULL) {
694	return (NULL);
695    }
696    type = _SCNetworkInterfaceGetIOInterfaceType(interface);
697    addr = _SCNetworkInterfaceGetHardwareAddress(interface);
698    if (type == NULL || addr == NULL) {
699	return (NULL);
700    }
701
702    n = CFArrayGetCount(db_list);
703    for (i = 0; i < n; i++) {
704	CFDataRef	a;
705	CFDictionaryRef	dict = CFArrayGetValueAtIndex(db_list, i);
706	CFNumberRef	t;
707
708	t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
709	a = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
710	if (t == NULL || a == NULL)
711	    continue;
712
713	if (CFEqual(type, t) && CFEqual(addr, a)) {
714	    if (where) {
715		*where = i;
716	    }
717	    return (dict);
718	}
719    }
720    return (NULL);
721}
722
723static CFDictionaryRef
724lookupInterfaceByUnit(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
725{
726    CFIndex 	i;
727    CFIndex	n;
728    CFNumberRef	type;
729    CFNumberRef	unit;
730
731    if (db_list == NULL) {
732	return (NULL);
733    }
734    type = _SCNetworkInterfaceGetIOInterfaceType(interface);
735    unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
736    if (type == NULL || unit == NULL) {
737	return (NULL);
738    }
739
740    n = CFArrayGetCount(db_list);
741    for (i = 0; i < n; i++) {
742	CFDictionaryRef	dict = CFArrayGetValueAtIndex(db_list, i);
743	CFNumberRef	t;
744	CFNumberRef	u;
745
746	t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
747	u = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
748	if (t == NULL || u == NULL) {
749	    continue;
750	}
751
752	if (CFEqual(type, t) && CFEqual(unit, u)) {
753	    if (where)
754		*where = i;
755	    return (dict);
756	}
757    }
758    return (NULL);
759}
760
761typedef struct {
762    CFDictionaryRef	    match_info;
763    CFStringRef		    match_type;
764    CFBooleanRef	    match_builtin;
765    CFMutableArrayRef	    matches;
766} matchContext, *matchContextRef;
767
768static CF_RETURNS_RETAINED CFDictionaryRef
769thinInterfaceInfo(CFDictionaryRef info)
770{
771    CFNumberRef	num;
772    int		vid;
773
774    if (CFDictionaryGetValueIfPresent(info, CFSTR(kUSBVendorID), (const void **)&num)
775	&& isA_CFNumber(num)
776	&& CFNumberGetValue(num, kCFNumberIntType, &vid)
777	&& (vid == kIOUSBVendorIDAppleComputer)) {
778	CFMutableDictionaryRef  thin;
779
780	// if this is an Apple USB device than we trust that
781	// the non-localized name will be correct.
782	thin = CFDictionaryCreateMutableCopy(NULL, 0, info);
783	CFDictionaryRemoveValue(thin, CFSTR(kUSBProductString));
784	CFDictionaryRemoveValue(thin, CFSTR(kUSBVendorID));
785	CFDictionaryRemoveValue(thin, CFSTR(kUSBProductID));
786	return thin;
787    }
788
789    return CFRetain(info);
790}
791
792static Boolean
793matchInterfaceInfo(CFDictionaryRef known_info, CFDictionaryRef match_info)
794{
795    Boolean match;
796
797    match = _SC_CFEqual(known_info, match_info);
798    if (!match &&
799	isA_CFDictionary(known_info) &&
800	isA_CFDictionary(match_info)) {
801
802	// if not an exact match, try thinning
803	known_info = thinInterfaceInfo(known_info);
804	match_info = thinInterfaceInfo(match_info);
805	match = _SC_CFEqual(known_info, match_info);
806	if (known_info != NULL) CFRelease(known_info);
807	if (match_info != NULL) CFRelease(match_info);
808    }
809
810    return match;
811}
812
813static void
814matchKnown(const void *value, void *context)
815{
816    CFDictionaryRef	known_dict	= (CFDictionaryRef)value;
817    matchContextRef	match_context	= (matchContextRef)context;
818
819    // match interface type
820    {
821	CFStringRef	known_type;
822
823	known_type = CFDictionaryGetValue(known_dict, CFSTR(kSCNetworkInterfaceType));
824	if (!_SC_CFEqual(known_type, match_context->match_type)) {
825	    return;
826	}
827    }
828
829    // match SCNetworkInterfaceInfo
830    {
831	CFDictionaryRef	known_info;
832
833	known_info = CFDictionaryGetValue(known_dict, CFSTR(kSCNetworkInterfaceInfo));
834	if (!matchInterfaceInfo(known_info, match_context->match_info)) {
835	    return;
836	}
837    }
838
839    // if requested, match [non-]builtin
840    if (match_context->match_builtin != NULL) {
841	CFBooleanRef	known_builtin;
842
843	known_builtin = CFDictionaryGetValue(known_dict, CFSTR(kIOBuiltin));
844	if (!isA_CFBoolean(known_builtin)) {
845	    known_builtin = kCFBooleanFalse;
846	}
847	if (!_SC_CFEqual(known_builtin, match_context->match_builtin)) {
848	    return;
849	}
850    }
851
852    // if we have a match
853    if (match_context->matches == NULL) {
854	match_context->matches = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
855    }
856    CFArrayAppendValue(match_context->matches, known_dict);
857
858    return;
859}
860
861static void
862matchUnnamed(const void *value, void *context)
863{
864    SCNetworkInterfaceRef   known_if	    = (SCNetworkInterfaceRef)value;
865    matchContextRef	    match_context   = (matchContextRef)context;
866
867    if (match_context->matches == NULL) {
868	return;
869    }
870
871    // match interface type
872    {
873	CFStringRef	known_type;
874
875	known_type = SCNetworkInterfaceGetInterfaceType(known_if);
876	if (!_SC_CFEqual(known_type, match_context->match_type)) {
877	    return;
878	}
879    }
880
881    // match SCNetworkInterfaceInfo
882    {
883	CFDictionaryRef	known_info;
884	Boolean		match;
885
886	known_info = _SCNetworkInterfaceCopyInterfaceInfo(known_if);
887	match = matchInterfaceInfo(known_info, match_context->match_info);
888	if (known_info != NULL) CFRelease(known_info);
889	if (!match) {
890	    return;
891	}
892    }
893
894    // if requested, match [non-]builtin
895    if (match_context->match_builtin != NULL) {
896	CFBooleanRef	known_builtin;
897
898	known_builtin = _SCNetworkInterfaceIsBuiltin(known_if) ? kCFBooleanTrue
899							       : kCFBooleanFalse;
900	if (!_SC_CFEqual(known_builtin, match_context->match_builtin)) {
901	    return;
902	}
903    }
904
905    // if we have a match
906    CFRelease(match_context->matches);
907    match_context->matches = NULL;
908
909    return;
910}
911
912static Boolean
913interfaceExists(CFStringRef prefix, CFNumberRef unit)
914{
915    Boolean		found	    = FALSE;
916    CFDictionaryRef	match_dict;
917    CFStringRef		match_keys[2];
918    CFTypeRef		match_vals[2];
919    CFDictionaryRef	matching;
920
921
922
923    io_registry_entry_t	entry		= MACH_PORT_NULL;
924    io_iterator_t		iterator	= MACH_PORT_NULL;
925    kern_return_t		kr;
926    mach_port_t			masterPort	= MACH_PORT_NULL;
927
928    kr = IOMasterPort(bootstrap_port, &masterPort);
929    if (kr != KERN_SUCCESS) {
930	SCLog(TRUE, LOG_ERR,
931	      CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
932	      kr);
933	goto error;
934    }
935
936    // look for kIONetworkInterface with matching prefix and unit
937    match_keys[0] = CFSTR(kIOInterfaceNamePrefix);
938    match_vals[0] = prefix;
939    match_keys[1] = CFSTR(kIOInterfaceUnit);
940    match_vals[1] = unit;
941    match_dict = CFDictionaryCreate(NULL,
942				    (const void **)match_keys,
943				    (const void **)match_vals,
944				    2,
945				    &kCFTypeDictionaryKeyCallBacks,
946				    &kCFTypeDictionaryValueCallBacks);
947
948    match_keys[0] = CFSTR(kIOProviderClassKey);
949    match_vals[0] = CFSTR(kIONetworkInterfaceClass);
950    match_keys[1] = CFSTR(kIOPropertyMatchKey);
951    match_vals[1] = match_dict;
952    matching = CFDictionaryCreate(NULL,
953				  (const void **)match_keys,
954				  (const void **)match_vals,
955				  sizeof(match_keys)/sizeof(match_keys[0]),
956				  &kCFTypeDictionaryKeyCallBacks,
957				  &kCFTypeDictionaryValueCallBacks);
958    CFRelease(match_dict);
959
960    // note: the "matching" dictionary will be consumed by the following
961    kr = IOServiceGetMatchingServices(masterPort, matching, &iterator);
962    if ((kr != kIOReturnSuccess) || (iterator == MACH_PORT_NULL)) {
963	// if no interface
964	goto error;
965    }
966
967    entry = IOIteratorNext(iterator);
968    if (entry == MACH_PORT_NULL) {
969	// if no interface
970	goto error;
971    }
972
973    found = TRUE;
974
975error:
976    if (masterPort != MACH_PORT_NULL) {
977	mach_port_deallocate(mach_task_self(), masterPort);
978    }
979    if (entry != MACH_PORT_NULL) {
980	IOObjectRelease(entry);
981    }
982    if (iterator != MACH_PORT_NULL) {
983	IOObjectRelease(iterator);
984    }
985
986    return (found);
987}
988
989/*
990 * lookupMatchingInterface
991 *
992 * Looks at the interfaces that have already been [or need to be] named with
993 * the goal of allowing a system using a single network interface/adaptor of
994 * a given type (vendor, model, ...) to not care about the specific adaptor
995 * that is used (i.e. swapping dongle's is OK).  Once a system has had more
996 * than one interface/adaptor connected at the same time than we assume that
997 * the network configuration is being setup for multi-homing that should be
998 * maintained.
999 *
1000 * If no matches are found or if more than one match is found, return NULL.
1001 * If a single match is found, return the match.
1002 */
1003static CFDictionaryRef
1004lookupMatchingInterface(SCNetworkInterfaceRef	interface,
1005			CFArrayRef		db_list,	// already named
1006			CFArrayRef		if_list,	// to be named
1007			CFIndex			if_list_index,
1008			CFBooleanRef		builtin)
1009{
1010    CFStringRef	    if_type;
1011    CFDictionaryRef match	    = NULL;
1012    matchContext    match_context;
1013
1014    if_type = SCNetworkInterfaceGetInterfaceType(interface);
1015    if (if_type == NULL) {
1016	return NULL;
1017    }
1018
1019    match_context.match_type	= if_type;
1020    match_context.match_info	= _SCNetworkInterfaceCopyInterfaceInfo(interface);
1021    match_context.match_builtin	= builtin;
1022    match_context.matches	= NULL;
1023
1024    // check for matches to interfaces that have already been named
1025    // ... and append each match that we find to match_context.matches
1026    if (db_list != NULL) {
1027	CFArrayApplyFunction(db_list,
1028			     CFRangeMake(0, CFArrayGetCount(db_list)),
1029			     matchKnown,
1030			     &match_context);
1031    }
1032
1033    // check for matches to interfaces that will be named
1034    // ... and CFRelease match_context.matches if we find another network
1035    //     interface of the same type that also needs to be named
1036    if (if_list != NULL) {
1037	CFIndex	   if_list_count;
1038
1039	if_list_count = CFArrayGetCount(if_list);
1040	if (if_list_index < if_list_count) {
1041	    CFArrayApplyFunction(if_list,
1042				 CFRangeMake(if_list_index, if_list_count - if_list_index),
1043				 matchUnnamed,
1044				 &match_context);
1045	}
1046    }
1047
1048    // check if we have a single match
1049    if (match_context.matches != NULL) {
1050	if (CFArrayGetCount(match_context.matches) == 1) {
1051	    match = CFArrayGetValueAtIndex(match_context.matches, 0);
1052	}
1053	CFRelease(match_context.matches);
1054    }
1055
1056    if (match != NULL) {
1057	Boolean		active	= TRUE;
1058	CFStringRef	name;
1059
1060	name = CFDictionaryGetValue(match, CFSTR(kIOBSDNameKey));
1061	if (isA_CFString(name)) {
1062	    CFStringRef	    prefix;
1063	    CFNumberRef	    unit;
1064
1065	    prefix = CFDictionaryGetValue(match, CFSTR(kIOInterfaceNamePrefix));
1066	    unit   = CFDictionaryGetValue(match, CFSTR(kIOInterfaceUnit));
1067	    if (isA_CFString(prefix) && isA_CFNumber(unit)) {
1068		if (!interfaceExists(prefix, unit)) {
1069		    active = FALSE;
1070		}
1071	    }
1072	}
1073
1074	if (active) {
1075	    match = NULL;
1076	}
1077    }
1078
1079    if (match_context.match_info != NULL) CFRelease(match_context.match_info);
1080    return match;
1081}
1082
1083static void
1084insertInterface(CFMutableArrayRef db_list, SCNetworkInterfaceRef interface)
1085{
1086    CFIndex		i;
1087    CFDictionaryRef	if_dict;
1088    CFStringRef		if_name;
1089    CFNumberRef		if_type;
1090    CFNumberRef		if_unit;
1091    CFIndex		n	= CFArrayGetCount(db_list);
1092    CFComparisonResult	res;
1093
1094    if_name = SCNetworkInterfaceGetBSDName(interface);
1095    if (if_name != NULL) {
1096	addTimestamp(S_state, if_name);
1097    }
1098
1099    if_dict = createInterfaceDict(interface);
1100    if_type = _SCNetworkInterfaceGetIOInterfaceType(interface);
1101    if_unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
1102    if ((if_type == NULL) || (if_unit == NULL)) {
1103	CFRelease(if_dict);
1104	return;
1105    }
1106
1107    for (i = 0; i < n; i++) {
1108	CFNumberRef	db_type;
1109	CFNumberRef	db_unit;
1110	CFDictionaryRef	dict	= CFArrayGetValueAtIndex(db_list, i);
1111
1112	db_type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
1113	db_unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
1114	res = CFNumberCompare(if_type, db_type, NULL);
1115	if (res == kCFCompareLessThan
1116	    || (res == kCFCompareEqualTo
1117		&& (CFNumberCompare(if_unit, db_unit, NULL)
1118		    == kCFCompareLessThan))) {
1119	    CFArrayInsertValueAtIndex(db_list, i, if_dict);
1120	    CFRelease(if_dict);
1121	    return;
1122	}
1123    }
1124
1125    CFArrayAppendValue(S_dblist, if_dict);
1126
1127#if	!TARGET_OS_EMBEDDED
1128    updateBTPANInformation(if_dict, NULL);
1129#endif	// !TARGET_OS_EMBEDDED
1130
1131    CFRelease(if_dict);
1132    return;
1133}
1134
1135static void
1136replaceInterface(SCNetworkInterfaceRef interface)
1137{
1138    int		n	= 0;
1139    CFIndex	where;
1140
1141    if (S_dblist == NULL) {
1142	S_dblist = CFArrayCreateMutable(NULL, 0,
1143					&kCFTypeArrayCallBacks);
1144    }
1145    // remove any dict that has our type/addr
1146    while (lookupInterfaceByAddress(S_dblist, interface, &where) != NULL) {
1147	CFArrayRemoveValueAtIndex(S_dblist, where);
1148	n++;
1149    }
1150    // remove any dict that has the same type/unit
1151    while (lookupInterfaceByUnit(S_dblist, interface, &where) != NULL) {
1152	CFArrayRemoveValueAtIndex(S_dblist, where);
1153	n++;
1154    }
1155    insertInterface(S_dblist, interface);
1156
1157    if (n > 1) {
1158	CFStringRef	issue;
1159
1160	issue = CFStringCreateWithFormat(NULL, NULL,
1161					 CFSTR("n = %d, %@"),
1162					 n,
1163					 interface);
1164	reportIssue("Multiple interfaces updated", issue);
1165	CFRelease(issue);
1166    }
1167
1168    return;
1169}
1170
1171static CFNumberRef
1172getHighestUnitForType(CFNumberRef if_type)
1173{
1174    int 		i;
1175    CFIndex		n;
1176    CFNumberRef		ret_unit	= NULL;
1177
1178    if (S_dblist == NULL) {
1179	return (NULL);
1180    }
1181
1182    n = CFArrayGetCount(S_dblist);
1183    for (i = 0; i < n; i++) {
1184	CFDictionaryRef	dict = CFArrayGetValueAtIndex(S_dblist, i);
1185	CFNumberRef	type;
1186
1187	type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
1188	if (CFEqual(type, if_type)) {
1189	    CFNumberRef	unit;
1190
1191	    unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
1192	    if (ret_unit == NULL
1193		|| (CFNumberCompare(unit, ret_unit, NULL)
1194		    == kCFCompareGreaterThan)) {
1195		ret_unit = unit;
1196	    }
1197	}
1198    }
1199
1200    return (ret_unit);
1201}
1202
1203/*
1204 * Function: ensureInterfaceHasUnit
1205 * Purpose:
1206 *   Ensure that the SCNetworkInterfaceRef has a unit number.  If it doesn't,
1207 *   release the interface and return NULL.
1208 */
1209static SCNetworkInterfaceRef
1210ensureInterfaceHasUnit(SCNetworkInterfaceRef net_if)
1211{
1212    if (net_if != NULL
1213	&& _SCNetworkInterfaceGetIOInterfaceUnit(net_if) == NULL) {
1214	CFRelease(net_if);
1215	net_if = NULL;
1216    }
1217    return (net_if);
1218}
1219
1220#ifdef	USE_REGISTRY_ENTRY_ID
1221static kern_return_t
1222registerInterfaceWithIORegistryEntryID(io_connect_t connect,
1223				       uint64_t	    entryID,
1224				       CFNumberRef  unit,
1225				       const int    command)
1226{
1227    CFDataRef			data;
1228    CFMutableDictionaryRef	dict;
1229    kern_return_t		kr;
1230    CFNumberRef			num;
1231
1232    dict = CFDictionaryCreateMutable(NULL, 0,
1233				     &kCFTypeDictionaryKeyCallBacks,
1234				     &kCFTypeDictionaryValueCallBacks);
1235    num = CFNumberCreate(NULL, kCFNumberIntType, &command);
1236    CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommandKey), num);
1237    CFRelease(num);
1238    data = CFDataCreate(NULL, (void *) &entryID, sizeof(entryID));
1239    CFDictionarySetValue(dict, CFSTR(kIORegistryEntryIDKey), data);
1240    CFRelease(data);
1241    CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit);
1242    kr = IOConnectSetCFProperties(connect, dict);
1243    CFRelease(dict);
1244    return kr;
1245}
1246
1247static SCNetworkInterfaceRef
1248copyInterfaceForIORegistryEntryID(uint64_t entryID)
1249{
1250    io_registry_entry_t		entry		= MACH_PORT_NULL;
1251    SCNetworkInterfaceRef	interface	= NULL;
1252    io_iterator_t		iterator	= MACH_PORT_NULL;
1253    kern_return_t		kr;
1254    mach_port_t			masterPort	= MACH_PORT_NULL;
1255
1256    kr = IOMasterPort(bootstrap_port, &masterPort);
1257    if (kr != KERN_SUCCESS) {
1258	SCLog(TRUE, LOG_ERR,
1259	      CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
1260	      kr);
1261	goto error;
1262    }
1263
1264    kr = IOServiceGetMatchingServices(masterPort,
1265				      IORegistryEntryIDMatching(entryID),
1266				      &iterator);
1267    if ((kr != KERN_SUCCESS) || (iterator == MACH_PORT_NULL)) {
1268	SCLog(TRUE, LOG_ERR,
1269	      CFSTR(MY_PLUGIN_NAME ": IOServiceGetMatchingServices(0x%llx) returned 0x%x/%d"),
1270	      entryID,
1271	      kr,
1272	      iterator);
1273	goto error;
1274    }
1275
1276    entry = IOIteratorNext(iterator);
1277    if (entry == MACH_PORT_NULL) {
1278	SCLog(TRUE, LOG_ERR,
1279	      CFSTR(MY_PLUGIN_NAME ": IORegistryEntryIDMatching(0x%llx) failed"),
1280	      entryID);
1281	goto error;
1282    }
1283
1284    interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry);
1285
1286 error:
1287    if (masterPort != MACH_PORT_NULL) {
1288	mach_port_deallocate(mach_task_self(), masterPort);
1289    }
1290    if (entry != MACH_PORT_NULL) {
1291	IOObjectRelease(entry);
1292    }
1293    if (iterator != MACH_PORT_NULL) {
1294	IOObjectRelease(iterator);
1295    }
1296    return (interface);
1297}
1298
1299static SCNetworkInterfaceRef
1300copyNamedInterfaceForIORegistryEntryID(uint64_t entryID)
1301{
1302    SCNetworkInterfaceRef	net_if;
1303
1304    net_if = copyInterfaceForIORegistryEntryID(entryID);
1305    return (ensureInterfaceHasUnit(net_if));
1306}
1307
1308#else	// USE_REGISTRY_ENTRY_ID
1309/*
1310 * Function: registerInterface
1311 * Purpose:
1312 *   Register a single interface with the given service path to the
1313 *   data link layer (BSD), using the specified unit number.
1314 */
1315static kern_return_t
1316registerInterfaceWithIOServicePath(io_connect_t	connect,
1317				   CFStringRef	path,
1318				   CFNumberRef	unit,
1319				   const int	command)
1320{
1321    CFMutableDictionaryRef	dict;
1322    kern_return_t		kr;
1323    CFNumberRef			num;
1324
1325    dict = CFDictionaryCreateMutable(NULL, 0,
1326				     &kCFTypeDictionaryKeyCallBacks,
1327				     &kCFTypeDictionaryValueCallBacks);
1328    num = CFNumberCreate(NULL, kCFNumberIntType, &command);
1329    CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommandKey), num);
1330    CFRelease(num);
1331    CFDictionarySetValue(dict, CFSTR(kIOPathMatchKey), path);
1332    CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit);
1333    kr = IOConnectSetCFProperties(connect, dict);
1334    CFRelease(dict);
1335    return kr;
1336}
1337
1338static SCNetworkInterfaceRef
1339copyInterfaceForIOKitPath(CFStringRef if_path)
1340{
1341    io_registry_entry_t		entry		= MACH_PORT_NULL;
1342    SCNetworkInterfaceRef	interface	= NULL;
1343    kern_return_t		kr;
1344    mach_port_t			masterPort	= MACH_PORT_NULL;
1345    io_string_t			path;
1346
1347    kr = IOMasterPort(bootstrap_port, &masterPort);
1348    if (kr != KERN_SUCCESS) {
1349	SCLog(TRUE, LOG_ERR,
1350	      CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
1351	      kr);
1352	goto error;
1353    }
1354    _SC_cfstring_to_cstring(if_path, path, sizeof(path), kCFStringEncodingASCII);
1355    entry = IORegistryEntryFromPath(masterPort, path);
1356    if (entry == MACH_PORT_NULL) {
1357	SCLog(TRUE, LOG_ERR,
1358	      CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath(%@) failed"),
1359	      if_path);
1360	goto error;
1361    }
1362
1363    interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry);
1364
1365 error:
1366    if (masterPort != MACH_PORT_NULL) {
1367	mach_port_deallocate(mach_task_self(), masterPort);
1368    }
1369    if (entry != MACH_PORT_NULL) {
1370	IOObjectRelease(entry);
1371    }
1372    return (interface);
1373
1374}
1375
1376static SCNetworkInterfaceRef
1377copyNamedInterfaceForIOKitPath(CFStringRef if_path)
1378{
1379    SCNetworkInterfaceRef	net_if;
1380
1381    net_if = copyInterfaceForIOKitPath(if_path);
1382    return (ensureInterfaceHasUnit(net_if));
1383}
1384
1385#endif	// USE_REGISTRY_ENTRY_ID
1386
1387static void
1388displayInterface(SCNetworkInterfaceRef interface)
1389{
1390    CFStringRef		addr;
1391    CFStringRef		name;
1392    CFNumberRef		type;
1393    CFNumberRef		unit;
1394
1395    name = SCNetworkInterfaceGetBSDName(interface);
1396    unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
1397    type = _SCNetworkInterfaceGetIOInterfaceType(interface);
1398    addr = SCNetworkInterfaceGetHardwareAddressString(interface);
1399
1400    SCLog(TRUE, LOG_INFO,
1401	  CFSTR(MY_PLUGIN_NAME ":   %s%@%sType: %@, %s%@%sMAC address: %@"),
1402	  (name != NULL) ? "BSD Name: " : "",
1403	  (name != NULL) ? name : CFSTR(""),
1404	  (name != NULL) ? ", " : "",
1405	  type,
1406	  (unit != NULL) ? "Unit: " : "",
1407	  (unit != NULL) ? (CFTypeRef)unit : (CFTypeRef)CFSTR(""),
1408	  (unit != NULL) ? ", " : "",
1409	  addr);
1410}
1411
1412static Boolean
1413builtinAvailable(SCNetworkInterfaceRef	interface,	// new interface
1414		 CFNumberRef		if_unit)	// desired unit
1415{
1416    CFIndex	i;
1417    CFNumberRef if_type	= _SCNetworkInterfaceGetIOInterfaceType(interface);
1418    CFIndex	n;
1419
1420    n = (S_dblist != NULL) ? CFArrayGetCount(S_dblist) : 0;
1421    for (i = 0; i < n; i++) {
1422	CFStringRef	    if_path;
1423	CFDictionaryRef	    known_dict	    = CFArrayGetValueAtIndex(S_dblist, i);
1424	CFStringRef	    known_path;
1425	CFNumberRef	    known_type;
1426	CFNumberRef	    known_unit;
1427
1428	known_type = CFDictionaryGetValue(known_dict, CFSTR(kIOInterfaceType));
1429	if (!_SC_CFEqual(if_type, known_type)) {
1430	    continue;	// if not the same interface type
1431	}
1432
1433	known_unit = CFDictionaryGetValue(known_dict, CFSTR(kIOInterfaceUnit));
1434	if (!_SC_CFEqual(if_unit, known_unit)) {
1435	    continue;	// if not the same interface unit
1436	}
1437
1438	if_path    = _SCNetworkInterfaceGetIOPath(interface);
1439	known_path = CFDictionaryGetValue(known_dict, CFSTR(kIOPathMatchKey));
1440	if (!_SC_CFEqual(if_path, known_path)) {
1441	    // if different IORegistry path
1442	    return FALSE;
1443	}
1444
1445	// if same type, same unit, same path
1446	return TRUE;
1447    }
1448
1449    // if interface type/unit not found
1450    return TRUE;
1451}
1452
1453static int
1454builtinCount(CFArrayRef if_list, CFIndex last, CFNumberRef if_type)
1455{
1456    CFIndex	i;
1457    int		n	= 0;
1458
1459    for (i = 0; i < last; i++) {
1460	SCNetworkInterfaceRef	builtin_if;
1461	CFNumberRef		builtin_type;
1462
1463	builtin_if   = CFArrayGetValueAtIndex(if_list, i);
1464	builtin_type = _SCNetworkInterfaceGetIOInterfaceType(builtin_if);
1465	if (CFEqual(if_type, builtin_type)) {
1466	    if (_SCNetworkInterfaceIsBuiltin(builtin_if)) {
1467		n++;	// if built-in interface
1468	    }
1469	}
1470    }
1471
1472    return n;
1473}
1474
1475static __inline__ boolean_t
1476isQuiet(void)
1477{
1478    return (S_quiet == MACH_PORT_NULL);
1479}
1480
1481static void
1482nameInterfaces(CFMutableArrayRef if_list)
1483{
1484    CFIndex	i;
1485    CFIndex	n	= CFArrayGetCount(if_list);
1486
1487    for (i = 0; i < n; i++) {
1488	uint64_t		entryID;
1489	SCNetworkInterfaceRef	interface;
1490	SCNetworkInterfaceRef	new_interface;
1491	CFStringRef		path;
1492	CFStringRef		str;
1493	CFNumberRef		type;
1494	CFNumberRef		unit;
1495	CFIndex			where;
1496
1497	interface = CFArrayGetValueAtIndex(if_list, i);
1498	path = _SCNetworkInterfaceGetIOPath(interface);
1499	type = _SCNetworkInterfaceGetIOInterfaceType(interface);
1500	unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
1501	entryID = _SCNetworkInterfaceGetIORegistryEntryID(interface);
1502
1503	if (unit != NULL) {
1504	    if (S_debug) {
1505		CFStringRef	if_name;
1506
1507		if_name = SCNetworkInterfaceGetBSDName(interface);
1508		if ((if_name == NULL) || !CFDictionaryContainsKey(S_state, if_name)) {
1509			SCLog(TRUE, LOG_INFO,
1510			      CFSTR(MY_PLUGIN_NAME ": Interface already has a unit number"));
1511			displayInterface(interface);
1512		}
1513	    }
1514
1515	    // update the list of interfaces that were previously named
1516	    if ((S_prev_active_list != NULL)
1517		&& lookupInterfaceByAddress(S_prev_active_list, interface, &where) != NULL) {
1518		CFArrayRemoveValueAtIndex(S_prev_active_list, where);
1519	    }
1520
1521	    replaceInterface(interface);
1522	} else {
1523	    CFDictionaryRef 	dbdict;
1524	    boolean_t		is_builtin;
1525	    kern_return_t	kr;
1526	    int			retries	= 0;
1527
1528	    dbdict = lookupInterfaceByAddress(S_dblist, interface, NULL);
1529	    if (dbdict != NULL) {
1530		unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
1531		CFRetain(unit);
1532
1533		SCLog(S_debug, LOG_INFO,
1534		      CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (from database)"),
1535		      unit);
1536	    }
1537
1538	    if ((dbdict == NULL) && !isQuiet()) {
1539		// if new interface, wait until quiet before naming
1540		addTimestamp(S_state, path);
1541		continue;
1542	    }
1543
1544	    is_builtin = _SCNetworkInterfaceIsBuiltin(interface);
1545
1546	    if (dbdict == NULL) {
1547		dbdict = lookupMatchingInterface(interface,
1548						 S_dblist,
1549						 if_list,
1550						 i + 1,
1551						 is_builtin ? kCFBooleanTrue : kCFBooleanFalse);
1552		if (dbdict != NULL) {
1553		    unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
1554		    CFRetain(unit);
1555
1556		    SCLog(S_debug, LOG_INFO,
1557			  CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (updating database)"),
1558			  unit);
1559		}
1560	    }
1561
1562	    if ((dbdict != NULL) && (S_prev_active_list != NULL)) {
1563		// update the list of interfaces that were previously named
1564		where = CFArrayGetFirstIndexOfValue(S_prev_active_list,
1565						    CFRangeMake(0, CFArrayGetCount(S_prev_active_list)),
1566						    dbdict);
1567		if (where != kCFNotFound) {
1568		    CFArrayRemoveValueAtIndex(S_prev_active_list, where);
1569		}
1570	    }
1571
1572	    if (dbdict == NULL) {
1573		int 		next_unit	= 0;
1574
1575		if (is_builtin) {
1576		    // built-in interface, try to use the reserved slots
1577		    next_unit = builtinCount(if_list, i, type);
1578
1579		    // But, before claiming a reserved slot we check to see if the
1580		    // slot had previously been used.  If so, and if the slot had been
1581		    // assigned to the same type of interface, then we will perform a
1582		    // replacement (e.g. assume that this was a board swap).  But, if
1583		    // the new interface is a different type then we assume that the
1584		    // built-in configuration has changed and allocate a new unit from
1585		    // the non-reserved slots.
1586
1587		    unit = CFNumberCreate(NULL, kCFNumberIntType, &next_unit);
1588		    if (!builtinAvailable(interface, unit)) {
1589			// if [built-in] unit not available
1590			SCLog(S_debug, LOG_INFO,
1591			      CFSTR(MY_PLUGIN_NAME ": Interface not assigned [built-in] unit %@"),
1592			      unit);
1593			CFRelease(unit);
1594			unit = NULL;
1595		    }
1596		}
1597
1598		if (unit == NULL) {
1599		    // not built-in (or built-in unit not available), allocate from
1600		    // the non-reserved slots
1601		    next_unit = builtinCount(if_list, n, type);
1602
1603		    unit = getHighestUnitForType(type);
1604		    if (unit != NULL) {
1605			int	high_unit;
1606
1607			CFNumberGetValue(unit, kCFNumberIntType, &high_unit);
1608			if (high_unit >= next_unit) {
1609			    next_unit = high_unit + 1;
1610			}
1611		    }
1612
1613		    unit = CFNumberCreate(NULL, kCFNumberIntType, &next_unit);
1614		}
1615
1616		SCLog(S_debug, LOG_INFO,
1617		      CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (%s)"),
1618		      unit,
1619		      is_builtin ? "built-in" : "next available");
1620	    }
1621
1622	retry :
1623
1624#ifdef	USE_REGISTRY_ENTRY_ID
1625	    kr = registerInterfaceWithIORegistryEntryID(S_connect,
1626							entryID,
1627							unit,
1628							(dbdict == NULL) ? kIONetworkStackRegisterInterfaceWithLowestUnit
1629									 : kIONetworkStackRegisterInterfaceWithUnit);
1630	    new_interface = copyNamedInterfaceForIORegistryEntryID(entryID);
1631#else	// USE_REGISTRY_ENTRY_ID
1632	    kr = registerInterfaceWithIOServicePath(S_connect,
1633						    path,
1634						    unit,
1635						    (dbdict == NULL) ? kRegisterInterface
1636								     : kRegisterInterfaceWithFixedUnit);
1637	    new_interface = copyNamedInterfaceForIOKitPath(path);
1638#endif	// USE_REGISTRY_ENTRY_ID
1639	    if (new_interface == NULL) {
1640		const char  *signature;
1641
1642		signature = (dbdict == NULL) ? "failed to name new interface"
1643					     : "failed to name known interface";
1644
1645		SCLog(TRUE, LOG_ERR,
1646		      CFSTR(MY_PLUGIN_NAME ": %s, kr=0x%x\n"
1647			    MY_PLUGIN_NAME ":   path = %@\n"
1648			    MY_PLUGIN_NAME ":   id   = 0x%llx\n"
1649			    MY_PLUGIN_NAME ":   unit = %@"),
1650		      signature,
1651		      kr,
1652		      path,
1653		      entryID,
1654		      unit);
1655
1656		if (S_debug) {
1657		    displayInterface(interface);
1658		}
1659
1660		// report issue w/MessageTracer
1661		str = CFStringCreateWithFormat(NULL, NULL,
1662					       CFSTR("kr=0x%x, path=%@, unit=%@"),
1663					       kr,
1664					       path,
1665					       unit);
1666		reportIssue(signature, str);
1667		CFRelease(str);
1668
1669		if ((dbdict != NULL) && (retries++ < 5)) {
1670		    usleep(50 * 1000);	// sleep 50ms between attempts
1671		    goto retry;
1672		}
1673	    }
1674	    else {
1675		CFNumberRef	new_unit;
1676
1677		if (retries > 0) {
1678		    SCLog(TRUE, LOG_ERR,
1679			  CFSTR(MY_PLUGIN_NAME ": %s interface named after %d %s\n"
1680				MY_PLUGIN_NAME ":   path = %@\n"
1681				MY_PLUGIN_NAME ":   unit = %@"),
1682			  (dbdict == NULL) ? "New" : "Known",
1683			  retries,
1684			  (retries == 1) ? "try" : "tries",
1685			  path,
1686			  unit);
1687
1688#ifdef	SHOW_NAMING_FAILURE
1689		    str = CFStringCreateWithFormat(NULL,
1690						   NULL,
1691						   CFSTR("\"%s\" interface named after %d %s, unit = %@"),
1692						   (dbdict == NULL) ? "New" : "Known",
1693						   retries,
1694						   (retries == 1) ? "try" : "tries",
1695						   unit);
1696		    CFUserNotificationDisplayNotice(0,
1697						    kCFUserNotificationStopAlertLevel,
1698						    NULL,
1699						    NULL,
1700						    NULL,
1701						    str,
1702						    CFSTR("Please report repeated failures."),
1703						    NULL);
1704		    CFRelease(str);
1705#endif	// SHOW_NAMING_FAILURE
1706		}
1707
1708		new_unit = _SCNetworkInterfaceGetIOInterfaceUnit(new_interface);
1709		if (CFEqual(unit, new_unit) == FALSE) {
1710		    SCLog(S_debug, LOG_INFO,
1711			  CFSTR(MY_PLUGIN_NAME
1712				": interface type %@ assigned "
1713				"unit %@ instead of %@"),
1714			  type, new_unit, unit);
1715		}
1716		if (S_debug) {
1717		    displayInterface(new_interface);
1718		}
1719
1720		// update if_list (with the interface name & unit)
1721		CFArraySetValueAtIndex(if_list, i, new_interface);
1722		CFRelease(new_interface);
1723		interface = new_interface;	// if_list holds the reference
1724
1725		if (is_builtin && (S_prev_active_list != NULL)) {
1726		    CFIndex	where;
1727
1728		    // update the list of [built-in] interfaces that were previously named
1729		    if (lookupInterfaceByUnit(S_prev_active_list, interface, &where) != NULL) {
1730			SCLog(S_debug, LOG_INFO,
1731			      CFSTR(MY_PLUGIN_NAME ":   and updated database (new address)"));
1732			CFArrayRemoveValueAtIndex(S_prev_active_list, where);
1733		    }
1734		}
1735		replaceInterface(interface);
1736	    }
1737	    CFRelease(unit);
1738	}
1739    }
1740    return;
1741}
1742
1743#if	!TARGET_OS_IPHONE
1744static void
1745updateNetworkConfiguration(CFArrayRef if_list)
1746{
1747    Boolean		do_commit	= FALSE;
1748    CFIndex		i;
1749    CFIndex		n;
1750    SCPreferencesRef	prefs		= NULL;
1751    SCNetworkSetRef	set		= NULL;
1752
1753    prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL);
1754
1755    set = SCNetworkSetCopyCurrent(prefs);
1756    if (set == NULL) {
1757	SCLog(TRUE, LOG_ERR, CFSTR(MY_PLUGIN_NAME ": No current set"));
1758	goto done;
1759    }
1760
1761    n = CFArrayGetCount(if_list);
1762    for (i = 0; i < n; i++) {
1763	SCNetworkInterfaceRef	interface;
1764
1765	interface = CFArrayGetValueAtIndex(if_list, i);
1766	if (SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface)) {
1767	    SCLog(TRUE, LOG_INFO,
1768		  CFSTR(MY_PLUGIN_NAME ": adding default configuration for %s"),
1769		  SCNetworkInterfaceGetBSDName(interface));
1770	    do_commit = TRUE;
1771	}
1772    }
1773
1774    if (do_commit) {
1775	Boolean	ok;
1776
1777	ok = SCPreferencesCommitChanges(prefs);
1778	if (!ok) {
1779	    SCLog(TRUE, LOG_INFO,
1780		  CFSTR(MY_PLUGIN_NAME ": updateNetworkConfiguration: SCPreferencesCommitChanges() failed: %s"),
1781		  SCErrorString(SCError()));
1782	    goto done;
1783	}
1784
1785	ok = SCPreferencesApplyChanges(prefs);
1786	if (!ok) {
1787	    SCLog(TRUE, LOG_INFO,
1788		  CFSTR(MY_PLUGIN_NAME ": updateNetworkConfiguration: SCPreferencesApplyChanges() failed: %s"),
1789		  SCErrorString(SCError()));
1790	    goto done;
1791	}
1792    }
1793
1794  done :
1795
1796    if (set != NULL) {
1797	CFRelease(set);
1798	set = NULL;
1799    }
1800
1801    if (prefs != NULL) {
1802	CFRelease(prefs);
1803	prefs = NULL;
1804    }
1805
1806    return;
1807}
1808#endif	// !TARGET_OS_IPHONE
1809
1810static void
1811updateInterfaces()
1812{
1813    if (S_connect == MACH_PORT_NULL) {
1814	// if we don't have the "IONetworkStack" connect object
1815	return;
1816    }
1817
1818    if (S_iflist != NULL) {
1819	CFIndex	n;
1820
1821	n = CFArrayGetCount(S_iflist);
1822	if (n > 1) {
1823	    CFArraySortValues(S_iflist, CFRangeMake(0, n), _SCNetworkInterfaceCompare, NULL);
1824	}
1825	nameInterfaces(S_iflist);
1826    }
1827
1828    if (isQuiet()) {
1829	/*
1830	 * The registry [matching] has quiesced so let's
1831	 * - save the DB with the interfaces that have been named
1832	 * - update the VLAN/BOND configuration
1833	 * - tell everyone that we've finished (at least for now)
1834	 * - log those interfaces which are no longer present
1835	 *   in the HW config (or have yet to show up).
1836	 */
1837	writeInterfaceList(S_dblist);
1838	updateVirtualNetworkInterfaceConfiguration(NULL, kSCPreferencesNotificationApply, NULL);
1839
1840#if	!TARGET_OS_IPHONE
1841	if (access("/usr/libexec/UserEventAgent",  X_OK) == -1
1842	    && errno == ENOENT) {
1843	    /*
1844	     * We are most likely booted into the Recovery OS with no "SCMonitor"
1845	     * UserEventAgent plugin running so let's make sure we update the
1846	     * network configuration for new interfaces.
1847	     */
1848	    updateNetworkConfiguration(S_iflist);
1849	}
1850#endif	// !TARGET_OS_IPHONE
1851
1852	updateStore();
1853
1854	if (S_iflist != NULL) {
1855	    CFRelease(S_iflist);
1856	    S_iflist = NULL;
1857	}
1858
1859	if (S_prev_active_list != NULL) {
1860	    if (S_debug) {
1861		CFIndex	i;
1862		CFIndex	n;
1863
1864		n = CFArrayGetCount(S_prev_active_list);
1865		if (n > 0) {
1866		    SCLog(TRUE, LOG_INFO,
1867			  CFSTR(MY_PLUGIN_NAME ": Interface%s not [yet] active"),
1868			  (n > 1) ? "s" : "");
1869		}
1870		for (i = 0; i < n; i++) {
1871		    CFDictionaryRef	if_dict;
1872		    CFStringRef		name;
1873		    CFNumberRef		type;
1874		    CFNumberRef		unit;
1875
1876		    if_dict = CFArrayGetValueAtIndex(S_prev_active_list, i);
1877		    name = CFDictionaryGetValue(if_dict, CFSTR(kIOBSDNameKey));
1878		    type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType));
1879		    unit = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceUnit));
1880		    SCLog(TRUE, LOG_INFO,
1881			  CFSTR(MY_PLUGIN_NAME ":   %s%@%sType: %@, Unit: %@"),
1882			  (name != NULL) ? "BSD Name: " : "",
1883			  (name != NULL) ? name : CFSTR(""),
1884			  (name != NULL) ? ", " : "",
1885			  type,
1886			  unit);
1887		}
1888	    }
1889	    CFRelease(S_prev_active_list);
1890	    S_prev_active_list = NULL;
1891	}
1892    } else {
1893	if ((S_prev_active_list != NULL) && (CFArrayGetCount(S_prev_active_list) == 0)) {
1894	    /*
1895	     * if we've named all of the interfaces that
1896	     * were used during the previous boot.
1897	     */
1898	    addTimestamp(S_state, CFSTR("*RELEASE*"));
1899	    SCLog(S_debug, LOG_INFO,
1900		  CFSTR(MY_PLUGIN_NAME ": last boot interfaces have been named"));
1901	    updateStore();
1902	    CFRelease(S_prev_active_list);
1903	    S_prev_active_list = NULL;
1904	}
1905    }
1906
1907    return;
1908}
1909
1910#if	!TARGET_OS_EMBEDDED
1911static CFComparisonResult
1912compareMacAddress(const void *val1, const void *val2, void *context)
1913{
1914    CFDataRef		mac1	= (CFDataRef)val1;
1915    CFDataRef		mac2	= (CFDataRef)val2;
1916    CFIndex		n1;
1917    CFIndex		n2;
1918    CFComparisonResult	res;
1919
1920    n1 = CFDataGetLength(mac1);
1921    n2 = CFDataGetLength(mac2);
1922    if (n1 < n2) {
1923	res = kCFCompareLessThan;
1924     } else if (n2 > n1) {
1925	res = kCFCompareGreaterThan;
1926     } else {
1927	res = bcmp(CFDataGetBytePtr(mac1), CFDataGetBytePtr(mac2), n1);
1928     }
1929
1930     return res;
1931}
1932
1933static CFStringRef
1934copyEthernetUUID()
1935{
1936    CFDataRef		addr;
1937    CFMutableArrayRef	addrs	= NULL;
1938    CFStringRef		guid	= NULL;
1939    CFIndex		i;
1940    CFIndex		n;
1941
1942    addrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1943    n = (S_dblist != NULL) ? CFArrayGetCount(S_dblist) : 0;
1944    for (i = 0; i < n; i++) {
1945	CFBooleanRef	builtin;
1946	CFDictionaryRef	dict;
1947	CFStringRef	type;
1948
1949	dict = CFArrayGetValueAtIndex(S_dblist, i);
1950	type = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceType));
1951	if (!isA_CFString(type) || !CFEqual(type, kSCNetworkInterfaceTypeEthernet)) {
1952	    continue;
1953	}
1954	builtin = CFDictionaryGetValue(dict, CFSTR(kIOBuiltin));
1955	if (!isA_CFBoolean(builtin) || !CFBooleanGetValue(builtin)) {
1956	    continue;
1957	}
1958	addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
1959	if (!isA_CFData(addr) || (CFDataGetLength(addr) != ETHER_ADDR_LEN)) {
1960	    continue;
1961	}
1962	CFArrayAppendValue(addrs, addr);
1963    }
1964
1965    if (CFArrayGetCount(addrs) == 0) {
1966	// if no ethernet interfaces, look for wireless
1967	for (i = 0; i < n; i++) {
1968	    CFDictionaryRef	dict;
1969	    CFStringRef		type;
1970
1971	    dict = CFArrayGetValueAtIndex(S_dblist, i);
1972	    type = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceType));
1973	    if (!isA_CFString(type) || !CFEqual(type, kSCNetworkInterfaceTypeIEEE80211)) {
1974		continue;
1975	    }
1976	    addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
1977	    if (!isA_CFData(addr) || (CFDataGetLength(addr) != ETHER_ADDR_LEN)) {
1978		continue;
1979	    }
1980	    CFArrayAppendValue(addrs, addr);
1981	}
1982    }
1983
1984    n = CFArrayGetCount(addrs);
1985    switch (n) {
1986	case 0 :
1987	    // if no network interfaces
1988	    break;
1989	default :
1990	    // sort by MAC address
1991	    CFArraySortValues(addrs, CFRangeMake(0, n), compareMacAddress, NULL);
1992
1993	    // fall through
1994	case 1 : {
1995	    CFUUIDBytes		bytes	= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
1996					    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
1997	    CFUUIDRef		uuid;
1998
1999	    // set GUID
2000	    addr = CFArrayGetValueAtIndex(addrs, 0);
2001	    bcopy(CFDataGetBytePtr(addr),
2002		  (void *)&bytes + sizeof(bytes) - ETHER_ADDR_LEN,
2003		  ETHER_ADDR_LEN);
2004	    uuid = CFUUIDCreateFromUUIDBytes(NULL, bytes);
2005	    guid = CFUUIDCreateString(NULL, uuid);
2006	    CFRelease(uuid);
2007
2008	    SCLog(TRUE, LOG_INFO,
2009		  CFSTR(MY_PLUGIN_NAME ": setting platform UUID [MAC] = %@"),
2010		  guid);
2011	    break;
2012	}
2013    }
2014
2015    if (addrs != NULL) CFRelease(addrs);
2016    return guid;
2017}
2018
2019#ifndef kIOPlatformUUIDKey
2020#define kIOPlatformUUIDKey "IOPlatformUUID"
2021#endif
2022static void
2023updatePlatformUUID()
2024{
2025    CFStringRef		guid	= NULL;
2026    kern_return_t	kr;
2027    io_registry_entry_t	platform;
2028
2029    platform = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/");
2030    if (platform == MACH_PORT_NULL) {
2031	goto done;
2032    }
2033
2034    guid = IORegistryEntryCreateCFProperty(platform, CFSTR(kIOPlatformUUIDKey), NULL, 0);
2035    if (guid != NULL) {
2036	// if GUID already defined
2037	goto done;
2038    }
2039
2040    guid = copyEthernetUUID();
2041    if (guid == NULL) {
2042	CFUUIDRef   uuid;
2043
2044	uuid = CFUUIDCreate(NULL);
2045	guid = CFUUIDCreateString(NULL, uuid);
2046	CFRelease(uuid);
2047
2048	SCLog(TRUE, LOG_INFO,
2049	      CFSTR(MY_PLUGIN_NAME ": setting platform UUID [random] = %@"),
2050	      guid);
2051    }
2052
2053if (getenv("DO_NOT_SET_PLATFORM_UUID") == NULL) {
2054    kr = IORegistryEntrySetCFProperty(platform, CFSTR(kIOPlatformUUIDKey), guid);
2055    if (kr != KERN_SUCCESS) {
2056	SCLog(TRUE, LOG_ERR,
2057	      CFSTR(MY_PLUGIN_NAME ": IORegistryEntrySetCFProperty(platform UUID) failed, kr=0x%x"),
2058	      kr);
2059    }
2060}
2061
2062    addTimestamp(S_state, CFSTR("*PLATFORM-UUID*"));
2063    updateStore();
2064
2065  done :
2066
2067    if (S_vproc_transaction != NULL) {
2068	vproc_transaction_end(NULL, S_vproc_transaction);
2069	S_vproc_transaction = NULL;
2070    }
2071
2072    if (platform != MACH_PORT_NULL) IOObjectRelease(platform);
2073    if (guid != NULL) CFRelease(guid);
2074    return;
2075}
2076#endif	// !TARGET_OS_EMBEDDED
2077
2078static void
2079interfaceArrivalCallback(void *refcon, io_iterator_t iter)
2080{
2081    io_object_t	obj;
2082
2083    while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) {
2084	SCNetworkInterfaceRef	interface;
2085
2086	interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj);
2087	if (interface != NULL) {
2088	    if (S_iflist == NULL) {
2089		S_iflist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2090	    }
2091	    CFArrayAppendValue(S_iflist, interface);
2092	    CFRelease(interface);
2093	}
2094	IOObjectRelease(obj);
2095    }
2096
2097    updateInterfaces();
2098    return;
2099}
2100
2101/*
2102 * Function: stackCallback
2103 * Purpose:
2104 *   Get a reference to the single IONetworkStack object instance in
2105 *   the kernel. Naming requests must be sent to this object, which is
2106 *   attached as a client to all network interface objects in the system.
2107 * Note:
2108 *   Call IOObjectRelease on the returned object.
2109 */
2110static void
2111stackCallback(void *refcon, io_iterator_t iter)
2112{
2113    kern_return_t	kr;
2114    io_object_t		stack;
2115
2116    stack = IOIteratorNext(iter);
2117    if (stack == MACH_PORT_NULL) {
2118	goto error;
2119    }
2120
2121    kr = IOServiceOpen(stack, mach_task_self(), 0, &S_connect);
2122    if (kr != KERN_SUCCESS) {
2123	SCLog(TRUE, LOG_ERR,
2124	      CFSTR(MY_PLUGIN_NAME ": IOServiceOpen returned 0x%x"),
2125	      kr);
2126	goto error;
2127    }
2128
2129    addTimestamp(S_state, CFSTR("*STACK*"));
2130    SCLog(S_debug, LOG_INFO,
2131	  CFSTR(MY_PLUGIN_NAME ": IONetworkStack found"));
2132
2133    if (S_stack != MACH_PORT_NULL) {
2134	IOObjectRelease(S_stack);
2135	S_stack = MACH_PORT_NULL;
2136    }
2137
2138    if ((S_timer != NULL) && CFRunLoopTimerIsValid(S_timer)) {
2139	// With the IONetworkStack object now available we can
2140	// reset (shorten?) the time we are willing to wait for
2141	// IOKit to quiesce.
2142	CFRunLoopTimerSetNextFireDate(S_timer,
2143				      CFAbsoluteTimeGetCurrent() + S_quiet_timeout);
2144    }
2145
2146    updateInterfaces();
2147
2148 error:
2149    if (stack != MACH_PORT_NULL) {
2150	IOObjectRelease(stack);
2151    }
2152
2153    return;
2154}
2155
2156static void
2157quietCallback(void		*refcon,
2158	      io_service_t	service,
2159	      natural_t		messageType,
2160	      void		*messageArgument)
2161{
2162    if (messageArgument != NULL) {
2163	// if not yet quiet
2164	return;
2165    }
2166
2167    if (messageType == kIOMessageServiceBusyStateChange) {
2168	addTimestamp(S_state, CFSTR("*QUIET*"));
2169	SCLog(S_debug, LOG_INFO,
2170	      CFSTR(MY_PLUGIN_NAME ": IOKit quiet"));
2171    }
2172
2173    if (S_connect == MACH_PORT_NULL) {
2174	SCLog(TRUE, LOG_ERR,
2175	      CFSTR(MY_PLUGIN_NAME ": No network stack object"));
2176	return;
2177    }
2178
2179    if (S_quiet != MACH_PORT_NULL) {
2180	IOObjectRelease(S_quiet);
2181	S_quiet = MACH_PORT_NULL;
2182    }
2183
2184    if (S_timer != NULL) {
2185	CFRunLoopTimerInvalidate(S_timer);
2186	CFRelease(S_timer);
2187	S_timer = NULL;
2188    }
2189
2190    // grab (and name) any additional interfaces.
2191    interfaceArrivalCallback((void *)S_notify, S_iter);
2192
2193#if	!TARGET_OS_EMBEDDED
2194    updatePlatformUUID();
2195#endif	// !TARGET_OS_EMBEDDED
2196
2197    return;
2198}
2199
2200static void
2201iterateRegistryBusy(io_iterator_t iterator, CFArrayRef nodes, CFMutableStringRef snapshot, int *count)
2202{
2203    kern_return_t	kr  = kIOReturnSuccess;;
2204    io_object_t		obj;
2205
2206    while ((kr == kIOReturnSuccess) &&
2207	   ((obj = IOIteratorNext(iterator)) != MACH_PORT_NULL)) {
2208	uint64_t		accumulated_busy_time;
2209	uint32_t		busy_state;
2210	io_name_t		location;
2211	io_name_t		name;
2212	CFMutableArrayRef	newNodes;
2213	uint64_t		state;
2214	CFMutableStringRef	str	= NULL;
2215
2216	if (nodes == NULL) {
2217	    newNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2218	} else {
2219	    newNodes = CFArrayCreateMutableCopy(NULL, 0, nodes);
2220	}
2221	assert(newNodes != NULL);
2222
2223	kr = IORegistryEntryGetName(obj, name);
2224	if (kr != kIOReturnSuccess) {
2225	    SCLog(TRUE, LOG_ERR,
2226		  CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryEntryGetName returned 0x%x"),
2227		  kr);
2228	    goto next;
2229	}
2230
2231	str = CFStringCreateMutable(NULL, 0);
2232	CFStringAppendCString(str, name, kCFStringEncodingUTF8);
2233
2234	kr = IORegistryEntryGetLocationInPlane(obj, kIOServicePlane, location);
2235	switch (kr) {
2236	    case kIOReturnSuccess :
2237		CFStringAppendCString(str, "@", kCFStringEncodingUTF8);
2238		CFStringAppendCString(str, location, kCFStringEncodingUTF8);
2239		break;
2240	    case kIOReturnNotFound :
2241		break;
2242	    default :
2243		SCLog(TRUE, LOG_ERR,
2244		      CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryEntryGetLocationInPlane returned 0x%x"),
2245		      kr);
2246		CFRelease(str);
2247		goto next;
2248	}
2249
2250	CFArrayAppendValue(newNodes, str);
2251	CFRelease(str);
2252
2253	kr = IOServiceGetBusyStateAndTime(obj, &state, &busy_state, &accumulated_busy_time);
2254	if (kr != kIOReturnSuccess) {
2255	    SCLog(TRUE, LOG_ERR,
2256		  CFSTR(MY_PLUGIN_NAME ": captureBusy IOServiceGetBusyStateAndTime returned 0x%x"),
2257		  kr);
2258	    goto next;
2259	}
2260
2261#ifdef	TEST_SNAPSHOT
2262	// report all nodes
2263	busy_state = 1;
2264#endif	// TEST_SNAPSHOT
2265
2266	if (busy_state != 0) {
2267	    CFStringRef	path;
2268
2269	    if ((*count)++ == 0) {
2270		CFStringAppend(snapshot, CFSTR("Busy services :"));
2271	    }
2272
2273	    path = CFStringCreateByCombiningStrings(NULL, newNodes, CFSTR("/"));
2274	    CFStringAppendFormat(snapshot, NULL,
2275				 CFSTR("\n  %@ [%s%s%s%d, %lld ms]"),
2276				 path,
2277				 (state & kIOServiceRegisteredState) ? "" : "!registered, ",
2278				 (state & kIOServiceMatchedState)    ? "" : "!matched, ",
2279				 (state & kIOServiceInactiveState)   ? "inactive, " : "",
2280				 busy_state,
2281				 accumulated_busy_time / kMillisecondScale);
2282	    CFRelease(path);
2283	}
2284
2285	kr = IORegistryIteratorEnterEntry(iterator);
2286	if (kr != kIOReturnSuccess) {
2287	    SCLog(TRUE, LOG_ERR,
2288		  CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryIteratorEnterEntry returned 0x%x"),
2289		  kr);
2290	    goto next;
2291	}
2292
2293	iterateRegistryBusy(iterator, newNodes, snapshot, count);
2294
2295	kr = IORegistryIteratorExitEntry(iterator);
2296	if (kr != kIOReturnSuccess) {
2297	    SCLog(TRUE, LOG_ERR,
2298		  CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryIteratorExitEntry returned 0x%x"),
2299		  kr);
2300	}
2301
2302      next :
2303
2304	CFRelease(newNodes);
2305	IOObjectRelease(obj);
2306    }
2307
2308    return;
2309}
2310
2311static CF_RETURNS_RETAINED CFStringRef
2312captureBusy()
2313{
2314    int			count		= 0;
2315    io_iterator_t	iterator	= MACH_PORT_NULL;
2316    kern_return_t	kr;
2317    CFMutableStringRef	snapshot;
2318
2319    snapshot = CFStringCreateMutable(NULL, 0);
2320
2321    kr = IORegistryCreateIterator(kIOMasterPortDefault,
2322				  kIOServicePlane,
2323				  0,
2324				  &iterator);
2325    if (kr != kIOReturnSuccess) {
2326	SCLog(TRUE, LOG_ERR,
2327	      CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryCreateIterator returned 0x%x"),
2328	      kr);
2329	return snapshot;
2330    }
2331
2332    iterateRegistryBusy(iterator, NULL, snapshot, &count);
2333    if (count == 0) {
2334	CFStringAppend(snapshot, CFSTR("w/no busy services"));
2335    }
2336
2337    IOObjectRelease(iterator);
2338    return snapshot;
2339}
2340
2341static void
2342timerCallback(CFRunLoopTimerRef	timer, void *info)
2343{
2344    CFStringRef	snapshot;
2345
2346    // We've been waiting for IOKit to quiesce and it just
2347    // hasn't happenned.  Time to just move on!
2348    addTimestamp(S_state, CFSTR("*TIMEOUT*"));
2349
2350    // log busy nodes
2351    snapshot = captureBusy();
2352    SCLog(TRUE, LOG_ERR,
2353	  CFSTR(MY_PLUGIN_NAME ": timed out waiting for IOKit to quiesce\n%@"),
2354	  snapshot);
2355    reportIssue("timed out waiting for IOKit to quiesce", snapshot);
2356    CFRelease(snapshot);
2357
2358    quietCallback((void *)S_notify, MACH_PORT_NULL, 0, NULL);
2359    return;
2360}
2361
2362static Boolean
2363setup_IOKit(CFBundleRef bundle)
2364{
2365    uint32_t		busy;
2366    kern_return_t	kr;
2367    mach_port_t		masterPort	= MACH_PORT_NULL;
2368    Boolean		ok		= FALSE;
2369    io_object_t		root		= MACH_PORT_NULL;
2370
2371    // read DB of previously named network interfaces
2372    S_dblist = readInterfaceList();
2373    if (S_dblist != NULL) {
2374	CFIndex	n;
2375
2376	n = CFArrayGetCount(S_dblist);
2377	if (n > 1) {
2378	    CFArraySortValues(S_dblist, CFRangeMake(0, n), if_unit_compare, NULL);
2379	}
2380    }
2381
2382    // get interfaces that were named during the last boot
2383    S_prev_active_list = previouslyActiveInterfaces();
2384
2385    // track how long we've waited to see each interface.
2386    S_state = CFDictionaryCreateMutable(NULL,
2387					0,
2388					&kCFTypeDictionaryKeyCallBacks,
2389					&kCFTypeDictionaryValueCallBacks);
2390    addTimestamp(S_state, CFSTR("*START*"));
2391
2392    // Creates and returns a notification object for receiving IOKit
2393    // notifications of new devices or state changes.
2394    kr = IOMasterPort(bootstrap_port, &masterPort);
2395    if (kr != KERN_SUCCESS) {
2396	SCLog(TRUE, LOG_ERR,
2397	      CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
2398	      kr);
2399	goto done;
2400    }
2401
2402    S_notify = IONotificationPortCreate(masterPort);
2403    if (S_notify == NULL) {
2404	SCLog(TRUE, LOG_ERR,
2405	      CFSTR(MY_PLUGIN_NAME ": IONotificationPortCreate failed"));
2406	goto done;
2407    }
2408
2409    // watch IOKit matching activity
2410    root = IORegistryEntryFromPath(masterPort, kIOServicePlane ":/");
2411    if (root == MACH_PORT_NULL) {
2412	SCLog(TRUE, LOG_ERR,
2413	      CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath failed"));
2414	goto done;
2415    }
2416
2417    kr = IOServiceAddInterestNotification(S_notify,
2418					  root,
2419					  kIOBusyInterest,
2420					  &quietCallback,
2421					  (void *)S_notify,	// refCon
2422					  &S_quiet);		// notification
2423    if (kr != KERN_SUCCESS) {
2424	SCLog(TRUE, LOG_ERR,
2425	      CFSTR(MY_PLUGIN_NAME ": IOServiceAddInterestNotification returned 0x%x"),
2426	      kr);
2427	goto done;
2428    }
2429
2430    kr = IOServiceGetBusyState(root, &busy);
2431    if (kr != KERN_SUCCESS) {
2432	SCLog(TRUE, LOG_ERR,
2433	      CFSTR(MY_PLUGIN_NAME ": IOServiceGetBusyState returned 0x%x"),
2434	      kr);
2435	goto done;
2436    }
2437
2438    // add a timer so we don't wait forever for IOKit to quiesce
2439    S_timer = CFRunLoopTimerCreate(NULL,
2440				   CFAbsoluteTimeGetCurrent() + S_stack_timeout,
2441				   0,
2442				   0,
2443				   0,
2444				   timerCallback,
2445				   NULL);
2446    if (S_timer == NULL) {
2447	SCLog(TRUE, LOG_ERR,
2448	      CFSTR(MY_PLUGIN_NAME ": CFRunLoopTimerCreate failed"));
2449	goto done;
2450    }
2451
2452    CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, kCFRunLoopDefaultMode);
2453
2454    // watch for the introduction of the IONetworkStack
2455    kr = IOServiceAddMatchingNotification(S_notify,
2456					  kIOFirstMatchNotification,
2457					  IOServiceMatching("IONetworkStack"),
2458					  &stackCallback,
2459					  (void *)S_notify,	// refCon
2460					  &S_stack);		// notification
2461    if (kr != KERN_SUCCESS) {
2462	SCLog(TRUE, LOG_ERR,
2463	      CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"),
2464	      kr);
2465	goto done;
2466    }
2467
2468    // check and see if the stack is already available and arm the
2469    // notification for its introduction.
2470    stackCallback((void *)S_notify, S_stack);
2471
2472    // watch for the introduction of new network interfaces
2473    kr = IOServiceAddMatchingNotification(S_notify,
2474					  kIOFirstMatchNotification,
2475					  IOServiceMatching("IONetworkInterface"),
2476					  &interfaceArrivalCallback,
2477					  (void *)S_notify,	// refCon
2478					  &S_iter);		// notification
2479    if (kr != KERN_SUCCESS) {
2480	SCLog(TRUE, LOG_ERR,
2481	      CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"),
2482	      kr);
2483	goto done;
2484    }
2485
2486    // Get the current list of matches and arm the notification for
2487    // future interface arrivals.
2488    interfaceArrivalCallback((void *)S_notify, S_iter);
2489
2490    // Check if IOKit has already quiesced.
2491    quietCallback((void *)S_notify,
2492		  MACH_PORT_NULL,
2493		  kIOMessageServiceBusyStateChange,
2494		  (void *)(uintptr_t)busy);
2495
2496    CFRunLoopAddSource(CFRunLoopGetCurrent(),
2497		       IONotificationPortGetRunLoopSource(S_notify),
2498		       kCFRunLoopDefaultMode);
2499
2500#ifdef	WAIT_PREVIOUS_BOOT_INTERFACES_OR_QUIET
2501    /*
2502     * Start the wheels turning until we've named all of
2503     * the interfaces that were used during the previous
2504     * boot, until IOKit [matching] has quiesced, or
2505     * until we've waited long enough.
2506     */
2507    CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, MY_PLUGIN_ID);
2508    CFRunLoopAddSource(CFRunLoopGetCurrent(),
2509		       IONotificationPortGetRunLoopSource(S_notify),
2510		       MY_PLUGIN_ID);
2511    while (S_prev_active_list != NULL) {
2512	int	rlStatus;
2513
2514	rlStatus = CFRunLoopRunInMode(MY_PLUGIN_ID, 1.0e10, TRUE);
2515    }
2516#endif	/* WAIT_PREVIOUS_BOOT_INTERFACES_OR_QUIET */
2517
2518#if	!TARGET_OS_EMBEDDED
2519    if (S_dblist != NULL) {
2520	// apply special handling for the BT-PAN interface (if present)
2521	CFArrayApplyFunction(S_dblist,
2522			     CFRangeMake(0, CFArrayGetCount(S_dblist)),
2523			     updateBTPANInformation,
2524			     NULL);
2525    }
2526#endif	// !TARGET_OS_EMBEDDED
2527
2528    ok = TRUE;
2529
2530 done:
2531    if (root != MACH_PORT_NULL) {
2532	IOObjectRelease(root);
2533    }
2534    if (masterPort != MACH_PORT_NULL) {
2535	mach_port_deallocate(mach_task_self(), masterPort);
2536    }
2537
2538    return ok;
2539}
2540
2541static Boolean
2542setup_Virtual(CFBundleRef bundle)
2543{
2544    // open a SCPreferences session
2545    S_prefs = SCPreferencesCreate(NULL, CFSTR(MY_PLUGIN_NAME), NULL);
2546    if (S_prefs == NULL) {
2547	SCLog(TRUE, LOG_ERR,
2548	      CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate() failed: %s"),
2549	      SCErrorString(SCError()));
2550	return FALSE;
2551    }
2552
2553    // register for change notifications.
2554    if (!SCPreferencesSetCallback(S_prefs, updateVirtualNetworkInterfaceConfiguration, NULL)) {
2555	SCLog(TRUE, LOG_ERR,
2556	      CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetCallBack() failed: %s"),
2557	      SCErrorString(SCError()));
2558	CFRelease(S_prefs);
2559	return FALSE;
2560    }
2561
2562    // schedule
2563    if (!SCPreferencesScheduleWithRunLoop(S_prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
2564	if (SCError() != kSCStatusNoStoreServer) {
2565	    SCLog(TRUE, LOG_ERR,
2566		  CFSTR(MY_PLUGIN_NAME ": SCPreferencesScheduleWithRunLoop() failed: %s"),
2567			SCErrorString(SCError()));
2568	    CFRelease(S_prefs);
2569	    return FALSE;
2570	}
2571    }
2572
2573    return TRUE;
2574}
2575
2576static void *
2577exec_InterfaceNamer(void *arg)
2578{
2579    CFBundleRef		bundle  = (CFBundleRef)arg;
2580    CFDictionaryRef	dict;
2581
2582    pthread_setname_np(MY_PLUGIN_NAME " thread");
2583
2584    dict = CFBundleGetInfoDictionary(bundle);
2585    if (isA_CFDictionary(dict)) {
2586	CFNumberRef	num;
2587
2588	num = CFDictionaryGetValue(dict, CFSTR(WAIT_STACK_TIMEOUT_KEY));
2589	if (num != NULL) {
2590	    if (!isA_CFNumber(num) ||
2591		!CFNumberGetValue(num, kCFNumberDoubleType, &S_stack_timeout) ||
2592		(S_stack_timeout <= 0.0)) {
2593		SCLog(TRUE, LOG_ERR,
2594		      CFSTR(MY_PLUGIN_NAME ": " WAIT_STACK_TIMEOUT_KEY " value error"));
2595		S_stack_timeout = WAIT_STACK_TIMEOUT_DEFAULT;
2596	    }
2597	}
2598
2599	num = CFDictionaryGetValue(dict, CFSTR(WAIT_QUIET_TIMEOUT_KEY));
2600	if (num != NULL) {
2601	    if (!isA_CFNumber(num) ||
2602		!CFNumberGetValue(num, kCFNumberDoubleType, &S_quiet_timeout) ||
2603		(S_quiet_timeout <= 0.0)) {
2604		SCLog(TRUE, LOG_ERR,
2605		      CFSTR(MY_PLUGIN_NAME ": " WAIT_QUIET_TIMEOUT_KEY " value error"));
2606		S_quiet_timeout = WAIT_QUIET_TIMEOUT_DEFAULT;
2607	    }
2608	}
2609    }
2610
2611    // setup virtual network interface monitoring
2612    if (!setup_Virtual(bundle)) {
2613	goto error;
2614    }
2615
2616    // setup [IOKit] network interface monitoring
2617    if (!setup_IOKit(bundle)) {
2618	goto error;
2619    }
2620
2621#if	!TARGET_OS_EMBEDDED
2622    // keep launchd from SIGKILL'ing us until after the platform-uuid has
2623    // been updated
2624    S_vproc_transaction = vproc_transaction_begin(NULL);
2625#endif	// !TARGET_OS_EMBEDDED
2626
2627    goto done;
2628
2629  error :
2630    if (S_connect != MACH_PORT_NULL) {
2631	IOServiceClose(S_connect);
2632	S_connect = MACH_PORT_NULL;
2633    }
2634    if (S_dblist != NULL) {
2635	CFRelease(S_dblist);
2636	S_dblist = NULL;
2637    }
2638    if (S_iter != MACH_PORT_NULL) {
2639	IOObjectRelease(S_iter);
2640	S_iter = MACH_PORT_NULL;
2641    }
2642    if (S_notify != MACH_PORT_NULL) {
2643	IONotificationPortDestroy(S_notify);
2644    }
2645    if (S_quiet != MACH_PORT_NULL) {
2646	IOObjectRelease(S_quiet);
2647	S_quiet = MACH_PORT_NULL;
2648    }
2649    if (S_stack != MACH_PORT_NULL) {
2650	IOObjectRelease(S_stack);
2651	S_stack = MACH_PORT_NULL;
2652    }
2653    if (S_state != NULL) {
2654	CFRelease(S_state);
2655	S_state = NULL;
2656    }
2657    if (S_timer != NULL) {
2658	CFRunLoopTimerInvalidate(S_timer);
2659	CFRelease(S_timer);
2660	S_timer = NULL;
2661    }
2662
2663  done :
2664    CFRelease(bundle);
2665    CFRunLoopRun();
2666
2667    return NULL;
2668}
2669
2670__private_extern__
2671void
2672load_InterfaceNamer(CFBundleRef bundle, Boolean bundleVerbose)
2673{
2674    pthread_attr_t  tattr;
2675    pthread_t	    tid;
2676
2677    if (bundleVerbose) {
2678	S_debug = TRUE;
2679    }
2680
2681    CFRetain(bundle);	// released in exec_InterfaceNamer
2682
2683    pthread_attr_init(&tattr);
2684    pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
2685    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
2686//  pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
2687    pthread_create(&tid, &tattr, exec_InterfaceNamer, bundle);
2688    pthread_attr_destroy(&tattr);
2689
2690    return;
2691}
2692
2693//------------------------------------------------------------------------
2694// Main function.
2695#ifdef MAIN
2696int
2697main(int argc, char ** argv)
2698{
2699    CFBundleRef bundle;
2700
2701    _sc_log     = FALSE;
2702    _sc_verbose = (argc > 1) ? TRUE : FALSE;
2703
2704    S_debug = _sc_verbose;
2705
2706    bundle = CFBundleGetMainBundle();
2707    CFRetain(bundle);	// released in exec_InterfaceNamer
2708
2709    (void)exec_InterfaceNamer();
2710
2711    /* not reached */
2712    exit(0);
2713    return 0;
2714}
2715#endif /* MAIN */
2716
2717#ifdef	TEST_PLATFORM_UUID
2718int
2719main(int argc, char ** argv)
2720{
2721    CFStringRef	guid;
2722    CFArrayRef	interfaces;
2723
2724    _sc_log     = FALSE;
2725    _sc_verbose = (argc > 1) ? TRUE : FALSE;
2726
2727    S_dblist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2728    interfaces = SCNetworkInterfaceCopyAll();
2729    if (interfaces != NULL) {
2730	CFIndex	i;
2731	CFIndex	n;
2732
2733	n = CFArrayGetCount(interfaces);
2734	for (i = 0; i < n; i++) {
2735	    CFDictionaryRef		dict;
2736	    SCNetworkInterfaceRef	interface;
2737
2738	    interface = CFArrayGetValueAtIndex(interfaces, i);
2739	    dict = createInterfaceDict(interface);
2740	    CFArrayAppendValue(S_dblist, dict);
2741	    CFRelease(dict);
2742	}
2743	CFRelease(interfaces);
2744    }
2745
2746    guid = copyEthernetUUID();
2747    SCPrint(TRUE, stdout, CFSTR("copyEthernetUUID()  = %@\n"), (guid != NULL) ? guid : CFSTR("NULL"));
2748    if (guid != NULL) CFRelease(guid);
2749
2750    updatePlatformUUID();
2751    CFRelease(S_dblist);
2752    exit(0);
2753    return 0;
2754}
2755#endif	/* TEST_PLATFORM_UUID */
2756
2757#ifdef	TEST_SNAPSHOT
2758int
2759main(int argc, char ** argv)
2760{
2761    CFStringRef	snapshot;
2762
2763    _sc_log     = FALSE;
2764    _sc_verbose = (argc > 1) ? TRUE : FALSE;
2765
2766    snapshot = captureBusy();
2767    SCPrint(TRUE, stdout, CFSTR("%@\n"), snapshot);
2768    CFRelease(snapshot);
2769
2770    exit(0);
2771    return 0;
2772}
2773#endif	/* TEST_SNAPSHOT */
2774
2775