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