Commit | Line | Data |
---|---|---|
dbf6a266 | 1 | /* |
17d3ee29 | 2 | * Copyright (c) 2001-2012 Apple Inc. All rights reserved. |
dbf6a266 A |
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 | * | |
af243a0d A |
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 | * | |
edebe297 A |
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 | * | |
dbf6a266 A |
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> | |
d0784775 A |
65 | #include <sys/ioctl.h> |
66 | #include <sys/socket.h> | |
dbf6a266 A |
67 | #include <sys/sockio.h> |
68 | #include <sys/stat.h> | |
6bb65964 | 69 | #include <sys/sysctl.h> |
dbf6a266 A |
70 | #include <sys/param.h> |
71 | #include <mach/mach.h> | |
edebe297 | 72 | #include <net/ethernet.h> |
d0784775 | 73 | #include <net/if.h> |
dbf6a266 | 74 | #include <net/if_types.h> |
17d3ee29 A |
75 | #include <pthread.h> |
76 | #include <vproc.h> | |
77 | ||
78 | #include <CommonCrypto/CommonDigest.h> | |
dbf6a266 A |
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 | ||
dbf6a266 | 87 | #include <IOKit/IOKitLib.h> |
d0784775 | 88 | #include <IOKit/IOKitLibPrivate.h> |
dbf6a266 | 89 | #include <IOKit/IOBSD.h> |
edebe297 | 90 | #include <IOKit/IOMessage.h> |
dbf6a266 A |
91 | #include <IOKit/network/IONetworkController.h> |
92 | #include <IOKit/network/IONetworkInterface.h> | |
af243a0d | 93 | #include <IOKit/network/IONetworkStack.h> |
d0784775 | 94 | #include <IOKit/usb/USB.h> |
dbf6a266 | 95 | |
af243a0d A |
96 | #ifdef kIONetworkStackUserCommandKey |
97 | #define USE_REGISTRY_ENTRY_ID | |
98 | #endif | |
99 | ||
100 | #ifndef USE_REGISTRY_ENTRY_ID | |
6bb65964 | 101 | // from <IOKit/network/IONetworkStack.h> |
af243a0d | 102 | #define kIONetworkStackUserCommandKey "IONetworkStackUserCommand" |
6bb65964 A |
103 | enum { |
104 | kRegisterInterfaceWithFixedUnit = 0, | |
105 | kRegisterInterface, | |
106 | kRegisterAllInterfaces | |
107 | }; | |
af243a0d | 108 | #endif // !USE_REGISTRY_ENTRY_ID |
edebe297 | 109 | |
d0784775 | 110 | #define kSCNetworkInterfaceInfo "SCNetworkInterfaceInfo" |
edebe297 A |
111 | #define kSCNetworkInterfaceType "SCNetworkInterfaceType" |
112 | #define kSCNetworkInterfaceActive "Active" | |
dbf6a266 A |
113 | |
114 | #define MY_PLUGIN_NAME "InterfaceNamer" | |
edebe297 | 115 | #define MY_PLUGIN_ID CFSTR("com.apple.SystemConfiguration." MY_PLUGIN_NAME) |
dbf6a266 | 116 | |
6bb65964 A |
117 | #define WAIT_STACK_TIMEOUT_KEY "WaitStackTimeout" |
118 | #define WAIT_STACK_TIMEOUT_DEFAULT 300.0 | |
119 | ||
edebe297 A |
120 | #define WAIT_QUIET_TIMEOUT_KEY "WaitQuietTimeout" |
121 | #define WAIT_QUIET_TIMEOUT_DEFAULT 60.0 | |
dbf6a266 | 122 | |
edebe297 A |
123 | /* |
124 | * S_connect | |
125 | * "IONetworkStack" connect object used to "name" an interface. | |
126 | */ | |
127 | static io_connect_t S_connect = MACH_PORT_NULL; | |
dbf6a266 | 128 | |
edebe297 A |
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; | |
dbf6a266 | 135 | |
edebe297 A |
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; | |
dbf6a266 | 182 | |
edebe297 A |
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). | |
6bb65964 A |
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. | |
edebe297 A |
203 | */ |
204 | static CFRunLoopTimerRef S_timer = NULL; | |
6bb65964 A |
205 | static double S_stack_timeout = WAIT_STACK_TIMEOUT_DEFAULT; |
206 | static double S_quiet_timeout = WAIT_QUIET_TIMEOUT_DEFAULT; | |
207 | ||
17d3ee29 A |
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 | |
edebe297 A |
216 | |
217 | /* | |
218 | * Virtual network interface configuration | |
6bb65964 A |
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 | |
edebe297 A |
223 | */ |
224 | static SCPreferencesRef S_prefs = NULL; | |
225 | static CFArrayRef S_bonds = NULL; | |
6bb65964 | 226 | static CFArrayRef S_bridges = NULL; |
edebe297 A |
227 | static CFArrayRef S_vlans = NULL; |
228 | ||
229 | static void | |
230 | addTimestamp(CFMutableDictionaryRef dict, CFStringRef key) | |
dbf6a266 | 231 | { |
edebe297 A |
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; | |
dbf6a266 A |
240 | } |
241 | ||
6bb65964 | 242 | #define INTERFACES CFSTR("Interfaces") |
6bb65964 A |
243 | #define NETWORK_INTERFACES_PREFS CFSTR("NetworkInterfaces.plist") |
244 | ||
dbf6a266 A |
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 | ||
6bb65964 A |
269 | static void |
270 | reportIssue(const char *signature, CFStringRef issue) | |
dbf6a266 | 271 | { |
6bb65964 | 272 | aslmsg m; |
dbf6a266 | 273 | |
6bb65964 A |
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); | |
dbf6a266 | 280 | |
6bb65964 | 281 | return; |
dbf6a266 A |
282 | } |
283 | ||
edebe297 A |
284 | static void |
285 | writeInterfaceList(CFArrayRef if_list) | |
286 | { | |
287 | CFArrayRef cur_list; | |
6bb65964 A |
288 | CFStringRef new_model; |
289 | CFStringRef old_model; | |
edebe297 A |
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 | ||
6bb65964 | 309 | old_model = SCPreferencesGetValue(prefs, MODEL); |
17d3ee29 | 310 | new_model = _SC_hw_model(); |
af243a0d A |
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; | |
6bb65964 | 316 | |
af243a0d A |
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 | } | |
6bb65964 | 339 | |
af243a0d A |
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 | } | |
6bb65964 A |
346 | } |
347 | ||
edebe297 A |
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 | ||
17d3ee29 | 368 | static CF_RETURNS_RETAINED CFMutableArrayRef |
dbf6a266 A |
369 | readInterfaceList() |
370 | { | |
edebe297 | 371 | CFArrayRef if_list; |
6bb65964 A |
372 | CFStringRef old_model; |
373 | CFMutableArrayRef plist = NULL; | |
374 | SCPreferencesRef prefs = NULL; | |
dbf6a266 | 375 | |
edebe297 | 376 | prefs = SCPreferencesCreate(NULL, MY_PLUGIN_ID, NETWORK_INTERFACES_PREFS); |
dbf6a266 | 377 | if (!prefs) { |
edebe297 | 378 | SCLog(TRUE, LOG_ERR, |
dbf6a266 A |
379 | CFSTR(MY_PLUGIN_NAME ": SCPreferencesCreate failed, %s"), |
380 | SCErrorString(SCError())); | |
381 | return (NULL); | |
382 | } | |
383 | ||
edebe297 | 384 | if_list = SCPreferencesGetValue(prefs, INTERFACES); |
6bb65964 A |
385 | if_list = isA_CFArray(if_list); |
386 | ||
387 | old_model = SCPreferencesGetValue(prefs, MODEL); | |
388 | if (old_model != NULL) { | |
389 | CFStringRef new_model; | |
390 | ||
17d3ee29 | 391 | new_model = _SC_hw_model(); |
6bb65964 A |
392 | if (!_SC_CFEqual(old_model, new_model)) { |
393 | // if interface list was created on other hardware | |
edebe297 | 394 | if_list = NULL; |
dbf6a266 | 395 | } |
dbf6a266 A |
396 | } |
397 | ||
edebe297 A |
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 | } | |
6bb65964 A |
415 | |
416 | if (prefs != NULL) { | |
dbf6a266 A |
417 | CFRelease(prefs); |
418 | } | |
419 | return (plist); | |
420 | } | |
421 | ||
17d3ee29 | 422 | static CF_RETURNS_RETAINED CFMutableArrayRef |
edebe297 | 423 | previouslyActiveInterfaces() |
dbf6a266 | 424 | { |
edebe297 A |
425 | CFMutableArrayRef active; |
426 | CFIndex i; | |
427 | CFIndex n; | |
dbf6a266 | 428 | |
edebe297 A |
429 | if (S_dblist == NULL) { |
430 | return NULL; | |
dbf6a266 A |
431 | } |
432 | ||
edebe297 | 433 | active = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
dbf6a266 | 434 | |
edebe297 A |
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 | } | |
dbf6a266 A |
449 | } |
450 | ||
edebe297 A |
451 | return active; |
452 | } | |
453 | ||
454 | static void | |
455 | updateStore(void) | |
456 | { | |
457 | CFStringRef key; | |
dbf6a266 | 458 | |
edebe297 A |
459 | key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@" MY_PLUGIN_NAME), |
460 | kSCDynamicStoreDomainPlugin); | |
17d3ee29 | 461 | (void)SCDynamicStoreSetValue(NULL, key, S_state); |
edebe297 | 462 | CFRelease(key); |
dbf6a266 | 463 | |
dbf6a266 A |
464 | return; |
465 | } | |
466 | ||
17d3ee29 | 467 | #if !TARGET_OS_IPHONE |
dbf6a266 | 468 | static void |
6bb65964 | 469 | updateBondInterfaceConfiguration(SCPreferencesRef prefs) |
dbf6a266 | 470 | { |
edebe297 | 471 | CFArrayRef interfaces; |
dbf6a266 | 472 | |
6bb65964 A |
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); | |
dbf6a266 A |
482 | return; |
483 | } | |
484 | ||
6bb65964 A |
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())); | |
edebe297 A |
492 | } |
493 | ||
6bb65964 A |
494 | return; |
495 | } | |
17d3ee29 | 496 | #endif // !TARGET_OS_IPHONE |
edebe297 | 497 | |
6bb65964 A |
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; | |
edebe297 | 507 | } |
6bb65964 A |
508 | |
509 | if (_SC_CFEqual(S_bridges, interfaces)) { | |
edebe297 | 510 | // if no change |
6bb65964 A |
511 | if (interfaces != NULL) CFRelease(interfaces); |
512 | return; | |
edebe297 | 513 | } |
edebe297 | 514 | |
6bb65964 A |
515 | if (S_bridges != NULL) CFRelease(S_bridges); |
516 | S_bridges = interfaces; | |
517 | ||
518 | if (!_SCBridgeInterfaceUpdateConfiguration(prefs)) { | |
edebe297 | 519 | SCLog(TRUE, LOG_ERR, |
6bb65964 | 520 | CFSTR(MY_PLUGIN_NAME ": _SCBridgeInterfaceUpdateConfiguration failed, %s"), |
dbf6a266 | 521 | SCErrorString(SCError())); |
edebe297 A |
522 | } |
523 | ||
6bb65964 A |
524 | return; |
525 | } | |
edebe297 | 526 | |
6bb65964 A |
527 | static void |
528 | updateVLANInterfaceConfiguration(SCPreferencesRef prefs) | |
529 | { | |
530 | CFArrayRef interfaces; | |
edebe297 A |
531 | |
532 | interfaces = SCVLANInterfaceCopyAll(prefs); | |
6bb65964 A |
533 | if ((interfaces != NULL) && (CFArrayGetCount(interfaces) == 0)) { |
534 | CFRelease(interfaces); | |
535 | interfaces = NULL; | |
dbf6a266 | 536 | } |
6bb65964 A |
537 | |
538 | if (_SC_CFEqual(S_vlans, interfaces)) { | |
edebe297 | 539 | // if no change |
6bb65964 A |
540 | if (interfaces != NULL) CFRelease(interfaces); |
541 | return; | |
edebe297 | 542 | } |
6bb65964 | 543 | |
edebe297 A |
544 | if (S_vlans != NULL) CFRelease(S_vlans); |
545 | S_vlans = interfaces; | |
dbf6a266 | 546 | |
edebe297 A |
547 | if (!_SCVLANInterfaceUpdateConfiguration(prefs)) { |
548 | SCLog(TRUE, LOG_ERR, | |
549 | CFSTR(MY_PLUGIN_NAME ": _SCVLANInterfaceUpdateConfiguration failed, %s"), | |
550 | SCErrorString(SCError())); | |
551 | } | |
dbf6a266 | 552 | |
6bb65964 A |
553 | return; |
554 | } | |
edebe297 | 555 | |
6bb65964 A |
556 | static void |
557 | updateVirtualNetworkInterfaceConfiguration(SCPreferencesRef prefs, | |
558 | SCPreferencesNotification notificationType, | |
559 | void *info) | |
560 | { | |
561 | if ((notificationType & kSCPreferencesNotificationApply) != kSCPreferencesNotificationApply) { | |
562 | return; | |
563 | } | |
edebe297 | 564 | |
6bb65964 A |
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 | ||
17d3ee29 | 582 | #if !TARGET_OS_IPHONE |
6bb65964 | 583 | updateBondInterfaceConfiguration (prefs); |
17d3ee29 | 584 | #endif // !TARGET_OS_IPHONE |
6bb65964 A |
585 | updateBridgeInterfaceConfiguration(prefs); |
586 | updateVLANInterfaceConfiguration (prefs); | |
587 | ||
588 | // we are finished with current prefs, wait for changes | |
edebe297 | 589 | SCPreferencesSynchronize(prefs); |
dbf6a266 A |
590 | return; |
591 | } | |
592 | ||
edebe297 A |
593 | static CFDictionaryRef |
594 | createInterfaceDict(SCNetworkInterfaceRef interface) | |
dbf6a266 | 595 | { |
edebe297 A |
596 | CFMutableDictionaryRef new_if; |
597 | CFTypeRef val; | |
dbf6a266 | 598 | |
edebe297 A |
599 | new_if = CFDictionaryCreateMutable(NULL, |
600 | 0, | |
601 | &kCFTypeDictionaryKeyCallBacks, | |
602 | &kCFTypeDictionaryValueCallBacks); | |
603 | ||
d0784775 A |
604 | val = _SCNetworkInterfaceCopyInterfaceInfo(interface); |
605 | if (val != NULL) { | |
606 | CFDictionarySetValue(new_if, CFSTR(kSCNetworkInterfaceInfo), val); | |
607 | CFRelease(val); | |
608 | } | |
609 | ||
edebe297 A |
610 | val = _SCNetworkInterfaceGetIOPath(interface); |
611 | if (val != NULL) { | |
612 | CFDictionarySetValue(new_if, CFSTR(kIOPathMatchKey), val); | |
dbf6a266 A |
613 | } |
614 | ||
edebe297 A |
615 | val = _SCNetworkInterfaceGetIOInterfaceType(interface); |
616 | if (val != NULL) { | |
617 | CFDictionarySetValue(new_if, CFSTR(kIOInterfaceType), val); | |
dbf6a266 A |
618 | } |
619 | ||
edebe297 A |
620 | val = _SCNetworkInterfaceGetIOInterfaceUnit(interface); |
621 | if (val != NULL) { | |
622 | CFDictionarySetValue(new_if, CFSTR(kIOInterfaceUnit), val); | |
623 | } | |
dbf6a266 | 624 | |
edebe297 A |
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 | } | |
dbf6a266 | 639 | |
edebe297 A |
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 | } | |
dbf6a266 A |
648 | |
649 | static CFDictionaryRef | |
edebe297 | 650 | lookupInterfaceByAddress(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where) |
dbf6a266 A |
651 | { |
652 | CFDataRef addr; | |
653 | CFIndex i; | |
654 | CFIndex n; | |
655 | CFNumberRef type; | |
656 | ||
edebe297 | 657 | if (db_list == NULL) { |
dbf6a266 A |
658 | return (NULL); |
659 | } | |
edebe297 A |
660 | type = _SCNetworkInterfaceGetIOInterfaceType(interface); |
661 | addr = _SCNetworkInterfaceGetHardwareAddress(interface); | |
dbf6a266 A |
662 | if (type == NULL || addr == NULL) { |
663 | return (NULL); | |
664 | } | |
665 | ||
edebe297 | 666 | n = CFArrayGetCount(db_list); |
dbf6a266 | 667 | for (i = 0; i < n; i++) { |
dbf6a266 | 668 | CFDataRef a; |
edebe297 | 669 | CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i); |
dbf6a266 A |
670 | CFNumberRef t; |
671 | ||
dbf6a266 | 672 | t = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType)); |
edebe297 A |
673 | a = CFDictionaryGetValue(dict, CFSTR(kIOMACAddress)); |
674 | if (t == NULL || a == NULL) | |
dbf6a266 A |
675 | continue; |
676 | ||
edebe297 | 677 | if (CFEqual(type, t) && CFEqual(addr, a)) { |
dbf6a266 A |
678 | if (where) { |
679 | *where = i; | |
680 | } | |
681 | return (dict); | |
682 | } | |
683 | } | |
684 | return (NULL); | |
685 | } | |
686 | ||
687 | static CFDictionaryRef | |
edebe297 | 688 | lookupInterfaceByUnit(CFArrayRef db_list, SCNetworkInterfaceRef interface, CFIndex * where) |
dbf6a266 A |
689 | { |
690 | CFIndex i; | |
691 | CFIndex n; | |
692 | CFNumberRef type; | |
693 | CFNumberRef unit; | |
694 | ||
edebe297 | 695 | if (db_list == NULL) { |
dbf6a266 A |
696 | return (NULL); |
697 | } | |
edebe297 A |
698 | type = _SCNetworkInterfaceGetIOInterfaceType(interface); |
699 | unit = _SCNetworkInterfaceGetIOInterfaceUnit(interface); | |
dbf6a266 A |
700 | if (type == NULL || unit == NULL) { |
701 | return (NULL); | |
702 | } | |
703 | ||
edebe297 | 704 | n = CFArrayGetCount(db_list); |
dbf6a266 | 705 | for (i = 0; i < n; i++) { |
edebe297 | 706 | CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i); |
dbf6a266 A |
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 | ||
edebe297 | 716 | if (CFEqual(type, t) && CFEqual(unit, u)) { |
dbf6a266 A |
717 | if (where) |
718 | *where = i; | |
719 | return (dict); | |
720 | } | |
721 | } | |
722 | return (NULL); | |
723 | } | |
724 | ||
d0784775 A |
725 | typedef struct { |
726 | CFDictionaryRef match_info; | |
727 | CFStringRef match_type; | |
728 | CFBooleanRef match_builtin; | |
729 | CFMutableArrayRef matches; | |
730 | } matchContext, *matchContextRef; | |
731 | ||
17d3ee29 | 732 | static CF_RETURNS_RETAINED CFDictionaryRef |
d0784775 | 733 | thinInterfaceInfo(CFDictionaryRef info) |
dbf6a266 | 734 | { |
d0784775 A |
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 | } | |
dbf6a266 | 752 | |
d0784775 A |
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); | |
dbf6a266 | 772 | } |
d0784775 A |
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); | |
a40a14f8 | 961 | } |
dbf6a266 | 962 | } |
d0784775 A |
963 | |
964 | if (active) { | |
965 | match = NULL; | |
966 | } | |
dbf6a266 | 967 | } |
d0784775 A |
968 | |
969 | if (match_context.match_info != NULL) CFRelease(match_context.match_info); | |
970 | return match; | |
dbf6a266 A |
971 | } |
972 | ||
973 | static void | |
edebe297 | 974 | insertInterface(CFMutableArrayRef db_list, SCNetworkInterfaceRef interface) |
dbf6a266 A |
975 | { |
976 | CFIndex i; | |
edebe297 A |
977 | CFDictionaryRef if_dict; |
978 | CFStringRef if_name; | |
dbf6a266 A |
979 | CFNumberRef if_type; |
980 | CFNumberRef if_unit; | |
edebe297 | 981 | CFIndex n = CFArrayGetCount(db_list); |
dbf6a266 A |
982 | CFComparisonResult res; |
983 | ||
edebe297 A |
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 | ||
dbf6a266 | 997 | for (i = 0; i < n; i++) { |
edebe297 A |
998 | CFNumberRef db_type; |
999 | CFNumberRef db_unit; | |
1000 | CFDictionaryRef dict = CFArrayGetValueAtIndex(db_list, i); | |
dbf6a266 | 1001 | |
edebe297 A |
1002 | db_type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType)); |
1003 | db_unit = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceUnit)); | |
1004 | res = CFNumberCompare(if_type, db_type, NULL); | |
dbf6a266 A |
1005 | if (res == kCFCompareLessThan |
1006 | || (res == kCFCompareEqualTo | |
edebe297 | 1007 | && (CFNumberCompare(if_unit, db_unit, NULL) |
dbf6a266 | 1008 | == kCFCompareLessThan))) { |
edebe297 A |
1009 | CFArrayInsertValueAtIndex(db_list, i, if_dict); |
1010 | CFRelease(if_dict); | |
dbf6a266 A |
1011 | return; |
1012 | } | |
1013 | } | |
edebe297 | 1014 | |
dbf6a266 | 1015 | CFArrayAppendValue(S_dblist, if_dict); |
edebe297 | 1016 | CFRelease(if_dict); |
dbf6a266 A |
1017 | return; |
1018 | } | |
1019 | ||
1020 | static void | |
edebe297 | 1021 | replaceInterface(SCNetworkInterfaceRef interface) |
dbf6a266 | 1022 | { |
6bb65964 A |
1023 | int n = 0; |
1024 | CFIndex where; | |
dbf6a266 A |
1025 | |
1026 | if (S_dblist == NULL) { | |
1027 | S_dblist = CFArrayCreateMutable(NULL, 0, | |
1028 | &kCFTypeArrayCallBacks); | |
1029 | } | |
edebe297 | 1030 | // remove any dict that has our type/addr |
6bb65964 | 1031 | while (lookupInterfaceByAddress(S_dblist, interface, &where) != NULL) { |
dbf6a266 | 1032 | CFArrayRemoveValueAtIndex(S_dblist, where); |
6bb65964 | 1033 | n++; |
dbf6a266 | 1034 | } |
edebe297 | 1035 | // remove any dict that has the same type/unit |
6bb65964 | 1036 | while (lookupInterfaceByUnit(S_dblist, interface, &where) != NULL) { |
dbf6a266 | 1037 | CFArrayRemoveValueAtIndex(S_dblist, where); |
6bb65964 | 1038 | n++; |
dbf6a266 | 1039 | } |
edebe297 | 1040 | insertInterface(S_dblist, interface); |
6bb65964 A |
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 | ||
dbf6a266 A |
1053 | return; |
1054 | } | |
1055 | ||
1056 | static CFNumberRef | |
1057 | getHighestUnitForType(CFNumberRef if_type) | |
1058 | { | |
1059 | int i; | |
1060 | CFIndex n; | |
edebe297 | 1061 | CFNumberRef ret_unit = NULL; |
dbf6a266 | 1062 | |
edebe297 | 1063 | if (S_dblist == NULL) { |
dbf6a266 | 1064 | return (NULL); |
edebe297 | 1065 | } |
dbf6a266 A |
1066 | |
1067 | n = CFArrayGetCount(S_dblist); | |
1068 | for (i = 0; i < n; i++) { | |
1069 | CFDictionaryRef dict = CFArrayGetValueAtIndex(S_dblist, i); | |
1070 | CFNumberRef type; | |
dbf6a266 A |
1071 | |
1072 | type = CFDictionaryGetValue(dict, CFSTR(kIOInterfaceType)); | |
1073 | if (CFEqual(type, if_type)) { | |
edebe297 A |
1074 | CFNumberRef unit; |
1075 | ||
dbf6a266 A |
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 | } | |
edebe297 | 1084 | |
dbf6a266 A |
1085 | return (ret_unit); |
1086 | } | |
1087 | ||
17d3ee29 A |
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 | ||
af243a0d A |
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 | |
17d3ee29 | 1133 | copyInterfaceForIORegistryEntryID(uint64_t entryID) |
af243a0d A |
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 | } | |
17d3ee29 A |
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 | ||
af243a0d | 1194 | #else // USE_REGISTRY_ENTRY_ID |
edebe297 A |
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 | */ | |
dbf6a266 | 1201 | static kern_return_t |
af243a0d A |
1202 | registerInterfaceWithIOServicePath(io_connect_t connect, |
1203 | CFStringRef path, | |
1204 | CFNumberRef unit, | |
1205 | const int command) | |
dbf6a266 A |
1206 | { |
1207 | CFMutableDictionaryRef dict; | |
edebe297 A |
1208 | kern_return_t kr; |
1209 | CFNumberRef num; | |
dbf6a266 A |
1210 | |
1211 | dict = CFDictionaryCreateMutable(NULL, 0, | |
1212 | &kCFTypeDictionaryKeyCallBacks, | |
1213 | &kCFTypeDictionaryValueCallBacks); | |
edebe297 | 1214 | num = CFNumberCreate(NULL, kCFNumberIntType, &command); |
af243a0d | 1215 | CFDictionarySetValue(dict, CFSTR(kIONetworkStackUserCommandKey), num); |
edebe297 A |
1216 | CFRelease(num); |
1217 | CFDictionarySetValue(dict, CFSTR(kIOPathMatchKey), path); | |
1218 | CFDictionarySetValue(dict, CFSTR(kIOInterfaceUnit), unit); | |
1219 | kr = IOConnectSetCFProperties(connect, dict); | |
1220 | CFRelease(dict); | |
dbf6a266 A |
1221 | return kr; |
1222 | } | |
1223 | ||
edebe297 | 1224 | static SCNetworkInterfaceRef |
17d3ee29 | 1225 | copyInterfaceForIOKitPath(CFStringRef if_path) |
dbf6a266 | 1226 | { |
edebe297 A |
1227 | io_registry_entry_t entry = MACH_PORT_NULL; |
1228 | SCNetworkInterfaceRef interface = NULL; | |
dbf6a266 | 1229 | kern_return_t kr; |
edebe297 | 1230 | mach_port_t masterPort = MACH_PORT_NULL; |
dbf6a266 A |
1231 | io_string_t path; |
1232 | ||
1233 | kr = IOMasterPort(bootstrap_port, &masterPort); | |
1234 | if (kr != KERN_SUCCESS) { | |
edebe297 | 1235 | SCLog(TRUE, LOG_ERR, |
6bb65964 | 1236 | CFSTR(MY_PLUGIN_NAME ": IOMasterPort returned 0x%x"), |
dbf6a266 A |
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) { | |
edebe297 | 1243 | SCLog(TRUE, LOG_ERR, |
dbf6a266 A |
1244 | CFSTR(MY_PLUGIN_NAME ": IORegistryEntryFromPath(%@) failed"), |
1245 | if_path); | |
1246 | goto error; | |
1247 | } | |
edebe297 A |
1248 | |
1249 | interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(entry); | |
dbf6a266 A |
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 | } | |
edebe297 | 1258 | return (interface); |
dbf6a266 A |
1259 | |
1260 | } | |
17d3ee29 A |
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 | ||
af243a0d | 1271 | #endif // USE_REGISTRY_ENTRY_ID |
dbf6a266 A |
1272 | |
1273 | static void | |
edebe297 | 1274 | displayInterface(SCNetworkInterfaceRef interface) |
dbf6a266 | 1275 | { |
edebe297 | 1276 | CFStringRef addr; |
dbf6a266 A |
1277 | CFStringRef name; |
1278 | CFNumberRef type; | |
1279 | CFNumberRef unit; | |
1280 | ||
edebe297 A |
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); | |
dbf6a266 A |
1296 | } |
1297 | ||
edebe297 A |
1298 | static int |
1299 | builtinCount(CFArrayRef if_list, CFIndex last, CFNumberRef if_type) | |
dbf6a266 | 1300 | { |
edebe297 A |
1301 | CFIndex i; |
1302 | int n = 0; | |
dbf6a266 | 1303 | |
edebe297 A |
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; | |
dbf6a266 A |
1318 | } |
1319 | ||
edebe297 A |
1320 | static __inline__ boolean_t |
1321 | isQuiet(void) | |
dbf6a266 | 1322 | { |
edebe297 | 1323 | return (S_quiet == MACH_PORT_NULL); |
dbf6a266 A |
1324 | } |
1325 | ||
1326 | static void | |
edebe297 | 1327 | nameInterfaces(CFMutableArrayRef if_list) |
dbf6a266 A |
1328 | { |
1329 | CFIndex i; | |
edebe297 | 1330 | CFIndex n = CFArrayGetCount(if_list); |
dbf6a266 A |
1331 | |
1332 | for (i = 0; i < n; i++) { | |
af243a0d | 1333 | uint64_t entryID; |
edebe297 | 1334 | SCNetworkInterfaceRef interface; |
17d3ee29 | 1335 | SCNetworkInterfaceRef new_interface; |
edebe297 | 1336 | CFStringRef path; |
af243a0d | 1337 | CFStringRef str; |
edebe297 A |
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); | |
af243a0d | 1346 | entryID = _SCNetworkInterfaceGetIORegistryEntryID(interface); |
edebe297 A |
1347 | |
1348 | if (unit != NULL) { | |
dbf6a266 | 1349 | if (S_debug) { |
edebe297 A |
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 | } | |
dbf6a266 | 1358 | } |
edebe297 A |
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); | |
dbf6a266 | 1364 | } |
17d3ee29 A |
1365 | |
1366 | replaceInterface(interface); | |
edebe297 A |
1367 | } else { |
1368 | CFDictionaryRef dbdict; | |
d0784775 | 1369 | boolean_t is_builtin; |
edebe297 | 1370 | kern_return_t kr; |
af243a0d | 1371 | int retries = 0; |
edebe297 A |
1372 | |
1373 | dbdict = lookupInterfaceByAddress(S_dblist, interface, NULL); | |
dbf6a266 | 1374 | if (dbdict != NULL) { |
edebe297 | 1375 | unit = CFDictionaryGetValue(dbdict, CFSTR(kIOInterfaceUnit)); |
dbf6a266 | 1376 | CFRetain(unit); |
edebe297 A |
1377 | |
1378 | SCLog(S_debug, LOG_INFO, | |
1379 | CFSTR(MY_PLUGIN_NAME ": Interface assigned unit %@ (from database)"), | |
1380 | unit); | |
d0784775 | 1381 | } |
edebe297 | 1382 | |
d0784775 A |
1383 | if ((dbdict == NULL) && !isQuiet()) { |
1384 | // if new interface, wait until quiet before naming | |
1385 | addTimestamp(S_state, path); | |
1386 | continue; | |
1387 | } | |
edebe297 | 1388 | |
d0784775 A |
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); | |
dbf6a266 | 1404 | } |
edebe297 A |
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) { | |
edebe297 A |
1418 | int next_unit = 0; |
1419 | ||
edebe297 A |
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 | ||
dbf6a266 | 1427 | unit = getHighestUnitForType(type); |
edebe297 | 1428 | if (unit != NULL) { |
dbf6a266 A |
1429 | int high_unit; |
1430 | ||
edebe297 | 1431 | CFNumberGetValue(unit, kCFNumberIntType, &high_unit); |
dbf6a266 A |
1432 | if (high_unit >= next_unit) { |
1433 | next_unit = high_unit + 1; | |
1434 | } | |
1435 | } | |
1436 | } | |
edebe297 A |
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"); | |
dbf6a266 | 1443 | } |
edebe297 | 1444 | |
af243a0d A |
1445 | retry : |
1446 | ||
1447 | #ifdef USE_REGISTRY_ENTRY_ID | |
1448 | kr = registerInterfaceWithIORegistryEntryID(S_connect, | |
1449 | entryID, | |
1450 | unit, | |
1451 | (dbdict == NULL) ? kIONetworkStackRegisterInterfaceWithLowestUnit | |
1452 | : kIONetworkStackRegisterInterfaceWithUnit); | |
17d3ee29 | 1453 | new_interface = copyNamedInterfaceForIORegistryEntryID(entryID); |
af243a0d A |
1454 | #else // USE_REGISTRY_ENTRY_ID |
1455 | kr = registerInterfaceWithIOServicePath(S_connect, | |
1456 | path, | |
1457 | unit, | |
1458 | (dbdict == NULL) ? kRegisterInterface | |
1459 | : kRegisterInterfaceWithFixedUnit); | |
17d3ee29 | 1460 | new_interface = copyNamedInterfaceForIOKitPath(path); |
af243a0d | 1461 | #endif // USE_REGISTRY_ENTRY_ID |
17d3ee29 | 1462 | if (new_interface == NULL) { |
6bb65964 A |
1463 | const char *signature; |
1464 | ||
1465 | signature = (dbdict == NULL) ? "failed to name new interface" | |
1466 | : "failed to name known interface"; | |
1467 | ||
edebe297 | 1468 | SCLog(TRUE, LOG_ERR, |
6bb65964 | 1469 | CFSTR(MY_PLUGIN_NAME ": %s, kr=0x%x\n" |
d0784775 | 1470 | MY_PLUGIN_NAME ": path = %@\n" |
af243a0d | 1471 | MY_PLUGIN_NAME ": id = 0x%llx\n" |
d0784775 | 1472 | MY_PLUGIN_NAME ": unit = %@"), |
6bb65964 | 1473 | signature, |
d0784775 A |
1474 | kr, |
1475 | path, | |
af243a0d | 1476 | entryID, |
d0784775 | 1477 | unit); |
6bb65964 | 1478 | |
dbf6a266 | 1479 | if (S_debug) { |
edebe297 | 1480 | displayInterface(interface); |
dbf6a266 | 1481 | } |
6bb65964 A |
1482 | |
1483 | // report issue w/MessageTracer | |
af243a0d A |
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 | } | |
17d3ee29 A |
1496 | } |
1497 | else { | |
1498 | CFNumberRef new_unit; | |
dbf6a266 | 1499 | |
af243a0d A |
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 | ||
17d3ee29 A |
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 | } | |
edebe297 | 1542 | |
17d3ee29 A |
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 | |
d0784775 | 1547 | |
17d3ee29 A |
1548 | if (is_builtin && (S_prev_active_list != NULL)) { |
1549 | CFIndex where; | |
d0784775 | 1550 | |
17d3ee29 A |
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); | |
d0784775 | 1556 | } |
dbf6a266 | 1557 | } |
17d3ee29 | 1558 | replaceInterface(interface); |
dbf6a266 A |
1559 | } |
1560 | CFRelease(unit); | |
1561 | } | |
1562 | } | |
dbf6a266 A |
1563 | return; |
1564 | } | |
1565 | ||
1566 | static void | |
edebe297 | 1567 | updateInterfaces() |
dbf6a266 | 1568 | { |
edebe297 A |
1569 | if (S_connect == MACH_PORT_NULL) { |
1570 | // if we don't have the "IONetworkStack" connect object | |
1571 | return; | |
1572 | } | |
dbf6a266 | 1573 | |
edebe297 A |
1574 | if (S_iflist != NULL) { |
1575 | CFIndex n; | |
dbf6a266 | 1576 | |
edebe297 A |
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; | |
dbf6a266 | 1606 | |
edebe297 | 1607 | n = CFArrayGetCount(S_prev_active_list); |
d0784775 A |
1608 | if (n > 0) { |
1609 | SCLog(TRUE, LOG_INFO, | |
1610 | CFSTR(MY_PLUGIN_NAME ": Interface%s not [yet] active"), | |
1611 | (n > 1) ? "s" : ""); | |
1612 | } | |
edebe297 A |
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 | } | |
dbf6a266 | 1631 | } |
edebe297 A |
1632 | CFRelease(S_prev_active_list); |
1633 | S_prev_active_list = NULL; | |
dbf6a266 | 1634 | } |
edebe297 A |
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 | ||
17d3ee29 | 1653 | #if !TARGET_OS_EMBEDDED |
edebe297 A |
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 | ||
17d3ee29 A |
1676 | static CFStringRef |
1677 | copyEthernetUUID() | |
edebe297 A |
1678 | { |
1679 | CFDataRef addr; | |
1680 | CFMutableArrayRef addrs = NULL; | |
17d3ee29 | 1681 | CFStringRef guid = NULL; |
edebe297 A |
1682 | CFIndex i; |
1683 | CFIndex n; | |
edebe297 A |
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 : | |
17d3ee29 | 1730 | // if no network interfaces |
edebe297 A |
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 }; | |
edebe297 A |
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, | |
17d3ee29 | 1752 | CFSTR(MY_PLUGIN_NAME ": setting platform UUID [MAC] = %@"), |
edebe297 | 1753 | guid); |
edebe297 A |
1754 | break; |
1755 | } | |
dbf6a266 | 1756 | } |
edebe297 | 1757 | |
17d3ee29 A |
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 | ||
edebe297 A |
1808 | done : |
1809 | ||
17d3ee29 A |
1810 | if (S_vproc_transaction != NULL) { |
1811 | vproc_transaction_end(NULL, S_vproc_transaction); | |
1812 | S_vproc_transaction = NULL; | |
1813 | } | |
1814 | ||
edebe297 A |
1815 | if (platform != MACH_PORT_NULL) IOObjectRelease(platform); |
1816 | if (guid != NULL) CFRelease(guid); | |
dbf6a266 A |
1817 | return; |
1818 | } | |
17d3ee29 | 1819 | #endif // !TARGET_OS_EMBEDDED |
dbf6a266 | 1820 | |
edebe297 A |
1821 | static void |
1822 | interfaceArrivalCallback(void *refcon, io_iterator_t iter) | |
1823 | { | |
1824 | io_object_t obj; | |
dbf6a266 | 1825 | |
edebe297 A |
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) | |
dbf6a266 A |
1855 | { |
1856 | kern_return_t kr; | |
edebe297 | 1857 | io_object_t stack; |
dbf6a266 | 1858 | |
edebe297 A |
1859 | stack = IOIteratorNext(iter); |
1860 | if (stack == MACH_PORT_NULL) { | |
1861 | goto error; | |
dbf6a266 A |
1862 | } |
1863 | ||
edebe297 | 1864 | kr = IOServiceOpen(stack, mach_task_self(), 0, &S_connect); |
dbf6a266 | 1865 | if (kr != KERN_SUCCESS) { |
edebe297 A |
1866 | SCLog(TRUE, LOG_ERR, |
1867 | CFSTR(MY_PLUGIN_NAME ": IOServiceOpen returned 0x%x"), | |
dbf6a266 A |
1868 | kr); |
1869 | goto error; | |
1870 | } | |
1871 | ||
edebe297 A |
1872 | addTimestamp(S_state, CFSTR("*STACK*")); |
1873 | SCLog(S_debug, LOG_INFO, | |
1874 | CFSTR(MY_PLUGIN_NAME ": IONetworkStack found")); | |
dbf6a266 | 1875 | |
edebe297 A |
1876 | if (S_stack != MACH_PORT_NULL) { |
1877 | IOObjectRelease(S_stack); | |
1878 | S_stack = MACH_PORT_NULL; | |
1879 | } | |
1880 | ||
6bb65964 A |
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 | ||
edebe297 A |
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, | |
dbf6a266 | 1918 | CFSTR(MY_PLUGIN_NAME ": No network stack object")); |
edebe297 | 1919 | return; |
dbf6a266 | 1920 | } |
edebe297 A |
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 | ||
17d3ee29 | 1936 | #if !TARGET_OS_EMBEDDED |
edebe297 | 1937 | updatePlatformUUID(); |
17d3ee29 | 1938 | #endif // !TARGET_OS_EMBEDDED |
edebe297 A |
1939 | |
1940 | return; | |
1941 | } | |
1942 | ||
1943 | static void | |
6bb65964 | 1944 | iterateRegistryBusy(io_iterator_t iterator, CFArrayRef nodes, CFMutableStringRef snapshot, int *count) |
edebe297 A |
1945 | { |
1946 | kern_return_t kr = kIOReturnSuccess;; | |
1947 | io_object_t obj; | |
1948 | ||
1949 | while ((kr == kIOReturnSuccess) && | |
1950 | ((obj = IOIteratorNext(iterator)) != MACH_PORT_NULL)) { | |
6bb65964 A |
1951 | uint64_t accumulated_busy_time; |
1952 | uint32_t busy_state; | |
edebe297 A |
1953 | io_name_t location; |
1954 | io_name_t name; | |
1955 | CFMutableArrayRef newNodes; | |
6bb65964 | 1956 | uint64_t state; |
edebe297 A |
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, | |
6bb65964 | 1968 | CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryEntryGetName returned 0x%x"), |
edebe297 A |
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, | |
6bb65964 | 1986 | CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryEntryGetLocationInPlane returned 0x%x"), |
edebe297 | 1987 | kr); |
a40a14f8 | 1988 | CFRelease(str); |
edebe297 | 1989 | goto next; |
edebe297 A |
1990 | } |
1991 | ||
1992 | CFArrayAppendValue(newNodes, str); | |
1993 | CFRelease(str); | |
1994 | ||
6bb65964 | 1995 | kr = IOServiceGetBusyStateAndTime(obj, &state, &busy_state, &accumulated_busy_time); |
edebe297 A |
1996 | if (kr != kIOReturnSuccess) { |
1997 | SCLog(TRUE, LOG_ERR, | |
6bb65964 | 1998 | CFSTR(MY_PLUGIN_NAME ": captureBusy IOServiceGetBusyStateAndTime returned 0x%x"), |
edebe297 A |
1999 | kr); |
2000 | goto next; | |
2001 | } | |
2002 | ||
6bb65964 A |
2003 | #ifdef TEST_SNAPSHOT |
2004 | // report all nodes | |
2005 | busy_state = 1; | |
2006 | #endif // TEST_SNAPSHOT | |
2007 | ||
2008 | if (busy_state != 0) { | |
edebe297 A |
2009 | CFStringRef path; |
2010 | ||
2011 | if ((*count)++ == 0) { | |
6bb65964 | 2012 | CFStringAppend(snapshot, CFSTR("Busy services :")); |
edebe297 A |
2013 | } |
2014 | ||
2015 | path = CFStringCreateByCombiningStrings(NULL, newNodes, CFSTR("/")); | |
6bb65964 A |
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, " : "", | |
17d3ee29 | 2022 | busy_state, |
6bb65964 | 2023 | accumulated_busy_time / kMillisecondScale); |
edebe297 A |
2024 | CFRelease(path); |
2025 | } | |
2026 | ||
2027 | kr = IORegistryIteratorEnterEntry(iterator); | |
2028 | if (kr != kIOReturnSuccess) { | |
2029 | SCLog(TRUE, LOG_ERR, | |
6bb65964 | 2030 | CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryIteratorEnterEntry returned 0x%x"), |
edebe297 A |
2031 | kr); |
2032 | goto next; | |
2033 | } | |
2034 | ||
6bb65964 | 2035 | iterateRegistryBusy(iterator, newNodes, snapshot, count); |
edebe297 A |
2036 | |
2037 | kr = IORegistryIteratorExitEntry(iterator); | |
2038 | if (kr != kIOReturnSuccess) { | |
2039 | SCLog(TRUE, LOG_ERR, | |
6bb65964 | 2040 | CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryIteratorExitEntry returned 0x%x"), |
edebe297 A |
2041 | kr); |
2042 | } | |
2043 | ||
2044 | next : | |
2045 | ||
2046 | CFRelease(newNodes); | |
2047 | IOObjectRelease(obj); | |
2048 | } | |
2049 | ||
2050 | return; | |
2051 | } | |
2052 | ||
17d3ee29 | 2053 | static CF_RETURNS_RETAINED CFStringRef |
6bb65964 | 2054 | captureBusy() |
edebe297 A |
2055 | { |
2056 | int count = 0; | |
2057 | io_iterator_t iterator = MACH_PORT_NULL; | |
2058 | kern_return_t kr; | |
6bb65964 A |
2059 | CFMutableStringRef snapshot; |
2060 | ||
2061 | snapshot = CFStringCreateMutable(NULL, 0); | |
edebe297 A |
2062 | |
2063 | kr = IORegistryCreateIterator(kIOMasterPortDefault, | |
2064 | kIOServicePlane, | |
2065 | 0, | |
2066 | &iterator); | |
2067 | if (kr != kIOReturnSuccess) { | |
2068 | SCLog(TRUE, LOG_ERR, | |
6bb65964 | 2069 | CFSTR(MY_PLUGIN_NAME ": captureBusy IORegistryCreateIterator returned 0x%x"), |
edebe297 | 2070 | kr); |
6bb65964 A |
2071 | return snapshot; |
2072 | } | |
2073 | ||
2074 | iterateRegistryBusy(iterator, NULL, snapshot, &count); | |
2075 | if (count == 0) { | |
2076 | CFStringAppend(snapshot, CFSTR("w/no busy services")); | |
edebe297 A |
2077 | } |
2078 | ||
edebe297 | 2079 | IOObjectRelease(iterator); |
6bb65964 | 2080 | return snapshot; |
edebe297 A |
2081 | } |
2082 | ||
2083 | static void | |
2084 | timerCallback(CFRunLoopTimerRef timer, void *info) | |
2085 | { | |
6bb65964 A |
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! | |
edebe297 | 2090 | addTimestamp(S_state, CFSTR("*TIMEOUT*")); |
6bb65964 A |
2091 | |
2092 | // log busy nodes | |
2093 | snapshot = captureBusy(); | |
edebe297 | 2094 | SCLog(TRUE, LOG_ERR, |
6bb65964 A |
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); | |
edebe297 | 2099 | |
edebe297 A |
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; | |
edebe297 A |
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; | |
edebe297 A |
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 | } | |
dbf6a266 A |
2122 | } |
2123 | ||
edebe297 A |
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 | ||
dbf6a266 A |
2134 | // Creates and returns a notification object for receiving IOKit |
2135 | // notifications of new devices or state changes. | |
edebe297 A |
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 | } | |
dbf6a266 A |
2143 | |
2144 | S_notify = IONotificationPortCreate(masterPort); | |
2145 | if (S_notify == NULL) { | |
edebe297 | 2146 | SCLog(TRUE, LOG_ERR, |
dbf6a266 | 2147 | CFSTR(MY_PLUGIN_NAME ": IONotificationPortCreate failed")); |
edebe297 A |
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; | |
dbf6a266 | 2178 | } |
edebe297 A |
2179 | |
2180 | // add a timer so we don't wait forever for IOKit to quiesce | |
edebe297 | 2181 | S_timer = CFRunLoopTimerCreate(NULL, |
6bb65964 | 2182 | CFAbsoluteTimeGetCurrent() + S_stack_timeout, |
edebe297 A |
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 | |
dbf6a266 A |
2215 | kr = IOServiceAddMatchingNotification(S_notify, |
2216 | kIOFirstMatchNotification, | |
2217 | IOServiceMatching("IONetworkInterface"), | |
2218 | &interfaceArrivalCallback, | |
edebe297 A |
2219 | (void *)S_notify, // refCon |
2220 | &S_iter); // notification | |
dbf6a266 | 2221 | if (kr != KERN_SUCCESS) { |
edebe297 A |
2222 | SCLog(TRUE, LOG_ERR, |
2223 | CFSTR(MY_PLUGIN_NAME ": IOServiceAddMatchingNotification returned 0x%x"), | |
dbf6a266 | 2224 | kr); |
edebe297 | 2225 | goto done; |
dbf6a266 A |
2226 | } |
2227 | ||
edebe297 | 2228 | // Get the current list of matches and arm the notification for |
dbf6a266 | 2229 | // future interface arrivals. |
edebe297 | 2230 | interfaceArrivalCallback((void *)S_notify, S_iter); |
dbf6a266 | 2231 | |
edebe297 A |
2232 | // Check if IOKit has already quiesced. |
2233 | quietCallback((void *)S_notify, | |
2234 | MACH_PORT_NULL, | |
2235 | kIOMessageServiceBusyStateChange, | |
a40a14f8 | 2236 | (void *)(uintptr_t)busy); |
dbf6a266 A |
2237 | |
2238 | CFRunLoopAddSource(CFRunLoopGetCurrent(), | |
2239 | IONotificationPortGetRunLoopSource(S_notify), | |
2240 | kCFRunLoopDefaultMode); | |
edebe297 A |
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); | |
dbf6a266 A |
2265 | } |
2266 | if (masterPort != MACH_PORT_NULL) { | |
2267 | mach_port_deallocate(mach_task_self(), masterPort); | |
2268 | } | |
edebe297 A |
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; | |
dbf6a266 | 2283 | } |
edebe297 A |
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)) { | |
a40a14f8 A |
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 | } | |
edebe297 A |
2303 | } |
2304 | ||
2305 | return TRUE; | |
2306 | } | |
2307 | ||
6bb65964 A |
2308 | static void * |
2309 | exec_InterfaceNamer(void *arg) | |
edebe297 | 2310 | { |
6bb65964 A |
2311 | CFBundleRef bundle = (CFBundleRef)arg; |
2312 | CFDictionaryRef dict; | |
2313 | ||
6bb65964 | 2314 | pthread_setname_np(MY_PLUGIN_NAME " thread"); |
6bb65964 A |
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 | } | |
edebe297 A |
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; | |
dbf6a266 | 2351 | } |
edebe297 | 2352 | |
17d3ee29 A |
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 | ||
6bb65964 | 2359 | goto done; |
edebe297 A |
2360 | |
2361 | error : | |
dbf6a266 A |
2362 | if (S_connect != MACH_PORT_NULL) { |
2363 | IOServiceClose(S_connect); | |
2364 | S_connect = MACH_PORT_NULL; | |
2365 | } | |
edebe297 A |
2366 | if (S_dblist != NULL) { |
2367 | CFRelease(S_dblist); | |
2368 | S_dblist = NULL; | |
2369 | } | |
dbf6a266 A |
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 | } | |
edebe297 A |
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 | ||
6bb65964 | 2395 | done : |
6bb65964 A |
2396 | CFRelease(bundle); |
2397 | CFRunLoopRun(); | |
6bb65964 A |
2398 | |
2399 | return NULL; | |
2400 | } | |
2401 | ||
2402 | __private_extern__ | |
2403 | void | |
2404 | load_InterfaceNamer(CFBundleRef bundle, Boolean bundleVerbose) | |
2405 | { | |
17d3ee29 A |
2406 | pthread_attr_t tattr; |
2407 | pthread_t tid; | |
2408 | ||
6bb65964 A |
2409 | if (bundleVerbose) { |
2410 | S_debug = TRUE; | |
2411 | } | |
2412 | ||
17d3ee29 | 2413 | CFRetain(bundle); // released in exec_InterfaceNamer |
6bb65964 | 2414 | |
17d3ee29 A |
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); | |
6bb65964 | 2421 | |
dbf6a266 A |
2422 | return; |
2423 | } | |
2424 | ||
2425 | //------------------------------------------------------------------------ | |
2426 | // Main function. | |
2427 | #ifdef MAIN | |
2428 | int | |
2429 | main(int argc, char ** argv) | |
2430 | { | |
17d3ee29 A |
2431 | CFBundleRef bundle; |
2432 | ||
edebe297 A |
2433 | _sc_log = FALSE; |
2434 | _sc_verbose = (argc > 1) ? TRUE : FALSE; | |
2435 | ||
17d3ee29 A |
2436 | S_debug = _sc_verbose; |
2437 | ||
2438 | bundle = CFBundleGetMainBundle(); | |
2439 | CFRetain(bundle); // released in exec_InterfaceNamer | |
2440 | ||
2441 | (void)exec_InterfaceNamer(); | |
2442 | ||
dbf6a266 A |
2443 | /* not reached */ |
2444 | exit(0); | |
2445 | return 0; | |
2446 | } | |
2447 | #endif /* MAIN */ | |
edebe297 A |
2448 | |
2449 | #ifdef TEST_PLATFORM_UUID | |
2450 | int | |
2451 | main(int argc, char ** argv) | |
2452 | { | |
17d3ee29 | 2453 | CFStringRef guid; |
edebe297 A |
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 | } | |
17d3ee29 A |
2477 | |
2478 | guid = copyEthernetUUID(); | |
2479 | SCPrint(TRUE, stdout, CFSTR("copyEthernetUUID() = %@\n"), (guid != NULL) ? guid : CFSTR("NULL")); | |
2480 | if (guid != NULL) CFRelease(guid); | |
2481 | ||
edebe297 A |
2482 | updatePlatformUUID(); |
2483 | CFRelease(S_dblist); | |
2484 | exit(0); | |
2485 | return 0; | |
2486 | } | |
2487 | #endif /* TEST_PLATFORM_UUID */ | |
6bb65964 A |
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 |