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