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