]> git.saurik.com Git - apple/configd.git/blob - Plugins/InterfaceNamer/ifnamer.c
configd-1061.101.1.tar.gz
[apple/configd.git] / Plugins / InterfaceNamer / ifnamer.c
1 /*
2 * Copyright (c) 2001-2020 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 <TargetConditionals.h>
61 #include <ctype.h>
62 #include <stdlib.h>
63 #include <stdio.h>
64 #include <unistd.h>
65 #include <fcntl.h>
66 #if TARGET_OS_IPHONE
67 #include <lockdown.h>
68 #include <notify.h>
69 #endif // TARGET_OS_IPHONE
70 #include <sys/ioctl.h>
71 #include <sys/stat.h>
72 #include <sys/sysctl.h>
73 #include <sys/param.h>
74 #include <mach/mach.h>
75 #include <net/ethernet.h>
76 #include <net/if_types.h>
77 #include <pthread.h>
78
79 #include <CommonCrypto/CommonDigest.h>
80
81 #include <CoreFoundation/CoreFoundation.h>
82
83 #include <SystemConfiguration/SystemConfiguration.h>
84 #include <SystemConfiguration/SCDPlugin.h>
85 #include <SystemConfiguration/SCPrivate.h>
86 #include <SystemConfiguration/SCValidation.h>
87 #include "SCNetworkConfigurationInternal.h"
88 #include "SCPreferencesInternal.h"
89 #include "plugin_shared.h"
90 #if !TARGET_OS_IPHONE
91 #include "InterfaceNamerControlPrefs.h"
92 #endif // !TARGET_OS_IPHONE
93
94 #ifdef TEST_INTERFACE_ASSIGNMENT
95 #undef INTERFACES_DEFAULT_CONFIG
96 #define INTERFACES_DEFAULT_CONFIG CFSTR("/tmp/ifnamer-test-NetworkInterfaces.plist")
97 #endif // TEST_INTERFACE_ASSIGNMENT
98
99 #include <IOKit/IOKitLib.h>
100 #include <IOKit/IOKitLibPrivate.h>
101 #include <IOKit/IOKitKeysPrivate.h>
102 #include <IOKit/IOBSD.h>
103 #include <IOKit/IOMessage.h>
104 #include <IOKit/network/IONetworkController.h>
105 #include <IOKit/network/IONetworkInterface.h>
106 #include <IOKit/network/IONetworkStack.h>
107 #include <IOKit/usb/USB.h>
108
109 #ifdef kIONetworkStackUserCommandKey
110 #define USE_REGISTRY_ENTRY_ID
111 #endif
112
113 #ifndef USE_REGISTRY_ENTRY_ID
114 // from <IOKit/network/IONetworkStack.h>
115 #define kIONetworkStackUserCommandKey "IONetworkStackUserCommand"
116 enum {
117 kRegisterInterfaceWithFixedUnit = 0,
118 kRegisterInterface,
119 kRegisterAllInterfaces
120 };
121 #endif // !USE_REGISTRY_ENTRY_ID
122
123 #define MY_PLUGIN_NAME "InterfaceNamer"
124 #define MY_PLUGIN_ID CFSTR("com.apple.SystemConfiguration." MY_PLUGIN_NAME)
125
126 #define WAIT_STACK_TIMEOUT_KEY "WaitStackTimeout"
127 #define WAIT_STACK_TIMEOUT_DEFAULT 300.0
128
129 #define WAIT_QUIET_TIMEOUT_KEY "WaitQuietTimeout"
130 #define WAIT_QUIET_TIMEOUT_DEFAULT 240.0
131
132 /*
133 * S_connect
134 * "IONetworkStack" connect object used to "name" an interface.
135 */
136 static io_connect_t S_connect = MACH_PORT_NULL;
137
138 /*
139 * S_dblist
140 * An array of CFDictionary's representing the interfaces
141 * that have been identified and [need to be] named.
142 */
143 static CFMutableArrayRef S_dblist = NULL;
144
145 /*
146 * S_iflist
147 * An array of SCNetworkInterface's representing the
148 * interfaces that have been identified.
149 */
150 static CFMutableArrayRef S_iflist = NULL;
151
152 /*
153 * S_iter
154 * IOServiceAddMatchingNotification object used to watch for
155 * new network interfaces.
156 */
157 static io_iterator_t S_iter = MACH_PORT_NULL;
158
159 #if !TARGET_OS_IPHONE
160 /*
161 * S_locked
162 * An array of CFData(WatchedInfo) objects representing those
163 * interfaces that have been connected to the system while
164 * locked.
165 */
166 static CFMutableArrayRef S_locked = NULL;
167 #endif // !TARGET_OS_IPHONE
168
169 /*
170 * S_notify
171 * notification object for receiving IOKit notifications of
172 * new devices or state changes.
173 */
174 static IONotificationPortRef S_notify = NULL;
175
176 /*
177 * S_preconfigured
178 * An array of CFData(WatchedInfo) objects representing those
179 * pre-configured interfaces that have been connected to the
180 * system.
181 */
182 static CFMutableArrayRef S_preconfigured = NULL;
183
184 /* S_prev_active_list
185 * An array of CFDictionary's representing the previously
186 * named interfaces.
187 */
188 static CFMutableArrayRef S_prev_active_list = NULL;
189
190 /*
191 * S_quiet
192 * IOServiceAddInterestNotification object used to watch for
193 * IOKit matching to quiesce.
194 */
195 static io_object_t S_quiet = MACH_PORT_NULL;
196
197 /*
198 * S_stack
199 * IOServiceAddMatchingNotification object used to watch for
200 * the availability of the "IONetworkStack" object.
201 */
202 static io_iterator_t S_stack = MACH_PORT_NULL;
203
204 /*
205 * S_state
206 * A dictionary containing Information about each network
207 * interface. For now, the key is the BSD name and the
208 * value is a CFNumber noting how long (in milliseconds)
209 * it took for the interface to be recognized/named.
210 */
211 static CFMutableDictionaryRef S_state = NULL;
212
213 #if TARGET_OS_IPHONE
214 /*
215 * S_trustedHostAttached
216 *
217 * Note: this global must only be updated on trustRequired_queue()
218 */
219 static Boolean S_trustedHostAttached = FALSE;
220
221 /*
222 *
223 * Note: this global must only be updated on trustRequired_queue()
224 */
225 static CFIndex S_trustedHostCount = 0;
226
227 /*
228 * S_trustRequired
229 * An array of CFData(WatchedInfo) objects representing those
230 * interfaces that require [lockdownd] trust.
231 */
232 static CFMutableArrayRef S_trustRequired = NULL;
233 #endif // TARGET_OS_IPHONE
234
235 /*
236 * S_timer
237 * CFRunLoopTimer tracking how long we are willing to wait
238 * for IOKit matching to quiesce (IOKitWaitQuiet).
239 *
240 * S_stack_timeout
241 * time to wait for the IONetworkStack object to appear before timeout
242 *
243 * S_quiet_timeout
244 * time to wait for the IOKit to quiesce (after the IONetworkStack is
245 * has appeared.
246 */
247 static CFRunLoopTimerRef S_timer = NULL;
248 static double S_stack_timeout = WAIT_STACK_TIMEOUT_DEFAULT;
249 static double S_quiet_timeout = WAIT_QUIET_TIMEOUT_DEFAULT;
250
251 /*
252 * Virtual network interface configuration
253 * S_prefs : SCPreferences to configuration
254 * S_bonds : most recently actived Bond configuration
255 * S_bridges : most recently actived Bridge configuration
256 * S_vlans : most recently actived VLAN configuration
257 */
258 static SCPreferencesRef S_prefs = NULL;
259 static CFArrayRef S_bonds = NULL;
260 static CFArrayRef S_bridges = NULL;
261 static CFArrayRef S_vlans = NULL;
262
263 /*
264 * Logging
265 */
266 __private_extern__
267 os_log_t
268 __log_InterfaceNamer(void)
269 {
270 static os_log_t log = NULL;
271
272 if (log == NULL) {
273 log = os_log_create("com.apple.SystemConfiguration", "InterfaceNamer");
274 }
275
276 return log;
277 }
278
279
280 static void
281 addTimestamp(CFMutableDictionaryRef dict, CFStringRef key)
282 {
283 CFAbsoluteTime now;
284 CFNumberRef val;
285
286 now = CFAbsoluteTimeGetCurrent();
287 val = CFNumberCreate(NULL, kCFNumberDoubleType, &now);
288 CFDictionaryAddValue(dict, key, val);
289 CFRelease(val);
290 return;
291 }
292
293 static CFComparisonResult
294 if_unit_compare(const void *val1, const void *val2, void *context)
295 {
296 #pragma unused(context)
297 CFComparisonResult res;
298 CFNumberRef type1;
299 CFNumberRef type2;
300 CFNumberRef unit1;
301 CFNumberRef unit2;
302
303 type1 = CFDictionaryGetValue((CFDictionaryRef)val1,
304 CFSTR(kIOInterfaceType));
305 type2 = CFDictionaryGetValue((CFDictionaryRef)val2,
306 CFSTR(kIOInterfaceType));
307 res = CFNumberCompare(type1, type2, NULL);
308 if (res != kCFCompareEqualTo) {
309 return (res);
310 }
311 unit1 = CFDictionaryGetValue((CFDictionaryRef)val1,
312 CFSTR(kIOInterfaceUnit));
313 unit2 = CFDictionaryGetValue((CFDictionaryRef)val2,
314 CFSTR(kIOInterfaceUnit));
315 return (CFNumberCompare(unit1, unit2, NULL));
316 }
317
318 static void
319 writeInterfaceListForModel(SCPreferencesRef prefs, CFStringRef old_model)
320 {
321 Boolean ok;
322 CFPropertyListRef plist;
323 SCPreferencesRef savedPrefs;
324 CFStringRef savedPrefsID;
325
326 savedPrefsID = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@-%@"),
327 INTERFACES_DEFAULT_CONFIG,
328 old_model);
329 savedPrefs = SCPreferencesCreate(NULL, CFSTR(MY_PLUGIN_NAME ":writeInterfaceListForModel"), savedPrefsID);
330 CFRelease(savedPrefsID);
331 if (savedPrefs == NULL) {
332 SC_log(LOG_NOTICE, "SCPreferencesCreate(\"NetworkInterfaces-<model>.plist\") failed: %s", SCErrorString(SCError()));
333 return;
334 }
335
336 plist = SCPreferencesPathGetValue(prefs, CFSTR("/"));
337 ok = SCPreferencesPathSetValue(savedPrefs, CFSTR("/"), plist);
338 if (!ok) {
339 SC_log(LOG_NOTICE, "SCPreferencesPathSetValue() failed: %s", SCErrorString(SCError()));
340 }
341
342 ok = SCPreferencesCommitChanges(savedPrefs);
343 CFRelease(savedPrefs);
344 if (!ok) {
345 SC_log(LOG_NOTICE, "SCPreferencesCommitChanges(\"NetworkInterfaces-<model>.plist\") failed: %s", SCErrorString(SCError()));
346 }
347
348 return;
349 }
350
351 static void
352 writeInterfaceList(CFArrayRef if_list)
353 {
354 CFArrayRef cur_list;
355 CFStringRef new_model;
356 SCPreferencesRef ni_prefs;
357 CFStringRef old_model;
358
359 if (!isA_CFArray(if_list)) {
360 return;
361 }
362
363 ni_prefs = SCPreferencesCreate(NULL, CFSTR(MY_PLUGIN_NAME ":writeInterfaceList"), INTERFACES_DEFAULT_CONFIG);
364 if (ni_prefs == NULL) {
365 SC_log(LOG_NOTICE, "SCPreferencesCreate(\"NetworkInterfaces.plist\") failed: %s", SCErrorString(SCError()));
366 return;
367 }
368
369 cur_list = SCPreferencesGetValue(ni_prefs, INTERFACES);
370 if (_SC_CFEqual(cur_list, if_list)) {
371 goto done;
372 }
373
374 old_model = SCPreferencesGetValue(ni_prefs, MODEL);
375 new_model = _SC_hw_model(FALSE);
376 if ((old_model != NULL) && !_SC_CFEqual(old_model, new_model)) {
377 // if new hardware
378 if ((old_model != NULL) && (cur_list != NULL)) {
379 SC_log(LOG_NOTICE, "Hardware model changed\n"
380 " created on \"%@\"\n"
381 " now on \"%@\"",
382 old_model,
383 new_model);
384
385 // save the interface list that was created on "other" hardware
386 writeInterfaceListForModel(ni_prefs, old_model);
387 }
388
389 SCPreferencesSetValue(ni_prefs, MODEL, new_model);
390 }
391
392 SCPreferencesSetValue(ni_prefs, INTERFACES, if_list);
393
394 if (!SCPreferencesCommitChanges(ni_prefs)) {
395 if (SCError() != EROFS) {
396 SC_log(LOG_NOTICE, "SCPreferencesCommitChanges() failed: %s", SCErrorString(SCError()));
397 }
398 goto done;
399 }
400
401 done:
402
403 CFRelease(ni_prefs);
404 return;
405 }
406
407 static CF_RETURNS_RETAINED CFMutableArrayRef
408 readInterfaceList()
409 {
410 CFArrayRef if_list;
411 SCPreferencesRef ni_prefs;
412 CFStringRef old_model;
413 static Boolean once = FALSE;
414 CFMutableArrayRef plist = NULL;
415
416 ni_prefs = SCPreferencesCreate(NULL, CFSTR(MY_PLUGIN_NAME ":readInterfaceList"), INTERFACES_DEFAULT_CONFIG);
417 if (ni_prefs == NULL) {
418 SC_log(LOG_NOTICE, "SCPreferencesCreate() failed: %s", SCErrorString(SCError()));
419 return (NULL);
420 }
421
422 if (!once) {
423 __SCNetworkConfigurationUpgrade(NULL, &ni_prefs, TRUE);
424 once = TRUE;
425 }
426
427 if_list = SCPreferencesGetValue(ni_prefs, INTERFACES);
428 if_list = isA_CFArray(if_list);
429
430 old_model = SCPreferencesGetValue(ni_prefs, MODEL);
431 if (old_model != NULL) {
432 CFStringRef new_model;
433
434 new_model = _SC_hw_model(FALSE);
435 if (!_SC_CFEqual(old_model, new_model)) {
436 /*
437 * If the interface list was created on other hardware then
438 * we start fresh.
439 */
440 if_list = NULL;
441 }
442 }
443
444 if (if_list != NULL) {
445 CFIndex n = CFArrayGetCount(if_list);
446
447 plist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
448 for (CFIndex i = 0; i < n; i++) {
449 CFDictionaryRef dict;
450
451 dict = CFArrayGetValueAtIndex(if_list, i);
452 if (isA_CFDictionary(dict) &&
453 CFDictionaryContainsKey(dict, CFSTR(kIOInterfaceType)) &&
454 CFDictionaryContainsKey(dict, CFSTR(kIOInterfaceUnit)) &&
455 CFDictionaryContainsKey(dict, CFSTR(kIOMACAddress))) {
456 CFArrayAppendValue(plist, dict);
457 }
458 }
459 }
460
461 if (plist != NULL) {
462 CFIndex n = CFArrayGetCount(plist);
463
464 if (n > 1) {
465 CFArraySortValues(plist, CFRangeMake(0, n), if_unit_compare, NULL);
466 }
467 }
468
469 CFRelease(ni_prefs);
470 return (plist);
471 }
472
473 static CF_RETURNS_RETAINED CFMutableArrayRef
474 previouslyActiveInterfaces()
475 {
476 CFMutableArrayRef active;
477 CFIndex n;
478
479 if (S_dblist == NULL) {
480 return NULL;
481 }
482
483 active = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
484
485 n = CFArrayGetCount(S_dblist);
486 for (CFIndex i = 0; i < n; i++) {
487 CFDictionaryRef if_dict;
488
489 if_dict = CFArrayGetValueAtIndex(S_dblist, i);
490 if (CFDictionaryContainsKey(if_dict, CFSTR(kSCNetworkInterfaceActive))) {
491 CFMutableDictionaryRef new_dict;
492
493 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, if_dict);
494 CFDictionaryRemoveValue(new_dict, CFSTR(kSCNetworkInterfaceActive));
495 CFArraySetValueAtIndex(S_dblist, i, new_dict);
496 CFArrayAppendValue(active, new_dict);
497 CFRelease(new_dict);
498 }
499 }
500
501 return active;
502 }
503
504 static void
505 updateInterfaces(void);
506
507 static void
508 updateStore(void)
509 {
510 CFStringRef key;
511
512 key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@" MY_PLUGIN_NAME), kSCDynamicStoreDomainPlugin);
513 (void)SCDynamicStoreSetValue(NULL, key, S_state);
514 CFRelease(key);
515
516 return;
517 }
518
519 #if !TARGET_OS_IPHONE
520 static void
521 updateBondInterfaceConfiguration(SCPreferencesRef prefs)
522 {
523 CFArrayRef interfaces;
524
525 interfaces = SCBondInterfaceCopyAll(prefs);
526 if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) {
527 CFRelease(interfaces);
528 interfaces = NULL;
529 }
530
531 if (_SC_CFEqual(S_bonds, interfaces)) {
532 // if no change
533 if (interfaces != NULL) CFRelease(interfaces);
534 return;
535 }
536
537 if (S_bonds != NULL) CFRelease(S_bonds);
538 S_bonds = interfaces;
539
540 if (!_SCBondInterfaceUpdateConfiguration(prefs)) {
541 SC_log(LOG_NOTICE, "_SCBondInterfaceUpdateConfiguration() failed: %s",
542 SCErrorString(SCError()));
543 }
544
545 return;
546 }
547 #endif // !TARGET_OS_IPHONE
548
549 static void
550 updateBridgeInterfaceConfiguration(SCPreferencesRef prefs)
551 {
552 CFArrayRef interfaces;
553
554 interfaces = SCBridgeInterfaceCopyAll(prefs);
555 if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) {
556 CFRelease(interfaces);
557 interfaces = NULL;
558 }
559
560 if (_SC_CFEqual(S_bridges, interfaces)) {
561 // if no change
562 if (interfaces != NULL) CFRelease(interfaces);
563 return;
564 }
565
566 if (S_bridges != NULL) CFRelease(S_bridges);
567 S_bridges = interfaces;
568
569 if (!_SCBridgeInterfaceUpdateConfiguration(prefs)) {
570 SC_log(LOG_NOTICE, "_SCBridgeInterfaceUpdateConfiguration() failed: %s",
571 SCErrorString(SCError()));
572 }
573
574 return;
575 }
576
577 static void
578 updateVLANInterfaceConfiguration(SCPreferencesRef prefs)
579 {
580 CFArrayRef interfaces;
581
582 interfaces = SCVLANInterfaceCopyAll(prefs);
583 if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) {
584 CFRelease(interfaces);
585 interfaces = NULL;
586 }
587
588 if (_SC_CFEqual(S_vlans, interfaces)) {
589 // if no change
590 if (interfaces != NULL) CFRelease(interfaces);
591 return;
592 }
593
594 if (S_vlans != NULL) CFRelease(S_vlans);
595 S_vlans = interfaces;
596
597 if (!_SCVLANInterfaceUpdateConfiguration(prefs)) {
598 SC_log(LOG_NOTICE, "_SCVLANInterfaceUpdateConfiguration() failed: %s",
599 SCErrorString(SCError()));
600 }
601
602 return;
603 }
604
605 static void
606 updateVirtualNetworkInterfaceConfiguration(SCPreferencesRef prefs,
607 SCPreferencesNotification notificationType,
608 void *info)
609 {
610 #pragma unused(info)
611 if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
612 return;
613 }
614
615 if (prefs == NULL) {
616 // if a new interface has been "named"
617 prefs = S_prefs;
618 if (S_bonds != NULL) {
619 CFRelease(S_bonds);
620 S_bonds = NULL;
621 }
622 if (S_bridges != NULL) {
623 CFRelease(S_bridges);
624 S_bridges = NULL;
625 }
626 if (S_vlans != NULL) {
627 CFRelease(S_vlans);
628 S_vlans = NULL;
629 }
630 }
631
632 #if !TARGET_OS_IPHONE
633 updateBondInterfaceConfiguration (prefs);
634 #endif // !TARGET_OS_IPHONE
635 updateBridgeInterfaceConfiguration(prefs);
636 updateVLANInterfaceConfiguration (prefs);
637
638 // we are finished with current prefs, wait for changes
639 SCPreferencesSynchronize(prefs);
640
641 return;
642 }
643
644 #if TARGET_OS_OSX
645
646 static void
647 updateBTPANInformation(const void *value, void *context)
648 {
649 #pragma unused(context)
650 CFDataRef addr;
651 CFDictionaryRef dict = (CFDictionaryRef)value;
652 CFStringRef if_name;
653 CFDictionaryRef info;
654 CFStringRef name;
655
656 if_name = CFDictionaryGetValue(dict, CFSTR(kIOBSDNameKey));
657 if (!isA_CFString(if_name)) {
658 // if no BSD name
659 return;
660 }
661
662 info = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceInfo));
663 if (!isA_CFDictionary(info)) {
664 // if no SCNetworkInterface info
665 return;
666 }
667
668 name = CFDictionaryGetValue(info, kSCPropUserDefinedName);
669 if (!isA_CFString(name) || !CFEqual(name, CFSTR(BT_PAN_NAME))) {
670 // if not BT-PAN interface
671 return;
672 }
673
674 CFDictionaryAddValue(S_state, kInterfaceNamerKey_BT_PAN_Name, if_name);
675
676 addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
677 if (isA_CFData(addr)) {
678 CFDictionaryAddValue(S_state, kInterfaceNamerKey_BT_PAN_Mac, addr);
679 }
680
681 return;
682 }
683 #endif // TARGET_OS_OSX
684
685 static CFDictionaryRef
686 createInterfaceDict(SCNetworkInterfaceRef interface, CFArrayRef matchingMACs)
687 {
688 CFMutableDictionaryRef new_if;
689 CFTypeRef val;
690
691 new_if = CFDictionaryCreateMutable(NULL,
692 0,
693 &kCFTypeDictionaryKeyCallBacks,
694 &kCFTypeDictionaryValueCallBacks);
695
696 val = _SCNetworkInterfaceCopyInterfaceInfo(interface);
697 if (val != NULL) {
698 CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceInfo), val);
699 CFRelease(val);
700 }
701
702 val = _SCNetworkInterfaceGetIOPath(interface);
703 if (val != NULL) {
704 CFDictionarySetValue(new_if, CFSTR(kIOPathMatchKey), val);
705 }
706
707 val = _SCNetworkInterfaceGetIOInterfaceNamePrefix(interface);
708 if (val != NULL) {
709 CFDictionarySetValue(new_if, CFSTR(kIOInterfaceNamePrefix), val);
710 }
711
712 val = _SCNetworkInterfaceGetIOInterfaceType(interface);
713 if (val != NULL) {
714 CFDictionarySetValue(new_if, CFSTR(kIOInterfaceType), val);
715 }
716
717 val = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
718 if (val != NULL) {
719 CFDictionarySetValue(new_if, CFSTR(kIOInterfaceUnit), val);
720 }
721
722 val = _SCNetworkInterfaceGetHardwareAddress(interface);
723 if (val != NULL) {
724 CFDictionarySetValue(new_if, CFSTR(kIOMACAddress), val);
725 }
726
727 val = SCNetworkInterfaceGetBSDName(interface);
728 if (val != NULL) {
729 CFDictionarySetValue(new_if, CFSTR(kIOBSDNameKey), val);
730 }
731
732 val = SCNetworkInterfaceGetInterfaceType(interface);
733 if (val != NULL) {
734 CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceType), val);
735 }
736
737 CFDictionarySetValue(new_if,
738 CFSTR(kIOBuiltin),
739 _SCNetworkInterfaceIsBuiltin(interface) ? kCFBooleanTrue : kCFBooleanFalse);
740
741 if (_SCNetworkInterfaceIsHiddenConfiguration(interface)) {
742 CFDictionarySetValue(new_if, kSCNetworkInterfaceHiddenConfigurationKey, kCFBooleanTrue);
743 }
744
745 CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceActive), kCFBooleanTrue);
746
747 if (matchingMACs != NULL) {
748 CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceMatchingMACs), matchingMACs);
749 }
750
751 return new_if;
752 }
753
754 static CFDictionaryRef
755 lookupInterfaceByAddress(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
756 {
757 CFDataRef addr;
758 CFIndex n;
759 CFNumberRef type;
760
761 if (db_list == NULL) {
762 return (NULL);
763 }
764 type = _SCNetworkInterfaceGetIOInterfaceType(interface);
765 addr = _SCNetworkInterfaceGetHardwareAddress(interface);
766 if (type == NULL || addr == NULL) {
767 return (NULL);
768 }
769
770 n = CFArrayGetCount(db_list);
771 for (CFIndex i = 0; i < n; i++) {
772 CFDataRef a;
773 CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
774 CFNumberRef t;
775
776 t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
777 a = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
778 if (t == NULL || a == NULL)
779 continue;
780
781 if (CFEqual(type, t) && CFEqual(addr, a)) {
782 if (where) {
783 *where = i;
784 }
785 return (dict);
786 }
787 }
788 return (NULL);
789 }
790
791 static CFDictionaryRef
792 lookupInterfaceByName(CFArrayRef db_list, CFStringRef bsdName, CFIndex * where)
793 {
794 CFIndex n;
795
796 if (db_list == NULL) {
797 return (NULL);
798 }
799
800 n = CFArrayGetCount(db_list);
801 for (CFIndex i = 0; i < n; i++) {
802 CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
803 CFStringRef name;
804
805 name = CFDictionaryGetValue(dict, CFSTR(kIOBSDNameKey));
806 if (_SC_CFEqual(name, bsdName)) {
807 if (where) {
808 *where = i;
809 }
810 return (dict);
811 }
812 }
813 return (NULL);
814 }
815
816 static CFDictionaryRef
817 lookupInterfaceByUnit(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
818 {
819 CFIndex n;
820 CFNumberRef type;
821 CFNumberRef unit;
822
823 if (db_list == NULL) {
824 return (NULL);
825 }
826 type = _SCNetworkInterfaceGetIOInterfaceType(interface);
827 unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
828 if (type == NULL || unit == NULL) {
829 return (NULL);
830 }
831
832 n = CFArrayGetCount(db_list);
833 for (CFIndex i = 0; i < n; i++) {
834 CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
835 CFNumberRef t;
836 CFNumberRef u;
837
838 t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
839 u = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
840 if (t == NULL || u == NULL) {
841 continue;
842 }
843
844 if (CFEqual(type, t) && CFEqual(unit, u)) {
845 if (where)
846 *where = i;
847 return (dict);
848 }
849 }
850 return (NULL);
851 }
852
853 typedef struct {
854 CFDictionaryRef match_info;
855 CFStringRef match_type;
856 CFBooleanRef match_builtin;
857 CFMutableArrayRef matches;
858 } matchContext, *matchContextRef;
859
860 static CF_RETURNS_RETAINED CFDictionaryRef
861 thinInterfaceInfo(CFDictionaryRef info)
862 {
863 CFNumberRef num;
864 int vid;
865
866 if (CFDictionaryGetValueIfPresent(info, CFSTR(kUSBVendorID), (const void **)&num)
867 && isA_CFNumber(num)
868 && CFNumberGetValue(num, kCFNumberIntType, &vid)
869 && (vid == kIOUSBAppleVendorID)) {
870 CFMutableDictionaryRef thin;
871
872 // if this is an Apple USB device than we trust that
873 // the non-localized name will be correct.
874 thin = CFDictionaryCreateMutableCopy(NULL, 0, info);
875 CFDictionaryRemoveValue(thin, CFSTR(kUSBProductString));
876 CFDictionaryRemoveValue(thin, CFSTR(kUSBVendorID));
877 CFDictionaryRemoveValue(thin, CFSTR(kUSBProductID));
878 return thin;
879 }
880
881 return CFRetain(info);
882 }
883
884 static Boolean
885 matchInterfaceInfo(CFDictionaryRef known_info, CFDictionaryRef match_info)
886 {
887 Boolean match;
888
889 match = _SC_CFEqual(known_info, match_info);
890 if (!match &&
891 isA_CFDictionary(known_info) &&
892 isA_CFDictionary(match_info)) {
893
894 // if not an exact match, try thinning
895 known_info = thinInterfaceInfo(known_info);
896 match_info = thinInterfaceInfo(match_info);
897 match = _SC_CFEqual(known_info, match_info);
898 if (known_info != NULL) CFRelease(known_info);
899 if (match_info != NULL) CFRelease(match_info);
900 }
901
902 return match;
903 }
904
905 static void
906 matchKnown(const void *value, void *context)
907 {
908 CFDictionaryRef known_dict = (CFDictionaryRef)value;
909 matchContextRef match_context = (matchContextRef)context;
910
911 // match interface type
912 {
913 CFStringRef known_type;
914
915 known_type = CFDictionaryGetValue(known_dict, CFSTR(kSCNetworkInterfaceType));
916 if (!_SC_CFEqual(known_type, match_context->match_type)) {
917 return;
918 }
919 }
920
921 // match SCNetworkInterfaceInfo
922 {
923 CFDictionaryRef known_info;
924
925 known_info = CFDictionaryGetValue(known_dict, CFSTR(kSCNetworkInterfaceInfo));
926 if (!matchInterfaceInfo(known_info, match_context->match_info)) {
927 return;
928 }
929 }
930
931 // if requested, match [non-]builtin
932 if (match_context->match_builtin != NULL) {
933 CFBooleanRef known_builtin;
934
935 known_builtin = CFDictionaryGetValue(known_dict, CFSTR(kIOBuiltin));
936 if (!isA_CFBoolean(known_builtin)) {
937 known_builtin = kCFBooleanFalse;
938 }
939 if (!_SC_CFEqual(known_builtin, match_context->match_builtin)) {
940 return;
941 }
942 }
943
944 // if we have a match
945 if (match_context->matches == NULL) {
946 match_context->matches = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
947 }
948 CFArrayAppendValue(match_context->matches, known_dict);
949
950 return;
951 }
952
953 static void
954 matchUnnamed(const void *value, void *context)
955 {
956 SCNetworkInterfaceRef known_if = (SCNetworkInterfaceRef)value;
957 matchContextRef match_context = (matchContextRef)context;
958
959 if (match_context->matches == NULL) {
960 return;
961 }
962
963 // match interface type
964 {
965 CFStringRef known_type;
966
967 known_type = SCNetworkInterfaceGetInterfaceType(known_if);
968 if (!_SC_CFEqual(known_type, match_context->match_type)) {
969 return;
970 }
971 }
972
973 // match SCNetworkInterfaceInfo
974 {
975 CFDictionaryRef known_info;
976 Boolean match;
977
978 known_info = _SCNetworkInterfaceCopyInterfaceInfo(known_if);
979 match = matchInterfaceInfo(known_info, match_context->match_info);
980 if (known_info != NULL) CFRelease(known_info);
981 if (!match) {
982 return;
983 }
984 }
985
986 // if requested, match [non-]builtin
987 if (match_context->match_builtin != NULL) {
988 CFBooleanRef known_builtin;
989
990 known_builtin = _SCNetworkInterfaceIsBuiltin(known_if) ? kCFBooleanTrue
991 : kCFBooleanFalse;
992 if (!_SC_CFEqual(known_builtin, match_context->match_builtin)) {
993 return;
994 }
995 }
996
997 // if we have a match
998 CFRelease(match_context->matches);
999 match_context->matches = NULL;
1000
1001 return;
1002 }
1003
1004 static Boolean
1005 interfaceExists(CFStringRef prefix, CFNumberRef unit)
1006 {
1007 Boolean found = FALSE;
1008 CFDictionaryRef match_dict;
1009 CFStringRef match_keys[2];
1010 CFTypeRef match_vals[2];
1011 CFDictionaryRef matching;
1012
1013
1014
1015 io_registry_entry_t entry = MACH_PORT_NULL;
1016 io_iterator_t iterator = MACH_PORT_NULL;
1017 kern_return_t kr;
1018 mach_port_t masterPort = MACH_PORT_NULL;
1019
1020 kr = IOMasterPort(bootstrap_port, &masterPort);
1021 if (kr != KERN_SUCCESS) {
1022 SC_log(LOG_ERR, "IOMasterPort returned 0x%x", kr);
1023 goto error;
1024 }
1025
1026 // look for kIONetworkInterface with matching prefix and unit
1027 match_keys[0] = CFSTR(kIOInterfaceNamePrefix);
1028 match_vals[0] = prefix;
1029 match_keys[1] = CFSTR(kIOInterfaceUnit);
1030 match_vals[1] = unit;
1031 match_dict = CFDictionaryCreate(NULL,
1032 (const void **)match_keys,
1033 (const void **)match_vals,
1034 2,
1035 &kCFTypeDictionaryKeyCallBacks,
1036 &kCFTypeDictionaryValueCallBacks);
1037
1038 match_keys[0] = CFSTR(kIOProviderClassKey);
1039 match_vals[0] = CFSTR(kIONetworkInterfaceClass);
1040 match_keys[1] = CFSTR(kIOPropertyMatchKey);
1041 match_vals[1] = match_dict;
1042 matching = CFDictionaryCreate(NULL,
1043 (const void **)match_keys,
1044 (const void **)match_vals,
1045 sizeof(match_keys)/sizeof(match_keys[0]),
1046 &kCFTypeDictionaryKeyCallBacks,
1047 &kCFTypeDictionaryValueCallBacks);
1048 CFRelease(match_dict);
1049
1050 // note: the "matching" dictionary will be consumed by the following
1051 kr = IOServiceGetMatchingServices(masterPort, matching, &iterator);
1052 if ((kr != kIOReturnSuccess) || (iterator == MACH_PORT_NULL)) {
1053 // if no interface
1054 goto error;
1055 }
1056
1057 entry = IOIteratorNext(iterator);
1058 if (entry == MACH_PORT_NULL) {
1059 // if no interface
1060 goto error;
1061 }
1062
1063 found = TRUE;
1064
1065 error:
1066 if (masterPort != MACH_PORT_NULL) {
1067 mach_port_deallocate(mach_task_self(), masterPort);
1068 }
1069 if (entry != MACH_PORT_NULL) {
1070 IOObjectRelease(entry);
1071 }
1072 if (iterator != MACH_PORT_NULL) {
1073 IOObjectRelease(iterator);
1074 }
1075
1076 return (found);
1077 }
1078
1079 /*
1080 * lookupMatchingInterface
1081 *
1082 * Looks at the interfaces that have already been [or need to be] named with
1083 * the goal of allowing a system using a single network interface/adaptor of
1084 * a given type (vendor, model, ...) to not care about the specific adaptor
1085 * that is used (i.e. swapping dongle's is OK). Once a system has had more
1086 * than one interface/adaptor connected at the same time than we assume that
1087 * the network configuration is being setup for multi-homing that should be
1088 * maintained.
1089 *
1090 * If no matches are found or if more than one match is found, return NULL.
1091 * If a single match is found, return the match.
1092 */
1093 static CFDictionaryRef
1094 lookupMatchingInterface(SCNetworkInterfaceRef interface,
1095 CFArrayRef db_list, // already named
1096 CFArrayRef if_list, // to be named
1097 CFIndex if_list_index,
1098 CFBooleanRef builtin)
1099 {
1100 CFStringRef if_type;
1101 CFDictionaryRef match = NULL;
1102 matchContext match_context;
1103
1104 if_type = SCNetworkInterfaceGetInterfaceType(interface);
1105 if (if_type == NULL) {
1106 return NULL;
1107 }
1108
1109 match_context.match_type = if_type;
1110 match_context.match_info = _SCNetworkInterfaceCopyInterfaceInfo(interface);
1111 match_context.match_builtin = builtin;
1112 match_context.matches = NULL;
1113
1114 // check for matches to interfaces that have already been named
1115 // ... and append each match that we find to match_context.matches
1116 if (db_list != NULL) {
1117 CFArrayApplyFunction(db_list,
1118 CFRangeMake(0, CFArrayGetCount(db_list)),
1119 matchKnown,
1120 &match_context);
1121 }
1122
1123 // check for matches to interfaces that will be named
1124 // ... and CFRelease match_context.matches if we find another network
1125 // interface of the same type that also needs to be named
1126 if (if_list != NULL) {
1127 CFIndex if_list_count;
1128
1129 if_list_count = CFArrayGetCount(if_list);
1130 if (if_list_index < if_list_count) {
1131 CFArrayApplyFunction(if_list,
1132 CFRangeMake(if_list_index, if_list_count - if_list_index),
1133 matchUnnamed,
1134 &match_context);
1135 }
1136 }
1137
1138 // check if we have a single match
1139 if (match_context.matches != NULL) {
1140 if (CFArrayGetCount(match_context.matches) == 1) {
1141 match = CFArrayGetValueAtIndex(match_context.matches, 0);
1142 }
1143 CFRelease(match_context.matches);
1144 }
1145
1146 if (match != NULL) {
1147 Boolean active = TRUE;
1148 CFStringRef name;
1149
1150 name = CFDictionaryGetValue(match, CFSTR(kIOBSDNameKey));
1151 if (isA_CFString(name)) {
1152 CFStringRef prefix;
1153 CFNumberRef unit;
1154
1155 prefix = CFDictionaryGetValue(match, CFSTR(kIOInterfaceNamePrefix));
1156 unit = CFDictionaryGetValue(match, CFSTR(kIOInterfaceUnit));
1157 if (isA_CFString(prefix) && isA_CFNumber(unit)) {
1158 if (!interfaceExists(prefix, unit)) {
1159 active = FALSE;
1160 }
1161 }
1162 }
1163
1164 if (active) {
1165 match = NULL;
1166 }
1167 }
1168
1169 if (match_context.match_info != NULL) CFRelease(match_context.match_info);
1170 return match;
1171 }
1172
1173 static void
1174 insertInterface(CFMutableArrayRef db_list, SCNetworkInterfaceRef interface, CFDictionaryRef db_dict_match)
1175 {
1176 CFIndex i;
1177 CFDictionaryRef if_dict;
1178 CFStringRef if_name;
1179 CFNumberRef if_type;
1180 CFNumberRef if_unit;
1181 CFArrayRef matchingMACs = NULL;
1182 CFIndex n = CFArrayGetCount(db_list);
1183 CFComparisonResult res;
1184
1185 if_name = SCNetworkInterfaceGetBSDName(interface);
1186 if (if_name != NULL) {
1187 addTimestamp(S_state, if_name);
1188 }
1189
1190 if (!_SCNetworkInterfaceIsBuiltin(interface) && (db_dict_match != NULL)) {
1191 CFDataRef addr_cur;
1192 CFDataRef addr_old;
1193
1194 matchingMACs = CFDictionaryGetValue(db_dict_match, CFSTR(kSCNetworkInterfaceMatchingMACs));
1195 if (matchingMACs != NULL) {
1196 CFRetain(matchingMACs);
1197 }
1198
1199 addr_old = CFDictionaryGetValue(db_dict_match, CFSTR(kIOMACAddress));
1200 addr_cur = _SCNetworkInterfaceGetHardwareAddress(interface);
1201 if ((addr_old != NULL) && (addr_cur != NULL) && !CFEqual(addr_old, addr_cur)) {
1202 CFMutableArrayRef matching_new;
1203
1204 // if MAC address changed, add previous MAC to history
1205 if (matchingMACs != NULL) {
1206 matching_new = CFArrayCreateMutableCopy(NULL, 0, matchingMACs);
1207 CFRelease(matchingMACs);
1208
1209 // remove duplicates of the now current MAC from history
1210 i = CFArrayGetFirstIndexOfValue(matching_new, CFRangeMake(0, CFArrayGetCount(matching_new)), addr_cur);
1211 if (i != kCFNotFound) {
1212 CFArrayRemoveValueAtIndex(matching_new, i);
1213 }
1214
1215 // remove duplicates of the previous MAC from history before re-inserting
1216 i = CFArrayGetFirstIndexOfValue(matching_new, CFRangeMake(0, CFArrayGetCount(matching_new)), addr_old);
1217 if (i != kCFNotFound) {
1218 CFArrayRemoveValueAtIndex(matching_new, i);
1219 }
1220 } else {
1221 matching_new = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1222 }
1223 CFArrayInsertValueAtIndex(matching_new, 0, addr_old);
1224
1225 // limit history size
1226 #define MATCHING_HISTORY_MAXLEN 32
1227 for (i = CFArrayGetCount(matching_new); i > MATCHING_HISTORY_MAXLEN; i--) {
1228 CFArrayRemoveValueAtIndex(matching_new, i - 1);
1229 }
1230
1231 matchingMACs = matching_new;
1232 }
1233 }
1234
1235 if_dict = createInterfaceDict(interface, matchingMACs);
1236 if (matchingMACs != NULL) {
1237 CFRelease(matchingMACs);
1238 }
1239
1240 if_type = _SCNetworkInterfaceGetIOInterfaceType(interface);
1241 if_unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
1242 if ((if_type == NULL) || (if_unit == NULL)) {
1243 CFRelease(if_dict);
1244 return;
1245 }
1246
1247 for (i = 0; i < n; i++) {
1248 CFNumberRef db_type;
1249 CFNumberRef db_unit;
1250 CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
1251
1252 db_type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
1253 db_unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
1254 res = CFNumberCompare(if_type, db_type, NULL);
1255 if (res == kCFCompareLessThan
1256 || (res == kCFCompareEqualTo
1257 && (CFNumberCompare(if_unit, db_unit, NULL)
1258 == kCFCompareLessThan))) {
1259 CFArrayInsertValueAtIndex(db_list, i, if_dict);
1260 CFRelease(if_dict);
1261 return;
1262 }
1263 }
1264
1265 CFArrayAppendValue(S_dblist, if_dict);
1266
1267 #if TARGET_OS_OSX
1268 updateBTPANInformation(if_dict, NULL);
1269 #endif // TARGET_OS_OSX
1270
1271 CFRelease(if_dict);
1272 return;
1273 }
1274
1275 static void
1276 removeInterface(CFMutableArrayRef db_list, SCNetworkInterfaceRef interface, CFDictionaryRef *matched)
1277 {
1278 CFDictionaryRef db_dict;
1279 int n = 0;
1280 CFIndex where;
1281
1282 // remove any dict that has our type/addr
1283 while (TRUE) {
1284 db_dict = lookupInterfaceByAddress(db_list, interface, &where);
1285 if (db_dict == NULL) {
1286 break;
1287 }
1288 if ((matched != NULL) && (*matched == NULL)) {
1289 *matched = CFRetain(db_dict);
1290 }
1291 CFArrayRemoveValueAtIndex(db_list, where);
1292 n++;
1293 }
1294
1295 // remove any dict that has the same type/unit
1296 while (TRUE) {
1297 db_dict = lookupInterfaceByUnit(db_list, interface, &where);
1298 if (db_dict == NULL) {
1299 break;
1300 }
1301 if ((matched != NULL) && (*matched == NULL)) {
1302 *matched = CFRetain(db_dict);
1303 }
1304 CFArrayRemoveValueAtIndex(db_list, where);
1305 n++;
1306 }
1307
1308 if (n > 1) {
1309 SC_log(LOG_ERR, "Multiple interfaces removed from database (n = %d, %@)", n, interface);
1310 }
1311
1312 return;
1313 }
1314
1315 static void
1316 replaceInterface(SCNetworkInterfaceRef interface)
1317 {
1318 CFDictionaryRef matched = NULL;
1319
1320 if (S_dblist == NULL) {
1321 S_dblist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1322 } else {
1323 // remove any matching interfaces
1324 removeInterface(S_dblist, interface, &matched);
1325 }
1326
1327 // [re-]insert the new interface
1328 insertInterface(S_dblist, interface, matched);
1329
1330 if (matched != NULL) {
1331 CFRelease(matched);
1332 }
1333
1334 return;
1335 }
1336
1337 static int
1338 getNextUnitForType(CFNumberRef if_type, int requested)
1339 {
1340 CFIndex n;
1341
1342 if (S_dblist == NULL) {
1343 return requested;
1344 }
1345
1346 n = CFArrayGetCount(S_dblist);
1347 for (CFIndex i = 0; i < n; i++) {
1348 CFDictionaryRef dict = CFArrayGetValueAtIndex(S_dblist, i);
1349 CFNumberRef type;
1350
1351 type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
1352 if (CFEqual(type, if_type)) {
1353 int u;
1354 CFNumberRef unit;
1355
1356 unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
1357 if (!isA_CFNumber(unit) ||
1358 !CFNumberGetValue(unit, kCFNumberIntType, &u)) {
1359 u = 0;
1360 }
1361
1362 if (u < requested) {
1363 // if we have not yet found our starting unit #
1364 continue;
1365 }
1366
1367 if (u == requested) {
1368 // our starting (or now proposed) unit # is "in use" so
1369 // let's keep searching
1370 requested++;
1371 continue;
1372 }
1373
1374 // we've found a unit # gap ... so let's re-assign it!
1375 }
1376 }
1377
1378 return requested;
1379 }
1380
1381 /*
1382 * Function: ensureInterfaceHasUnit
1383 * Purpose:
1384 * Ensure that the SCNetworkInterfaceRef has a unit number. If it doesn't,
1385 * release the interface and return NULL.
1386 */
1387 static SCNetworkInterfaceRef
1388 ensureInterfaceHasUnit(SCNetworkInterfaceRef net_if)
1389 {
1390 if (net_if != NULL
1391 && _SCNetworkInterfaceGetIOInterfaceUnit(net_if) == NULL) {
1392 CFRelease(net_if);
1393 net_if = NULL;
1394 }
1395 return (net_if);
1396 }
1397
1398 #ifdef USE_REGISTRY_ENTRY_ID
1399 static kern_return_t
1400 registerInterfaceWithIORegistryEntryID(io_connect_t connect,
1401 uint64_t entryID,
1402 CFNumberRef unit,
1403 const int command)
1404 {
1405 CFDataRef data;
1406 CFMutableDictionaryRef dict;
1407 kern_return_t kr;
1408 CFNumberRef num;
1409
1410 dict = CFDictionaryCreateMutable(NULL, 0,
1411 &kCFTypeDictionaryKeyCallBacks,
1412 &kCFTypeDictionaryValueCallBacks);
1413 num = CFNumberCreate(NULL, kCFNumberIntType, &command);
1414 CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommandKey), num);
1415 CFRelease(num);
1416 data = CFDataCreate(NULL, (void *) &entryID, sizeof(entryID));
1417 CFDictionarySetValue(dict, CFSTR(kIORegistryEntryIDKey), data);
1418 CFRelease(data);
1419 CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit);
1420 kr = IOConnectSetCFProperties(connect, dict);
1421 CFRelease(dict);
1422 return kr;
1423 }
1424
1425 static SCNetworkInterfaceRef
1426 copyInterfaceForIORegistryEntryID(uint64_t entryID)
1427 {
1428 io_registry_entry_t entry = MACH_PORT_NULL;
1429 SCNetworkInterfaceRef interface = NULL;
1430 io_iterator_t iterator = MACH_PORT_NULL;
1431 kern_return_t kr;
1432 mach_port_t masterPort = MACH_PORT_NULL;
1433
1434 kr = IOMasterPort(bootstrap_port, &masterPort);
1435 if (kr != KERN_SUCCESS) {
1436 SC_log(LOG_ERR, "IOMasterPort returned 0x%x", kr);
1437 goto error;
1438 }
1439
1440 kr = IOServiceGetMatchingServices(masterPort,
1441 IORegistryEntryIDMatching(entryID),
1442 &iterator);
1443 if ((kr != KERN_SUCCESS) || (iterator == MACH_PORT_NULL)) {
1444 SC_log(LOG_NOTICE, "IOServiceGetMatchingServices(0x%llx) returned 0x%x/%d",
1445 entryID,
1446 kr,
1447 iterator);
1448 goto error;
1449 }
1450
1451 entry = IOIteratorNext(iterator);
1452 if (entry == MACH_PORT_NULL) {
1453 SC_log(LOG_NOTICE, "IORegistryEntryIDMatching(0x%llx) failed", entryID);
1454 goto error;
1455 }
1456
1457 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry);
1458
1459 error:
1460 if (masterPort != MACH_PORT_NULL) {
1461 mach_port_deallocate(mach_task_self(), masterPort);
1462 }
1463 if (entry != MACH_PORT_NULL) {
1464 IOObjectRelease(entry);
1465 }
1466 if (iterator != MACH_PORT_NULL) {
1467 IOObjectRelease(iterator);
1468 }
1469 return (interface);
1470 }
1471
1472 static SCNetworkInterfaceRef
1473 copyNamedInterfaceForIORegistryEntryID(uint64_t entryID)
1474 {
1475 SCNetworkInterfaceRef net_if;
1476
1477 net_if = copyInterfaceForIORegistryEntryID(entryID);
1478 return (ensureInterfaceHasUnit(net_if));
1479 }
1480
1481 #else // USE_REGISTRY_ENTRY_ID
1482 /*
1483 * Function: registerInterface
1484 * Purpose:
1485 * Register a single interface with the given service path to the
1486 * data link layer (BSD), using the specified unit number.
1487 */
1488 static kern_return_t
1489 registerInterfaceWithIOServicePath(io_connect_t connect,
1490 CFStringRef path,
1491 CFNumberRef unit,
1492 const int command)
1493 {
1494 CFMutableDictionaryRef dict;
1495 kern_return_t kr;
1496 CFNumberRef num;
1497
1498 dict = CFDictionaryCreateMutable(NULL, 0,
1499 &kCFTypeDictionaryKeyCallBacks,
1500 &kCFTypeDictionaryValueCallBacks);
1501 num = CFNumberCreate(NULL, kCFNumberIntType, &command);
1502 CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommandKey), num);
1503 CFRelease(num);
1504 CFDictionarySetValue(dict, CFSTR(kIOPathMatchKey), path);
1505 CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit);
1506 kr = IOConnectSetCFProperties(connect, dict);
1507 CFRelease(dict);
1508 return kr;
1509 }
1510
1511 static SCNetworkInterfaceRef
1512 copyInterfaceForIOKitPath(CFStringRef if_path)
1513 {
1514 io_registry_entry_t entry = MACH_PORT_NULL;
1515 SCNetworkInterfaceRef interface = NULL;
1516 kern_return_t kr;
1517 mach_port_t masterPort = MACH_PORT_NULL;
1518 io_string_t path;
1519
1520 kr = IOMasterPort(bootstrap_port, &masterPort);
1521 if (kr != KERN_SUCCESS) {
1522 SC_log(LOG_ERR, "IOMasterPort returned 0x%x", kr);
1523 goto error;
1524 }
1525 _SC_cfstring_to_cstring(if_path, path, sizeof(path), kCFStringEncodingASCII);
1526 entry = IORegistryEntryFromPath(masterPort, path);
1527 if (entry == MACH_PORT_NULL) {
1528 SC_log(LOG_NOTICE, "IORegistryEntryFromPath(%@) failed", if_path);
1529 goto error;
1530 }
1531
1532 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry);
1533
1534 error:
1535 if (masterPort != MACH_PORT_NULL) {
1536 mach_port_deallocate(mach_task_self(), masterPort);
1537 }
1538 if (entry != MACH_PORT_NULL) {
1539 IOObjectRelease(entry);
1540 }
1541 return (interface);
1542
1543 }
1544
1545 static SCNetworkInterfaceRef
1546 copyNamedInterfaceForIOKitPath(CFStringRef if_path)
1547 {
1548 SCNetworkInterfaceRef net_if;
1549
1550 net_if = copyInterfaceForIOKitPath(if_path);
1551 return (ensureInterfaceHasUnit(net_if));
1552 }
1553
1554 #endif // USE_REGISTRY_ENTRY_ID
1555
1556 static void
1557 displayInterface(SCNetworkInterfaceRef interface)
1558 {
1559 CFStringRef addr;
1560 CFStringRef name;
1561 CFNumberRef type;
1562 CFNumberRef unit;
1563
1564 name = SCNetworkInterfaceGetBSDName(interface);
1565 unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
1566 type = _SCNetworkInterfaceGetIOInterfaceType(interface);
1567 addr = SCNetworkInterfaceGetHardwareAddressString(interface);
1568
1569 SC_log(LOG_INFO, " %s%@%sType: %@, %s%@%sMAC address: %@",
1570 (name != NULL) ? "BSD Name: " : "",
1571 (name != NULL) ? name : CFSTR(""),
1572 (name != NULL) ? ", " : "",
1573 type,
1574 (unit != NULL) ? "Unit: " : "",
1575 (unit != NULL) ? (CFTypeRef)unit : (CFTypeRef)CFSTR(""),
1576 (unit != NULL) ? ", " : "",
1577 (addr != NULL) ? addr : CFSTR("?"));
1578 }
1579
1580 static Boolean
1581 builtinAvailable(SCNetworkInterfaceRef interface, // new interface
1582 CFNumberRef if_unit) // desired unit
1583 {
1584 CFIndex i;
1585 CFNumberRef if_type = _SCNetworkInterfaceGetIOInterfaceType(interface);
1586 CFIndex n;
1587
1588 n = (S_dblist != NULL) ? CFArrayGetCount(S_dblist) : 0;
1589 for (i = 0; i < n; i++) {
1590 CFStringRef if_path;
1591 CFDictionaryRef known_dict = CFArrayGetValueAtIndex(S_dblist, i);
1592 CFStringRef known_path;
1593 CFNumberRef known_type;
1594 CFNumberRef known_unit;
1595
1596 known_type = CFDictionaryGetValue(known_dict, CFSTR(kIOInterfaceType));
1597 if (!_SC_CFEqual(if_type, known_type)) {
1598 continue; // if not the same interface type
1599 }
1600
1601 known_unit = CFDictionaryGetValue(known_dict, CFSTR(kIOInterfaceUnit));
1602 if (!_SC_CFEqual(if_unit, known_unit)) {
1603 continue; // if not the same interface unit
1604 }
1605
1606 if_path = _SCNetworkInterfaceGetIOPath(interface);
1607 known_path = CFDictionaryGetValue(known_dict, CFSTR(kIOPathMatchKey));
1608 if (!_SC_CFEqual(if_path, known_path)) {
1609 // if different IORegistry path
1610 return FALSE;
1611 }
1612
1613 // if same type, same unit, same path
1614 return TRUE;
1615 }
1616
1617 // if interface type/unit not found
1618 return TRUE;
1619 }
1620
1621 static int
1622 builtinCount(CFArrayRef if_list, CFIndex last, CFNumberRef if_type)
1623 {
1624 CFIndex i;
1625 int n = 0;
1626
1627 for (i = 0; i < last; i++) {
1628 SCNetworkInterfaceRef builtin_if;
1629 CFNumberRef builtin_type;
1630
1631 builtin_if = CFArrayGetValueAtIndex(if_list, i);
1632 builtin_type = _SCNetworkInterfaceGetIOInterfaceType(builtin_if);
1633 if (CFEqual(if_type, builtin_type)) {
1634 if (_SCNetworkInterfaceIsBuiltin(builtin_if)) {
1635 n++; // if built-in interface
1636 }
1637 }
1638 }
1639
1640 return n;
1641 }
1642
1643
1644 #pragma mark -
1645 #pragma mark Interface monitoring (e.g. watch for "detach")
1646
1647
1648 typedef struct WatchedInfo *WatchedInfoRef;
1649
1650 typedef void (*InterfaceUpdateCallBack) (
1651 CFDataRef watched,
1652 natural_t messageType,
1653 void *messageArgument
1654 );
1655
1656 typedef struct {
1657 SCNetworkInterfaceRef interface;
1658 io_service_t interface_node;
1659 io_object_t notification;
1660 InterfaceUpdateCallBack callback;
1661 } WatchedInfo;
1662
1663 static void
1664 watcherRelease(CFDataRef watched);
1665
1666 static void
1667 updateWatchedInterface(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
1668 {
1669 #pragma unused(service)
1670 #pragma unused(messageArgument)
1671 switch (messageType) {
1672 case kIOMessageServiceIsTerminated : { // if [watched] interface yanked
1673 SCNetworkInterfaceRef remove = NULL;
1674 CFDataRef watched = (CFDataRef)refCon;
1675 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
1676
1677 remove = watchedInfo->interface;
1678 if (!_SCNetworkInterfaceIsBuiltin(remove) &&
1679 _SCNetworkInterfaceIsApplePreconfigured(remove)) {
1680 // if not built-in *and* pre-configured, retain for cleanup
1681 CFRetain(remove);
1682 } else {
1683 remove = NULL;
1684 }
1685
1686 CFRetain(watched);
1687 watchedInfo->callback(watched, messageType, messageArgument);
1688 watcherRelease(watched);
1689 CFRelease(watched);
1690
1691 if (remove != NULL) {
1692 // if interface is not built-in *and* pre-configured
1693 SC_log(LOG_INFO, "Interface released unit %@ (from database)",
1694 _SCNetworkInterfaceGetIOInterfaceUnit(remove));
1695 removeInterface(S_dblist, remove, NULL);
1696 CFRelease(remove);
1697
1698 // update the DB with the [remaining] interfaces that have been named
1699 writeInterfaceList(S_dblist);
1700 }
1701
1702 break;
1703 }
1704
1705 default :
1706 return;
1707 }
1708
1709 return;
1710 }
1711
1712 static CFDataRef
1713 watcherCreate(SCNetworkInterfaceRef interface, InterfaceUpdateCallBack callback)
1714 {
1715 uint64_t entryID;
1716 io_service_t interface_node;
1717 kern_return_t kr;
1718 CFDictionaryRef matching;
1719 CFMutableDataRef watched;
1720 WatchedInfo *watchedInfo;
1721
1722 // get the IORegistry node
1723 entryID = _SCNetworkInterfaceGetIORegistryEntryID(interface);
1724 matching = IORegistryEntryIDMatching(entryID);
1725 interface_node = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
1726 if (interface_node == MACH_PORT_NULL) {
1727 // interface no longer present
1728 return NULL;
1729 }
1730
1731 // create [locked/trusted] interface watcher
1732 watched = CFDataCreateMutable(NULL, sizeof(WatchedInfo));
1733 CFDataSetLength(watched, sizeof(WatchedInfo));
1734 watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
1735 memset(watchedInfo, 0, sizeof(*watchedInfo));
1736
1737 // retain interface
1738 watchedInfo->interface = CFRetain(interface);
1739
1740 // ... and the interface node
1741 watchedInfo->interface_node = interface_node;
1742
1743 // ... and set the callback
1744 watchedInfo->callback = callback;
1745
1746 kr = IOServiceAddInterestNotification(S_notify, // IONotificationPortRef
1747 watchedInfo->interface_node, // io_service_t
1748 kIOGeneralInterest, // interestType
1749 updateWatchedInterface, // IOServiceInterestCallback
1750 (void *)watched, // refCon
1751 &watchedInfo->notification); // notification
1752 if (kr != KERN_SUCCESS) {
1753 SC_log(LOG_ERR,
1754 "IOServiceAddInterestNotification() failed, kr = 0x%x",
1755 kr);
1756 watcherRelease(watched);
1757 CFRelease(watched);
1758 return NULL;
1759 }
1760
1761 return watched;
1762 }
1763
1764 static void
1765 watcherRelease(CFDataRef watched)
1766 {
1767 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
1768
1769 // release watcher
1770 if (watchedInfo->notification != IO_OBJECT_NULL) {
1771 IOObjectRelease(watchedInfo->notification);
1772 watchedInfo->notification = IO_OBJECT_NULL;
1773 }
1774
1775 // release interface node
1776 if (watchedInfo->interface_node != IO_OBJECT_NULL) {
1777 IOObjectRelease(watchedInfo->interface_node);
1778 watchedInfo->interface_node = IO_OBJECT_NULL;
1779 }
1780
1781 // release interface
1782 if (watchedInfo->interface != NULL) {
1783 CFRelease(watchedInfo->interface);
1784 watchedInfo->interface = NULL;
1785 }
1786
1787 return;
1788 }
1789
1790 static Boolean
1791 isWatchedInterface(CFArrayRef watchedInterfaces, SCNetworkInterfaceRef interface)
1792 {
1793 CFIndex n;
1794
1795 n = (watchedInterfaces != NULL) ? CFArrayGetCount(watchedInterfaces) : 0;
1796 for (CFIndex i = 0; i < n; i++) {
1797 CFDataRef watched = CFArrayGetValueAtIndex(watchedInterfaces, i);
1798 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
1799
1800 if (CFEqual((watchedInfo->interface), interface)) {
1801 return TRUE;
1802 }
1803 }
1804
1805 return FALSE;
1806 }
1807
1808
1809 #pragma mark -
1810 #pragma mark Locked device support [macOS]
1811
1812
1813 #if !TARGET_OS_IPHONE
1814 static void
1815 shareLocked(void)
1816 {
1817 CFIndex n;
1818
1819 n = (S_locked != NULL) ? CFArrayGetCount(S_locked) : 0;
1820 if (n > 0) {
1821 CFMutableArrayRef locked;
1822
1823 locked = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1824
1825 for (CFIndex i = 0; i < n; i++) {
1826 CFStringRef addr;
1827 CFStringRef name;
1828 CFStringRef path;
1829 CFStringRef str;
1830 CFDataRef watched = CFArrayGetValueAtIndex(S_locked, i);
1831 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
1832
1833 name = SCNetworkInterfaceGetLocalizedDisplayName(watchedInfo->interface);
1834 addr = SCNetworkInterfaceGetHardwareAddressString(watchedInfo->interface);
1835 path = _SCNetworkInterfaceGetIOPath(watchedInfo->interface);
1836 str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@: %@: %@"),
1837 (name != NULL) ? name : CFSTR("?"),
1838 (addr != NULL) ? addr : CFSTR("?"),
1839 path);
1840 CFArrayAppendValue(locked, str);
1841 CFRelease(str);
1842 }
1843
1844 CFDictionarySetValue(S_state, kInterfaceNamerKey_LockedInterfaces, locked);
1845 CFRelease(locked);
1846 } else {
1847 CFDictionaryRemoveValue(S_state, kInterfaceNamerKey_LockedInterfaces);
1848 }
1849
1850 updateStore();
1851
1852 return;
1853 }
1854
1855 static boolean_t
1856 blockNewInterfaces()
1857 {
1858 static boolean_t allow = TRUE;
1859 static dispatch_once_t once;
1860
1861 dispatch_once(&once, ^{
1862 allow = InterfaceNamerControlPrefsAllowNewInterfaces();
1863 });
1864
1865 return !allow;
1866 }
1867
1868 static boolean_t
1869 isConsoleLocked()
1870 {
1871 CFArrayRef console_sessions;
1872 boolean_t locked = FALSE;
1873 io_registry_entry_t root;
1874
1875 root = IORegistryGetRootEntry(kIOMasterPortDefault);
1876 console_sessions = IORegistryEntryCreateCFProperty(root,
1877 CFSTR(kIOConsoleUsersKey),
1878 NULL,
1879 0);
1880 if (isA_CFArray(console_sessions)) {
1881 CFIndex n;
1882
1883 n = CFArrayGetCount(console_sessions);
1884 for (CFIndex i = 0; i < n; i++) {
1885 CFBooleanRef isLocked;
1886 CFBooleanRef isLoginDone;
1887 CFBooleanRef onConsole;
1888 CFDictionaryRef session;
1889
1890 session = CFArrayGetValueAtIndex(console_sessions, i);
1891 if (!isA_CFDictionary(session)) {
1892 // if not dictionary
1893 continue;
1894 }
1895
1896 if (!CFDictionaryGetValueIfPresent(session,
1897 CFSTR(kIOConsoleSessionOnConsoleKey),
1898 (const void **)&onConsole) ||
1899 !isA_CFBoolean(onConsole) ||
1900 !CFBooleanGetValue(onConsole)) {
1901 // if not "on console" session
1902 continue;
1903 }
1904
1905 if ((n > 1) &&
1906 CFDictionaryGetValueIfPresent(session,
1907 CFSTR(kIOConsoleSessionLoginDoneKey),
1908 (const void **)&isLoginDone) &&
1909 isA_CFBoolean(isLoginDone) &&
1910 !CFBooleanGetValue(isLoginDone)) {
1911 // if @ loginwindow
1912 SC_log(LOG_INFO, "multiple sessions, console @ loginwindow");
1913 locked = TRUE;
1914 goto done;
1915 }
1916
1917 if (CFDictionaryGetValueIfPresent(session,
1918 CFSTR(kIOConsoleSessionScreenIsLockedKey),
1919 (const void **)&isLocked) &&
1920 isA_CFBoolean(isLocked) &&
1921 CFBooleanGetValue(isLocked)) {
1922 // if screen locked
1923 SC_log(LOG_INFO, "console screen locked");
1924 locked = TRUE;
1925 goto done;
1926 }
1927 }
1928 }
1929
1930 SC_log(LOG_INFO, "console not locked");
1931
1932 done :
1933
1934 if (console_sessions != NULL) {
1935 CFRelease(console_sessions);
1936 }
1937 IOObjectRelease(root);
1938
1939 return locked;
1940 }
1941
1942 //#define ENABLE_LOCKED_CONSOLE_INTERFACE_NOTIFICATIONS
1943 #ifdef ENABLE_LOCKED_CONSOLE_INTERFACE_NOTIFICATIONS
1944
1945 static CFUserNotificationRef userNotification;
1946 static CFRunLoopSourceRef userRls;
1947
1948 static void
1949 lockedNotification_remove(void)
1950 {
1951 if (userRls != NULL) {
1952 CFRunLoopSourceInvalidate(userRls);
1953 userRls = NULL;
1954 }
1955
1956 if (userNotification != NULL) {
1957 SInt32 status;
1958
1959 status = CFUserNotificationCancel(userNotification);
1960 if (status != 0) {
1961 SC_log(LOG_ERR,
1962 "CFUserNotificationCancel() failed, status=%d",
1963 (int)status);
1964 }
1965 CFRelease(userNotification);
1966 userNotification = NULL;
1967 }
1968
1969 return;
1970 }
1971
1972 #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns"
1973
1974 static void
1975 lockedNotification_reply(CFUserNotificationRef userNotification, CFOptionFlags response_flags)
1976 {
1977 #pragma unused(userNotification)
1978
1979 CFIndex n;
1980
1981 n = (S_locked != NULL) ? CFArrayGetCount(S_locked) : 0;
1982 for (CFIndex i = 0; i < n; i++) {
1983 CFDataRef watched = CFArrayGetValueAtIndex(S_locked, i);
1984 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
1985
1986 // process user response
1987 switch (response_flags & 0x3) {
1988 case kCFUserNotificationDefaultResponse: {
1989 // if OK'd, [re-]process new interfaces
1990 if (i == 0) {
1991 SC_log(LOG_INFO, "Reprocessing %ld [locked] interface%s", n, n == 1 ? "" : "s");
1992
1993 if (S_iflist == NULL) {
1994 S_iflist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1995 }
1996 }
1997
1998 // add the interface to those newly discovered
1999 CFArrayAppendValue(S_iflist, watchedInfo->interface);
2000 break;
2001 }
2002 default: {
2003 // if cancelled, ignore [remaining] new interfaces
2004 SC_log(LOG_INFO, "[locked] interface ignored");
2005 SC_log(LOG_INFO, " path = %@", _SCNetworkInterfaceGetIOPath(watchedInfo->interface));
2006 break;
2007 }
2008 }
2009
2010 // stop watching
2011 watcherRelease(watched);
2012 }
2013
2014 if (S_locked != NULL) {
2015 CFRelease(S_locked);
2016 S_locked = NULL;
2017 }
2018
2019 lockedNotification_remove();
2020
2021 if (S_iflist != NULL) {
2022 updateInterfaces();
2023 }
2024
2025 return;
2026 }
2027
2028 static void
2029 lockedNotification_add(void)
2030 {
2031 CFBundleRef bundle;
2032 CFMutableDictionaryRef dict;
2033 SInt32 error = 0;
2034 CFMutableArrayRef message;
2035 CFIndex n;
2036 CFURLRef url = NULL;
2037
2038 n = (S_locked != NULL) ? CFArrayGetCount(S_locked) : 0;
2039 if (n == 0) {
2040 // no locked interfaces, no notification needed
2041 return;
2042 }
2043
2044 dict = CFDictionaryCreateMutable(NULL,
2045 0,
2046 &kCFTypeDictionaryKeyCallBacks,
2047 &kCFTypeDictionaryValueCallBacks);
2048
2049 // set localization URL
2050 bundle = _SC_CFBundleGet();
2051 if (bundle != NULL) {
2052 url = CFBundleCopyBundleURL(bundle);
2053 }
2054 if (url != NULL) {
2055 // set URL
2056 CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url);
2057 CFRelease(url);
2058 } else {
2059 SC_log(LOG_ERR, "can't find bundle");
2060 goto done;
2061 }
2062
2063 // set icon URL
2064 url = CFURLCreateFromFileSystemRepresentation(NULL,
2065 (const UInt8 *)MY_ICON_PATH,
2066 sizeof(MY_ICON_PATH) - 1,
2067 FALSE);
2068 if (url != NULL) {
2069 CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url);
2070 CFRelease(url);
2071 }
2072
2073 // header
2074 CFDictionarySetValue(dict,
2075 kCFUserNotificationAlertHeaderKey,
2076 (n == 1) ? CFSTR("LOCKED_SINGLE_INTERFACE_HEADER")
2077 : CFSTR("LOCKED_MULTIPLE_INTERFACES_HEADER"));
2078
2079 // message
2080 message = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2081 CFArrayAppendValue(message,
2082 (n == 1) ? CFSTR("LOCKED_SINGLE_INTERFACE_MESSAGE")
2083 : CFSTR("LOCKED_MULTIPLE_INTERFACES_MESSAGE"));
2084 for (CFIndex i = 0; i < n; i++) {
2085 CFStringRef name;
2086 CFStringRef str;
2087 CFDataRef watched = CFArrayGetValueAtIndex(S_locked, i);
2088 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
2089
2090 name = SCNetworkInterfaceGetLocalizedDisplayName(watchedInfo->interface);
2091 str = CFStringCreateWithFormat(NULL, NULL, CFSTR("\r\t%@"), name);
2092 CFArrayAppendValue(message, str);
2093 CFRelease(str);
2094 }
2095 CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message);
2096 CFRelease(message);
2097
2098 // button titles
2099 CFDictionarySetValue(dict,
2100 kCFUserNotificationDefaultButtonTitleKey,
2101 CFSTR("LOCKED_INTERFACES_IGNORE"));
2102 CFDictionarySetValue(dict,
2103 kCFUserNotificationAlternateButtonTitleKey,
2104 (n == 1) ? CFSTR("LOCKED_SINGLE_INTERFACE_ADD")
2105 : CFSTR("LOCKED_MULTIPLE_INTERFACES_ADD"));
2106
2107 // create and post notification
2108 userNotification = CFUserNotificationCreate(NULL,
2109 0,
2110 kCFUserNotificationNoteAlertLevel,
2111 &error,
2112 dict);
2113 if (userNotification == NULL) {
2114 SC_log(LOG_ERR, "CFUserNotificationCreate() failed: %d", (int)error);
2115 goto done;
2116 }
2117
2118 // establish callback
2119 userRls = CFUserNotificationCreateRunLoopSource(NULL,
2120 userNotification,
2121 lockedNotification_reply,
2122 0);
2123 if (userRls == NULL) {
2124 SC_log(LOG_ERR, "CFUserNotificationCreateRunLoopSource() failed");
2125 CFRelease(userNotification);
2126 userNotification = NULL;
2127 goto done;
2128 }
2129 CFRunLoopAddSource(CFRunLoopGetCurrent(), userRls, kCFRunLoopDefaultMode);
2130
2131 done :
2132
2133 if (dict != NULL) CFRelease(dict);
2134 return;
2135 }
2136
2137 static void
2138 lockedNotification_update(void)
2139 {
2140 // if present, remove current notification
2141 lockedNotification_remove();
2142
2143 // post notification (if needed)
2144 lockedNotification_add();
2145
2146 return;
2147 }
2148
2149 #endif // ENABLE_LOCKED_CONSOLE_INTERFACE_NOTIFICATIONS
2150
2151 static void
2152 lockedInterfaceUpdated(CFDataRef watched, natural_t messageType, void *messageArgument)
2153 {
2154 #pragma unused(messageArgument)
2155 Boolean updated = FALSE;
2156 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
2157
2158 switch (messageType) {
2159 case kIOMessageServiceIsTerminated : { // if [locked] interface yanked
2160 SC_log(LOG_INFO, "[locked] interface removed");
2161 SC_log(LOG_INFO, " path = %@", _SCNetworkInterfaceGetIOPath(watchedInfo->interface));
2162
2163 if (S_locked != NULL) {
2164 CFIndex i;
2165 CFIndex n = CFArrayGetCount(S_locked);
2166
2167 i = CFArrayGetFirstIndexOfValue(S_locked, CFRangeMake(0, n), watched);
2168 if (i != kCFNotFound) {
2169 CFArrayRemoveValueAtIndex(S_locked, i);
2170 if (CFArrayGetCount(S_locked) == 0) {
2171 CFRelease(S_locked);
2172 S_locked = NULL;
2173 }
2174 updated = TRUE;
2175 }
2176 }
2177
2178 break;
2179 }
2180
2181 default :
2182 return;
2183 }
2184
2185 if (updated) {
2186 #ifdef ENABLE_LOCKED_CONSOLE_INTERFACE_NOTIFICATIONS
2187 // update user notification after interface removed
2188 lockedNotification_update();
2189 #endif // ENABLE_LOCKED_CONSOLE_INTERFACE_NOTIFICATIONS
2190
2191 // post info about interfaces not added because the console is locked
2192 shareLocked();
2193 }
2194
2195 return;
2196 }
2197
2198 static void
2199 watchLockedInterface(SCNetworkInterfaceRef interface)
2200 {
2201 Boolean updated = FALSE;
2202 CFDataRef watched;
2203
2204 watched = watcherCreate(interface, lockedInterfaceUpdated);
2205 if (watched != NULL) {
2206 SC_log(LOG_INFO, "watching [locked] interface");
2207 SC_log(LOG_INFO, " path = %@", _SCNetworkInterfaceGetIOPath(interface));
2208
2209 if (S_locked == NULL) {
2210 S_locked = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2211 }
2212 CFArrayAppendValue(S_locked, watched);
2213 updated = TRUE;
2214 }
2215
2216 if (updated) {
2217 // post info about interfaces not added because the console is locked
2218 shareLocked();
2219
2220 #ifdef ENABLE_LOCKED_CONSOLE_INTERFACE_NOTIFICATIONS
2221 // post/update user notification with new interface
2222 lockedNotification_update();
2223 #endif // ENABLE_LOCKED_CONSOLE_INTERFACE_NOTIFICATIONS
2224 }
2225
2226 return;
2227 }
2228 #endif // !TARGET_OS_IPHONE
2229
2230
2231 #pragma mark -
2232 #pragma mark Trust required support [iOS]
2233
2234
2235 #if TARGET_OS_IPHONE
2236
2237 #include <SoftLinking/WeakLinking.h>
2238 WEAK_LINK_FORCE_IMPORT(lockdown_is_host_trusted);
2239 WEAK_LINK_FORCE_IMPORT(kLockdownNotificationHostAttached);
2240 WEAK_LINK_FORCE_IMPORT(kLockdownNotificationHostDetached);
2241 WEAK_LINK_FORCE_IMPORT(kLockdownNotificationTrustedHostAttached);
2242 WEAK_LINK_FORCE_IMPORT(kLockdownNotificationTrustedPTPAttached);
2243
2244 static Boolean
2245 haveLockdown()
2246 {
2247 Boolean haveLibrary;
2248
2249 haveLibrary = ((lockdown_is_host_trusted != NULL) &&
2250 (&kLockdownNotificationHostAttached != NULL) &&
2251 (&kLockdownNotificationHostDetached != NULL) &&
2252 (&kLockdownNotificationTrustedHostAttached != NULL) &&
2253 (&kLockdownNotificationTrustedPTPAttached != NULL)
2254 );
2255 return haveLibrary;
2256 }
2257
2258 static void
2259 shareExcluded()
2260 {
2261 CFIndex n;
2262
2263 n = (S_trustRequired != NULL) ? CFArrayGetCount(S_trustRequired) : 0;
2264 if ((n > 0) && !S_trustedHostAttached) {
2265 CFMutableArrayRef excluded;
2266
2267 // if we have interfaces that require not [yet] granted "trust".
2268
2269 excluded = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2270
2271 for (CFIndex i = 0; i < n; i++) {
2272 CFStringRef bsdName;
2273 CFDataRef watched = CFArrayGetValueAtIndex(S_trustRequired, i);
2274 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
2275
2276 bsdName = SCNetworkInterfaceGetBSDName(watchedInfo->interface);
2277 if (bsdName == NULL) {
2278 SC_log(LOG_NOTICE, "[trust required] interface w/no BSD name not excluded");
2279 SC_log(LOG_NOTICE, " interface = %@", watchedInfo->interface);
2280 continue;
2281 }
2282 CFArrayAppendValue(excluded, bsdName);
2283 }
2284
2285 CFDictionarySetValue(S_state, kInterfaceNamerKey_ExcludedInterfaces, excluded);
2286 CFRelease(excluded);
2287 } else {
2288 CFDictionaryRemoveValue(S_state, kInterfaceNamerKey_ExcludedInterfaces);
2289 }
2290
2291 updateStore();
2292
2293 return;
2294 }
2295
2296 static dispatch_queue_t
2297 trustRequired_queue()
2298 {
2299 static dispatch_once_t once;
2300 static dispatch_queue_t q;
2301
2302 dispatch_once(&once, ^{
2303 q = dispatch_queue_create("Trust Required queue", NULL);
2304 });
2305
2306 return q;
2307
2308 }
2309
2310 // runs on "Trust Required" dispatch queue
2311 static void
2312 trustRequiredNotification_update(CFRunLoopRef rl, CFStringRef reason)
2313 {
2314 Boolean changed = FALSE;
2315 CFStringRef error = NULL;
2316 CFIndex n;
2317 Boolean trusted;
2318
2319 /*
2320 * determine whether the device has "trusted" the host (or other device)
2321 */
2322 trusted = lockdown_is_host_trusted(MY_PLUGIN_ID, NULL, &error);
2323 n = (S_trustRequired != NULL) ? CFArrayGetCount(S_trustRequired) : 0;
2324 if ((S_trustedHostCount != n) || (S_trustedHostAttached != trusted)) {
2325 changed = TRUE;
2326 }
2327
2328 SC_log(LOG_INFO, "%@, trusted = %s%s%@, %ld interface%s)%s",
2329 reason,
2330 trusted ? "Yes" : "No",
2331 (error != NULL) ? ", error = " : "",
2332 (error != NULL) ? error : CFSTR(""),
2333 n,
2334 (n == 1) ? "" : "s",
2335 changed ? " *" : "");
2336
2337 if (changed) {
2338 S_trustedHostAttached = trusted;
2339 S_trustedHostCount = n;
2340 CFRunLoopPerformBlock(rl, kCFRunLoopDefaultMode, ^{
2341 shareExcluded();
2342 });
2343 CFRunLoopWakeUp(rl);
2344 }
2345
2346 if (error != NULL) {
2347 CFRelease(error);
2348 }
2349
2350 return;
2351 }
2352
2353 static void
2354 trustRequiredInterfaceUpdated(CFDataRef watched, natural_t messageType, void *messageArgument)
2355 {
2356 #pragma unused(messageArgument)
2357 Boolean updated = FALSE;
2358 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
2359
2360 switch (messageType) {
2361 case kIOMessageServiceIsTerminated : { // if [locked] interface yanked
2362 SC_log(LOG_INFO, "[trust required] interface removed");
2363 SC_log(LOG_INFO, " path = %@", _SCNetworkInterfaceGetIOPath(watchedInfo->interface));
2364
2365 if (S_trustRequired != NULL) {
2366 CFIndex i;
2367 CFIndex n = CFArrayGetCount(S_trustRequired);
2368
2369 i = CFArrayGetFirstIndexOfValue(S_trustRequired, CFRangeMake(0, n), watched);
2370 if (i != kCFNotFound) {
2371 CFArrayRemoveValueAtIndex(S_trustRequired, i);
2372 if (CFArrayGetCount(S_trustRequired) == 0) {
2373 CFRelease(S_trustRequired);
2374 S_trustRequired = NULL;
2375 }
2376 updated = TRUE;
2377 }
2378 }
2379
2380 break;
2381 }
2382
2383 default :
2384 return;
2385 }
2386
2387 if (updated) {
2388 CFRunLoopRef rl = CFRunLoopGetCurrent();
2389
2390 CFRetain(rl);
2391 dispatch_async(trustRequired_queue(), ^{
2392 trustRequiredNotification_update(rl, CFSTR("TrustRequired interface removed"));
2393 CFRelease(rl);
2394 });
2395 }
2396
2397 return;
2398 }
2399
2400 static void
2401 watchTrustedStatus(CFStringRef notification, CFStringRef reason)
2402 {
2403 const char * key;
2404 int notify_token = -1;
2405 uint32_t ret;
2406 CFRunLoopRef rl = CFRunLoopGetCurrent();
2407
2408 key = CFStringGetCStringPtr(notification, kCFStringEncodingUTF8);
2409 assert(key != NULL);
2410
2411 CFRetain(rl);
2412 CFRetain(reason);
2413 ret = notify_register_dispatch(key,
2414 &notify_token,
2415 trustRequired_queue(),
2416 ^(int token){
2417 #pragma unused(token)
2418 trustRequiredNotification_update(rl, reason);
2419 });
2420 if (ret != NOTIFY_STATUS_OK) {
2421 SC_log(LOG_ERR, "notify_register_dispatch(%@) failed: %u", notification, ret);
2422 CFRelease(rl);
2423 CFRelease(reason);
2424 }
2425
2426 return;
2427 }
2428
2429 static void
2430 updateTrustRequiredInterfaces(CFArrayRef interfaces)
2431 {
2432 CFIndex n;
2433 Boolean updated = FALSE;
2434
2435 n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
2436 for (CFIndex i = 0; i < n; i++) {
2437 SCNetworkInterfaceRef interface;
2438
2439 interface = CFArrayGetValueAtIndex(interfaces, i);
2440 if (_SCNetworkInterfaceIsTrustRequired(interface) &&
2441 !isWatchedInterface(S_trustRequired, interface)) {
2442 CFDataRef watched;
2443
2444 watched = watcherCreate(interface, trustRequiredInterfaceUpdated);
2445 if (watched != NULL) {
2446 CFStringRef bsdName;
2447
2448 bsdName = SCNetworkInterfaceGetBSDName(interface);
2449 if (bsdName != NULL) {
2450 SC_log(LOG_INFO, "watching [trust required] interface: %@", bsdName);
2451 } else {
2452 SC_log(LOG_INFO, "watching [trust required] interface w/no BSD name");
2453 SC_log(LOG_INFO, " interface = %@", interface);
2454 }
2455
2456 if (S_trustRequired == NULL) {
2457 S_trustRequired = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2458 }
2459 CFArrayAppendValue(S_trustRequired, watched);
2460 updated = TRUE;
2461 }
2462 }
2463 }
2464
2465 if (updated) {
2466 static dispatch_once_t once;
2467 CFRunLoopRef rl = CFRunLoopGetCurrent();
2468
2469 dispatch_once(&once, ^{
2470 // watch for "Host attached"
2471 watchTrustedStatus(kLockdownNotificationHostAttached,
2472 CFSTR("Host attached"));
2473
2474 // watch for "Host detached"
2475 watchTrustedStatus(kLockdownNotificationHostDetached,
2476 CFSTR("Host detached"));
2477
2478 // watch for "Trusted host attached"
2479 watchTrustedStatus(kLockdownNotificationTrustedHostAttached,
2480 CFSTR("Trusted Host attached"));
2481
2482 // watch for "Trusted PDP attached"
2483 watchTrustedStatus(kLockdownNotificationTrustedPTPAttached,
2484 CFSTR("Trusted PTP attached"));
2485 });
2486
2487 CFRetain(rl);
2488 dispatch_async(trustRequired_queue(), ^{
2489 trustRequiredNotification_update(rl, CFSTR("TrustRequired interface added"));
2490 CFRelease(rl);
2491 });
2492 }
2493
2494 return;
2495 }
2496 #endif // TARGET_OS_IPHONE
2497
2498
2499 #pragma mark -
2500 #pragma mark Pre-configured interface support
2501
2502
2503 static void
2504 sharePreconfigured()
2505 {
2506 CFIndex n;
2507
2508 n = (S_preconfigured != NULL) ? CFArrayGetCount(S_preconfigured) : 0;
2509 if (n > 0) {
2510 CFMutableArrayRef preconfigured;
2511
2512 preconfigured = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2513
2514 for (CFIndex i = 0; i < n; i++) {
2515 CFStringRef bsdName;
2516 CFDataRef watched = CFArrayGetValueAtIndex(S_preconfigured, i);
2517 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
2518
2519 bsdName = SCNetworkInterfaceGetBSDName(watchedInfo->interface);
2520 if (bsdName == NULL) {
2521 SC_log(LOG_NOTICE, "pre-configured interface w/no BSD name");
2522 SC_log(LOG_NOTICE, " interface = %@", watchedInfo->interface);
2523 continue;
2524 }
2525 CFArrayAppendValue(preconfigured, bsdName);
2526 }
2527
2528 CFDictionarySetValue(S_state, kInterfaceNamerKey_PreConfiguredInterfaces, preconfigured);
2529 CFRelease(preconfigured);
2530 } else {
2531 CFDictionaryRemoveValue(S_state, kInterfaceNamerKey_PreConfiguredInterfaces);
2532 }
2533
2534 updateStore();
2535
2536 return;
2537 }
2538
2539 static void
2540 preconfiguredInterfaceUpdated(CFDataRef watched, natural_t messageType, void *messageArgument)
2541 {
2542 Boolean updated = FALSE;
2543 WatchedInfo *watchedInfo = (WatchedInfo *)(void *)CFDataGetBytePtr(watched);
2544
2545 #pragma unused(messageArgument)
2546 switch (messageType) {
2547 case kIOMessageServiceIsTerminated : { // if [locked] interface yanked
2548 CFStringRef bsdName;
2549
2550 bsdName = SCNetworkInterfaceGetBSDName(watchedInfo->interface);
2551 if (bsdName != NULL) {
2552 SC_log(LOG_INFO, "[pre-configured] interface removed: %@", bsdName);
2553 } else {
2554 SC_log(LOG_INFO, "[pre-configured] interface w/no BSD name removed");
2555 SC_log(LOG_INFO, " interface = %@", watchedInfo->interface);
2556 }
2557
2558 if (S_preconfigured != NULL) {
2559 CFIndex i;
2560 CFIndex n = CFArrayGetCount(S_preconfigured);
2561
2562 i = CFArrayGetFirstIndexOfValue(S_preconfigured, CFRangeMake(0, n), watched);
2563 if (i != kCFNotFound) {
2564 CFArrayRemoveValueAtIndex(S_preconfigured, i);
2565 if (CFArrayGetCount(S_preconfigured) == 0) {
2566 CFRelease(S_preconfigured);
2567 S_preconfigured = NULL;
2568 }
2569 updated = TRUE;
2570 }
2571 }
2572
2573 break;
2574 }
2575
2576 default :
2577 return;
2578 }
2579
2580 if (updated) {
2581 sharePreconfigured();
2582 }
2583
2584 return;
2585 }
2586
2587 static void
2588 updatePreConfiguredInterfaces(CFArrayRef interfaces)
2589 {
2590 CFIndex n;
2591 Boolean updated = FALSE;
2592
2593 n = (interfaces != NULL) ? CFArrayGetCount(interfaces) : 0;
2594 for (CFIndex i = 0; i < n; i++) {
2595 SCNetworkInterfaceRef interface;
2596
2597 interface = CFArrayGetValueAtIndex(interfaces, i);
2598 if (_SCNetworkInterfaceIsApplePreconfigured(interface) &&
2599 !isWatchedInterface(S_preconfigured, interface)) {
2600 CFDataRef watched;
2601
2602 watched = watcherCreate(interface, preconfiguredInterfaceUpdated);
2603 if (watched != NULL) {
2604 CFStringRef bsdName;
2605
2606 bsdName = SCNetworkInterfaceGetBSDName(interface);
2607 if (bsdName != NULL) {
2608 SC_log(LOG_INFO, "watching [pre-configured] interface: %@", bsdName);
2609 } else {
2610 SC_log(LOG_INFO, "watching [pre-configured] interface w/no BSD name");
2611 SC_log(LOG_INFO, " interface = %@", interface);
2612 }
2613
2614 if (S_preconfigured == NULL) {
2615 S_preconfigured = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
2616 }
2617 CFArrayAppendValue(S_preconfigured, watched);
2618 updated = TRUE;
2619 }
2620 }
2621 }
2622
2623 if (updated) {
2624 sharePreconfigured();
2625 }
2626
2627 return;
2628 }
2629
2630
2631 #pragma mark -
2632 #pragma mark Interface naming
2633
2634
2635 static __inline__ boolean_t
2636 isQuiet(void)
2637 {
2638 return (S_quiet == MACH_PORT_NULL);
2639 }
2640
2641 static Boolean
2642 wasPreviouslyUsedInterface(CFDictionaryRef dbdict, SCNetworkInterfaceRef interface)
2643 {
2644 CFArrayRef matchingMACs;
2645
2646 matchingMACs = CFDictionaryGetValue(dbdict, CFSTR(kSCNetworkInterfaceMatchingMACs));
2647 if (matchingMACs != NULL) {
2648 CFDataRef addr;
2649
2650 addr = _SCNetworkInterfaceGetHardwareAddress(interface);
2651 if (addr != NULL) {
2652 if (CFArrayContainsValue(matchingMACs,
2653 CFRangeMake(0, CFArrayGetCount(matchingMACs)),
2654 addr)) {
2655 return TRUE;
2656 }
2657 }
2658 }
2659
2660 return FALSE;
2661 }
2662
2663 static void
2664 nameInterfaces(CFMutableArrayRef if_list)
2665 {
2666 CFIndex i;
2667 CFIndex n = CFArrayGetCount(if_list);
2668
2669 for (i = 0; i < n; i++) {
2670 uint64_t entryID;
2671 SCNetworkInterfaceRef interface;
2672 SCNetworkInterfaceRef new_interface;
2673 CFStringRef path;
2674 CFNumberRef type;
2675 CFNumberRef unit;
2676 CFIndex where;
2677
2678 interface = CFArrayGetValueAtIndex(if_list, i);
2679 path = _SCNetworkInterfaceGetIOPath(interface);
2680 type = _SCNetworkInterfaceGetIOInterfaceType(interface);
2681 unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
2682 entryID = _SCNetworkInterfaceGetIORegistryEntryID(interface);
2683
2684 if (unit != NULL) {
2685 CFStringRef if_name;
2686
2687 if_name = SCNetworkInterfaceGetBSDName(interface);
2688 if ((if_name == NULL) || !CFDictionaryContainsKey(S_state, if_name)) {
2689 SC_log(LOG_INFO, "Interface already has a unit number");
2690 displayInterface(interface);
2691 }
2692
2693 // update the list of interfaces that were previously named
2694 if ((S_prev_active_list != NULL)
2695 && lookupInterfaceByAddress(S_prev_active_list, interface, &where) != NULL) {
2696 CFArrayRemoveValueAtIndex(S_prev_active_list, where);
2697 }
2698
2699 replaceInterface(interface);
2700 } else {
2701 CFDictionaryRef dbdict;
2702 boolean_t is_builtin;
2703 kern_return_t kr;
2704 int retries = 0;
2705
2706 dbdict = lookupInterfaceByAddress(S_dblist, interface, NULL);
2707 if (dbdict != NULL) {
2708 unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
2709 CFRetain(unit);
2710
2711 SC_log(LOG_INFO, "Interface assigned unit %@ (from database)", unit);
2712 }
2713
2714 if ((dbdict == NULL) && !isQuiet()) {
2715 // if new interface, wait until quiet before naming
2716 addTimestamp(S_state, path);
2717 continue;
2718 }
2719
2720 is_builtin = _SCNetworkInterfaceIsBuiltin(interface);
2721
2722 if (dbdict == NULL) {
2723 dbdict = lookupMatchingInterface(interface,
2724 S_dblist,
2725 if_list,
2726 i + 1,
2727 is_builtin ? kCFBooleanTrue : kCFBooleanFalse);
2728
2729 if ((dbdict != NULL) && wasPreviouslyUsedInterface(dbdict, interface)) {
2730 unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
2731 CFRetain(unit);
2732
2733 SC_log(LOG_INFO, "Interface assigned unit %@ (updating database w/previously used interface)", unit);
2734 }
2735
2736 #if !TARGET_OS_IPHONE
2737 if ((unit == NULL) &&
2738 !is_builtin &&
2739 (dbdict != NULL) &&
2740 blockNewInterfaces() &&
2741 !_SCNetworkInterfaceIsApplePreconfigured(interface) &&
2742 isConsoleLocked()) {
2743 CFStringRef addr;
2744
2745 // if new (but matching) interface and console locked, ignore
2746 addr = SCNetworkInterfaceGetHardwareAddressString(interface);
2747 SC_log(LOG_NOTICE, "Console locked, network interface* ignored");
2748 SC_log(LOG_INFO, " path = %@, addr = %@",
2749 path,
2750 (addr != NULL) ? addr : CFSTR("?"));
2751 watchLockedInterface(interface);
2752 continue;
2753 }
2754 #endif // !TARGET_OS_IPHONE
2755
2756 if ((unit == NULL) && (dbdict != NULL)) {
2757 unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
2758 CFRetain(unit);
2759
2760 SC_log(LOG_INFO, "Interface assigned unit %@ (updating database w/new interface)", unit);
2761 }
2762 }
2763
2764 if ((dbdict != NULL) && (S_prev_active_list != NULL)) {
2765 // update the list of interfaces that were previously named
2766 where = CFArrayGetFirstIndexOfValue(S_prev_active_list,
2767 CFRangeMake(0, CFArrayGetCount(S_prev_active_list)),
2768 dbdict);
2769 if (where != kCFNotFound) {
2770 CFArrayRemoveValueAtIndex(S_prev_active_list, where);
2771 }
2772 }
2773
2774 if (dbdict == NULL) {
2775 int next_unit = 0;
2776
2777 if (is_builtin) {
2778 // built-in interface, try to use the reserved slots
2779 next_unit = builtinCount(if_list, i, type);
2780
2781 // But, before claiming a reserved slot we check to see if the
2782 // slot had previously been used. If so, and if the slot had been
2783 // assigned to the same type of interface, then we will perform a
2784 // replacement (e.g. assume that this was a board swap). But, if
2785 // the new interface is a different type then we assume that the
2786 // built-in configuration has changed and allocate a new unit from
2787 // the non-reserved slots.
2788
2789 unit = CFNumberCreate(NULL, kCFNumberIntType, &next_unit);
2790 if (!builtinAvailable(interface, unit)) {
2791 // if [built-in] unit not available
2792 SC_log(LOG_INFO, "Interface not assigned [built-in] unit %@", unit);
2793 CFRelease(unit);
2794 unit = NULL;
2795 }
2796 }
2797
2798 #if !TARGET_OS_IPHONE
2799 if (!is_builtin &&
2800 (unit == NULL) &&
2801 blockNewInterfaces() &&
2802 !_SCNetworkInterfaceIsApplePreconfigured(interface) &&
2803 isConsoleLocked()) {
2804 CFStringRef addr;
2805
2806 // if new interface and console locked, ignore
2807 addr = SCNetworkInterfaceGetHardwareAddressString(interface);
2808 SC_log(LOG_NOTICE, "Console locked, network interface ignored");
2809 SC_log(LOG_INFO, " path = %@, addr = %@",
2810 path,
2811 (addr != NULL) ? addr : CFSTR("?"));
2812 watchLockedInterface(interface);
2813 continue;
2814 }
2815 #endif // !TARGET_OS_IPHONE
2816
2817 if (unit == NULL) {
2818 // not built-in (or built-in unit not available), allocate from
2819 // the non-reserved slots
2820 next_unit = builtinCount(if_list, n, type);
2821 next_unit = getNextUnitForType(type, next_unit);
2822 unit = CFNumberCreate(NULL, kCFNumberIntType, &next_unit);
2823 }
2824
2825 SC_log(LOG_INFO, "Interface assigned unit %@ (%s)",
2826 unit,
2827 is_builtin ? "built-in" : "next available");
2828 }
2829
2830 retry :
2831
2832 #ifdef USE_REGISTRY_ENTRY_ID
2833 kr = registerInterfaceWithIORegistryEntryID(S_connect,
2834 entryID,
2835 unit,
2836 (dbdict == NULL) ? kIONetworkStackRegisterInterfaceWithLowestUnit
2837 : kIONetworkStackRegisterInterfaceWithUnit);
2838 new_interface = copyNamedInterfaceForIORegistryEntryID(entryID);
2839 #else // USE_REGISTRY_ENTRY_ID
2840 kr = registerInterfaceWithIOServicePath(S_connect,
2841 path,
2842 unit,
2843 (dbdict == NULL) ? kRegisterInterface
2844 : kRegisterInterfaceWithFixedUnit);
2845 new_interface = copyNamedInterfaceForIOKitPath(path);
2846 #endif // USE_REGISTRY_ENTRY_ID
2847 if (new_interface == NULL) {
2848 const char *signature;
2849
2850 signature = (dbdict == NULL) ? "failed to name new interface"
2851 : "failed to name known interface";
2852
2853 SC_log(LOG_NOTICE, "%s, kr=0x%x\n"
2854 " path = %@\n"
2855 " id = 0x%llx\n"
2856 " unit = %@",
2857 signature,
2858 kr,
2859 path,
2860 entryID,
2861 unit);
2862
2863 displayInterface(interface);
2864
2865 if ((dbdict != NULL) && (retries++ < 5)) {
2866 usleep(50 * 1000); // sleep 50ms between attempts
2867 goto retry;
2868 }
2869 }
2870 else {
2871 CFNumberRef new_unit;
2872
2873 if (retries > 0) {
2874 SC_log(LOG_INFO, "%s interface named after %d %s\n"
2875 " path = %@\n"
2876 " unit = %@",
2877 (dbdict == NULL) ? "New" : "Known",
2878 retries,
2879 (retries == 1) ? "try" : "tries",
2880 path,
2881 unit);
2882
2883 #ifdef SHOW_NAMING_FAILURE
2884 str = CFStringCreateWithFormat(NULL,
2885 NULL,
2886 CFSTR("\"%s\" interface named after %d %s, unit = %@"),
2887 (dbdict == NULL) ? "New" : "Known",
2888 retries,
2889 (retries == 1) ? "try" : "tries",
2890 unit);
2891 CFUserNotificationDisplayNotice(0,
2892 kCFUserNotificationStopAlertLevel,
2893 NULL,
2894 NULL,
2895 NULL,
2896 str,
2897 CFSTR("Please report repeated failures."),
2898 NULL);
2899 CFRelease(str);
2900 #endif // SHOW_NAMING_FAILURE
2901 }
2902
2903 new_unit = _SCNetworkInterfaceGetIOInterfaceUnit(new_interface);
2904 if (!CFEqual(unit, new_unit)) {
2905 SC_log(LOG_INFO, "interface type %@ assigned unit %@ instead of %@",
2906 type, new_unit, unit);
2907 }
2908
2909 displayInterface(new_interface);
2910
2911 // update if_list (with the interface name & unit)
2912 CFArraySetValueAtIndex(if_list, i, new_interface);
2913 CFRelease(new_interface);
2914 interface = new_interface; // if_list holds the reference
2915
2916 if (is_builtin && (S_prev_active_list != NULL)) {
2917 CFIndex where;
2918
2919 // update the list of [built-in] interfaces that were previously named
2920 if (lookupInterfaceByUnit(S_prev_active_list, interface, &where) != NULL) {
2921 SC_log(LOG_DEBUG, " and updated database (new address)");
2922 CFArrayRemoveValueAtIndex(S_prev_active_list, where);
2923 }
2924 }
2925 replaceInterface(interface);
2926 }
2927 CFRelease(unit);
2928 }
2929 }
2930 return;
2931 }
2932
2933 #if !TARGET_OS_IPHONE
2934
2935 #define INSTALL_ENVIRONMENT "__OSINSTALL_ENVIRONMENT"
2936
2937 static Boolean
2938 isRecoveryOS()
2939 {
2940 static Boolean isRecovery = FALSE;
2941 static dispatch_once_t once;
2942
2943 /*
2944 * We check to see if the __OSINSTALL_ENVIRONMENT env var is present. If
2945 * so, then we are most likely booted into the Recovery OS with no [Aqua]
2946 * "SCMonitor" [UserEventAgent] plugin.
2947 */
2948 dispatch_once(&once, ^{
2949 if (getenv(INSTALL_ENVIRONMENT) != NULL) {
2950 isRecovery = TRUE;
2951 }
2952
2953 });
2954
2955 return isRecovery;
2956 }
2957
2958 static void
2959 updateNetworkConfiguration(CFArrayRef if_list)
2960 {
2961 Boolean do_commit = FALSE;
2962 CFIndex i;
2963 CFIndex n;
2964 SCPreferencesRef prefs = NULL;
2965 SCNetworkSetRef set = NULL;
2966
2967 prefs = SCPreferencesCreate(NULL, CFSTR(MY_PLUGIN_NAME ":updateNetworkConfiguration"), NULL);
2968 if (prefs == NULL) {
2969 SC_log(LOG_NOTICE, "SCPreferencesCreate() failed: %s", SCErrorString(SCError()));
2970 return;
2971 }
2972
2973 set = SCNetworkSetCopyCurrent(prefs);
2974 if (set == NULL) {
2975 SC_log(LOG_INFO, "No current set, adding default");
2976 set = _SCNetworkSetCreateDefault(prefs);
2977 if (set == NULL) {
2978 SC_log(LOG_NOTICE, "_SCNetworkSetCreateDefault() failed: %s", SCErrorString(SCError()));
2979 goto done;
2980 }
2981 }
2982
2983 n = (if_list != NULL) ? CFArrayGetCount(if_list) : 0;
2984 for (i = 0; i < n; i++) {
2985 SCNetworkInterfaceRef interface;
2986
2987 interface = CFArrayGetValueAtIndex(if_list, i);
2988 if (SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface)) {
2989 SC_log(LOG_INFO, "adding default configuration for %@",
2990 SCNetworkInterfaceGetBSDName(interface));
2991 do_commit = TRUE;
2992 }
2993 }
2994
2995 if (do_commit) {
2996 Boolean ok;
2997
2998 ok = SCPreferencesCommitChanges(prefs);
2999 if (!ok) {
3000 SC_log(LOG_NOTICE, "SCPreferencesCommitChanges() failed: %s", SCErrorString(SCError()));
3001 goto done;
3002 }
3003
3004 ok = SCPreferencesApplyChanges(prefs);
3005 if (!ok) {
3006 SC_log(LOG_NOTICE, "SCPreferencesApplyChanges() failed: %s", SCErrorString(SCError()));
3007 goto done;
3008 }
3009 }
3010
3011 done :
3012
3013 if (set != NULL) {
3014 CFRelease(set);
3015 set = NULL;
3016 }
3017
3018 if (prefs != NULL) {
3019 CFRelease(prefs);
3020 prefs = NULL;
3021 }
3022
3023 return;
3024 }
3025 #endif // !TARGET_OS_IPHONE
3026
3027 static void
3028 removeInactiveInterfaces(void)
3029 {
3030 CFIndex n;
3031
3032 /*
3033 * remove any previous interfaces that were built-in,
3034 * were active, and were hidden (pre-configured) that
3035 * are no longer plugged in.
3036 */
3037
3038 if ((S_dblist == NULL) || (S_prev_active_list == NULL)) {
3039 return;
3040 }
3041
3042 n = CFArrayGetCount(S_prev_active_list);
3043 for (CFIndex i = n - 1; i >= 0; i--) {
3044 CFBooleanRef builtin;
3045 CFBooleanRef hidden;
3046 CFDictionaryRef if_dict;
3047 CFDictionaryRef info;
3048 CFStringRef name;
3049 CFIndex where;
3050
3051 if_dict = CFArrayGetValueAtIndex(S_prev_active_list, i);
3052
3053 // Note: keep the following logic in sync with _SCNetworkInterfaceIsApplePreconfigured()
3054
3055 name = CFDictionaryGetValue(if_dict, CFSTR(kIOBSDNameKey));
3056 if (!isA_CFString(name)) {
3057 // if no BSD name
3058 continue;
3059 }
3060
3061 hidden = CFDictionaryGetValue(if_dict, kSCNetworkInterfaceHiddenConfigurationKey);
3062 if (!isA_CFBoolean(hidden) || !CFBooleanGetValue(hidden)) {
3063 // if not hidden
3064 continue;
3065 }
3066
3067 builtin = CFDictionaryGetValue(if_dict, CFSTR(kIOBuiltin));
3068 if (isA_CFBoolean(builtin) && CFBooleanGetValue(builtin)) {
3069 // if [hidden] builtin
3070 goto remove;
3071 }
3072
3073 info = CFDictionaryGetValue(if_dict, CFSTR(kSCNetworkInterfaceInfo));
3074 if (isA_CFDictionary(info)) {
3075 int vid;
3076 CFNumberRef vidNum;
3077
3078 if (CFDictionaryGetValueIfPresent(info, CFSTR(kUSBVendorID), (const void **)&vidNum) &&
3079 isA_CFNumber(vidNum) &&
3080 CFNumberGetValue(vidNum, kCFNumberIntType, &vid) &&
3081 (vid == kIOUSBAppleVendorID)) {
3082 // if [hidden] Apple interface
3083 goto remove;
3084 }
3085 }
3086
3087 continue;
3088
3089 remove :
3090
3091 SC_log(LOG_INFO, "Removing no-longer-active \"hidden\" interface: %@", name);
3092
3093 if (lookupInterfaceByName(S_dblist, name, &where) != NULL) {
3094 // remove from the list of interfaces we know about
3095 CFArrayRemoveValueAtIndex(S_dblist, where);
3096 // remove from the previously active list
3097 CFArrayRemoveValueAtIndex(S_prev_active_list, i);
3098 }
3099 }
3100
3101 return;
3102 }
3103
3104 static void
3105 reportInactiveInterfaces(void)
3106 {
3107 CFIndex n;
3108
3109 /*
3110 * report any previous interfaces that are not [yet] active
3111 */
3112
3113 if (S_prev_active_list == NULL) {
3114 return;
3115 }
3116
3117 n = CFArrayGetCount(S_prev_active_list);
3118 if (n > 0) {
3119 SC_log(LOG_INFO, "Interface%s not [yet] active",
3120 (n > 1) ? "s" : "");
3121 }
3122 for (CFIndex i = 0; i < n; i++) {
3123 CFDictionaryRef if_dict;
3124 CFStringRef name;
3125 CFNumberRef type;
3126 CFNumberRef unit;
3127
3128 if_dict = CFArrayGetValueAtIndex(S_prev_active_list, i);
3129 name = CFDictionaryGetValue(if_dict, CFSTR(kIOBSDNameKey));
3130 type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType));
3131 unit = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceUnit));
3132 SC_log(LOG_INFO, " %s%@%sType: %@, Unit: %@",
3133 (name != NULL) ? "BSD Name: " : "",
3134 (name != NULL) ? name : CFSTR(""),
3135 (name != NULL) ? ", " : "",
3136 type,
3137 unit);
3138 }
3139
3140 return;
3141 }
3142
3143 static void
3144 updateInterfaces(void)
3145 {
3146 if (S_connect == MACH_PORT_NULL) {
3147 // if we don't have the "IONetworkStack" connect object
3148 return;
3149 }
3150
3151 if (S_iflist != NULL) {
3152 CFIndex n;
3153
3154 n = CFArrayGetCount(S_iflist);
3155 if (n > 1) {
3156 CFArraySortValues(S_iflist, CFRangeMake(0, n), _SCNetworkInterfaceCompare, NULL);
3157 }
3158 nameInterfaces(S_iflist);
3159 }
3160
3161 /*
3162 * Update the list of [Apple] pre-configured interfaces
3163 */
3164 updatePreConfiguredInterfaces(S_iflist);
3165
3166 #if TARGET_OS_IPHONE
3167 /*
3168 * Update the list of "trust required" interfaces
3169 */
3170 if (haveLockdown()) {
3171 updateTrustRequiredInterfaces(S_iflist);
3172 }
3173 #endif // TARGET_OS_IPHONE
3174
3175 if (isQuiet()) {
3176 /*
3177 * The registry [matching] has quiesced
3178 */
3179
3180 // remove any inactive interfaces
3181 removeInactiveInterfaces();
3182
3183 // save the DB with the interfaces that have been named
3184 writeInterfaceList(S_dblist);
3185
3186 // update the VLAN/BOND configuration
3187 updateVirtualNetworkInterfaceConfiguration(NULL, kSCPreferencesNotificationApply, NULL);
3188
3189 #if !TARGET_OS_IPHONE
3190 if (isRecoveryOS()) {
3191 /*
3192 * We are most likely booted into the Recovery OS with no "SCMonitor"
3193 * UserEventAgent plugin running so let's make sure we update the
3194 * network configuration for new interfaces.
3195 */
3196 updateNetworkConfiguration(S_iflist);
3197 }
3198 #endif // !TARGET_OS_IPHONE
3199
3200 // tell everyone that we've finished (at least for now)
3201 updateStore();
3202
3203 // log those interfaces which are no longer present in
3204 // the HW config (or have yet to show up).
3205 reportInactiveInterfaces();
3206
3207 if (S_prev_active_list != NULL) {
3208 CFRelease(S_prev_active_list);
3209 S_prev_active_list = NULL;
3210 }
3211
3212 if (S_iflist != NULL) {
3213 CFRelease(S_iflist);
3214 S_iflist = NULL;
3215 }
3216 } else {
3217 if ((S_prev_active_list != NULL) && (CFArrayGetCount(S_prev_active_list) == 0)) {
3218 /*
3219 * if we've named all of the interfaces that
3220 * were used during the previous boot.
3221 */
3222 addTimestamp(S_state, CFSTR("*RELEASE*"));
3223 SC_log(LOG_INFO, "last boot interfaces have been named");
3224 updateStore();
3225 CFRelease(S_prev_active_list);
3226 S_prev_active_list = NULL;
3227 }
3228 }
3229
3230 return;
3231 }
3232
3233 static void
3234 interfaceArrivalCallback(void *refcon, io_iterator_t iter)
3235 {
3236 #pragma unused(refcon)
3237 io_object_t obj;
3238
3239 while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) {
3240 SCNetworkInterfaceRef interface;
3241
3242 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj);
3243 if (interface != NULL) {
3244 if (S_iflist == NULL) {
3245 S_iflist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3246 }
3247 CFArrayAppendValue(S_iflist, interface);
3248 CFRelease(interface);
3249 }
3250 IOObjectRelease(obj);
3251 }
3252
3253 updateInterfaces();
3254
3255 return;
3256 }
3257
3258 /*
3259 * Function: stackCallback
3260 * Purpose:
3261 * Get a reference to the single IONetworkStack object instance in
3262 * the kernel. Naming requests must be sent to this object, which is
3263 * attached as a client to all network interface objects in the system.
3264 * Note:
3265 * Call IOObjectRelease on the returned object.
3266 */
3267 static void
3268 stackCallback(void *refcon, io_iterator_t iter)
3269 {
3270 #pragma unused(refcon)
3271 kern_return_t kr;
3272 io_object_t stack;
3273
3274 stack = IOIteratorNext(iter);
3275 if (stack == MACH_PORT_NULL) {
3276 goto error;
3277 }
3278
3279 kr = IOServiceOpen(stack, mach_task_self(), 0, &S_connect);
3280 if (kr != KERN_SUCCESS) {
3281 SC_log(LOG_ERR, "IOServiceOpen returned 0x%x", kr);
3282 goto error;
3283 }
3284
3285 addTimestamp(S_state, CFSTR("*STACK*"));
3286 SC_log(LOG_INFO, "IONetworkStack found");
3287
3288 if (S_stack != MACH_PORT_NULL) {
3289 IOObjectRelease(S_stack);
3290 S_stack = MACH_PORT_NULL;
3291 }
3292
3293 if ((S_timer != NULL) && CFRunLoopTimerIsValid(S_timer)) {
3294 // With the IONetworkStack object now available we can
3295 // reset (shorten?) the time we are willing to wait for
3296 // IOKit to quiesce.
3297 CFRunLoopTimerSetNextFireDate(S_timer,
3298 CFAbsoluteTimeGetCurrent() + S_quiet_timeout);
3299 }
3300
3301 updateInterfaces();
3302
3303 error:
3304
3305 if (stack != MACH_PORT_NULL) {
3306 IOObjectRelease(stack);
3307 }
3308
3309 return;
3310 }
3311
3312 static void
3313 quietCallback(void *refcon,
3314 io_service_t service,
3315 natural_t messageType,
3316 void *messageArgument)
3317 {
3318 #pragma unused(refcon)
3319 #pragma unused(service)
3320 if (messageArgument != NULL) {
3321 // if not yet quiet
3322 return;
3323 }
3324
3325 if (messageType == kIOMessageServiceBusyStateChange) {
3326 addTimestamp(S_state, kInterfaceNamerKey_Quiet);
3327 SC_log(LOG_INFO, "IOKit quiet");
3328 }
3329
3330 if (S_connect == MACH_PORT_NULL) {
3331 SC_log(LOG_ERR, "No network stack object");
3332 return;
3333 }
3334
3335 if (S_quiet != MACH_PORT_NULL) {
3336 IOObjectRelease(S_quiet);
3337 S_quiet = MACH_PORT_NULL;
3338 }
3339
3340 if (S_timer != NULL) {
3341 CFRunLoopTimerInvalidate(S_timer);
3342 CFRelease(S_timer);
3343 S_timer = NULL;
3344 }
3345
3346 // grab (and name) any additional interfaces.
3347 interfaceArrivalCallback((void *)S_notify, S_iter);
3348
3349 if (messageType == kIOMessageServiceBusyStateChange) {
3350 addTimestamp(S_state, CFSTR("*QUIET&NAMED*"));
3351 updateStore();
3352 }
3353
3354 return;
3355 }
3356
3357 static void
3358 iterateRegistryBusy(io_iterator_t iterator, CFArrayRef nodes, int *count)
3359 {
3360 kern_return_t kr = kIOReturnSuccess;;
3361 io_object_t obj;
3362
3363 while ((kr == kIOReturnSuccess) &&
3364 ((obj = IOIteratorNext(iterator)) != MACH_PORT_NULL)) {
3365 uint64_t accumulated_busy_time;
3366 uint32_t busy_state;
3367 io_name_t location;
3368 io_name_t name;
3369 CFMutableArrayRef newNodes;
3370 uint64_t state;
3371 CFMutableStringRef str = NULL;
3372
3373 if (nodes == NULL) {
3374 newNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3375 } else {
3376 newNodes = CFArrayCreateMutableCopy(NULL, 0, nodes);
3377 }
3378 assert(newNodes != NULL);
3379
3380 kr = IORegistryEntryGetName(obj, name);
3381 if (kr != kIOReturnSuccess) {
3382 SC_log(LOG_NOTICE, "IORegistryEntryGetName() returned 0x%x", kr);
3383 goto next;
3384 }
3385
3386 str = CFStringCreateMutable(NULL, 0);
3387 CFStringAppendCString(str, name, kCFStringEncodingUTF8);
3388
3389 kr = IORegistryEntryGetLocationInPlane(obj, kIOServicePlane, location);
3390 switch (kr) {
3391 case kIOReturnSuccess :
3392 CFStringAppendCString(str, "@", kCFStringEncodingUTF8);
3393 CFStringAppendCString(str, location, kCFStringEncodingUTF8);
3394 break;
3395 case kIOReturnNotFound :
3396 break;
3397 default :
3398 SC_log(LOG_NOTICE, "IORegistryEntryGetLocationInPlane() returned 0x%x", kr);
3399 CFRelease(str);
3400 goto next;
3401 }
3402
3403 CFArrayAppendValue(newNodes, str);
3404 CFRelease(str);
3405
3406 kr = IOServiceGetBusyStateAndTime(obj, &state, &busy_state, &accumulated_busy_time);
3407 if (kr != kIOReturnSuccess) {
3408 SC_log(LOG_NOTICE, "IOServiceGetBusyStateAndTime() returned 0x%x", kr);
3409 goto next;
3410 }
3411
3412 #ifdef TEST_SNAPSHOT
3413 // report all nodes
3414 busy_state = 1;
3415 #endif // TEST_SNAPSHOT
3416
3417 if (busy_state != 0) {
3418 CFStringRef path;
3419
3420 if ((*count)++ == 0) {
3421 SC_log(LOG_ERR, "Busy services :");
3422 }
3423
3424 path = CFStringCreateByCombiningStrings(NULL, newNodes, CFSTR("/"));
3425 SC_log(LOG_ERR, " %@ [%s%s%s%d, %lld ms]",
3426 path,
3427 (state & kIOServiceRegisteredState) ? "" : "!registered, ",
3428 (state & kIOServiceMatchedState) ? "" : "!matched, ",
3429 (state & kIOServiceInactiveState) ? "inactive, " : "",
3430 busy_state,
3431 accumulated_busy_time / kMillisecondScale);
3432 CFRelease(path);
3433 }
3434
3435 kr = IORegistryIteratorEnterEntry(iterator);
3436 if (kr != kIOReturnSuccess) {
3437 SC_log(LOG_NOTICE, "IORegistryIteratorEnterEntry() returned 0x%x", kr);
3438 goto next;
3439 }
3440
3441 iterateRegistryBusy(iterator, newNodes, count);
3442
3443 kr = IORegistryIteratorExitEntry(iterator);
3444 if (kr != kIOReturnSuccess) {
3445 SC_log(LOG_NOTICE, "IORegistryIteratorExitEntry() returned 0x%x", kr);
3446 }
3447
3448 next :
3449
3450 CFRelease(newNodes);
3451 IOObjectRelease(obj);
3452 }
3453
3454 return;
3455 }
3456
3457 static void
3458 captureBusy()
3459 {
3460 int count = 0;
3461 io_iterator_t iterator = MACH_PORT_NULL;
3462 kern_return_t kr;
3463
3464 kr = IORegistryCreateIterator(kIOMasterPortDefault,
3465 kIOServicePlane,
3466 0,
3467 &iterator);
3468 if (kr != kIOReturnSuccess) {
3469 SC_log(LOG_NOTICE, "IORegistryCreateIterator() returned 0x%x", kr);
3470 return;
3471 }
3472
3473 iterateRegistryBusy(iterator, NULL, &count);
3474 if (count == 0) {
3475 SC_log(LOG_ERR, "w/no busy services");
3476 }
3477
3478 IOObjectRelease(iterator);
3479 }
3480
3481 static void
3482 timerCallback(CFRunLoopTimerRef timer, void *info)
3483 {
3484 #pragma unused(timer)
3485 #pragma unused(info)
3486 // We've been waiting for IOKit to quiesce and it just
3487 // hasn't happenned. Time to just move on!
3488 addTimestamp(S_state, kInterfaceNamerKey_Timeout);
3489
3490 // log busy nodes
3491 SC_log(LOG_ERR, "timed out waiting for IOKit to quiesce");
3492 captureBusy();
3493
3494 quietCallback((void *)S_notify, MACH_PORT_NULL, 0, NULL);
3495
3496 addTimestamp(S_state, CFSTR("*TIMEOUT&NAMED*"));
3497 updateStore();
3498
3499 return;
3500 }
3501
3502 static Boolean
3503 setup_IOKit(CFBundleRef bundle)
3504 {
3505 #pragma unused(bundle)
3506 uint32_t busy;
3507 kern_return_t kr;
3508 mach_port_t masterPort = MACH_PORT_NULL;
3509 Boolean ok = FALSE;
3510 io_object_t root = MACH_PORT_NULL;
3511
3512 // read DB of previously named network interfaces
3513 S_dblist = readInterfaceList();
3514 if (S_dblist != NULL) {
3515 CFIndex n;
3516
3517 n = CFArrayGetCount(S_dblist);
3518 if (n > 1) {
3519 CFArraySortValues(S_dblist, CFRangeMake(0, n), if_unit_compare, NULL);
3520 }
3521 }
3522
3523 // get interfaces that were named during the last boot
3524 S_prev_active_list = previouslyActiveInterfaces();
3525
3526 // track how long we've waited to see each interface.
3527 S_state = CFDictionaryCreateMutable(NULL,
3528 0,
3529 &kCFTypeDictionaryKeyCallBacks,
3530 &kCFTypeDictionaryValueCallBacks);
3531 addTimestamp(S_state, CFSTR("*START*"));
3532
3533 // Creates and returns a notification object for receiving IOKit
3534 // notifications of new devices or state changes.
3535 kr = IOMasterPort(bootstrap_port, &masterPort);
3536 if (kr != KERN_SUCCESS) {
3537 SC_log(LOG_ERR, "IOMasterPort returned 0x%x", kr);
3538 goto done;
3539 }
3540
3541 S_notify = IONotificationPortCreate(masterPort);
3542 if (S_notify == NULL) {
3543 SC_log(LOG_ERR, "IONotificationPortCreate failed");
3544 goto done;
3545 }
3546
3547 // watch IOKit matching activity
3548 root = IORegistryEntryFromPath(masterPort, kIOServicePlane ":/");
3549 if (root == MACH_PORT_NULL) {
3550 SC_log(LOG_ERR, "IORegistryEntryFromPath failed");
3551 goto done;
3552 }
3553
3554 kr = IOServiceAddInterestNotification(S_notify,
3555 root,
3556 kIOBusyInterest,
3557 &quietCallback,
3558 (void *)S_notify, // refCon
3559 &S_quiet); // notification
3560 if (kr != KERN_SUCCESS) {
3561 SC_log(LOG_ERR, "IOServiceAddInterestNotification returned 0x%x", kr);
3562 goto done;
3563 }
3564
3565 kr = IOServiceGetBusyState(root, &busy);
3566 if (kr != KERN_SUCCESS) {
3567 SC_log(LOG_ERR, "IOServiceGetBusyState returned 0x%x", kr);
3568 goto done;
3569 }
3570
3571 // add a timer so we don't wait forever for IOKit to quiesce
3572 S_timer = CFRunLoopTimerCreate(NULL,
3573 CFAbsoluteTimeGetCurrent() + S_stack_timeout,
3574 0,
3575 0,
3576 0,
3577 timerCallback,
3578 NULL);
3579 if (S_timer == NULL) {
3580 SC_log(LOG_ERR, "CFRunLoopTimerCreate failed");
3581 goto done;
3582 }
3583
3584 CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, kCFRunLoopDefaultMode);
3585
3586 // watch for the introduction of the IONetworkStack
3587 kr = IOServiceAddMatchingNotification(S_notify,
3588 kIOFirstMatchNotification,
3589 IOServiceMatching("IONetworkStack"),
3590 &stackCallback,
3591 (void *)S_notify, // refCon
3592 &S_stack); // notification
3593 if (kr != KERN_SUCCESS) {
3594 SC_log(LOG_ERR, "IOServiceAddMatchingNotification returned 0x%x", kr);
3595 goto done;
3596 }
3597
3598 // check and see if the stack is already available and arm the
3599 // notification for its introduction.
3600 stackCallback((void *)S_notify, S_stack);
3601
3602 // watch for the introduction of new network interfaces
3603 kr = IOServiceAddMatchingNotification(S_notify,
3604 kIOFirstMatchNotification,
3605 IOServiceMatching("IONetworkInterface"),
3606 &interfaceArrivalCallback,
3607 (void *)S_notify, // refCon
3608 &S_iter); // notification
3609 if (kr != KERN_SUCCESS) {
3610 SC_log(LOG_ERR, "IOServiceAddMatchingNotification returned 0x%x", kr);
3611 goto done;
3612 }
3613
3614 // Get the current list of matches and arm the notification for
3615 // future interface arrivals.
3616 interfaceArrivalCallback((void *)S_notify, S_iter);
3617
3618 // Check if IOKit has already quiesced.
3619 quietCallback((void *)S_notify,
3620 MACH_PORT_NULL,
3621 kIOMessageServiceBusyStateChange,
3622 (void *)(uintptr_t)busy);
3623
3624 CFRunLoopAddSource(CFRunLoopGetCurrent(),
3625 IONotificationPortGetRunLoopSource(S_notify),
3626 kCFRunLoopDefaultMode);
3627
3628 #ifdef WAIT_PREVIOUS_BOOT_INTERFACES_OR_QUIET
3629 /*
3630 * Start the wheels turning until we've named all of
3631 * the interfaces that were used during the previous
3632 * boot, until IOKit [matching] has quiesced, or
3633 * until we've waited long enough.
3634 */
3635 CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, MY_PLUGIN_ID);
3636 CFRunLoopAddSource(CFRunLoopGetCurrent(),
3637 IONotificationPortGetRunLoopSource(S_notify),
3638 MY_PLUGIN_ID);
3639 while (S_prev_active_list != NULL) {
3640 int rlStatus;
3641
3642 rlStatus = CFRunLoopRunInMode(MY_PLUGIN_ID, 1.0e10, TRUE);
3643 }
3644 #endif /* WAIT_PREVIOUS_BOOT_INTERFACES_OR_QUIET */
3645
3646 #if TARGET_OS_OSX
3647 if (S_dblist != NULL) {
3648 // apply special handling for the BT-PAN interface (if present)
3649 CFArrayApplyFunction(S_dblist,
3650 CFRangeMake(0, CFArrayGetCount(S_dblist)),
3651 updateBTPANInformation,
3652 NULL);
3653 }
3654 #endif // TARGET_OS_OSX
3655
3656 ok = TRUE;
3657
3658 done:
3659 if (root != MACH_PORT_NULL) {
3660 IOObjectRelease(root);
3661 }
3662 if (masterPort != MACH_PORT_NULL) {
3663 mach_port_deallocate(mach_task_self(), masterPort);
3664 }
3665
3666 return ok;
3667 }
3668
3669 static Boolean
3670 setup_Virtual(CFBundleRef bundle)
3671 {
3672 #pragma unused(bundle)
3673 // open a SCPreferences session
3674 S_prefs = SCPreferencesCreate(NULL, CFSTR(MY_PLUGIN_NAME ":setup_Virtual"), NULL);
3675 if (S_prefs == NULL) {
3676 SC_log(LOG_ERR, "SCPreferencesCreate() failed: %s",
3677 SCErrorString(SCError()));
3678 return FALSE;
3679 }
3680
3681 // register for change notifications.
3682 if (!SCPreferencesSetCallback(S_prefs, updateVirtualNetworkInterfaceConfiguration, NULL)) {
3683 SC_log(LOG_ERR, "SCPreferencesSetCallBack() failed: %s", SCErrorString(SCError()));
3684 CFRelease(S_prefs);
3685 return FALSE;
3686 }
3687
3688 // schedule
3689 if (!SCPreferencesScheduleWithRunLoop(S_prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
3690 if (SCError() != kSCStatusNoStoreServer) {
3691 SC_log(LOG_ERR, "SCPreferencesScheduleWithRunLoop() failed: %s", SCErrorString(SCError()));
3692 CFRelease(S_prefs);
3693 return FALSE;
3694 }
3695 }
3696
3697 return TRUE;
3698 }
3699
3700 static void *
3701 exec_InterfaceNamer(void *arg)
3702 {
3703 CFBundleRef bundle = (CFBundleRef)arg;
3704 CFDictionaryRef dict;
3705
3706 pthread_setname_np(MY_PLUGIN_NAME " thread");
3707
3708 dict = CFBundleGetInfoDictionary(bundle);
3709 if (isA_CFDictionary(dict)) {
3710 CFNumberRef num;
3711
3712 num = CFDictionaryGetValue(dict, CFSTR(WAIT_STACK_TIMEOUT_KEY));
3713 if (num != NULL) {
3714 if (!isA_CFNumber(num) ||
3715 !CFNumberGetValue(num, kCFNumberDoubleType, &S_stack_timeout) ||
3716 (S_stack_timeout <= 0.0)) {
3717 SC_log(LOG_NOTICE, WAIT_STACK_TIMEOUT_KEY " value error");
3718 S_stack_timeout = WAIT_STACK_TIMEOUT_DEFAULT;
3719 }
3720 }
3721
3722 num = CFDictionaryGetValue(dict, CFSTR(WAIT_QUIET_TIMEOUT_KEY));
3723 if (num != NULL) {
3724 if (!isA_CFNumber(num) ||
3725 !CFNumberGetValue(num, kCFNumberDoubleType, &S_quiet_timeout) ||
3726 (S_quiet_timeout <= 0.0)) {
3727 SC_log(LOG_NOTICE, WAIT_QUIET_TIMEOUT_KEY " value error");
3728 S_quiet_timeout = WAIT_QUIET_TIMEOUT_DEFAULT;
3729 }
3730 }
3731 }
3732
3733 // setup virtual network interface monitoring
3734 if (!setup_Virtual(bundle)) {
3735 goto error;
3736 }
3737
3738 // setup [IOKit] network interface monitoring
3739 if (!setup_IOKit(bundle)) {
3740 goto error;
3741 }
3742
3743 goto done;
3744
3745 error :
3746 if (S_connect != MACH_PORT_NULL) {
3747 IOServiceClose(S_connect);
3748 S_connect = MACH_PORT_NULL;
3749 }
3750 if (S_dblist != NULL) {
3751 CFRelease(S_dblist);
3752 S_dblist = NULL;
3753 }
3754 if (S_iter != MACH_PORT_NULL) {
3755 IOObjectRelease(S_iter);
3756 S_iter = MACH_PORT_NULL;
3757 }
3758 if (S_notify != MACH_PORT_NULL) {
3759 IONotificationPortDestroy(S_notify);
3760 }
3761 if (S_quiet != MACH_PORT_NULL) {
3762 IOObjectRelease(S_quiet);
3763 S_quiet = MACH_PORT_NULL;
3764 }
3765 if (S_stack != MACH_PORT_NULL) {
3766 IOObjectRelease(S_stack);
3767 S_stack = MACH_PORT_NULL;
3768 }
3769 if (S_state != NULL) {
3770 CFRelease(S_state);
3771 S_state = NULL;
3772 }
3773 if (S_timer != NULL) {
3774 CFRunLoopTimerInvalidate(S_timer);
3775 CFRelease(S_timer);
3776 S_timer = NULL;
3777 }
3778
3779 done :
3780 CFRelease(bundle);
3781 CFRunLoopRun();
3782
3783 return NULL;
3784 }
3785
3786 __private_extern__
3787 void
3788 load_InterfaceNamer(CFBundleRef bundle, Boolean bundleVerbose)
3789 {
3790 #pragma unused(bundleVerbose)
3791 pthread_attr_t tattr;
3792 pthread_t tid;
3793
3794 CFRetain(bundle); // released in exec_InterfaceNamer
3795
3796 pthread_attr_init(&tattr);
3797 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
3798 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
3799 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
3800 pthread_create(&tid, &tattr, exec_InterfaceNamer, bundle);
3801 pthread_attr_destroy(&tattr);
3802
3803 return;
3804 }
3805
3806 //------------------------------------------------------------------------
3807 // Main function.
3808 #ifdef TEST_INTERFACE_ASSIGNMENT
3809 int
3810 main(int argc, char ** argv)
3811 {
3812 #pragma unused(argv)
3813 CFBundleRef bundle;
3814 CFMutableArrayRef interfaces;
3815 CFArrayRef interfaces_all;
3816 CFIndex n;
3817
3818 _sc_log = FALSE;
3819 _sc_verbose = (argc > 1) ? TRUE : FALSE;
3820
3821 bundle = CFBundleGetMainBundle();
3822 CFRetain(bundle); // released in exec_InterfaceNamer
3823
3824 // setup
3825 setup_IOKit(bundle);
3826
3827 // but, when running this test we know that the IORegistry has already quiesced
3828 IOObjectRelease(S_quiet);
3829 S_quiet = MACH_PORT_NULL;
3830
3831 // collect the interfaces
3832 interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
3833 interfaces_all = SCNetworkInterfaceCopyAll();
3834 n = CFArrayGetCount(interfaces_all);
3835 for (CFIndex i = 0; i < n; i++) {
3836 SCNetworkInterfaceRef interface = CFArrayGetValueAtIndex(interfaces_all, i);
3837 SCNetworkInterfacePrivateRef interfacePrivate = (SCNetworkInterfacePrivateRef)interface;
3838
3839 if (interfacePrivate->type == NULL) {
3840 // skip interfaces with a kIOInterfaceType property
3841 continue;
3842 }
3843
3844 if (interfacePrivate->unit != NULL) {
3845 // remove any already assigned unit #
3846 CFRelease(interfacePrivate->unit);
3847 interfacePrivate->unit = NULL;
3848 }
3849
3850 CFArrayAppendValue(interfaces, interface);
3851 }
3852 CFRelease(interfaces_all);
3853 SC_log(LOG_INFO, "interfaces = %@", interfaces);
3854
3855 // exercise the interface naming assignments
3856 nameInterfaces(interfaces);
3857 CFRelease(interfaces);
3858
3859 exit(0);
3860 return 0;
3861 }
3862 #endif /* MAIN */
3863
3864 #ifdef TEST_SNAPSHOT
3865 int
3866 main(int argc, char ** argv)
3867 {
3868 _sc_log = FALSE;
3869 _sc_verbose = (argc > 1) ? TRUE : FALSE;
3870
3871 captureBusy();
3872
3873 exit(0);
3874 return 0;
3875 }
3876 #endif /* TEST_SNAPSHOT */
3877