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