1/*
2 * Copyright (c) 2001-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 * 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    asl_object_t  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_release(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(FALSE);
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(FALSE);
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#define	BT_PAN_MAC	BT_PAN_NAME " (MAC)"
594
595static void
596updateBTPANInformation(const void *value, void *context)
597{
598    CFDataRef		addr;
599    CFDictionaryRef	dict    = (CFDictionaryRef)value;
600    CFStringRef		if_name;
601    CFDictionaryRef	info;
602    CFStringRef		name;
603
604    if_name = CFDictionaryGetValue(dict, CFSTR(kIOBSDNameKey));
605    if (!isA_CFString(if_name)) {
606	// if no BSD name
607	return;
608    }
609
610    info = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceInfo));
611    if (!isA_CFDictionary(info)) {
612	// if no SCNetworkInterface info
613	return;
614    }
615
616    name = CFDictionaryGetValue(info, kSCPropUserDefinedName);
617    if (!isA_CFString(name) || !CFEqual(name, CFSTR(BT_PAN_NAME))) {
618	// if not BT-PAN interface
619	return;
620    }
621
622    CFDictionaryAddValue(S_state, CFSTR("_" BT_PAN_NAME "_"), if_name);
623
624    addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
625    if (isA_CFData(addr)) {
626	CFDictionaryAddValue(S_state, CFSTR("_" BT_PAN_MAC "_"), addr);
627    }
628
629    return;
630}
631#endif	// !TARGET_OS_EMBEDDED
632
633static CFDictionaryRef
634createInterfaceDict(SCNetworkInterfaceRef interface)
635{
636    CFMutableDictionaryRef	new_if;
637    CFTypeRef			val;
638
639    new_if = CFDictionaryCreateMutable(NULL,
640				       0,
641				       &kCFTypeDictionaryKeyCallBacks,
642				       &kCFTypeDictionaryValueCallBacks);
643
644    val = _SCNetworkInterfaceCopyInterfaceInfo(interface);
645    if (val != NULL) {
646	CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceInfo), val);
647	CFRelease(val);
648    }
649
650    val = _SCNetworkInterfaceGetIOPath(interface);
651    if (val != NULL) {
652	CFDictionarySetValue(new_if, CFSTR(kIOPathMatchKey), val);
653    }
654
655    val = _SCNetworkInterfaceGetIOInterfaceNamePrefix(interface);
656    if (val != NULL) {
657	CFDictionarySetValue(new_if, CFSTR(kIOInterfaceNamePrefix), val);
658    }
659
660    val = _SCNetworkInterfaceGetIOInterfaceType(interface);
661    if (val != NULL) {
662	CFDictionarySetValue(new_if, CFSTR(kIOInterfaceType), val);
663    }
664
665    val = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
666    if (val != NULL) {
667	CFDictionarySetValue(new_if, CFSTR(kIOInterfaceUnit), val);
668    }
669
670    val = _SCNetworkInterfaceGetHardwareAddress(interface);
671    if (val != NULL) {
672	CFDictionarySetValue(new_if, CFSTR(kIOMACAddress), val);
673    }
674
675    val = SCNetworkInterfaceGetBSDName(interface);
676    if (val != NULL) {
677	CFDictionarySetValue(new_if, CFSTR(kIOBSDNameKey), val);
678    }
679
680    val = SCNetworkInterfaceGetInterfaceType(interface);
681    if (val != NULL) {
682	CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceType), val);
683    }
684
685    CFDictionarySetValue(new_if,
686			 CFSTR(kIOBuiltin),
687			 _SCNetworkInterfaceIsBuiltin(interface) ? kCFBooleanTrue : kCFBooleanFalse);
688
689    CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceActive), kCFBooleanTrue);
690
691    return new_if;
692}
693
694static CFDictionaryRef
695lookupInterfaceByAddress(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
696{
697    CFDataRef	addr;
698    CFIndex	i;
699    CFIndex	n;
700    CFNumberRef	type;
701
702    if (db_list == NULL) {
703	return (NULL);
704    }
705    type = _SCNetworkInterfaceGetIOInterfaceType(interface);
706    addr = _SCNetworkInterfaceGetHardwareAddress(interface);
707    if (type == NULL || addr == NULL) {
708	return (NULL);
709    }
710
711    n = CFArrayGetCount(db_list);
712    for (i = 0; i < n; i++) {
713	CFDataRef	a;
714	CFDictionaryRef	dict = CFArrayGetValueAtIndex(db_list, i);
715	CFNumberRef	t;
716
717	t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
718	a = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
719	if (t == NULL || a == NULL)
720	    continue;
721
722	if (CFEqual(type, t) && CFEqual(addr, a)) {
723	    if (where) {
724		*where = i;
725	    }
726	    return (dict);
727	}
728    }
729    return (NULL);
730}
731
732static CFDictionaryRef
733lookupInterfaceByUnit(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
734{
735    CFIndex 	i;
736    CFIndex	n;
737    CFNumberRef	type;
738    CFNumberRef	unit;
739
740    if (db_list == NULL) {
741	return (NULL);
742    }
743    type = _SCNetworkInterfaceGetIOInterfaceType(interface);
744    unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
745    if (type == NULL || unit == NULL) {
746	return (NULL);
747    }
748
749    n = CFArrayGetCount(db_list);
750    for (i = 0; i < n; i++) {
751	CFDictionaryRef	dict = CFArrayGetValueAtIndex(db_list, i);
752	CFNumberRef	t;
753	CFNumberRef	u;
754
755	t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
756	u = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
757	if (t == NULL || u == NULL) {
758	    continue;
759	}
760
761	if (CFEqual(type, t) && CFEqual(unit, u)) {
762	    if (where)
763		*where = i;
764	    return (dict);
765	}
766    }
767    return (NULL);
768}
769
770typedef struct {
771    CFDictionaryRef	    match_info;
772    CFStringRef		    match_type;
773    CFBooleanRef	    match_builtin;
774    CFMutableArrayRef	    matches;
775} matchContext, *matchContextRef;
776
777static CF_RETURNS_RETAINED CFDictionaryRef
778thinInterfaceInfo(CFDictionaryRef info)
779{
780    CFNumberRef	num;
781    int		vid;
782
783    if (CFDictionaryGetValueIfPresent(info, CFSTR(kUSBVendorID), (const void **)&num)
784	&& isA_CFNumber(num)
785	&& CFNumberGetValue(num, kCFNumberIntType, &vid)
786	&& (vid == kIOUSBVendorIDAppleComputer)) {
787	CFMutableDictionaryRef  thin;
788
789	// if this is an Apple USB device than we trust that
790	// the non-localized name will be correct.
791	thin = CFDictionaryCreateMutableCopy(NULL, 0, info);
792	CFDictionaryRemoveValue(thin, CFSTR(kUSBProductString));
793	CFDictionaryRemoveValue(thin, CFSTR(kUSBVendorID));
794	CFDictionaryRemoveValue(thin, CFSTR(kUSBProductID));
795	return thin;
796    }
797
798    return CFRetain(info);
799}
800
801static Boolean
802matchInterfaceInfo(CFDictionaryRef known_info, CFDictionaryRef match_info)
803{
804    Boolean match;
805
806    match = _SC_CFEqual(known_info, match_info);
807    if (!match &&
808	isA_CFDictionary(known_info) &&
809	isA_CFDictionary(match_info)) {
810
811	// if not an exact match, try thinning
812	known_info = thinInterfaceInfo(known_info);
813	match_info = thinInterfaceInfo(match_info);
814	match = _SC_CFEqual(known_info, match_info);
815	if (known_info != NULL) CFRelease(known_info);
816	if (match_info != NULL) CFRelease(match_info);
817    }
818
819    return match;
820}
821
822static void
823matchKnown(const void *value, void *context)
824{
825    CFDictionaryRef	known_dict	= (CFDictionaryRef)value;
826    matchContextRef	match_context	= (matchContextRef)context;
827
828    // match interface type
829    {
830	CFStringRef	known_type;
831
832	known_type = CFDictionaryGetValue(known_dict, CFSTR(kSCNetworkInterfaceType));
833	if (!_SC_CFEqual(known_type, match_context->match_type)) {
834	    return;
835	}
836    }
837
838    // match SCNetworkInterfaceInfo
839    {
840	CFDictionaryRef	known_info;
841
842	known_info = CFDictionaryGetValue(known_dict, CFSTR(kSCNetworkInterfaceInfo));
843	if (!matchInterfaceInfo(known_info, match_context->match_info)) {
844	    return;
845	}
846    }
847
848    // if requested, match [non-]builtin
849    if (match_context->match_builtin != NULL) {
850	CFBooleanRef	known_builtin;
851
852	known_builtin = CFDictionaryGetValue(known_dict, CFSTR(kIOBuiltin));
853	if (!isA_CFBoolean(known_builtin)) {
854	    known_builtin = kCFBooleanFalse;
855	}
856	if (!_SC_CFEqual(known_builtin, match_context->match_builtin)) {
857	    return;
858	}
859    }
860
861    // if we have a match
862    if (match_context->matches == NULL) {
863	match_context->matches = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
864    }
865    CFArrayAppendValue(match_context->matches, known_dict);
866
867    return;
868}
869
870static void
871matchUnnamed(const void *value, void *context)
872{
873    SCNetworkInterfaceRef   known_if	    = (SCNetworkInterfaceRef)value;
874    matchContextRef	    match_context   = (matchContextRef)context;
875
876    if (match_context->matches == NULL) {
877	return;
878    }
879
880    // match interface type
881    {
882	CFStringRef	known_type;
883
884	known_type = SCNetworkInterfaceGetInterfaceType(known_if);
885	if (!_SC_CFEqual(known_type, match_context->match_type)) {
886	    return;
887	}
888    }
889
890    // match SCNetworkInterfaceInfo
891    {
892	CFDictionaryRef	known_info;
893	Boolean		match;
894
895	known_info = _SCNetworkInterfaceCopyInterfaceInfo(known_if);
896	match = matchInterfaceInfo(known_info, match_context->match_info);
897	if (known_info != NULL) CFRelease(known_info);
898	if (!match) {
899	    return;
900	}
901    }
902
903    // if requested, match [non-]builtin
904    if (match_context->match_builtin != NULL) {
905	CFBooleanRef	known_builtin;
906
907	known_builtin = _SCNetworkInterfaceIsBuiltin(known_if) ? kCFBooleanTrue
908							       : kCFBooleanFalse;
909	if (!_SC_CFEqual(known_builtin, match_context->match_builtin)) {
910	    return;
911	}
912    }
913
914    // if we have a match
915    CFRelease(match_context->matches);
916    match_context->matches = NULL;
917
918    return;
919}
920
921static Boolean
922interfaceExists(CFStringRef prefix, CFNumberRef unit)
923{
924    Boolean		found	    = FALSE;
925    CFDictionaryRef	match_dict;
926    CFStringRef		match_keys[2];
927    CFTypeRef		match_vals[2];
928    CFDictionaryRef	matching;
929
930
931
932    io_registry_entry_t	entry		= MACH_PORT_NULL;
933    io_iterator_t		iterator	= MACH_PORT_NULL;
934    kern_return_t		kr;
935    mach_port_t			masterPort	= MACH_PORT_NULL;
936
937    kr = IOMasterPort(bootstrap_port, &masterPort);
938    if (kr != KERN_SUCCESS) {
939	SCLog(TRUE, LOG_ERR,
940	      CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
941	      kr);
942	goto error;
943    }
944
945    // look for kIONetworkInterface with matching prefix and unit
946    match_keys[0] = CFSTR(kIOInterfaceNamePrefix);
947    match_vals[0] = prefix;
948    match_keys[1] = CFSTR(kIOInterfaceUnit);
949    match_vals[1] = unit;
950    match_dict = CFDictionaryCreate(NULL,
951				    (const void **)match_keys,
952				    (const void **)match_vals,
953				    2,
954				    &kCFTypeDictionaryKeyCallBacks,
955				    &kCFTypeDictionaryValueCallBacks);
956
957    match_keys[0] = CFSTR(kIOProviderClassKey);
958    match_vals[0] = CFSTR(kIONetworkInterfaceClass);
959    match_keys[1] = CFSTR(kIOPropertyMatchKey);
960    match_vals[1] = match_dict;
961    matching = CFDictionaryCreate(NULL,
962				  (const void **)match_keys,
963				  (const void **)match_vals,
964				  sizeof(match_keys)/sizeof(match_keys[0]),
965				  &kCFTypeDictionaryKeyCallBacks,
966				  &kCFTypeDictionaryValueCallBacks);
967    CFRelease(match_dict);
968
969    // note: the "matching" dictionary will be consumed by the following
970    kr = IOServiceGetMatchingServices(masterPort, matching, &iterator);
971    if ((kr != kIOReturnSuccess) || (iterator == MACH_PORT_NULL)) {
972	// if no interface
973	goto error;
974    }
975
976    entry = IOIteratorNext(iterator);
977    if (entry == MACH_PORT_NULL) {
978	// if no interface
979	goto error;
980    }
981
982    found = TRUE;
983
984error:
985    if (masterPort != MACH_PORT_NULL) {
986	mach_port_deallocate(mach_task_self(), masterPort);
987    }
988    if (entry != MACH_PORT_NULL) {
989	IOObjectRelease(entry);
990    }
991    if (iterator != MACH_PORT_NULL) {
992	IOObjectRelease(iterator);
993    }
994
995    return (found);
996}
997
998/*
999 * lookupMatchingInterface
1000 *
1001 * Looks at the interfaces that have already been [or need to be] named with
1002 * the goal of allowing a system using a single network interface/adaptor of
1003 * a given type (vendor, model, ...) to not care about the specific adaptor
1004 * that is used (i.e. swapping dongle's is OK).  Once a system has had more
1005 * than one interface/adaptor connected at the same time than we assume that
1006 * the network configuration is being setup for multi-homing that should be
1007 * maintained.
1008 *
1009 * If no matches are found or if more than one match is found, return NULL.
1010 * If a single match is found, return the match.
1011 */
1012static CFDictionaryRef
1013lookupMatchingInterface(SCNetworkInterfaceRef	interface,
1014			CFArrayRef		db_list,	// already named
1015			CFArrayRef		if_list,	// to be named
1016			CFIndex			if_list_index,
1017			CFBooleanRef		builtin)
1018{
1019    CFStringRef	    if_type;
1020    CFDictionaryRef match	    = NULL;
1021    matchContext    match_context;
1022
1023    if_type = SCNetworkInterfaceGetInterfaceType(interface);
1024    if (if_type == NULL) {
1025	return NULL;
1026    }
1027
1028    match_context.match_type	= if_type;
1029    match_context.match_info	= _SCNetworkInterfaceCopyInterfaceInfo(interface);
1030    match_context.match_builtin	= builtin;
1031    match_context.matches	= NULL;
1032
1033    // check for matches to interfaces that have already been named
1034    // ... and append each match that we find to match_context.matches
1035    if (db_list != NULL) {
1036	CFArrayApplyFunction(db_list,
1037			     CFRangeMake(0, CFArrayGetCount(db_list)),
1038			     matchKnown,
1039			     &match_context);
1040    }
1041
1042    // check for matches to interfaces that will be named
1043    // ... and CFRelease match_context.matches if we find another network
1044    //     interface of the same type that also needs to be named
1045    if (if_list != NULL) {
1046	CFIndex	   if_list_count;
1047
1048	if_list_count = CFArrayGetCount(if_list);
1049	if (if_list_index < if_list_count) {
1050	    CFArrayApplyFunction(if_list,
1051				 CFRangeMake(if_list_index, if_list_count - if_list_index),
1052				 matchUnnamed,
1053				 &match_context);
1054	}
1055    }
1056
1057    // check if we have a single match
1058    if (match_context.matches != NULL) {
1059	if (CFArrayGetCount(match_context.matches) == 1) {
1060	    match = CFArrayGetValueAtIndex(match_context.matches, 0);
1061	}
1062	CFRelease(match_context.matches);
1063    }
1064
1065    if (match != NULL) {
1066	Boolean		active	= TRUE;
1067	CFStringRef	name;
1068
1069	name = CFDictionaryGetValue(match, CFSTR(kIOBSDNameKey));
1070	if (isA_CFString(name)) {
1071	    CFStringRef	    prefix;
1072	    CFNumberRef	    unit;
1073
1074	    prefix = CFDictionaryGetValue(match, CFSTR(kIOInterfaceNamePrefix));
1075	    unit   = CFDictionaryGetValue(match, CFSTR(kIOInterfaceUnit));
1076	    if (isA_CFString(prefix) && isA_CFNumber(unit)) {
1077		if (!interfaceExists(prefix, unit)) {
1078		    active = FALSE;
1079		}
1080	    }
1081	}
1082
1083	if (active) {
1084	    match = NULL;
1085	}
1086    }
1087
1088    if (match_context.match_info != NULL) CFRelease(match_context.match_info);
1089    return match;
1090}
1091
1092static void
1093insertInterface(CFMutableArrayRef db_list, SCNetworkInterfaceRef interface)
1094{
1095    CFIndex		i;
1096    CFDictionaryRef	if_dict;
1097    CFStringRef		if_name;
1098    CFNumberRef		if_type;
1099    CFNumberRef		if_unit;
1100    CFIndex		n	= CFArrayGetCount(db_list);
1101    CFComparisonResult	res;
1102
1103    if_name = SCNetworkInterfaceGetBSDName(interface);
1104    if (if_name != NULL) {
1105	addTimestamp(S_state, if_name);
1106    }
1107
1108    if_dict = createInterfaceDict(interface);
1109    if_type = _SCNetworkInterfaceGetIOInterfaceType(interface);
1110    if_unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
1111    if ((if_type == NULL) || (if_unit == NULL)) {
1112	CFRelease(if_dict);
1113	return;
1114    }
1115
1116    for (i = 0; i < n; i++) {
1117	CFNumberRef	db_type;
1118	CFNumberRef	db_unit;
1119	CFDictionaryRef	dict	= CFArrayGetValueAtIndex(db_list, i);
1120
1121	db_type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
1122	db_unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
1123	res = CFNumberCompare(if_type, db_type, NULL);
1124	if (res == kCFCompareLessThan
1125	    || (res == kCFCompareEqualTo
1126		&& (CFNumberCompare(if_unit, db_unit, NULL)
1127		    == kCFCompareLessThan))) {
1128	    CFArrayInsertValueAtIndex(db_list, i, if_dict);
1129	    CFRelease(if_dict);
1130	    return;
1131	}
1132    }
1133
1134    CFArrayAppendValue(S_dblist, if_dict);
1135
1136#if	!TARGET_OS_EMBEDDED
1137    updateBTPANInformation(if_dict, NULL);
1138#endif	// !TARGET_OS_EMBEDDED
1139
1140    CFRelease(if_dict);
1141    return;
1142}
1143
1144static void
1145replaceInterface(SCNetworkInterfaceRef interface)
1146{
1147    int		n	= 0;
1148    CFIndex	where;
1149
1150    if (S_dblist == NULL) {
1151	S_dblist = CFArrayCreateMutable(NULL, 0,
1152					&kCFTypeArrayCallBacks);
1153    }
1154    // remove any dict that has our type/addr
1155    while (lookupInterfaceByAddress(S_dblist, interface, &where) != NULL) {
1156	CFArrayRemoveValueAtIndex(S_dblist, where);
1157	n++;
1158    }
1159    // remove any dict that has the same type/unit
1160    while (lookupInterfaceByUnit(S_dblist, interface, &where) != NULL) {
1161	CFArrayRemoveValueAtIndex(S_dblist, where);
1162	n++;
1163    }
1164    insertInterface(S_dblist, interface);
1165
1166    if (n > 1) {
1167	CFStringRef	issue;
1168
1169	issue = CFStringCreateWithFormat(NULL, NULL,
1170					 CFSTR("n = %d, %@"),
1171					 n,
1172					 interface);
1173	reportIssue("Multiple interfaces updated", issue);
1174	CFRelease(issue);
1175    }
1176
1177    return;
1178}
1179
1180static CFNumberRef
1181getHighestUnitForType(CFNumberRef if_type)
1182{
1183    int 		i;
1184    CFIndex		n;
1185    CFNumberRef		ret_unit	= NULL;
1186
1187    if (S_dblist == NULL) {
1188	return (NULL);
1189    }
1190
1191    n = CFArrayGetCount(S_dblist);
1192    for (i = 0; i < n; i++) {
1193	CFDictionaryRef	dict = CFArrayGetValueAtIndex(S_dblist, i);
1194	CFNumberRef	type;
1195
1196	type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
1197	if (CFEqual(type, if_type)) {
1198	    CFNumberRef	unit;
1199
1200	    unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
1201	    if (ret_unit == NULL
1202		|| (CFNumberCompare(unit, ret_unit, NULL)
1203		    == kCFCompareGreaterThan)) {
1204		ret_unit = unit;
1205	    }
1206	}
1207    }
1208
1209    return (ret_unit);
1210}
1211
1212/*
1213 * Function: ensureInterfaceHasUnit
1214 * Purpose:
1215 *   Ensure that the SCNetworkInterfaceRef has a unit number.  If it doesn't,
1216 *   release the interface and return NULL.
1217 */
1218static SCNetworkInterfaceRef
1219ensureInterfaceHasUnit(SCNetworkInterfaceRef net_if)
1220{
1221    if (net_if != NULL
1222	&& _SCNetworkInterfaceGetIOInterfaceUnit(net_if) == NULL) {
1223	CFRelease(net_if);
1224	net_if = NULL;
1225    }
1226    return (net_if);
1227}
1228
1229#ifdef	USE_REGISTRY_ENTRY_ID
1230static kern_return_t
1231registerInterfaceWithIORegistryEntryID(io_connect_t connect,
1232				       uint64_t	    entryID,
1233				       CFNumberRef  unit,
1234				       const int    command)
1235{
1236    CFDataRef			data;
1237    CFMutableDictionaryRef	dict;
1238    kern_return_t		kr;
1239    CFNumberRef			num;
1240
1241    dict = CFDictionaryCreateMutable(NULL, 0,
1242				     &kCFTypeDictionaryKeyCallBacks,
1243				     &kCFTypeDictionaryValueCallBacks);
1244    num = CFNumberCreate(NULL, kCFNumberIntType, &command);
1245    CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommandKey), num);
1246    CFRelease(num);
1247    data = CFDataCreate(NULL, (void *) &entryID, sizeof(entryID));
1248    CFDictionarySetValue(dict, CFSTR(kIORegistryEntryIDKey), data);
1249    CFRelease(data);
1250    CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit);
1251    kr = IOConnectSetCFProperties(connect, dict);
1252    CFRelease(dict);
1253    return kr;
1254}
1255
1256static SCNetworkInterfaceRef
1257copyInterfaceForIORegistryEntryID(uint64_t entryID)
1258{
1259    io_registry_entry_t		entry		= MACH_PORT_NULL;
1260    SCNetworkInterfaceRef	interface	= NULL;
1261    io_iterator_t		iterator	= MACH_PORT_NULL;
1262    kern_return_t		kr;
1263    mach_port_t			masterPort	= MACH_PORT_NULL;
1264
1265    kr = IOMasterPort(bootstrap_port, &masterPort);
1266    if (kr != KERN_SUCCESS) {
1267	SCLog(TRUE, LOG_ERR,
1268	      CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
1269	      kr);
1270	goto error;
1271    }
1272
1273    kr = IOServiceGetMatchingServices(masterPort,
1274				      IORegistryEntryIDMatching(entryID),
1275				      &iterator);
1276    if ((kr != KERN_SUCCESS) || (iterator == MACH_PORT_NULL)) {
1277	SCLog(TRUE, LOG_ERR,
1278	      CFSTR(MY_PLUGIN_NAME ": IOServiceGetMatchingServices(0x%llx) returned 0x%x/%d"),
1279	      entryID,
1280	      kr,
1281	      iterator);
1282	goto error;
1283    }
1284
1285    entry = IOIteratorNext(iterator);
1286    if (entry == MACH_PORT_NULL) {
1287	SCLog(TRUE, LOG_ERR,
1288	      CFSTR(MY_PLUGIN_NAME ": IORegistryEntryIDMatching(0x%llx) failed"),
1289	      entryID);
1290	goto error;
1291    }
1292
1293    interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry);
1294
1295 error:
1296    if (masterPort != MACH_PORT_NULL) {
1297	mach_port_deallocate(mach_task_self(), masterPort);
1298    }
1299    if (entry != MACH_PORT_NULL) {
1300	IOObjectRelease(entry);
1301    }
1302    if (iterator != MACH_PORT_NULL) {
1303	IOObjectRelease(iterator);
1304    }
1305    return (interface);
1306}
1307
1308static SCNetworkInterfaceRef
1309copyNamedInterfaceForIORegistryEntryID(uint64_t entryID)
1310{
1311    SCNetworkInterfaceRef	net_if;
1312
1313    net_if = copyInterfaceForIORegistryEntryID(entryID);
1314    return (ensureInterfaceHasUnit(net_if));
1315}
1316
1317#else	// USE_REGISTRY_ENTRY_ID
1318/*
1319 * Function: registerInterface
1320 * Purpose:
1321 *   Register a single interface with the given service path to the
1322 *   data link layer (BSD), using the specified unit number.
1323 */
1324static kern_return_t
1325registerInterfaceWithIOServicePath(io_connect_t	connect,
1326				   CFStringRef	path,
1327				   CFNumberRef	unit,
1328				   const int	command)
1329{
1330    CFMutableDictionaryRef	dict;
1331    kern_return_t		kr;
1332    CFNumberRef			num;
1333
1334    dict = CFDictionaryCreateMutable(NULL, 0,
1335				     &kCFTypeDictionaryKeyCallBacks,
1336				     &kCFTypeDictionaryValueCallBacks);
1337    num = CFNumberCreate(NULL, kCFNumberIntType, &command);
1338    CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommandKey), num);
1339    CFRelease(num);
1340    CFDictionarySetValue(dict, CFSTR(kIOPathMatchKey), path);
1341    CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit);
1342    kr = IOConnectSetCFProperties(connect, dict);
1343    CFRelease(dict);
1344    return kr;
1345}
1346
1347static SCNetworkInterfaceRef
1348copyInterfaceForIOKitPath(CFStringRef if_path)
1349{
1350    io_registry_entry_t		entry		= MACH_PORT_NULL;
1351    SCNetworkInterfaceRef	interface	= NULL;
1352    kern_return_t		kr;
1353    mach_port_t			masterPort	= MACH_PORT_NULL;
1354    io_string_t			path;
1355
1356    kr = IOMasterPort(bootstrap_port, &masterPort);
1357    if (kr != KERN_SUCCESS) {
1358	SCLog(TRUE, LOG_ERR,
1359	      CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
1360	      kr);
1361	goto error;
1362    }
1363    _SC_cfstring_to_cstring(if_path, path, sizeof(path), kCFStringEncodingASCII);
1364    entry = IORegistryEntryFromPath(masterPort, path);
1365    if (entry == MACH_PORT_NULL) {
1366	SCLog(TRUE, LOG_ERR,
1367	      CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath(%@) failed"),
1368	      if_path);
1369	goto error;
1370    }
1371
1372    interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry);
1373
1374 error:
1375    if (masterPort != MACH_PORT_NULL) {
1376	mach_port_deallocate(mach_task_self(), masterPort);
1377    }
1378    if (entry != MACH_PORT_NULL) {
1379	IOObjectRelease(entry);
1380    }
1381    return (interface);
1382
1383}
1384
1385static SCNetworkInterfaceRef
1386copyNamedInterfaceForIOKitPath(CFStringRef if_path)
1387{
1388    SCNetworkInterfaceRef	net_if;
1389
1390    net_if = copyInterfaceForIOKitPath(if_path);
1391    return (ensureInterfaceHasUnit(net_if));
1392}
1393
1394#endif	// USE_REGISTRY_ENTRY_ID
1395
1396static void
1397displayInterface(SCNetworkInterfaceRef interface)
1398{
1399    CFStringRef		addr;
1400    CFStringRef		name;
1401    CFNumberRef		type;
1402    CFNumberRef		unit;
1403
1404    name = SCNetworkInterfaceGetBSDName(interface);
1405    unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
1406    type = _SCNetworkInterfaceGetIOInterfaceType(interface);
1407    addr = SCNetworkInterfaceGetHardwareAddressString(interface);
1408
1409    SCLog(TRUE, LOG_INFO,
1410	  CFSTR(MY_PLUGIN_NAME ":   %s%@%sType: %@, %s%@%sMAC address: %@"),
1411	  (name != NULL) ? "BSD Name: " : "",
1412	  (name != NULL) ? name : CFSTR(""),
1413	  (name != NULL) ? ", " : "",
1414	  type,
1415	  (unit != NULL) ? "Unit: " : "",
1416	  (unit != NULL) ? (CFTypeRef)unit : (CFTypeRef)CFSTR(""),
1417	  (unit != NULL) ? ", " : "",
1418	  addr);
1419}
1420
1421static Boolean
1422builtinAvailable(SCNetworkInterfaceRef	interface,	// new interface
1423		 CFNumberRef		if_unit)	// desired unit
1424{
1425    CFIndex	i;
1426    CFNumberRef if_type	= _SCNetworkInterfaceGetIOInterfaceType(interface);
1427    CFIndex	n;
1428
1429    n = (S_dblist != NULL) ? CFArrayGetCount(S_dblist) : 0;
1430    for (i = 0; i < n; i++) {
1431	CFStringRef	    if_path;
1432	CFDictionaryRef	    known_dict	    = CFArrayGetValueAtIndex(S_dblist, i);
1433	CFStringRef	    known_path;
1434	CFNumberRef	    known_type;
1435	CFNumberRef	    known_unit;
1436
1437	known_type = CFDictionaryGetValue(known_dict, CFSTR(kIOInterfaceType));
1438	if (!_SC_CFEqual(if_type, known_type)) {
1439	    continue;	// if not the same interface type
1440	}
1441
1442	known_unit = CFDictionaryGetValue(known_dict, CFSTR(kIOInterfaceUnit));
1443	if (!_SC_CFEqual(if_unit, known_unit)) {
1444	    continue;	// if not the same interface unit
1445	}
1446
1447	if_path    = _SCNetworkInterfaceGetIOPath(interface);
1448	known_path = CFDictionaryGetValue(known_dict, CFSTR(kIOPathMatchKey));
1449	if (!_SC_CFEqual(if_path, known_path)) {
1450	    // if different IORegistry path
1451	    return FALSE;
1452	}
1453
1454	// if same type, same unit, same path
1455	return TRUE;
1456    }
1457
1458    // if interface type/unit not found
1459    return TRUE;
1460}
1461
1462static int
1463builtinCount(CFArrayRef if_list, CFIndex last, CFNumberRef if_type)
1464{
1465    CFIndex	i;
1466    int		n	= 0;
1467
1468    for (i = 0; i < last; i++) {
1469	SCNetworkInterfaceRef	builtin_if;
1470	CFNumberRef		builtin_type;
1471
1472	builtin_if   = CFArrayGetValueAtIndex(if_list, i);
1473	builtin_type = _SCNetworkInterfaceGetIOInterfaceType(builtin_if);
1474	if (CFEqual(if_type, builtin_type)) {
1475	    if (_SCNetworkInterfaceIsBuiltin(builtin_if)) {
1476		n++;	// if built-in interface
1477	    }
1478	}
1479    }
1480
1481    return n;
1482}
1483
1484static __inline__ boolean_t
1485isQuiet(void)
1486{
1487    return (S_quiet == MACH_PORT_NULL);
1488}
1489
1490static void
1491nameInterfaces(CFMutableArrayRef if_list)
1492{
1493    CFIndex	i;
1494    CFIndex	n	= CFArrayGetCount(if_list);
1495
1496    for (i = 0; i < n; i++) {
1497	uint64_t		entryID;
1498	SCNetworkInterfaceRef	interface;
1499	SCNetworkInterfaceRef	new_interface;
1500	CFStringRef		path;
1501	CFStringRef		str;
1502	CFNumberRef		type;
1503	CFNumberRef		unit;
1504	CFIndex			where;
1505
1506	interface = CFArrayGetValueAtIndex(if_list, i);
1507	path = _SCNetworkInterfaceGetIOPath(interface);
1508	type = _SCNetworkInterfaceGetIOInterfaceType(interface);
1509	unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
1510	entryID = _SCNetworkInterfaceGetIORegistryEntryID(interface);
1511
1512	if (unit != NULL) {
1513	    if (S_debug) {
1514		CFStringRef	if_name;
1515
1516		if_name = SCNetworkInterfaceGetBSDName(interface);
1517		if ((if_name == NULL) || !CFDictionaryContainsKey(S_state, if_name)) {
1518			SCLog(TRUE, LOG_INFO,
1519			      CFSTR(MY_PLUGIN_NAME ": Interface already has a unit number"));
1520			displayInterface(interface);
1521		}
1522	    }
1523
1524	    // update the list of interfaces that were previously named
1525	    if ((S_prev_active_list != NULL)
1526		&& lookupInterfaceByAddress(S_prev_active_list, interface, &where) != NULL) {
1527		CFArrayRemoveValueAtIndex(S_prev_active_list, where);
1528	    }
1529
1530	    replaceInterface(interface);
1531	} else {
1532	    CFDictionaryRef 	dbdict;
1533	    boolean_t		is_builtin;
1534	    kern_return_t	kr;
1535	    int			retries	= 0;
1536
1537	    dbdict = lookupInterfaceByAddress(S_dblist, interface, NULL);
1538	    if (dbdict != NULL) {
1539		unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
1540		CFRetain(unit);
1541
1542		SCLog(S_debug, LOG_INFO,
1543		      CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (from database)"),
1544		      unit);
1545	    }
1546
1547	    if ((dbdict == NULL) && !isQuiet()) {
1548		// if new interface, wait until quiet before naming
1549		addTimestamp(S_state, path);
1550		continue;
1551	    }
1552
1553	    is_builtin = _SCNetworkInterfaceIsBuiltin(interface);
1554
1555	    if (dbdict == NULL) {
1556		dbdict = lookupMatchingInterface(interface,
1557						 S_dblist,
1558						 if_list,
1559						 i + 1,
1560						 is_builtin ? kCFBooleanTrue : kCFBooleanFalse);
1561		if (dbdict != NULL) {
1562		    unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
1563		    CFRetain(unit);
1564
1565		    SCLog(S_debug, LOG_INFO,
1566			  CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (updating database)"),
1567			  unit);
1568		}
1569	    }
1570
1571	    if ((dbdict != NULL) && (S_prev_active_list != NULL)) {
1572		// update the list of interfaces that were previously named
1573		where = CFArrayGetFirstIndexOfValue(S_prev_active_list,
1574						    CFRangeMake(0, CFArrayGetCount(S_prev_active_list)),
1575						    dbdict);
1576		if (where != kCFNotFound) {
1577		    CFArrayRemoveValueAtIndex(S_prev_active_list, where);
1578		}
1579	    }
1580
1581	    if (dbdict == NULL) {
1582		int 		next_unit	= 0;
1583
1584		if (is_builtin) {
1585		    // built-in interface, try to use the reserved slots
1586		    next_unit = builtinCount(if_list, i, type);
1587
1588		    // But, before claiming a reserved slot we check to see if the
1589		    // slot had previously been used.  If so, and if the slot had been
1590		    // assigned to the same type of interface, then we will perform a
1591		    // replacement (e.g. assume that this was a board swap).  But, if
1592		    // the new interface is a different type then we assume that the
1593		    // built-in configuration has changed and allocate a new unit from
1594		    // the non-reserved slots.
1595
1596		    unit = CFNumberCreate(NULL, kCFNumberIntType, &next_unit);
1597		    if (!builtinAvailable(interface, unit)) {
1598			// if [built-in] unit not available
1599			SCLog(S_debug, LOG_INFO,
1600			      CFSTR(MY_PLUGIN_NAME ": Interface not assigned [built-in] unit %@"),
1601			      unit);
1602			CFRelease(unit);
1603			unit = NULL;
1604		    }
1605		}
1606
1607		if (unit == NULL) {
1608		    // not built-in (or built-in unit not available), allocate from
1609		    // the non-reserved slots
1610		    next_unit = builtinCount(if_list, n, type);
1611
1612		    unit = getHighestUnitForType(type);
1613		    if (unit != NULL) {
1614			int	high_unit;
1615
1616			CFNumberGetValue(unit, kCFNumberIntType, &high_unit);
1617			if (high_unit >= next_unit) {
1618			    next_unit = high_unit + 1;
1619			}
1620		    }
1621
1622		    unit = CFNumberCreate(NULL, kCFNumberIntType, &next_unit);
1623		}
1624
1625		SCLog(S_debug, LOG_INFO,
1626		      CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (%s)"),
1627		      unit,
1628		      is_builtin ? "built-in" : "next available");
1629	    }
1630
1631	retry :
1632
1633#ifdef	USE_REGISTRY_ENTRY_ID
1634	    kr = registerInterfaceWithIORegistryEntryID(S_connect,
1635							entryID,
1636							unit,
1637							(dbdict == NULL) ? kIONetworkStackRegisterInterfaceWithLowestUnit
1638									 : kIONetworkStackRegisterInterfaceWithUnit);
1639	    new_interface = copyNamedInterfaceForIORegistryEntryID(entryID);
1640#else	// USE_REGISTRY_ENTRY_ID
1641	    kr = registerInterfaceWithIOServicePath(S_connect,
1642						    path,
1643						    unit,
1644						    (dbdict == NULL) ? kRegisterInterface
1645								     : kRegisterInterfaceWithFixedUnit);
1646	    new_interface = copyNamedInterfaceForIOKitPath(path);
1647#endif	// USE_REGISTRY_ENTRY_ID
1648	    if (new_interface == NULL) {
1649		const char  *signature;
1650
1651		signature = (dbdict == NULL) ? "failed to name new interface"
1652					     : "failed to name known interface";
1653
1654		SCLog(TRUE, LOG_ERR,
1655		      CFSTR(MY_PLUGIN_NAME ": %s, kr=0x%x\n"
1656			    MY_PLUGIN_NAME ":   path = %@\n"
1657			    MY_PLUGIN_NAME ":   id   = 0x%llx\n"
1658			    MY_PLUGIN_NAME ":   unit = %@"),
1659		      signature,
1660		      kr,
1661		      path,
1662		      entryID,
1663		      unit);
1664
1665		if (S_debug) {
1666		    displayInterface(interface);
1667		}
1668
1669		// report issue w/MessageTracer
1670		str = CFStringCreateWithFormat(NULL, NULL,
1671					       CFSTR("kr=0x%x, path=%@, unit=%@"),
1672					       kr,
1673					       path,
1674					       unit);
1675		reportIssue(signature, str);
1676		CFRelease(str);
1677
1678		if ((dbdict != NULL) && (retries++ < 5)) {
1679		    usleep(50 * 1000);	// sleep 50ms between attempts
1680		    goto retry;
1681		}
1682	    }
1683	    else {
1684		CFNumberRef	new_unit;
1685
1686		if (retries > 0) {
1687		    SCLog(TRUE, LOG_ERR,
1688			  CFSTR(MY_PLUGIN_NAME ": %s interface named after %d %s\n"
1689				MY_PLUGIN_NAME ":   path = %@\n"
1690				MY_PLUGIN_NAME ":   unit = %@"),
1691			  (dbdict == NULL) ? "New" : "Known",
1692			  retries,
1693			  (retries == 1) ? "try" : "tries",
1694			  path,
1695			  unit);
1696
1697#ifdef	SHOW_NAMING_FAILURE
1698		    str = CFStringCreateWithFormat(NULL,
1699						   NULL,
1700						   CFSTR("\"%s\" interface named after %d %s, unit = %@"),
1701						   (dbdict == NULL) ? "New" : "Known",
1702						   retries,
1703						   (retries == 1) ? "try" : "tries",
1704						   unit);
1705		    CFUserNotificationDisplayNotice(0,
1706						    kCFUserNotificationStopAlertLevel,
1707						    NULL,
1708						    NULL,
1709						    NULL,
1710						    str,
1711						    CFSTR("Please report repeated failures."),
1712						    NULL);
1713		    CFRelease(str);
1714#endif	// SHOW_NAMING_FAILURE
1715		}
1716
1717		new_unit = _SCNetworkInterfaceGetIOInterfaceUnit(new_interface);
1718		if (CFEqual(unit, new_unit) == FALSE) {
1719		    SCLog(S_debug, LOG_INFO,
1720			  CFSTR(MY_PLUGIN_NAME
1721				": interface type %@ assigned "
1722				"unit %@ instead of %@"),
1723			  type, new_unit, unit);
1724		}
1725		if (S_debug) {
1726		    displayInterface(new_interface);
1727		}
1728
1729		// update if_list (with the interface name & unit)
1730		CFArraySetValueAtIndex(if_list, i, new_interface);
1731		CFRelease(new_interface);
1732		interface = new_interface;	// if_list holds the reference
1733
1734		if (is_builtin && (S_prev_active_list != NULL)) {
1735		    CFIndex	where;
1736
1737		    // update the list of [built-in] interfaces that were previously named
1738		    if (lookupInterfaceByUnit(S_prev_active_list, interface, &where) != NULL) {
1739			SCLog(S_debug, LOG_INFO,
1740			      CFSTR(MY_PLUGIN_NAME ":   and updated database (new address)"));
1741			CFArrayRemoveValueAtIndex(S_prev_active_list, where);
1742		    }
1743		}
1744		replaceInterface(interface);
1745	    }
1746	    CFRelease(unit);
1747	}
1748    }
1749    return;
1750}
1751
1752#if	!TARGET_OS_IPHONE
1753static void
1754updateNetworkConfiguration(CFArrayRef if_list)
1755{
1756    Boolean		do_commit	= FALSE;
1757    CFIndex		i;
1758    CFIndex		n;
1759    SCPreferencesRef	prefs		= NULL;
1760    SCNetworkSetRef	set		= NULL;
1761
1762    prefs = SCPreferencesCreate(NULL, CFSTR("InterfaceNamer:updateNetworkConfiguration"), NULL);
1763
1764    set = SCNetworkSetCopyCurrent(prefs);
1765    if (set == NULL) {
1766	SCLog(TRUE, LOG_ERR, CFSTR(MY_PLUGIN_NAME ": No current set"));
1767	goto done;
1768    }
1769
1770    n = CFArrayGetCount(if_list);
1771    for (i = 0; i < n; i++) {
1772	SCNetworkInterfaceRef	interface;
1773
1774	interface = CFArrayGetValueAtIndex(if_list, i);
1775	if (SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface)) {
1776	    SCLog(TRUE, LOG_INFO,
1777		  CFSTR(MY_PLUGIN_NAME ": adding default configuration for %@"),
1778		  SCNetworkInterfaceGetBSDName(interface));
1779	    do_commit = TRUE;
1780	}
1781    }
1782
1783    if (do_commit) {
1784	Boolean	ok;
1785
1786	ok = SCPreferencesCommitChanges(prefs);
1787	if (!ok) {
1788	    SCLog(TRUE, LOG_INFO,
1789		  CFSTR(MY_PLUGIN_NAME ": updateNetworkConfiguration: SCPreferencesCommitChanges() failed: %s"),
1790		  SCErrorString(SCError()));
1791	    goto done;
1792	}
1793
1794	ok = SCPreferencesApplyChanges(prefs);
1795	if (!ok) {
1796	    SCLog(TRUE, LOG_INFO,
1797		  CFSTR(MY_PLUGIN_NAME ": updateNetworkConfiguration: SCPreferencesApplyChanges() failed: %s"),
1798		  SCErrorString(SCError()));
1799	    goto done;
1800	}
1801    }
1802
1803  done :
1804
1805    if (set != NULL) {
1806	CFRelease(set);
1807	set = NULL;
1808    }
1809
1810    if (prefs != NULL) {
1811	CFRelease(prefs);
1812	prefs = NULL;
1813    }
1814
1815    return;
1816}
1817#endif	// !TARGET_OS_IPHONE
1818
1819static void
1820updateInterfaces()
1821{
1822    if (S_connect == MACH_PORT_NULL) {
1823	// if we don't have the "IONetworkStack" connect object
1824	return;
1825    }
1826
1827    if (S_iflist != NULL) {
1828	CFIndex	n;
1829
1830	n = CFArrayGetCount(S_iflist);
1831	if (n > 1) {
1832	    CFArraySortValues(S_iflist, CFRangeMake(0, n), _SCNetworkInterfaceCompare, NULL);
1833	}
1834	nameInterfaces(S_iflist);
1835    }
1836
1837    if (isQuiet()) {
1838	/*
1839	 * The registry [matching] has quiesced so let's
1840	 * - save the DB with the interfaces that have been named
1841	 * - update the VLAN/BOND configuration
1842	 * - tell everyone that we've finished (at least for now)
1843	 * - log those interfaces which are no longer present
1844	 *   in the HW config (or have yet to show up).
1845	 */
1846	writeInterfaceList(S_dblist);
1847	updateVirtualNetworkInterfaceConfiguration(NULL, kSCPreferencesNotificationApply, NULL);
1848
1849#if	!TARGET_OS_IPHONE
1850	if (access("/usr/libexec/UserEventAgent",  X_OK) == -1
1851	    && errno == ENOENT) {
1852	    /*
1853	     * We are most likely booted into the Recovery OS with no "SCMonitor"
1854	     * UserEventAgent plugin running so let's make sure we update the
1855	     * network configuration for new interfaces.
1856	     */
1857	    updateNetworkConfiguration(S_iflist);
1858	}
1859#endif	// !TARGET_OS_IPHONE
1860
1861	updateStore();
1862
1863	if (S_iflist != NULL) {
1864	    CFRelease(S_iflist);
1865	    S_iflist = NULL;
1866	}
1867
1868	if (S_prev_active_list != NULL) {
1869	    if (S_debug) {
1870		CFIndex	i;
1871		CFIndex	n;
1872
1873		n = CFArrayGetCount(S_prev_active_list);
1874		if (n > 0) {
1875		    SCLog(TRUE, LOG_INFO,
1876			  CFSTR(MY_PLUGIN_NAME ": Interface%s not [yet] active"),
1877			  (n > 1) ? "s" : "");
1878		}
1879		for (i = 0; i < n; i++) {
1880		    CFDictionaryRef	if_dict;
1881		    CFStringRef		name;
1882		    CFNumberRef		type;
1883		    CFNumberRef		unit;
1884
1885		    if_dict = CFArrayGetValueAtIndex(S_prev_active_list, i);
1886		    name = CFDictionaryGetValue(if_dict, CFSTR(kIOBSDNameKey));
1887		    type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType));
1888		    unit = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceUnit));
1889		    SCLog(TRUE, LOG_INFO,
1890			  CFSTR(MY_PLUGIN_NAME ":   %s%@%sType: %@, Unit: %@"),
1891			  (name != NULL) ? "BSD Name: " : "",
1892			  (name != NULL) ? name : CFSTR(""),
1893			  (name != NULL) ? ", " : "",
1894			  type,
1895			  unit);
1896		}
1897	    }
1898	    CFRelease(S_prev_active_list);
1899	    S_prev_active_list = NULL;
1900	}
1901    } else {
1902	if ((S_prev_active_list != NULL) && (CFArrayGetCount(S_prev_active_list) == 0)) {
1903	    /*
1904	     * if we've named all of the interfaces that
1905	     * were used during the previous boot.
1906	     */
1907	    addTimestamp(S_state, CFSTR("*RELEASE*"));
1908	    SCLog(S_debug, LOG_INFO,
1909		  CFSTR(MY_PLUGIN_NAME ": last boot interfaces have been named"));
1910	    updateStore();
1911	    CFRelease(S_prev_active_list);
1912	    S_prev_active_list = NULL;
1913	}
1914    }
1915
1916    return;
1917}
1918
1919#if	!TARGET_OS_EMBEDDED
1920static CFComparisonResult
1921compareMacAddress(const void *val1, const void *val2, void *context)
1922{
1923    CFDataRef		mac1	= (CFDataRef)val1;
1924    CFDataRef		mac2	= (CFDataRef)val2;
1925    CFIndex		n1;
1926    CFIndex		n2;
1927    CFComparisonResult	res;
1928
1929    n1 = CFDataGetLength(mac1);
1930    n2 = CFDataGetLength(mac2);
1931    if (n1 < n2) {
1932	res = kCFCompareLessThan;
1933     } else if (n2 > n1) {
1934	res = kCFCompareGreaterThan;
1935     } else {
1936	res = bcmp(CFDataGetBytePtr(mac1), CFDataGetBytePtr(mac2), n1);
1937     }
1938
1939     return res;
1940}
1941
1942static CFStringRef
1943copyEthernetUUID()
1944{
1945    CFDataRef		addr;
1946    CFMutableArrayRef	addrs	= NULL;
1947    CFStringRef		guid	= NULL;
1948    CFIndex		i;
1949    CFIndex		n;
1950
1951    addrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1952    n = (S_dblist != NULL) ? CFArrayGetCount(S_dblist) : 0;
1953    for (i = 0; i < n; i++) {
1954	CFBooleanRef	builtin;
1955	CFDictionaryRef	dict;
1956	CFStringRef	type;
1957
1958	dict = CFArrayGetValueAtIndex(S_dblist, i);
1959	type = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceType));
1960	if (!isA_CFString(type) || !CFEqual(type, kSCNetworkInterfaceTypeEthernet)) {
1961	    continue;
1962	}
1963	builtin = CFDictionaryGetValue(dict, CFSTR(kIOBuiltin));
1964	if (!isA_CFBoolean(builtin) || !CFBooleanGetValue(builtin)) {
1965	    continue;
1966	}
1967	addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
1968	if (!isA_CFData(addr) || (CFDataGetLength(addr) != ETHER_ADDR_LEN)) {
1969	    continue;
1970	}
1971	CFArrayAppendValue(addrs, addr);
1972    }
1973
1974    if (CFArrayGetCount(addrs) == 0) {
1975	// if no ethernet interfaces, look for wireless
1976	for (i = 0; i < n; i++) {
1977	    CFDictionaryRef	dict;
1978	    CFStringRef		type;
1979
1980	    dict = CFArrayGetValueAtIndex(S_dblist, i);
1981	    type = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceType));
1982	    if (!isA_CFString(type) || !CFEqual(type, kSCNetworkInterfaceTypeIEEE80211)) {
1983		continue;
1984	    }
1985	    addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
1986	    if (!isA_CFData(addr) || (CFDataGetLength(addr) != ETHER_ADDR_LEN)) {
1987		continue;
1988	    }
1989	    CFArrayAppendValue(addrs, addr);
1990	}
1991    }
1992
1993    n = CFArrayGetCount(addrs);
1994    switch (n) {
1995	case 0 :
1996	    // if no network interfaces
1997	    break;
1998	default :
1999	    // sort by MAC address
2000	    CFArraySortValues(addrs, CFRangeMake(0, n), compareMacAddress, NULL);
2001
2002	    // fall through
2003	case 1 : {
2004	    CFUUIDBytes		bytes	= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
2005					    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
2006	    CFUUIDRef		uuid;
2007
2008	    // set GUID
2009	    addr = CFArrayGetValueAtIndex(addrs, 0);
2010	    bcopy(CFDataGetBytePtr(addr),
2011		  (void *)&bytes + sizeof(bytes) - ETHER_ADDR_LEN,
2012		  ETHER_ADDR_LEN);
2013	    uuid = CFUUIDCreateFromUUIDBytes(NULL, bytes);
2014	    guid = CFUUIDCreateString(NULL, uuid);
2015	    CFRelease(uuid);
2016
2017	    SCLog(TRUE, LOG_INFO,
2018		  CFSTR(MY_PLUGIN_NAME ": setting platform UUID [MAC] = %@"),
2019		  guid);
2020	    break;
2021	}
2022    }
2023
2024    if (addrs != NULL) CFRelease(addrs);
2025    return guid;
2026}
2027
2028#ifndef kIOPlatformUUIDKey
2029#define kIOPlatformUUIDKey "IOPlatformUUID"
2030#endif
2031static void
2032updatePlatformUUID()
2033{
2034    CFStringRef		guid	= NULL;
2035    kern_return_t	kr;
2036    io_registry_entry_t	platform;
2037
2038    platform = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/");
2039    if (platform == MACH_PORT_NULL) {
2040	goto done;
2041    }
2042
2043    guid = IORegistryEntryCreateCFProperty(platform, CFSTR(kIOPlatformUUIDKey), NULL, 0);
2044    if (guid != NULL) {
2045	// if GUID already defined
2046	goto done;
2047    }
2048
2049    guid = copyEthernetUUID();
2050    if (guid == NULL) {
2051	CFUUIDRef   uuid;
2052
2053	uuid = CFUUIDCreate(NULL);
2054	guid = CFUUIDCreateString(NULL, uuid);
2055	CFRelease(uuid);
2056
2057	SCLog(TRUE, LOG_INFO,
2058	      CFSTR(MY_PLUGIN_NAME ": setting platform UUID [random] = %@"),
2059	      guid);
2060    }
2061
2062if (getenv("DO_NOT_SET_PLATFORM_UUID") == NULL) {
2063    kr = IORegistryEntrySetCFProperty(platform, CFSTR(kIOPlatformUUIDKey), guid);
2064    if (kr != KERN_SUCCESS) {
2065	SCLog(TRUE, LOG_ERR,
2066	      CFSTR(MY_PLUGIN_NAME ": IORegistryEntrySetCFProperty(platform UUID) failed, kr=0x%x"),
2067	      kr);
2068    }
2069}
2070
2071    addTimestamp(S_state, CFSTR("*PLATFORM-UUID*"));
2072    updateStore();
2073
2074  done :
2075
2076    if (S_vproc_transaction != NULL) {
2077	vproc_transaction_end(NULL, S_vproc_transaction);
2078	S_vproc_transaction = NULL;
2079    }
2080
2081    if (platform != MACH_PORT_NULL) IOObjectRelease(platform);
2082    if (guid != NULL) CFRelease(guid);
2083    return;
2084}
2085#endif	// !TARGET_OS_EMBEDDED
2086
2087static void
2088interfaceArrivalCallback(void *refcon, io_iterator_t iter)
2089{
2090    io_object_t	obj;
2091
2092    while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) {
2093	SCNetworkInterfaceRef	interface;
2094
2095	interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj);
2096	if (interface != NULL) {
2097	    if (S_iflist == NULL) {
2098		S_iflist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2099	    }
2100	    CFArrayAppendValue(S_iflist, interface);
2101	    CFRelease(interface);
2102	}
2103	IOObjectRelease(obj);
2104    }
2105
2106    updateInterfaces();
2107    return;
2108}
2109
2110/*
2111 * Function: stackCallback
2112 * Purpose:
2113 *   Get a reference to the single IONetworkStack object instance in
2114 *   the kernel. Naming requests must be sent to this object, which is
2115 *   attached as a client to all network interface objects in the system.
2116 * Note:
2117 *   Call IOObjectRelease on the returned object.
2118 */
2119static void
2120stackCallback(void *refcon, io_iterator_t iter)
2121{
2122    kern_return_t	kr;
2123    io_object_t		stack;
2124
2125    stack = IOIteratorNext(iter);
2126    if (stack == MACH_PORT_NULL) {
2127	goto error;
2128    }
2129
2130    kr = IOServiceOpen(stack, mach_task_self(), 0, &S_connect);
2131    if (kr != KERN_SUCCESS) {
2132	SCLog(TRUE, LOG_ERR,
2133	      CFSTR(MY_PLUGIN_NAME ": IOServiceOpen returned 0x%x"),
2134	      kr);
2135	goto error;
2136    }
2137
2138    addTimestamp(S_state, CFSTR("*STACK*"));
2139    SCLog(S_debug, LOG_INFO,
2140	  CFSTR(MY_PLUGIN_NAME ": IONetworkStack found"));
2141
2142    if (S_stack != MACH_PORT_NULL) {
2143	IOObjectRelease(S_stack);
2144	S_stack = MACH_PORT_NULL;
2145    }
2146
2147    if ((S_timer != NULL) && CFRunLoopTimerIsValid(S_timer)) {
2148	// With the IONetworkStack object now available we can
2149	// reset (shorten?) the time we are willing to wait for
2150	// IOKit to quiesce.
2151	CFRunLoopTimerSetNextFireDate(S_timer,
2152				      CFAbsoluteTimeGetCurrent() + S_quiet_timeout);
2153    }
2154
2155    updateInterfaces();
2156
2157 error:
2158    if (stack != MACH_PORT_NULL) {
2159	IOObjectRelease(stack);
2160    }
2161
2162    return;
2163}
2164
2165static void
2166quietCallback(void		*refcon,
2167	      io_service_t	service,
2168	      natural_t		messageType,
2169	      void		*messageArgument)
2170{
2171    if (messageArgument != NULL) {
2172	// if not yet quiet
2173	return;
2174    }
2175
2176    if (messageType == kIOMessageServiceBusyStateChange) {
2177	addTimestamp(S_state, CFSTR("*QUIET*"));
2178	SCLog(S_debug, LOG_INFO,
2179	      CFSTR(MY_PLUGIN_NAME ": IOKit quiet"));
2180    }
2181
2182    if (S_connect == MACH_PORT_NULL) {
2183	SCLog(TRUE, LOG_ERR,
2184	      CFSTR(MY_PLUGIN_NAME ": No network stack object"));
2185	return;
2186    }
2187
2188    if (S_quiet != MACH_PORT_NULL) {
2189	IOObjectRelease(S_quiet);
2190	S_quiet = MACH_PORT_NULL;
2191    }
2192
2193    if (S_timer != NULL) {
2194	CFRunLoopTimerInvalidate(S_timer);
2195	CFRelease(S_timer);
2196	S_timer = NULL;
2197    }
2198
2199    // grab (and name) any additional interfaces.
2200    interfaceArrivalCallback((void *)S_notify, S_iter);
2201
2202#if	!TARGET_OS_EMBEDDED
2203    updatePlatformUUID();
2204#endif	// !TARGET_OS_EMBEDDED
2205
2206    return;
2207}
2208
2209static void
2210iterateRegistryBusy(io_iterator_t iterator, CFArrayRef nodes, CFMutableStringRef snapshot, int *count)
2211{
2212    kern_return_t	kr  = kIOReturnSuccess;;
2213    io_object_t		obj;
2214
2215    while ((kr == kIOReturnSuccess) &&
2216	   ((obj = IOIteratorNext(iterator)) != MACH_PORT_NULL)) {
2217	uint64_t		accumulated_busy_time;
2218	uint32_t		busy_state;
2219	io_name_t		location;
2220	io_name_t		name;
2221	CFMutableArrayRef	newNodes;
2222	uint64_t		state;
2223	CFMutableStringRef	str	= NULL;
2224
2225	if (nodes == NULL) {
2226	    newNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2227	} else {
2228	    newNodes = CFArrayCreateMutableCopy(NULL, 0, nodes);
2229	}
2230	assert(newNodes != NULL);
2231
2232	kr = IORegistryEntryGetName(obj, name);
2233	if (kr != kIOReturnSuccess) {
2234	    SCLog(TRUE, LOG_ERR,
2235		  CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryEntryGetName returned 0x%x"),
2236		  kr);
2237	    goto next;
2238	}
2239
2240	str = CFStringCreateMutable(NULL, 0);
2241	CFStringAppendCString(str, name, kCFStringEncodingUTF8);
2242
2243	kr = IORegistryEntryGetLocationInPlane(obj, kIOServicePlane, location);
2244	switch (kr) {
2245	    case kIOReturnSuccess :
2246		CFStringAppendCString(str, "@", kCFStringEncodingUTF8);
2247		CFStringAppendCString(str, location, kCFStringEncodingUTF8);
2248		break;
2249	    case kIOReturnNotFound :
2250		break;
2251	    default :
2252		SCLog(TRUE, LOG_ERR,
2253		      CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryEntryGetLocationInPlane returned 0x%x"),
2254		      kr);
2255		CFRelease(str);
2256		goto next;
2257	}
2258
2259	CFArrayAppendValue(newNodes, str);
2260	CFRelease(str);
2261
2262	kr = IOServiceGetBusyStateAndTime(obj, &state, &busy_state, &accumulated_busy_time);
2263	if (kr != kIOReturnSuccess) {
2264	    SCLog(TRUE, LOG_ERR,
2265		  CFSTR(MY_PLUGIN_NAME ": captureBusy IOServiceGetBusyStateAndTime returned 0x%x"),
2266		  kr);
2267	    goto next;
2268	}
2269
2270#ifdef	TEST_SNAPSHOT
2271	// report all nodes
2272	busy_state = 1;
2273#endif	// TEST_SNAPSHOT
2274
2275	if (busy_state != 0) {
2276	    CFStringRef	path;
2277
2278	    if ((*count)++ == 0) {
2279		CFStringAppend(snapshot, CFSTR("Busy services :"));
2280	    }
2281
2282	    path = CFStringCreateByCombiningStrings(NULL, newNodes, CFSTR("/"));
2283	    CFStringAppendFormat(snapshot, NULL,
2284				 CFSTR("\n  %@ [%s%s%s%d, %lld ms]"),
2285				 path,
2286				 (state & kIOServiceRegisteredState) ? "" : "!registered, ",
2287				 (state & kIOServiceMatchedState)    ? "" : "!matched, ",
2288				 (state & kIOServiceInactiveState)   ? "inactive, " : "",
2289				 busy_state,
2290				 accumulated_busy_time / kMillisecondScale);
2291	    CFRelease(path);
2292	}
2293
2294	kr = IORegistryIteratorEnterEntry(iterator);
2295	if (kr != kIOReturnSuccess) {
2296	    SCLog(TRUE, LOG_ERR,
2297		  CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryIteratorEnterEntry returned 0x%x"),
2298		  kr);
2299	    goto next;
2300	}
2301
2302	iterateRegistryBusy(iterator, newNodes, snapshot, count);
2303
2304	kr = IORegistryIteratorExitEntry(iterator);
2305	if (kr != kIOReturnSuccess) {
2306	    SCLog(TRUE, LOG_ERR,
2307		  CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryIteratorExitEntry returned 0x%x"),
2308		  kr);
2309	}
2310
2311      next :
2312
2313	CFRelease(newNodes);
2314	IOObjectRelease(obj);
2315    }
2316
2317    return;
2318}
2319
2320static CF_RETURNS_RETAINED CFStringRef
2321captureBusy()
2322{
2323    int			count		= 0;
2324    io_iterator_t	iterator	= MACH_PORT_NULL;
2325    kern_return_t	kr;
2326    CFMutableStringRef	snapshot;
2327
2328    snapshot = CFStringCreateMutable(NULL, 0);
2329
2330    kr = IORegistryCreateIterator(kIOMasterPortDefault,
2331				  kIOServicePlane,
2332				  0,
2333				  &iterator);
2334    if (kr != kIOReturnSuccess) {
2335	SCLog(TRUE, LOG_ERR,
2336	      CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryCreateIterator returned 0x%x"),
2337	      kr);
2338	return snapshot;
2339    }
2340
2341    iterateRegistryBusy(iterator, NULL, snapshot, &count);
2342    if (count == 0) {
2343	CFStringAppend(snapshot, CFSTR("w/no busy services"));
2344    }
2345
2346    IOObjectRelease(iterator);
2347    return snapshot;
2348}
2349
2350static void
2351timerCallback(CFRunLoopTimerRef	timer, void *info)
2352{
2353    CFStringRef	snapshot;
2354
2355    // We've been waiting for IOKit to quiesce and it just
2356    // hasn't happenned.  Time to just move on!
2357    addTimestamp(S_state, CFSTR("*TIMEOUT*"));
2358
2359    // log busy nodes
2360    snapshot = captureBusy();
2361    SCLog(TRUE, LOG_ERR,
2362	  CFSTR(MY_PLUGIN_NAME ": timed out waiting for IOKit to quiesce\n%@"),
2363	  snapshot);
2364    reportIssue("timed out waiting for IOKit to quiesce", snapshot);
2365    CFRelease(snapshot);
2366
2367    quietCallback((void *)S_notify, MACH_PORT_NULL, 0, NULL);
2368    return;
2369}
2370
2371static Boolean
2372setup_IOKit(CFBundleRef bundle)
2373{
2374    uint32_t		busy;
2375    kern_return_t	kr;
2376    mach_port_t		masterPort	= MACH_PORT_NULL;
2377    Boolean		ok		= FALSE;
2378    io_object_t		root		= MACH_PORT_NULL;
2379
2380    // read DB of previously named network interfaces
2381    S_dblist = readInterfaceList();
2382    if (S_dblist != NULL) {
2383	CFIndex	n;
2384
2385	n = CFArrayGetCount(S_dblist);
2386	if (n > 1) {
2387	    CFArraySortValues(S_dblist, CFRangeMake(0, n), if_unit_compare, NULL);
2388	}
2389    }
2390
2391    // get interfaces that were named during the last boot
2392    S_prev_active_list = previouslyActiveInterfaces();
2393
2394    // track how long we've waited to see each interface.
2395    S_state = CFDictionaryCreateMutable(NULL,
2396					0,
2397					&kCFTypeDictionaryKeyCallBacks,
2398					&kCFTypeDictionaryValueCallBacks);
2399    addTimestamp(S_state, CFSTR("*START*"));
2400
2401    // Creates and returns a notification object for receiving IOKit
2402    // notifications of new devices or state changes.
2403    kr = IOMasterPort(bootstrap_port, &masterPort);
2404    if (kr != KERN_SUCCESS) {
2405	SCLog(TRUE, LOG_ERR,
2406	      CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
2407	      kr);
2408	goto done;
2409    }
2410
2411    S_notify = IONotificationPortCreate(masterPort);
2412    if (S_notify == NULL) {
2413	SCLog(TRUE, LOG_ERR,
2414	      CFSTR(MY_PLUGIN_NAME ": IONotificationPortCreate failed"));
2415	goto done;
2416    }
2417
2418    // watch IOKit matching activity
2419    root = IORegistryEntryFromPath(masterPort, kIOServicePlane ":/");
2420    if (root == MACH_PORT_NULL) {
2421	SCLog(TRUE, LOG_ERR,
2422	      CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath failed"));
2423	goto done;
2424    }
2425
2426    kr = IOServiceAddInterestNotification(S_notify,
2427					  root,
2428					  kIOBusyInterest,
2429					  &quietCallback,
2430					  (void *)S_notify,	// refCon
2431					  &S_quiet);		// notification
2432    if (kr != KERN_SUCCESS) {
2433	SCLog(TRUE, LOG_ERR,
2434	      CFSTR(MY_PLUGIN_NAME ": IOServiceAddInterestNotification returned 0x%x"),
2435	      kr);
2436	goto done;
2437    }
2438
2439    kr = IOServiceGetBusyState(root, &busy);
2440    if (kr != KERN_SUCCESS) {
2441	SCLog(TRUE, LOG_ERR,
2442	      CFSTR(MY_PLUGIN_NAME ": IOServiceGetBusyState returned 0x%x"),
2443	      kr);
2444	goto done;
2445    }
2446
2447    // add a timer so we don't wait forever for IOKit to quiesce
2448    S_timer = CFRunLoopTimerCreate(NULL,
2449				   CFAbsoluteTimeGetCurrent() + S_stack_timeout,
2450				   0,
2451				   0,
2452				   0,
2453				   timerCallback,
2454				   NULL);
2455    if (S_timer == NULL) {
2456	SCLog(TRUE, LOG_ERR,
2457	      CFSTR(MY_PLUGIN_NAME ": CFRunLoopTimerCreate failed"));
2458	goto done;
2459    }
2460
2461    CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, kCFRunLoopDefaultMode);
2462
2463    // watch for the introduction of the IONetworkStack
2464    kr = IOServiceAddMatchingNotification(S_notify,
2465					  kIOFirstMatchNotification,
2466					  IOServiceMatching("IONetworkStack"),
2467					  &stackCallback,
2468					  (void *)S_notify,	// refCon
2469					  &S_stack);		// notification
2470    if (kr != KERN_SUCCESS) {
2471	SCLog(TRUE, LOG_ERR,
2472	      CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"),
2473	      kr);
2474	goto done;
2475    }
2476
2477    // check and see if the stack is already available and arm the
2478    // notification for its introduction.
2479    stackCallback((void *)S_notify, S_stack);
2480
2481    // watch for the introduction of new network interfaces
2482    kr = IOServiceAddMatchingNotification(S_notify,
2483					  kIOFirstMatchNotification,
2484					  IOServiceMatching("IONetworkInterface"),
2485					  &interfaceArrivalCallback,
2486					  (void *)S_notify,	// refCon
2487					  &S_iter);		// notification
2488    if (kr != KERN_SUCCESS) {
2489	SCLog(TRUE, LOG_ERR,
2490	      CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"),
2491	      kr);
2492	goto done;
2493    }
2494
2495    // Get the current list of matches and arm the notification for
2496    // future interface arrivals.
2497    interfaceArrivalCallback((void *)S_notify, S_iter);
2498
2499    // Check if IOKit has already quiesced.
2500    quietCallback((void *)S_notify,
2501		  MACH_PORT_NULL,
2502		  kIOMessageServiceBusyStateChange,
2503		  (void *)(uintptr_t)busy);
2504
2505    CFRunLoopAddSource(CFRunLoopGetCurrent(),
2506		       IONotificationPortGetRunLoopSource(S_notify),
2507		       kCFRunLoopDefaultMode);
2508
2509#ifdef	WAIT_PREVIOUS_BOOT_INTERFACES_OR_QUIET
2510    /*
2511     * Start the wheels turning until we've named all of
2512     * the interfaces that were used during the previous
2513     * boot, until IOKit [matching] has quiesced, or
2514     * until we've waited long enough.
2515     */
2516    CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, MY_PLUGIN_ID);
2517    CFRunLoopAddSource(CFRunLoopGetCurrent(),
2518		       IONotificationPortGetRunLoopSource(S_notify),
2519		       MY_PLUGIN_ID);
2520    while (S_prev_active_list != NULL) {
2521	int	rlStatus;
2522
2523	rlStatus = CFRunLoopRunInMode(MY_PLUGIN_ID, 1.0e10, TRUE);
2524    }
2525#endif	/* WAIT_PREVIOUS_BOOT_INTERFACES_OR_QUIET */
2526
2527#if	!TARGET_OS_EMBEDDED
2528    if (S_dblist != NULL) {
2529	// apply special handling for the BT-PAN interface (if present)
2530	CFArrayApplyFunction(S_dblist,
2531			     CFRangeMake(0, CFArrayGetCount(S_dblist)),
2532			     updateBTPANInformation,
2533			     NULL);
2534    }
2535#endif	// !TARGET_OS_EMBEDDED
2536
2537    ok = TRUE;
2538
2539 done:
2540    if (root != MACH_PORT_NULL) {
2541	IOObjectRelease(root);
2542    }
2543    if (masterPort != MACH_PORT_NULL) {
2544	mach_port_deallocate(mach_task_self(), masterPort);
2545    }
2546
2547    return ok;
2548}
2549
2550static Boolean
2551setup_Virtual(CFBundleRef bundle)
2552{
2553    // open a SCPreferences session
2554    S_prefs = SCPreferencesCreate(NULL, CFSTR(MY_PLUGIN_NAME), NULL);
2555    if (S_prefs == NULL) {
2556	SCLog(TRUE, LOG_ERR,
2557	      CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate() failed: %s"),
2558	      SCErrorString(SCError()));
2559	return FALSE;
2560    }
2561
2562    // register for change notifications.
2563    if (!SCPreferencesSetCallback(S_prefs, updateVirtualNetworkInterfaceConfiguration, NULL)) {
2564	SCLog(TRUE, LOG_ERR,
2565	      CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetCallBack() failed: %s"),
2566	      SCErrorString(SCError()));
2567	CFRelease(S_prefs);
2568	return FALSE;
2569    }
2570
2571    // schedule
2572    if (!SCPreferencesScheduleWithRunLoop(S_prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
2573	if (SCError() != kSCStatusNoStoreServer) {
2574	    SCLog(TRUE, LOG_ERR,
2575		  CFSTR(MY_PLUGIN_NAME ": SCPreferencesScheduleWithRunLoop() failed: %s"),
2576			SCErrorString(SCError()));
2577	    CFRelease(S_prefs);
2578	    return FALSE;
2579	}
2580    }
2581
2582    return TRUE;
2583}
2584
2585static void *
2586exec_InterfaceNamer(void *arg)
2587{
2588    CFBundleRef		bundle  = (CFBundleRef)arg;
2589    CFDictionaryRef	dict;
2590
2591    pthread_setname_np(MY_PLUGIN_NAME " thread");
2592
2593    dict = CFBundleGetInfoDictionary(bundle);
2594    if (isA_CFDictionary(dict)) {
2595	CFNumberRef	num;
2596
2597	num = CFDictionaryGetValue(dict, CFSTR(WAIT_STACK_TIMEOUT_KEY));
2598	if (num != NULL) {
2599	    if (!isA_CFNumber(num) ||
2600		!CFNumberGetValue(num, kCFNumberDoubleType, &S_stack_timeout) ||
2601		(S_stack_timeout <= 0.0)) {
2602		SCLog(TRUE, LOG_ERR,
2603		      CFSTR(MY_PLUGIN_NAME ": " WAIT_STACK_TIMEOUT_KEY " value error"));
2604		S_stack_timeout = WAIT_STACK_TIMEOUT_DEFAULT;
2605	    }
2606	}
2607
2608	num = CFDictionaryGetValue(dict, CFSTR(WAIT_QUIET_TIMEOUT_KEY));
2609	if (num != NULL) {
2610	    if (!isA_CFNumber(num) ||
2611		!CFNumberGetValue(num, kCFNumberDoubleType, &S_quiet_timeout) ||
2612		(S_quiet_timeout <= 0.0)) {
2613		SCLog(TRUE, LOG_ERR,
2614		      CFSTR(MY_PLUGIN_NAME ": " WAIT_QUIET_TIMEOUT_KEY " value error"));
2615		S_quiet_timeout = WAIT_QUIET_TIMEOUT_DEFAULT;
2616	    }
2617	}
2618    }
2619
2620    // setup virtual network interface monitoring
2621    if (!setup_Virtual(bundle)) {
2622	goto error;
2623    }
2624
2625    // setup [IOKit] network interface monitoring
2626    if (!setup_IOKit(bundle)) {
2627	goto error;
2628    }
2629
2630#if	!TARGET_OS_EMBEDDED
2631    // keep launchd from SIGKILL'ing us until after the platform-uuid has
2632    // been updated
2633    S_vproc_transaction = vproc_transaction_begin(NULL);
2634#endif	// !TARGET_OS_EMBEDDED
2635
2636    goto done;
2637
2638  error :
2639    if (S_connect != MACH_PORT_NULL) {
2640	IOServiceClose(S_connect);
2641	S_connect = MACH_PORT_NULL;
2642    }
2643    if (S_dblist != NULL) {
2644	CFRelease(S_dblist);
2645	S_dblist = NULL;
2646    }
2647    if (S_iter != MACH_PORT_NULL) {
2648	IOObjectRelease(S_iter);
2649	S_iter = MACH_PORT_NULL;
2650    }
2651    if (S_notify != MACH_PORT_NULL) {
2652	IONotificationPortDestroy(S_notify);
2653    }
2654    if (S_quiet != MACH_PORT_NULL) {
2655	IOObjectRelease(S_quiet);
2656	S_quiet = MACH_PORT_NULL;
2657    }
2658    if (S_stack != MACH_PORT_NULL) {
2659	IOObjectRelease(S_stack);
2660	S_stack = MACH_PORT_NULL;
2661    }
2662    if (S_state != NULL) {
2663	CFRelease(S_state);
2664	S_state = NULL;
2665    }
2666    if (S_timer != NULL) {
2667	CFRunLoopTimerInvalidate(S_timer);
2668	CFRelease(S_timer);
2669	S_timer = NULL;
2670    }
2671
2672  done :
2673    CFRelease(bundle);
2674    CFRunLoopRun();
2675
2676    return NULL;
2677}
2678
2679__private_extern__
2680void
2681load_InterfaceNamer(CFBundleRef bundle, Boolean bundleVerbose)
2682{
2683    pthread_attr_t  tattr;
2684    pthread_t	    tid;
2685
2686    if (bundleVerbose) {
2687	S_debug = TRUE;
2688    }
2689
2690    CFRetain(bundle);	// released in exec_InterfaceNamer
2691
2692    pthread_attr_init(&tattr);
2693    pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
2694    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
2695//  pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
2696    pthread_create(&tid, &tattr, exec_InterfaceNamer, bundle);
2697    pthread_attr_destroy(&tattr);
2698
2699    return;
2700}
2701
2702//------------------------------------------------------------------------
2703// Main function.
2704#ifdef MAIN
2705int
2706main(int argc, char ** argv)
2707{
2708    CFBundleRef bundle;
2709
2710    _sc_log     = FALSE;
2711    _sc_verbose = (argc > 1) ? TRUE : FALSE;
2712
2713    S_debug = _sc_verbose;
2714
2715    bundle = CFBundleGetMainBundle();
2716    CFRetain(bundle);	// released in exec_InterfaceNamer
2717
2718    (void)exec_InterfaceNamer();
2719
2720    /* not reached */
2721    exit(0);
2722    return 0;
2723}
2724#endif /* MAIN */
2725
2726#ifdef	TEST_PLATFORM_UUID
2727int
2728main(int argc, char ** argv)
2729{
2730    CFStringRef	guid;
2731    CFArrayRef	interfaces;
2732
2733    _sc_log     = FALSE;
2734    _sc_verbose = (argc > 1) ? TRUE : FALSE;
2735
2736    S_dblist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2737    interfaces = SCNetworkInterfaceCopyAll();
2738    if (interfaces != NULL) {
2739	CFIndex	i;
2740	CFIndex	n;
2741
2742	n = CFArrayGetCount(interfaces);
2743	for (i = 0; i < n; i++) {
2744	    CFDictionaryRef		dict;
2745	    SCNetworkInterfaceRef	interface;
2746
2747	    interface = CFArrayGetValueAtIndex(interfaces, i);
2748	    dict = createInterfaceDict(interface);
2749	    CFArrayAppendValue(S_dblist, dict);
2750	    CFRelease(dict);
2751	}
2752	CFRelease(interfaces);
2753    }
2754
2755    guid = copyEthernetUUID();
2756    SCPrint(TRUE, stdout, CFSTR("copyEthernetUUID()  = %@\n"), (guid != NULL) ? guid : CFSTR("NULL"));
2757    if (guid != NULL) CFRelease(guid);
2758
2759    updatePlatformUUID();
2760    CFRelease(S_dblist);
2761    exit(0);
2762    return 0;
2763}
2764#endif	/* TEST_PLATFORM_UUID */
2765
2766#ifdef	TEST_SNAPSHOT
2767int
2768main(int argc, char ** argv)
2769{
2770    CFStringRef	snapshot;
2771
2772    _sc_log     = FALSE;
2773    _sc_verbose = (argc > 1) ? TRUE : FALSE;
2774
2775    snapshot = captureBusy();
2776    SCPrint(TRUE, stdout, CFSTR("%@\n"), snapshot);
2777    CFRelease(snapshot);
2778
2779    exit(0);
2780    return 0;
2781}
2782#endif	/* TEST_SNAPSHOT */
2783
2784