]> git.saurik.com Git - apple/configd.git/blob - Plugins/InterfaceNamer/ifnamer.c
configd-212.2.tar.gz
[apple/configd.git] / Plugins / InterfaceNamer / ifnamer.c
1 /*
2 * Copyright (c) 2001-2007 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 * November 6, 2006 Allan Nathanson <ajn@apple.com>
28 * Dan Markarian <markarian@apple.com>
29 * Dieter Siegmund <dieter@apple.com>
30 * - updated code to name interfaces quicker (without need for
31 * calling IOKitWaitQuiet).
32 *
33 * October 3, 2003 Allan Nathanson <ajn@apple.com>
34 * - sort new interfaces by IOKit path (rather than MAC address) to
35 * help facilitate a more predictable interface-->name mapping for
36 * like hardware configurations.
37 *
38 * June 23, 2001 Allan Nathanson <ajn@apple.com>
39 * - update to public SystemConfiguration.framework APIs
40 *
41 * January 23, 2001 Dieter Siegmund <dieter@apple.com>
42 * - initial revision
43 */
44
45 /*
46 * ifnamer.c
47 * - module that receives IOKit Network Interface messages
48 * and names any interface that currently does not have a name
49 * - uses Interface Type and MACAddress as the unique identifying
50 * keys; any interface that doesn't contain both of these properties
51 * is ignored and not processed
52 * - stores the Interface Type, MACAddress, and Unit in permanent storage
53 * to give persistent interface names
54 */
55
56 #include <ctype.h>
57 #include <stdlib.h>
58 #include <stdio.h>
59 #include <unistd.h>
60 #include <fcntl.h>
61 #include <sys/sockio.h>
62 #include <sys/stat.h>
63 #include <sys/param.h>
64 #include <mach/mach.h>
65 #include <net/ethernet.h>
66 #include <net/if_types.h>
67
68 #include <CoreFoundation/CoreFoundation.h>
69
70 #include <SystemConfiguration/SystemConfiguration.h>
71 #include <SystemConfiguration/SCDPlugin.h>
72 #include <SystemConfiguration/SCPrivate.h> // for SCLog(), SCPrint()
73 #include <SystemConfiguration/SCValidation.h>
74
75 #include <IOKit/IOKitLib.h>
76 #include <IOKit/IOBSD.h>
77 #include <IOKit/IOMessage.h>
78 #include <IOKit/network/IONetworkController.h>
79 #include <IOKit/network/IONetworkInterface.h>
80
81 #ifndef kIOBuiltin
82 #define kIOBuiltin "IOBuiltin"
83 #endif
84
85 #define kIONetworkStackUserCommand "IONetworkStackUserCommand"
86 #define kRegisterInterface 1
87
88 #define kSCNetworkInterfaceType "SCNetworkInterfaceType"
89 #define kSCNetworkInterfaceActive "Active"
90
91 #define MY_PLUGIN_NAME "InterfaceNamer"
92 #define MY_PLUGIN_ID CFSTR("com.apple.SystemConfiguration." MY_PLUGIN_NAME)
93
94 #define WAIT_QUIET_TIMEOUT_KEY "WaitQuietTimeout"
95 #define WAIT_QUIET_TIMEOUT_DEFAULT 60.0
96
97 /*
98 * S_connect
99 * "IONetworkStack" connect object used to "name" an interface.
100 */
101 static io_connect_t S_connect = MACH_PORT_NULL;
102
103 /*
104 * S_dblist
105 * An array of CFDictionary's representing the interfaces
106 * that have been identified and [need to be] named.
107 */
108 static CFMutableArrayRef S_dblist = NULL;
109
110 /*
111 * S_debug
112 * A boolean that enables additional logging.
113 */
114 static boolean_t S_debug = FALSE;
115
116 /*
117 * S_iflist
118 * An array of SCNetworkInterface's representing the
119 * interfaces that have been identified.
120 */
121 static CFMutableArrayRef S_iflist = NULL;
122
123 /*
124 * S_iter
125 * IOServiceAddMatchingNotification object used to watch for
126 * new network interfaces.
127 */
128 static io_iterator_t S_iter = MACH_PORT_NULL;
129
130 /*
131 * S_notify
132 * notification object for receiving IOKit notifications of
133 * new devices or state changes.
134 */
135 static IONotificationPortRef S_notify = NULL;
136
137 /* S_prev_active_list
138 * An array of CFDictionary's representing the previously
139 * named interfaces.
140 */
141 static CFMutableArrayRef S_prev_active_list = NULL;
142
143 /*
144 * S_quiet
145 * IOServiceAddInterestNotification object used to watch for
146 * IOKit matching to quiesce.
147 */
148 static io_object_t S_quiet = MACH_PORT_NULL;
149
150 /*
151 * S_stack
152 * IOServiceAddMatchingNotification object used to watch for
153 * the availability of the "IONetworkStack" object.
154 */
155 static io_iterator_t S_stack = MACH_PORT_NULL;
156
157 /*
158 * S_state
159 * A dictionary containing Information about each network
160 * interface. For now, the key is the BSD name and the
161 * value is a CFNumber noting how long (in milliseconds)
162 * it took for the interface to be recognized/named.
163 */
164 static CFMutableDictionaryRef S_state = NULL;
165
166 /*
167 * S_timer
168 * CFRunLoopTimer tracking how long we are willing to wait
169 * for IOKit matching to quiesce (IOKitWaitQuiet).
170 */
171 static CFRunLoopTimerRef S_timer = NULL;
172
173 /*
174 * Virtual network interface configuration
175 * S_prefs : SCPreferences to configuration
176 * S_bonds : most recently actived Bond configuration
177 * S_vlans : most recently actived VLAN configuration
178 */
179 static SCPreferencesRef S_prefs = NULL;
180 static CFArrayRef S_bonds = NULL;
181 static CFArrayRef S_vlans = NULL;
182
183 static void
184 addTimestamp(CFMutableDictionaryRef dict, CFStringRef key)
185 {
186 CFAbsoluteTime now;
187 CFNumberRef val;
188
189 now = CFAbsoluteTimeGetCurrent();
190 val = CFNumberCreate(NULL, kCFNumberDoubleType, &now);
191 CFDictionaryAddValue(dict, key, val);
192 CFRelease(val);
193 return;
194 }
195
196 static CFComparisonResult
197 if_unit_compare(const void *val1, const void *val2, void *context)
198 {
199 CFComparisonResult res;
200 CFNumberRef type1;
201 CFNumberRef type2;
202 CFNumberRef unit1;
203 CFNumberRef unit2;
204
205 type1 = CFDictionaryGetValue((CFDictionaryRef)val1,
206 CFSTR(kIOInterfaceType));
207 type2 = CFDictionaryGetValue((CFDictionaryRef)val2,
208 CFSTR(kIOInterfaceType));
209 res = CFNumberCompare(type1, type2, NULL);
210 if (res != kCFCompareEqualTo) {
211 return (res);
212 }
213 unit1 = CFDictionaryGetValue((CFDictionaryRef)val1,
214 CFSTR(kIOInterfaceUnit));
215 unit2 = CFDictionaryGetValue((CFDictionaryRef)val2,
216 CFSTR(kIOInterfaceUnit));
217 return (CFNumberCompare(unit1, unit2, NULL));
218 }
219
220 static void *
221 read_file(char * filename, size_t * data_length)
222 {
223 void * data = NULL;
224 size_t len = 0;
225 int fd = -1;
226 struct stat sb;
227
228 *data_length = 0;
229 if (stat(filename, &sb) == -1)
230 goto done;
231 len = sb.st_size;
232 if (len == 0)
233 goto done;
234
235 data = malloc(len);
236 if (data == NULL)
237 goto done;
238
239 fd = open(filename, O_RDONLY);
240 if (fd == -1)
241 goto done;
242
243 if (read(fd, data, len) != len) {
244 SCLog(TRUE, LOG_ERR,
245 CFSTR(MY_PLUGIN_NAME ": read %s failed, %s"),
246 filename, strerror(errno));
247 goto done;
248 }
249
250 done:
251 if (fd != -1)
252 close(fd);
253 if (data) {
254 *data_length = len;
255 }
256 return (data);
257 }
258
259 static CFPropertyListRef
260 readPropertyList(char * filename)
261 {
262 void * buf;
263 size_t bufsize;
264 CFDataRef data = NULL;
265 CFPropertyListRef plist = NULL;
266 CFStringRef errorString = NULL;
267
268 buf = read_file(filename, &bufsize);
269 if (buf == NULL) {
270 return (NULL);
271 }
272 data = CFDataCreate(NULL, buf, bufsize);
273 if (data == NULL) {
274 goto error;
275 }
276
277 plist = CFPropertyListCreateFromXMLData(NULL, data,
278 kCFPropertyListMutableContainers,
279 &errorString);
280 if (plist == NULL) {
281 if (errorString != NULL) {
282 SCLog(TRUE, LOG_ERR,
283 CFSTR(MY_PLUGIN_NAME ": %@"),
284 errorString);
285 CFRelease(errorString);
286 }
287 }
288 error:
289 if (data)
290 CFRelease(data);
291 if (buf)
292 free(buf);
293 return (plist);
294 }
295
296 #define INTERFACES CFSTR("Interfaces")
297 #define NETWORK_INTERFACES_PREFS CFSTR("NetworkInterfaces.plist")
298 #define OLD_NETWORK_INTERFACES_FILE "/var/db/NetworkInterfaces.xml"
299
300 static void
301 writeInterfaceList(CFArrayRef if_list)
302 {
303 CFArrayRef cur_list;
304 SCPreferencesRef prefs;
305
306 if (isA_CFArray(if_list) == NULL) {
307 return;
308 }
309
310 prefs = SCPreferencesCreate(NULL, MY_PLUGIN_ID, NETWORK_INTERFACES_PREFS);
311 if (prefs == NULL) {
312 SCLog(TRUE, LOG_ERR,
313 CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate failed, %s"),
314 SCErrorString(SCError()));
315 return;
316 }
317
318 cur_list = SCPreferencesGetValue(prefs, INTERFACES);
319 if (_SC_CFEqual(cur_list, if_list)) {
320 goto done;
321 }
322
323 if (!SCPreferencesSetValue(prefs, INTERFACES, if_list)) {
324 SCLog(TRUE, LOG_ERR,
325 CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetValue failed, %s"),
326 SCErrorString(SCError()));
327 goto done;
328 }
329
330 if (!SCPreferencesCommitChanges(prefs)) {
331 SCLog((SCError() != EROFS), LOG_ERR,
332 CFSTR(MY_PLUGIN_NAME ": SCPreferencesCommitChanges failed, %s"),
333 SCErrorString(SCError()));
334 goto done;
335 }
336
337 done:
338
339 CFRelease(prefs);
340 return;
341 }
342
343 static CFMutableArrayRef
344 readInterfaceList()
345 {
346 CFArrayRef if_list;
347 CFMutableArrayRef plist = NULL;
348 SCPreferencesRef prefs = NULL;
349
350 prefs = SCPreferencesCreate(NULL, MY_PLUGIN_ID, NETWORK_INTERFACES_PREFS);
351 if (!prefs) {
352 SCLog(TRUE, LOG_ERR,
353 CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate failed, %s"),
354 SCErrorString(SCError()));
355 return (NULL);
356 }
357
358 if_list = SCPreferencesGetValue(prefs, INTERFACES);
359 if (!isA_CFArray(if_list)) {
360 if_list = (CFArrayRef)readPropertyList(OLD_NETWORK_INTERFACES_FILE);
361 if (if_list == NULL) {
362 goto done;
363 }
364 if (!isA_CFArray(if_list)) {
365 CFRelease(if_list);
366 if_list = NULL;
367 goto done;
368 }
369 writeInterfaceList(if_list);
370 (void)unlink(OLD_NETWORK_INTERFACES_FILE);
371 }
372
373 done:
374 if (if_list != NULL) {
375 CFIndex i;
376 CFIndex n = CFArrayGetCount(if_list);
377
378 plist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
379 for (i = 0; i < n; i++) {
380 CFDictionaryRef dict;
381
382 dict = CFArrayGetValueAtIndex(if_list, i);
383 if (isA_CFDictionary(dict) &&
384 CFDictionaryContainsKey(dict, CFSTR(kIOInterfaceType)) &&
385 CFDictionaryContainsKey(dict, CFSTR(kIOInterfaceUnit)) &&
386 CFDictionaryContainsKey(dict, CFSTR(kIOMACAddress))) {
387 CFArrayAppendValue(plist, dict);
388 }
389 }
390 }
391 if (prefs) {
392 CFRelease(prefs);
393 }
394 return (plist);
395 }
396
397 static CFMutableArrayRef
398 previouslyActiveInterfaces()
399 {
400 CFMutableArrayRef active;
401 CFIndex i;
402 CFIndex n;
403
404 if (S_dblist == NULL) {
405 return NULL;
406 }
407
408 active = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
409
410 n = CFArrayGetCount(S_dblist);
411 for (i = 0; i < n; i++) {
412 CFDictionaryRef if_dict;
413
414 if_dict = CFArrayGetValueAtIndex(S_dblist, i);
415 if (CFDictionaryContainsKey(if_dict, CFSTR(kSCNetworkInterfaceActive))) {
416 CFMutableDictionaryRef new_dict;
417
418 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, if_dict);
419 CFDictionaryRemoveValue(new_dict, CFSTR(kSCNetworkInterfaceActive));
420 CFArraySetValueAtIndex(S_dblist, i, new_dict);
421 CFArrayAppendValue(active, new_dict);
422 CFRelease(new_dict);
423 }
424 }
425
426 return active;
427 }
428
429 static void
430 updateStore(void)
431 {
432 CFStringRef key;
433 SCDynamicStoreRef store;
434
435 store = SCDynamicStoreCreate(NULL, CFSTR(MY_PLUGIN_NAME), NULL, NULL);
436 if (store == NULL) {
437 return;
438 }
439
440 key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@" MY_PLUGIN_NAME),
441 kSCDynamicStoreDomainPlugin);
442 (void)SCDynamicStoreSetValue(store, key, S_state);
443 CFRelease(key);
444 CFRelease(store);
445
446 return;
447 }
448
449 static void
450 updateVirtualNetworkInterfaceConfiguration(SCPreferencesRef prefs,
451 SCPreferencesNotification notificationType,
452 void *info)
453 {
454 CFArrayRef interfaces;
455
456 if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) {
457 return;
458 }
459
460 if (prefs == NULL) {
461 // if a new interface has been "named"
462 prefs = S_prefs;
463 if (S_bonds != NULL) {
464 CFRelease(S_bonds);
465 S_bonds = NULL;
466 }
467 if (S_vlans != NULL) {
468 CFRelease(S_vlans);
469 S_vlans = NULL;
470 }
471 }
472
473 // update Bond configuration
474
475 interfaces = SCBondInterfaceCopyAll(prefs);
476 if ((S_bonds == NULL) && (interfaces == NULL)) {
477 // if no change
478 goto vlan;
479 }
480 if ((S_bonds != NULL) && (interfaces != NULL) && CFEqual(S_bonds, interfaces)) {
481 // if no change
482 CFRelease(interfaces);
483 goto vlan;
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 vlan :
495
496 // update VLAN configuration
497
498 interfaces = SCVLANInterfaceCopyAll(prefs);
499 if ((S_vlans == NULL) && (interfaces == NULL)) {
500 // if no change
501 goto done;
502 }
503 if ((S_vlans != NULL) && (interfaces != NULL) && CFEqual(S_vlans, interfaces)) {
504 // if no change
505 CFRelease(interfaces);
506 goto done;
507 }
508 if (S_vlans != NULL) CFRelease(S_vlans);
509 S_vlans = interfaces;
510
511 if (!_SCVLANInterfaceUpdateConfiguration(prefs)) {
512 SCLog(TRUE, LOG_ERR,
513 CFSTR(MY_PLUGIN_NAME ": _SCVLANInterfaceUpdateConfiguration failed, %s"),
514 SCErrorString(SCError()));
515 }
516
517 done :
518
519 // we are finished with current prefs, wait for changes
520
521 SCPreferencesSynchronize(prefs);
522 return;
523 }
524
525 static CFDictionaryRef
526 createInterfaceDict(SCNetworkInterfaceRef interface)
527 {
528 CFMutableDictionaryRef new_if;
529 CFTypeRef val;
530
531 new_if = CFDictionaryCreateMutable(NULL,
532 0,
533 &kCFTypeDictionaryKeyCallBacks,
534 &kCFTypeDictionaryValueCallBacks);
535
536 val = _SCNetworkInterfaceGetIOPath(interface);
537 if (val != NULL) {
538 CFDictionarySetValue(new_if, CFSTR(kIOPathMatchKey), val);
539 }
540
541 val = _SCNetworkInterfaceGetIOInterfaceType(interface);
542 if (val != NULL) {
543 CFDictionarySetValue(new_if, CFSTR(kIOInterfaceType), val);
544 }
545
546 val = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
547 if (val != NULL) {
548 CFDictionarySetValue(new_if, CFSTR(kIOInterfaceUnit), val);
549 }
550
551 val = _SCNetworkInterfaceGetHardwareAddress(interface);
552 if (val != NULL) {
553 CFDictionarySetValue(new_if, CFSTR(kIOMACAddress), val);
554 }
555
556 val = SCNetworkInterfaceGetBSDName(interface);
557 if (val != NULL) {
558 CFDictionarySetValue(new_if, CFSTR(kIOBSDNameKey), val);
559 }
560
561 val = SCNetworkInterfaceGetInterfaceType(interface);
562 if (val != NULL) {
563 CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceType), val);
564 }
565
566 CFDictionarySetValue(new_if,
567 CFSTR(kIOBuiltin),
568 _SCNetworkInterfaceIsBuiltin(interface) ? kCFBooleanTrue : kCFBooleanFalse);
569
570 CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceActive), kCFBooleanTrue);
571
572 return new_if;
573 }
574
575 static CFDictionaryRef
576 lookupInterfaceByAddress(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
577 {
578 CFDataRef addr;
579 CFIndex i;
580 CFIndex n;
581 CFNumberRef type;
582
583 if (db_list == NULL) {
584 return (NULL);
585 }
586 type = _SCNetworkInterfaceGetIOInterfaceType(interface);
587 addr = _SCNetworkInterfaceGetHardwareAddress(interface);
588 if (type == NULL || addr == NULL) {
589 return (NULL);
590 }
591
592 n = CFArrayGetCount(db_list);
593 for (i = 0; i < n; i++) {
594 CFDataRef a;
595 CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
596 CFNumberRef t;
597
598 t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
599 a = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
600 if (t == NULL || a == NULL)
601 continue;
602
603 if (CFEqual(type, t) && CFEqual(addr, a)) {
604 if (where) {
605 *where = i;
606 }
607 return (dict);
608 }
609 }
610 return (NULL);
611 }
612
613 static CFDictionaryRef
614 lookupInterfaceByUnit(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where)
615 {
616 CFIndex i;
617 CFIndex n;
618 CFNumberRef type;
619 CFNumberRef unit;
620
621 if (db_list == NULL) {
622 return (NULL);
623 }
624 type = _SCNetworkInterfaceGetIOInterfaceType(interface);
625 unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
626 if (type == NULL || unit == NULL) {
627 return (NULL);
628 }
629
630 n = CFArrayGetCount(db_list);
631 for (i = 0; i < n; i++) {
632 CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
633 CFNumberRef t;
634 CFNumberRef u;
635
636 t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
637 u = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
638 if (t == NULL || u == NULL) {
639 continue;
640 }
641
642 if (CFEqual(type, t) && CFEqual(unit, u)) {
643 if (where)
644 *where = i;
645 return (dict);
646 }
647 }
648 return (NULL);
649 }
650
651 static CFDictionaryRef
652 lookupAirPortInterface(CFArrayRef db_list, CFIndex * where)
653 {
654 CFIndex i;
655 CFIndex n;
656
657 if (db_list == NULL) {
658 return (NULL);
659 }
660 n = CFArrayGetCount(db_list);
661 for (i = 0; i < n; i++) {
662 CFDictionaryRef dict;
663 CFStringRef if_type;
664
665 dict = CFArrayGetValueAtIndex(db_list, i);
666 if_type = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceType));
667 if ((if_type != NULL) &&
668 CFEqual(if_type, kSCNetworkInterfaceTypeIEEE80211)) {
669 if (where)
670 *where = i;
671 return (dict);
672 }
673 }
674 return (NULL);
675 }
676
677 static void
678 insertInterface(CFMutableArrayRef db_list, SCNetworkInterfaceRef interface)
679 {
680 CFIndex i;
681 CFDictionaryRef if_dict;
682 CFStringRef if_name;
683 CFNumberRef if_type;
684 CFNumberRef if_unit;
685 CFIndex n = CFArrayGetCount(db_list);
686 CFComparisonResult res;
687
688 if_name = SCNetworkInterfaceGetBSDName(interface);
689 if (if_name != NULL) {
690 addTimestamp(S_state, if_name);
691 }
692
693 if_dict = createInterfaceDict(interface);
694 if_type = _SCNetworkInterfaceGetIOInterfaceType(interface);
695 if_unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
696 if ((if_type == NULL) || (if_unit == NULL)) {
697 CFRelease(if_dict);
698 return;
699 }
700
701 for (i = 0; i < n; i++) {
702 CFNumberRef db_type;
703 CFNumberRef db_unit;
704 CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i);
705
706 db_type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
707 db_unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
708 res = CFNumberCompare(if_type, db_type, NULL);
709 if (res == kCFCompareLessThan
710 || (res == kCFCompareEqualTo
711 && (CFNumberCompare(if_unit, db_unit, NULL)
712 == kCFCompareLessThan))) {
713 CFArrayInsertValueAtIndex(db_list, i, if_dict);
714 CFRelease(if_dict);
715 return;
716 }
717 }
718
719 CFArrayAppendValue(S_dblist, if_dict);
720 CFRelease(if_dict);
721 return;
722 }
723
724 static void
725 replaceInterface(SCNetworkInterfaceRef interface)
726 {
727 CFIndex where;
728
729 if (S_dblist == NULL) {
730 S_dblist = CFArrayCreateMutable(NULL, 0,
731 &kCFTypeArrayCallBacks);
732 }
733 // remove any dict that has our type/addr
734 if (lookupInterfaceByAddress(S_dblist, interface, &where) != NULL) {
735 CFArrayRemoveValueAtIndex(S_dblist, where);
736 }
737 // remove any dict that has the same type/unit
738 if (lookupInterfaceByUnit(S_dblist, interface, &where) != NULL) {
739 CFArrayRemoveValueAtIndex(S_dblist, where);
740 }
741 insertInterface(S_dblist, interface);
742 return;
743 }
744
745 static CFNumberRef
746 getHighestUnitForType(CFNumberRef if_type)
747 {
748 int i;
749 CFIndex n;
750 CFNumberRef ret_unit = NULL;
751
752 if (S_dblist == NULL) {
753 return (NULL);
754 }
755
756 n = CFArrayGetCount(S_dblist);
757 for (i = 0; i < n; i++) {
758 CFDictionaryRef dict = CFArrayGetValueAtIndex(S_dblist, i);
759 CFNumberRef type;
760
761 type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType));
762 if (CFEqual(type, if_type)) {
763 CFNumberRef unit;
764
765 unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit));
766 if (ret_unit == NULL
767 || (CFNumberCompare(unit, ret_unit, NULL)
768 == kCFCompareGreaterThan)) {
769 ret_unit = unit;
770 }
771 }
772 }
773
774 return (ret_unit);
775 }
776
777 /*
778 * Function: registerInterface
779 * Purpose:
780 * Register a single interface with the given service path to the
781 * data link layer (BSD), using the specified unit number.
782 */
783 static kern_return_t
784 registerInterface(io_connect_t connect,
785 CFStringRef path,
786 CFNumberRef unit)
787 {
788 static const int command = kRegisterInterface;
789 CFMutableDictionaryRef dict;
790 kern_return_t kr;
791 CFNumberRef num;
792
793 dict = CFDictionaryCreateMutable(NULL, 0,
794 &kCFTypeDictionaryKeyCallBacks,
795 &kCFTypeDictionaryValueCallBacks);
796 num = CFNumberCreate(NULL, kCFNumberIntType, &command);
797 CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommand), num);
798 CFRelease(num);
799 CFDictionarySetValue(dict, CFSTR(kIOPathMatchKey), path);
800 CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit);
801 kr = IOConnectSetCFProperties(connect, dict);
802 CFRelease(dict);
803 return kr;
804 }
805
806 static SCNetworkInterfaceRef
807 lookupIOKitPath(CFStringRef if_path)
808 {
809 io_registry_entry_t entry = MACH_PORT_NULL;
810 SCNetworkInterfaceRef interface = NULL;
811 kern_return_t kr;
812 mach_port_t masterPort = MACH_PORT_NULL;
813 io_string_t path;
814
815 kr = IOMasterPort(bootstrap_port, &masterPort);
816 if (kr != KERN_SUCCESS) {
817 SCLog(TRUE, LOG_ERR,
818 CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x\n"),
819 kr);
820 goto error;
821 }
822 _SC_cfstring_to_cstring(if_path, path, sizeof(path), kCFStringEncodingASCII);
823 entry = IORegistryEntryFromPath(masterPort, path);
824 if (entry == MACH_PORT_NULL) {
825 SCLog(TRUE, LOG_ERR,
826 CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath(%@) failed"),
827 if_path);
828 goto error;
829 }
830
831 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry);
832
833 error:
834 if (masterPort != MACH_PORT_NULL) {
835 mach_port_deallocate(mach_task_self(), masterPort);
836 }
837 if (entry != MACH_PORT_NULL) {
838 IOObjectRelease(entry);
839 }
840 return (interface);
841
842 }
843
844 static void
845 displayInterface(SCNetworkInterfaceRef interface)
846 {
847 CFStringRef addr;
848 CFStringRef name;
849 CFNumberRef type;
850 CFNumberRef unit;
851
852 name = SCNetworkInterfaceGetBSDName(interface);
853 unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
854 type = _SCNetworkInterfaceGetIOInterfaceType(interface);
855 addr = SCNetworkInterfaceGetHardwareAddressString(interface);
856
857 SCLog(TRUE, LOG_INFO,
858 CFSTR(MY_PLUGIN_NAME ": %s%@%sType: %@, %s%@%sMAC address: %@"),
859 (name != NULL) ? "BSD Name: " : "",
860 (name != NULL) ? name : CFSTR(""),
861 (name != NULL) ? ", " : "",
862 type,
863 (unit != NULL) ? "Unit: " : "",
864 (unit != NULL) ? (CFTypeRef)unit : (CFTypeRef)CFSTR(""),
865 (unit != NULL) ? ", " : "",
866 addr);
867 }
868
869 static int
870 builtinCount(CFArrayRef if_list, CFIndex last, CFNumberRef if_type)
871 {
872 CFIndex i;
873 int n = 0;
874
875 for (i = 0; i < last; i++) {
876 SCNetworkInterfaceRef builtin_if;
877 CFNumberRef builtin_type;
878
879 builtin_if = CFArrayGetValueAtIndex(if_list, i);
880 builtin_type = _SCNetworkInterfaceGetIOInterfaceType(builtin_if);
881 if (CFEqual(if_type, builtin_type)) {
882 if (_SCNetworkInterfaceIsBuiltin(builtin_if)) {
883 n++; // if built-in interface
884 }
885 }
886 }
887
888 return n;
889 }
890
891 static __inline__ boolean_t
892 isQuiet(void)
893 {
894 return (S_quiet == MACH_PORT_NULL);
895 }
896
897 static void
898 nameInterfaces(CFMutableArrayRef if_list)
899 {
900 CFIndex i;
901 CFIndex n = CFArrayGetCount(if_list);
902
903 for (i = 0; i < n; i++) {
904 SCNetworkInterfaceRef interface;
905 CFStringRef path;
906 CFNumberRef type;
907 CFNumberRef unit;
908 CFIndex where;
909
910 interface = CFArrayGetValueAtIndex(if_list, i);
911 path = _SCNetworkInterfaceGetIOPath(interface);
912 type = _SCNetworkInterfaceGetIOInterfaceType(interface);
913 unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface);
914
915 if (unit != NULL) {
916 if (S_debug) {
917 CFStringRef if_name;
918
919 if_name = SCNetworkInterfaceGetBSDName(interface);
920 if ((if_name == NULL) || !CFDictionaryContainsKey(S_state, if_name)) {
921 SCLog(TRUE, LOG_INFO,
922 CFSTR(MY_PLUGIN_NAME ": Interface already has a unit number"));
923 displayInterface(interface);
924 }
925 }
926
927 // update the list of interfaces that were previously named
928 if ((S_prev_active_list != NULL)
929 && lookupInterfaceByAddress(S_prev_active_list, interface, &where) != NULL) {
930 CFArrayRemoveValueAtIndex(S_prev_active_list, where);
931 }
932 } else {
933 CFDictionaryRef dbdict;
934 kern_return_t kr;
935
936 dbdict = lookupInterfaceByAddress(S_dblist, interface, NULL);
937 if (dbdict != NULL) {
938 unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
939 CFRetain(unit);
940
941 SCLog(S_debug, LOG_INFO,
942 CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (from database)"),
943 unit);
944 } else {
945 CFStringRef if_type;
946
947 if_type = SCNetworkInterfaceGetInterfaceType(interface);
948 if ((if_type != NULL) &&
949 CFEqual(if_type, kSCNetworkInterfaceTypeIEEE80211)) {
950 dbdict = lookupAirPortInterface(S_dblist, NULL);
951 if (dbdict != NULL) {
952 unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit));
953 CFRetain(unit);
954
955 SCLog(S_debug, LOG_INFO,
956 CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (updating database)"),
957 unit);
958 }
959 }
960 }
961
962 if ((dbdict != NULL) && (S_prev_active_list != NULL)) {
963 // update the list of interfaces that were previously named
964 where = CFArrayGetFirstIndexOfValue(S_prev_active_list,
965 CFRangeMake(0, CFArrayGetCount(S_prev_active_list)),
966 dbdict);
967 if (where != kCFNotFound) {
968 CFArrayRemoveValueAtIndex(S_prev_active_list, where);
969 }
970 }
971
972 if (dbdict == NULL) {
973 boolean_t is_builtin;
974 int next_unit = 0;
975
976 if (!isQuiet()) {
977 // if new interface, wait until quiet before naming
978 addTimestamp(S_state, path);
979 continue;
980 }
981
982 is_builtin = _SCNetworkInterfaceIsBuiltin(interface);
983 next_unit = 0;
984 if (is_builtin) {
985 // built-in interface, use the reserved slots
986 next_unit = builtinCount(if_list, i, type);
987 } else {
988 // not built-in, skip over the reserved slots
989 next_unit = builtinCount(if_list, n, type);
990
991 unit = getHighestUnitForType(type);
992 if (unit != NULL) {
993 int high_unit;
994
995 CFNumberGetValue(unit, kCFNumberIntType, &high_unit);
996 if (high_unit >= next_unit) {
997 next_unit = high_unit + 1;
998 }
999 }
1000 }
1001 unit = CFNumberCreate(NULL, kCFNumberIntType, &next_unit);
1002
1003 SCLog(S_debug, LOG_INFO,
1004 CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (%s)"),
1005 unit,
1006 is_builtin ? "built-in" : "next available");
1007 }
1008
1009 kr = registerInterface(S_connect, path, unit);
1010 if (kr != KERN_SUCCESS) {
1011 SCLog(TRUE, LOG_ERR,
1012 CFSTR(MY_PLUGIN_NAME ": failed to name the interface, kr=0x%x"),
1013 kr);
1014 if (S_debug) {
1015 displayInterface(interface);
1016 }
1017 } else {
1018 SCNetworkInterfaceRef new_interface;
1019
1020 new_interface = lookupIOKitPath(path);
1021 if (new_interface != NULL) {
1022 CFNumberRef new_unit;
1023
1024 new_unit = _SCNetworkInterfaceGetIOInterfaceUnit(new_interface);
1025 if (CFEqual(unit, new_unit) == FALSE) {
1026 SCLog(S_debug, LOG_INFO,
1027 CFSTR(MY_PLUGIN_NAME
1028 ": interface type %@ assigned "
1029 "unit %@ instead of %@"),
1030 type, new_unit, unit);
1031 }
1032 if (S_debug) {
1033 displayInterface(new_interface);
1034 }
1035
1036 // update if_list (with the interface name & unit)
1037 CFArraySetValueAtIndex(if_list, i, new_interface);
1038 CFRelease(new_interface);
1039 interface = new_interface; // if_list holds the reference
1040 }
1041 }
1042
1043 CFRelease(unit);
1044 }
1045
1046 // update db
1047 replaceInterface(interface);
1048 }
1049 return;
1050 }
1051
1052 static void
1053 updateInterfaces()
1054 {
1055 if (S_connect == MACH_PORT_NULL) {
1056 // if we don't have the "IONetworkStack" connect object
1057 return;
1058 }
1059
1060 if (S_iflist != NULL) {
1061 CFIndex n;
1062
1063 n = CFArrayGetCount(S_iflist);
1064 if (n > 1) {
1065 CFArraySortValues(S_iflist, CFRangeMake(0, n), _SCNetworkInterfaceCompare, NULL);
1066 }
1067 nameInterfaces(S_iflist);
1068 }
1069
1070 if (isQuiet()) {
1071 /*
1072 * The registry [matching] has quiesced so let's
1073 * - save the DB with the interfaces that have been named
1074 * - update the VLAN/BOND configuration
1075 * - tell everyone that we've finished (at least for now)
1076 * - log those interfaces which are no longer present
1077 * in the HW config (or have yet to show up).
1078 */
1079 writeInterfaceList(S_dblist);
1080 updateVirtualNetworkInterfaceConfiguration(NULL, kSCPreferencesNotificationApply, NULL);
1081 updateStore();
1082
1083 if (S_iflist != NULL) {
1084 CFRelease(S_iflist);
1085 S_iflist = NULL;
1086 }
1087
1088 if (S_prev_active_list != NULL) {
1089 if (S_debug) {
1090 CFIndex i;
1091 CFIndex n;
1092
1093 n = CFArrayGetCount(S_prev_active_list);
1094 SCLog(TRUE, LOG_INFO,
1095 CFSTR(MY_PLUGIN_NAME ": Interface%s not [yet] active"),
1096 (n > 0) ? "s" : "");
1097 for (i = 0; i < n; i++) {
1098 CFDictionaryRef if_dict;
1099 CFStringRef name;
1100 CFNumberRef type;
1101 CFNumberRef unit;
1102
1103 if_dict = CFArrayGetValueAtIndex(S_prev_active_list, i);
1104 name = CFDictionaryGetValue(if_dict, CFSTR(kIOBSDNameKey));
1105 type = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceType));
1106 unit = CFDictionaryGetValue(if_dict, CFSTR(kIOInterfaceUnit));
1107 SCLog(TRUE, LOG_INFO,
1108 CFSTR(MY_PLUGIN_NAME ": %s%@%sType: %@, Unit: %@"),
1109 (name != NULL) ? "BSD Name: " : "",
1110 (name != NULL) ? name : CFSTR(""),
1111 (name != NULL) ? ", " : "",
1112 type,
1113 unit);
1114 }
1115 }
1116 CFRelease(S_prev_active_list);
1117 S_prev_active_list = NULL;
1118 }
1119 } else {
1120 if ((S_prev_active_list != NULL) && (CFArrayGetCount(S_prev_active_list) == 0)) {
1121 /*
1122 * if we've named all of the interfaces that
1123 * were used during the previous boot.
1124 */
1125 addTimestamp(S_state, CFSTR("*RELEASE*"));
1126 SCLog(S_debug, LOG_INFO,
1127 CFSTR(MY_PLUGIN_NAME ": last boot interfaces have been named"));
1128 updateStore();
1129 CFRelease(S_prev_active_list);
1130 S_prev_active_list = NULL;
1131 }
1132 }
1133
1134 return;
1135 }
1136
1137 static CFComparisonResult
1138 compareMacAddress(const void *val1, const void *val2, void *context)
1139 {
1140 CFDataRef mac1 = (CFDataRef)val1;
1141 CFDataRef mac2 = (CFDataRef)val2;
1142 CFIndex n1;
1143 CFIndex n2;
1144 CFComparisonResult res;
1145
1146 n1 = CFDataGetLength(mac1);
1147 n2 = CFDataGetLength(mac2);
1148 if (n1 < n2) {
1149 res = kCFCompareLessThan;
1150 } else if (n2 > n1) {
1151 res = kCFCompareGreaterThan;
1152 } else {
1153 res = bcmp(CFDataGetBytePtr(mac1), CFDataGetBytePtr(mac2), n1);
1154 }
1155
1156 return res;
1157 }
1158
1159 static void
1160 updatePlatformUUID()
1161 {
1162 CFDataRef addr;
1163 CFMutableArrayRef addrs = NULL;
1164 CFStringRef guid;
1165 CFIndex i;
1166 CFIndex n;
1167 io_registry_entry_t platform;
1168
1169 platform = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/");
1170 if (platform == MACH_PORT_NULL) {
1171 return;
1172 }
1173
1174 guid = IORegistryEntryCreateCFProperty(platform, CFSTR(kIOPlatformUUIDKey), NULL, 0);
1175 if (guid != NULL) {
1176 // if GUID already defined
1177 goto done;
1178 }
1179
1180 addrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1181 n = (S_dblist != NULL) ? CFArrayGetCount(S_dblist) : 0;
1182 for (i = 0; i < n; i++) {
1183 CFBooleanRef builtin;
1184 CFDictionaryRef dict;
1185 CFStringRef type;
1186
1187 dict = CFArrayGetValueAtIndex(S_dblist, i);
1188 type = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceType));
1189 if (!isA_CFString(type) || !CFEqual(type, kSCNetworkInterfaceTypeEthernet)) {
1190 continue;
1191 }
1192 builtin = CFDictionaryGetValue(dict, CFSTR(kIOBuiltin));
1193 if (!isA_CFBoolean(builtin) || !CFBooleanGetValue(builtin)) {
1194 continue;
1195 }
1196 addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
1197 if (!isA_CFData(addr) || (CFDataGetLength(addr) != ETHER_ADDR_LEN)) {
1198 continue;
1199 }
1200 CFArrayAppendValue(addrs, addr);
1201 }
1202
1203 if (CFArrayGetCount(addrs) == 0) {
1204 // if no ethernet interfaces, look for wireless
1205 for (i = 0; i < n; i++) {
1206 CFDictionaryRef dict;
1207 CFStringRef type;
1208
1209 dict = CFArrayGetValueAtIndex(S_dblist, i);
1210 type = CFDictionaryGetValue(dict, CFSTR(kSCNetworkInterfaceType));
1211 if (!isA_CFString(type) || !CFEqual(type, kSCNetworkInterfaceTypeIEEE80211)) {
1212 continue;
1213 }
1214 addr = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress));
1215 if (!isA_CFData(addr) || (CFDataGetLength(addr) != ETHER_ADDR_LEN)) {
1216 continue;
1217 }
1218 CFArrayAppendValue(addrs, addr);
1219 }
1220 }
1221
1222 n = CFArrayGetCount(addrs);
1223 switch (n) {
1224 case 0 :
1225 SCLog(TRUE, LOG_ERR,
1226 CFSTR(MY_PLUGIN_NAME ": no network interfaces, could not update platform UUID"));
1227 break;
1228 default :
1229 // sort by MAC address
1230 CFArraySortValues(addrs, CFRangeMake(0, n), compareMacAddress, NULL);
1231
1232 // fall through
1233 case 1 : {
1234 CFUUIDBytes bytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
1235 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
1236 kern_return_t kr;
1237 CFUUIDRef uuid;
1238
1239 // set GUID
1240 addr = CFArrayGetValueAtIndex(addrs, 0);
1241 bcopy(CFDataGetBytePtr(addr),
1242 (void *)&bytes + sizeof(bytes) - ETHER_ADDR_LEN,
1243 ETHER_ADDR_LEN);
1244 uuid = CFUUIDCreateFromUUIDBytes(NULL, bytes);
1245 guid = CFUUIDCreateString(NULL, uuid);
1246 CFRelease(uuid);
1247
1248 SCLog(TRUE, LOG_INFO,
1249 CFSTR(MY_PLUGIN_NAME ": setting platform UUID = %@"),
1250 guid);
1251 kr = IORegistryEntrySetCFProperty(platform, CFSTR(kIOPlatformUUIDKey), guid);
1252 if (kr != KERN_SUCCESS) {
1253 SCLog(TRUE, LOG_ERR,
1254 CFSTR(MY_PLUGIN_NAME ": IORegistryEntrySetCFProperty(platform UUID) failed, kr=0x%x"),
1255 kr);
1256 }
1257
1258 addTimestamp(S_state, CFSTR("*PLATFORM-UUID*"));
1259 updateStore();
1260 break;
1261 }
1262 }
1263
1264 done :
1265
1266 if (addrs != NULL) CFRelease(addrs);
1267 if (platform != MACH_PORT_NULL) IOObjectRelease(platform);
1268 if (guid != NULL) CFRelease(guid);
1269 return;
1270 }
1271
1272 static void
1273 interfaceArrivalCallback(void *refcon, io_iterator_t iter)
1274 {
1275 io_object_t obj;
1276
1277 while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) {
1278 SCNetworkInterfaceRef interface;
1279
1280 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj);
1281 if (interface != NULL) {
1282 if (S_iflist == NULL) {
1283 S_iflist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1284 }
1285 CFArrayAppendValue(S_iflist, interface);
1286 CFRelease(interface);
1287 }
1288 IOObjectRelease(obj);
1289 }
1290
1291 updateInterfaces();
1292 return;
1293 }
1294
1295 /*
1296 * Function: stackCallback
1297 * Purpose:
1298 * Get a reference to the single IONetworkStack object instance in
1299 * the kernel. Naming requests must be sent to this object, which is
1300 * attached as a client to all network interface objects in the system.
1301 * Note:
1302 * Call IOObjectRelease on the returned object.
1303 */
1304 static void
1305 stackCallback(void *refcon, io_iterator_t iter)
1306 {
1307 kern_return_t kr;
1308 io_object_t stack;
1309
1310 stack = IOIteratorNext(iter);
1311 if (stack == MACH_PORT_NULL) {
1312 goto error;
1313 }
1314
1315 kr = IOServiceOpen(stack, mach_task_self(), 0, &S_connect);
1316 if (kr != KERN_SUCCESS) {
1317 SCLog(TRUE, LOG_ERR,
1318 CFSTR(MY_PLUGIN_NAME ": IOServiceOpen returned 0x%x"),
1319 kr);
1320 goto error;
1321 }
1322
1323 addTimestamp(S_state, CFSTR("*STACK*"));
1324 SCLog(S_debug, LOG_INFO,
1325 CFSTR(MY_PLUGIN_NAME ": IONetworkStack found"));
1326
1327 if (S_stack != MACH_PORT_NULL) {
1328 IOObjectRelease(S_stack);
1329 S_stack = MACH_PORT_NULL;
1330 }
1331
1332 updateInterfaces();
1333
1334 error:
1335 if (stack != MACH_PORT_NULL) {
1336 IOObjectRelease(stack);
1337 }
1338
1339 return;
1340 }
1341
1342 static void
1343 quietCallback(void *refcon,
1344 io_service_t service,
1345 natural_t messageType,
1346 void *messageArgument)
1347 {
1348 if (messageArgument != NULL) {
1349 // if not yet quiet
1350 return;
1351 }
1352
1353 if (messageType == kIOMessageServiceBusyStateChange) {
1354 addTimestamp(S_state, CFSTR("*QUIET*"));
1355 SCLog(S_debug, LOG_INFO,
1356 CFSTR(MY_PLUGIN_NAME ": IOKit quiet"));
1357 }
1358
1359 if (S_connect == MACH_PORT_NULL) {
1360 SCLog(TRUE, LOG_ERR,
1361 CFSTR(MY_PLUGIN_NAME ": No network stack object"));
1362 return;
1363 }
1364
1365 if (S_quiet != MACH_PORT_NULL) {
1366 IOObjectRelease(S_quiet);
1367 S_quiet = MACH_PORT_NULL;
1368 }
1369
1370 if (S_timer != NULL) {
1371 CFRunLoopTimerInvalidate(S_timer);
1372 CFRelease(S_timer);
1373 S_timer = NULL;
1374 }
1375
1376 // grab (and name) any additional interfaces.
1377 interfaceArrivalCallback((void *)S_notify, S_iter);
1378
1379 updatePlatformUUID();
1380
1381 return;
1382 }
1383
1384 static void
1385 iterateRegistryBusy(io_iterator_t iterator, CFArrayRef nodes, int *count)
1386 {
1387 kern_return_t kr = kIOReturnSuccess;;
1388 io_object_t obj;
1389
1390 while ((kr == kIOReturnSuccess) &&
1391 ((obj = IOIteratorNext(iterator)) != MACH_PORT_NULL)) {
1392 uint32_t busy;
1393 io_name_t location;
1394 io_name_t name;
1395 CFMutableArrayRef newNodes;
1396 CFMutableStringRef str = NULL;
1397
1398 if (nodes == NULL) {
1399 newNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1400 } else {
1401 newNodes = CFArrayCreateMutableCopy(NULL, 0, nodes);
1402 }
1403
1404 kr = IORegistryEntryGetName(obj, name);
1405 if (kr != kIOReturnSuccess) {
1406 SCLog(TRUE, LOG_ERR,
1407 CFSTR(MY_PLUGIN_NAME ": reportBusy IORegistryEntryGetName returned 0x%x"),
1408 kr);
1409 goto next;
1410 }
1411
1412 str = CFStringCreateMutable(NULL, 0);
1413 CFStringAppendCString(str, name, kCFStringEncodingUTF8);
1414
1415 kr = IORegistryEntryGetLocationInPlane(obj, kIOServicePlane, location);
1416 switch (kr) {
1417 case kIOReturnSuccess :
1418 CFStringAppendCString(str, "@", kCFStringEncodingUTF8);
1419 CFStringAppendCString(str, location, kCFStringEncodingUTF8);
1420 break;
1421 case kIOReturnNotFound :
1422 break;
1423 default :
1424 SCLog(TRUE, LOG_ERR,
1425 CFSTR(MY_PLUGIN_NAME ": reportBusy IORegistryEntryGetLocationInPlane returned 0x%x"),
1426 kr);
1427 goto next;
1428 break;
1429 }
1430
1431 CFArrayAppendValue(newNodes, str);
1432 CFRelease(str);
1433
1434 kr = IOServiceGetBusyState(obj, &busy);
1435 if (kr != kIOReturnSuccess) {
1436 SCLog(TRUE, LOG_ERR,
1437 CFSTR(MY_PLUGIN_NAME ": reportBusy IOServiceGetBusyState returned 0x%x"),
1438 kr);
1439 goto next;
1440 }
1441
1442 if (busy != 0) {
1443 CFStringRef path;
1444
1445 if ((*count)++ == 0) {
1446 SCLog(TRUE, LOG_WARNING, CFSTR(MY_PLUGIN_NAME ": Busy services :"));
1447 }
1448
1449 path = CFStringCreateByCombiningStrings(NULL, newNodes, CFSTR("/"));
1450 SCLog(TRUE, LOG_WARNING, CFSTR(MY_PLUGIN_NAME ": %@ [%d]"), path, busy);
1451 CFRelease(path);
1452 }
1453
1454 kr = IORegistryIteratorEnterEntry(iterator);
1455 if (kr != kIOReturnSuccess) {
1456 SCLog(TRUE, LOG_ERR,
1457 CFSTR(MY_PLUGIN_NAME ": reportBusy IORegistryIteratorEnterEntry returned 0x%x"),
1458 kr);
1459 goto next;
1460 }
1461
1462 iterateRegistryBusy(iterator, newNodes, count);
1463
1464 kr = IORegistryIteratorExitEntry(iterator);
1465 if (kr != kIOReturnSuccess) {
1466 SCLog(TRUE, LOG_ERR,
1467 CFSTR(MY_PLUGIN_NAME ": reportBusy IORegistryIteratorExitEntry returned 0x%x"),
1468 kr);
1469 }
1470
1471 next :
1472
1473 CFRelease(newNodes);
1474 IOObjectRelease(obj);
1475 }
1476
1477 return;
1478 }
1479
1480 static void
1481 reportBusy()
1482 {
1483 int count = 0;
1484 io_iterator_t iterator = MACH_PORT_NULL;
1485 kern_return_t kr;
1486
1487 kr = IORegistryCreateIterator(kIOMasterPortDefault,
1488 kIOServicePlane,
1489 0,
1490 &iterator);
1491 if (kr != kIOReturnSuccess) {
1492 SCLog(TRUE, LOG_ERR,
1493 CFSTR(MY_PLUGIN_NAME ": reportBusy IORegistryCreateIterator returned 0x%x"),
1494 kr);
1495 return;
1496 }
1497
1498 iterateRegistryBusy(iterator, NULL, &count);
1499 SCLog((count == 0), LOG_WARNING,
1500 CFSTR(MY_PLUGIN_NAME ": w/no busy services"));
1501 IOObjectRelease(iterator);
1502 return;
1503 }
1504
1505 static void
1506 timerCallback(CFRunLoopTimerRef timer, void *info)
1507 {
1508 /*
1509 * We've been waiting for IOKit to quiesce and it just
1510 * hasn't happenned. Time to just move on!
1511 */
1512 addTimestamp(S_state, CFSTR("*TIMEOUT*"));
1513 SCLog(TRUE, LOG_ERR,
1514 CFSTR(MY_PLUGIN_NAME ": timed out waiting for IOKit to quiesce"));
1515
1516 reportBusy();
1517 quietCallback((void *)S_notify, MACH_PORT_NULL, 0, NULL);
1518 return;
1519 }
1520
1521 static Boolean
1522 setup_IOKit(CFBundleRef bundle)
1523 {
1524 uint32_t busy;
1525 CFDictionaryRef dict;
1526 kern_return_t kr;
1527 mach_port_t masterPort = MACH_PORT_NULL;
1528 Boolean ok = FALSE;
1529 io_object_t root = MACH_PORT_NULL;
1530 double timeout = WAIT_QUIET_TIMEOUT_DEFAULT;
1531
1532 // read DB of previously named network interfaces
1533 S_dblist = readInterfaceList();
1534 if (S_dblist != NULL) {
1535 CFIndex n;
1536
1537 n = CFArrayGetCount(S_dblist);
1538 if (n > 1) {
1539 CFArraySortValues(S_dblist, CFRangeMake(0, n), if_unit_compare, NULL);
1540 }
1541 }
1542
1543 // get interfaces that were named during the last boot
1544 S_prev_active_list = previouslyActiveInterfaces();
1545
1546 // track how long we've waited to see each interface.
1547 S_state = CFDictionaryCreateMutable(NULL,
1548 0,
1549 &kCFTypeDictionaryKeyCallBacks,
1550 &kCFTypeDictionaryValueCallBacks);
1551 addTimestamp(S_state, CFSTR("*START*"));
1552
1553 // Creates and returns a notification object for receiving IOKit
1554 // notifications of new devices or state changes.
1555 kr = IOMasterPort(bootstrap_port, &masterPort);
1556 if (kr != KERN_SUCCESS) {
1557 SCLog(TRUE, LOG_ERR,
1558 CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"),
1559 kr);
1560 goto done;
1561 }
1562
1563 S_notify = IONotificationPortCreate(masterPort);
1564 if (S_notify == NULL) {
1565 SCLog(TRUE, LOG_ERR,
1566 CFSTR(MY_PLUGIN_NAME ": IONotificationPortCreate failed"));
1567 goto done;
1568 }
1569
1570 // watch IOKit matching activity
1571 root = IORegistryEntryFromPath(masterPort, kIOServicePlane ":/");
1572 if (root == MACH_PORT_NULL) {
1573 SCLog(TRUE, LOG_ERR,
1574 CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath failed"));
1575 goto done;
1576 }
1577
1578 kr = IOServiceAddInterestNotification(S_notify,
1579 root,
1580 kIOBusyInterest,
1581 &quietCallback,
1582 (void *)S_notify, // refCon
1583 &S_quiet); // notification
1584 if (kr != KERN_SUCCESS) {
1585 SCLog(TRUE, LOG_ERR,
1586 CFSTR(MY_PLUGIN_NAME ": IOServiceAddInterestNotification returned 0x%x"),
1587 kr);
1588 goto done;
1589 }
1590
1591 kr = IOServiceGetBusyState(root, &busy);
1592 if (kr != KERN_SUCCESS) {
1593 SCLog(TRUE, LOG_ERR,
1594 CFSTR(MY_PLUGIN_NAME ": IOServiceGetBusyState returned 0x%x"),
1595 kr);
1596 goto done;
1597 }
1598
1599 // add a timer so we don't wait forever for IOKit to quiesce
1600 dict = CFBundleGetInfoDictionary(bundle);
1601 if (isA_CFDictionary(dict)) {
1602 CFNumberRef num;
1603
1604 num = CFDictionaryGetValue(dict, CFSTR(WAIT_QUIET_TIMEOUT_KEY));
1605 if (num != NULL) {
1606 if (!isA_CFNumber(num) ||
1607 !CFNumberGetValue(num, kCFNumberDoubleType, &timeout) ||
1608 (timeout <= 0.0)) {
1609 SCLog(TRUE, LOG_ERR,
1610 CFSTR(MY_PLUGIN_NAME ": " WAIT_QUIET_TIMEOUT_KEY " value error"));
1611 timeout = WAIT_QUIET_TIMEOUT_DEFAULT;
1612 }
1613 }
1614 }
1615 S_timer = CFRunLoopTimerCreate(NULL,
1616 CFAbsoluteTimeGetCurrent() + timeout,
1617 0,
1618 0,
1619 0,
1620 timerCallback,
1621 NULL);
1622 if (S_timer == NULL) {
1623 SCLog(TRUE, LOG_ERR,
1624 CFSTR(MY_PLUGIN_NAME ": CFRunLoopTimerCreate failed"));
1625 goto done;
1626 }
1627
1628 CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, kCFRunLoopDefaultMode);
1629
1630 // watch for the introduction of the IONetworkStack
1631 kr = IOServiceAddMatchingNotification(S_notify,
1632 kIOFirstMatchNotification,
1633 IOServiceMatching("IONetworkStack"),
1634 &stackCallback,
1635 (void *)S_notify, // refCon
1636 &S_stack); // notification
1637 if (kr != KERN_SUCCESS) {
1638 SCLog(TRUE, LOG_ERR,
1639 CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"),
1640 kr);
1641 goto done;
1642 }
1643
1644 // check and see if the stack is already available and arm the
1645 // notification for its introduction.
1646 stackCallback((void *)S_notify, S_stack);
1647
1648 // watch for the introduction of new network interfaces
1649 kr = IOServiceAddMatchingNotification(S_notify,
1650 kIOFirstMatchNotification,
1651 IOServiceMatching("IONetworkInterface"),
1652 &interfaceArrivalCallback,
1653 (void *)S_notify, // refCon
1654 &S_iter); // notification
1655 if (kr != KERN_SUCCESS) {
1656 SCLog(TRUE, LOG_ERR,
1657 CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"),
1658 kr);
1659 goto done;
1660 }
1661
1662 // Get the current list of matches and arm the notification for
1663 // future interface arrivals.
1664 interfaceArrivalCallback((void *)S_notify, S_iter);
1665
1666 // Check if IOKit has already quiesced.
1667 quietCallback((void *)S_notify,
1668 MACH_PORT_NULL,
1669 kIOMessageServiceBusyStateChange,
1670 (void *)busy);
1671
1672 CFRunLoopAddSource(CFRunLoopGetCurrent(),
1673 IONotificationPortGetRunLoopSource(S_notify),
1674 kCFRunLoopDefaultMode);
1675
1676 #ifdef WAIT_PREVIOUS_BOOT_INTERFACES_OR_QUIET
1677 /*
1678 * Start the wheels turning until we've named all of
1679 * the interfaces that were used during the previous
1680 * boot, until IOKit [matching] has quiesced, or
1681 * until we've waited long enough.
1682 */
1683 CFRunLoopAddTimer(CFRunLoopGetCurrent(), S_timer, MY_PLUGIN_ID);
1684 CFRunLoopAddSource(CFRunLoopGetCurrent(),
1685 IONotificationPortGetRunLoopSource(S_notify),
1686 MY_PLUGIN_ID);
1687 while (S_prev_active_list != NULL) {
1688 int rlStatus;
1689
1690 rlStatus = CFRunLoopRunInMode(MY_PLUGIN_ID, 1.0e10, TRUE);
1691 }
1692 #endif /* WAIT_PREVIOUS_BOOT_INTERFACES_OR_QUIET */
1693
1694 ok = TRUE;
1695
1696 done:
1697 if (root != MACH_PORT_NULL) {
1698 IOObjectRelease(root);
1699 }
1700 if (masterPort != MACH_PORT_NULL) {
1701 mach_port_deallocate(mach_task_self(), masterPort);
1702 }
1703
1704 return ok;
1705 }
1706
1707 static Boolean
1708 setup_Virtual(CFBundleRef bundle)
1709 {
1710 // open a SCPreferences session
1711 S_prefs = SCPreferencesCreate(NULL, CFSTR(MY_PLUGIN_NAME), NULL);
1712 if (S_prefs == NULL) {
1713 SCLog(TRUE, LOG_ERR,
1714 CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate() failed: %s"),
1715 SCErrorString(SCError()));
1716 return FALSE;
1717 }
1718
1719 // register for change notifications.
1720 if (!SCPreferencesSetCallback(S_prefs, updateVirtualNetworkInterfaceConfiguration, NULL)) {
1721 SCLog(TRUE, LOG_ERR,
1722 CFSTR(MY_PLUGIN_NAME ": SCPreferencesSetCallBack() failed: %s"),
1723 SCErrorString(SCError()));
1724 CFRelease(S_prefs);
1725 return FALSE;
1726 }
1727
1728 // schedule
1729 if (!SCPreferencesScheduleWithRunLoop(S_prefs, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
1730 SCLog(TRUE, LOG_ERR,
1731 CFSTR(MY_PLUGIN_NAME ": SCPreferencesScheduleWithRunLoop() failed: %s"),
1732 SCErrorString(SCError()));
1733 CFRelease(S_prefs);
1734 return FALSE;
1735 }
1736
1737 return TRUE;
1738 }
1739
1740 __private_extern__
1741 void
1742 load_InterfaceNamer(CFBundleRef bundle, Boolean bundleVerbose)
1743 {
1744 if (bundleVerbose) {
1745 S_debug = TRUE;
1746 }
1747
1748 // setup virtual network interface monitoring
1749 if (!setup_Virtual(bundle)) {
1750 goto error;
1751 }
1752
1753 // setup [IOKit] network interface monitoring
1754 if (!setup_IOKit(bundle)) {
1755 goto error;
1756 }
1757
1758 return;
1759
1760 error :
1761 if (S_connect != MACH_PORT_NULL) {
1762 IOServiceClose(S_connect);
1763 S_connect = MACH_PORT_NULL;
1764 }
1765 if (S_dblist != NULL) {
1766 CFRelease(S_dblist);
1767 S_dblist = NULL;
1768 }
1769 if (S_iter != MACH_PORT_NULL) {
1770 IOObjectRelease(S_iter);
1771 S_iter = MACH_PORT_NULL;
1772 }
1773 if (S_notify != MACH_PORT_NULL) {
1774 IONotificationPortDestroy(S_notify);
1775 }
1776 if (S_quiet != MACH_PORT_NULL) {
1777 IOObjectRelease(S_quiet);
1778 S_quiet = MACH_PORT_NULL;
1779 }
1780 if (S_stack != MACH_PORT_NULL) {
1781 IOObjectRelease(S_stack);
1782 S_stack = MACH_PORT_NULL;
1783 }
1784 if (S_state != NULL) {
1785 CFRelease(S_state);
1786 S_state = NULL;
1787 }
1788 if (S_timer != NULL) {
1789 CFRunLoopTimerInvalidate(S_timer);
1790 CFRelease(S_timer);
1791 S_timer = NULL;
1792 }
1793
1794 return;
1795 }
1796
1797 //------------------------------------------------------------------------
1798 // Main function.
1799 #ifdef MAIN
1800 int
1801 main(int argc, char ** argv)
1802 {
1803 _sc_log = FALSE;
1804 _sc_verbose = (argc > 1) ? TRUE : FALSE;
1805
1806 load_InterfaceNamer(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
1807 CFRunLoopRun();
1808 /* not reached */
1809 exit(0);
1810 return 0;
1811 }
1812 #endif /* MAIN */
1813
1814 #ifdef TEST_PLATFORM_UUID
1815 int
1816 main(int argc, char ** argv)
1817 {
1818 CFArrayRef interfaces;
1819
1820 _sc_log = FALSE;
1821 _sc_verbose = (argc > 1) ? TRUE : FALSE;
1822
1823 S_dblist = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1824 interfaces = SCNetworkInterfaceCopyAll();
1825 if (interfaces != NULL) {
1826 CFIndex i;
1827 CFIndex n;
1828
1829 n = CFArrayGetCount(interfaces);
1830 for (i = 0; i < n; i++) {
1831 CFDictionaryRef dict;
1832 SCNetworkInterfaceRef interface;
1833
1834 interface = CFArrayGetValueAtIndex(interfaces, i);
1835 dict = createInterfaceDict(interface);
1836 CFArrayAppendValue(S_dblist, dict);
1837 CFRelease(dict);
1838 }
1839 CFRelease(interfaces);
1840 }
1841 updatePlatformUUID();
1842 CFRelease(S_dblist);
1843 exit(0);
1844 return 0;
1845 }
1846 #endif /* TEST_PLATFORM_UUID */