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