]>
Commit | Line | Data |
---|---|---|
dbf6a266 | 1 | /* |
59647b27 | 2 | * Copyright (c) 2002-2007, 2011, 2013, 2015-2020 Apple Inc. All rights reserved. |
dbf6a266 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
9de8ab86 | 5 | * |
dbf6a266 A |
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. | |
9de8ab86 | 12 | * |
dbf6a266 A |
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. | |
9de8ab86 | 20 | * |
dbf6a266 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
24 | /* | |
25 | * Modification History | |
26 | * | |
27 | * October 21, 2000 Allan Nathanson <ajn@apple.com> | |
28 | * - initial revision | |
29 | */ | |
30 | ||
31 | ||
32 | #include <stdio.h> | |
33 | #include <unistd.h> | |
dbf6a266 A |
34 | #include <sys/ioctl.h> |
35 | #include <sys/socket.h> | |
36 | #include <sys/wait.h> | |
37 | #include <net/if.h> | |
38 | #include <net/if_media.h> | |
39 | ||
afb19109 | 40 | #define SC_LOG_HANDLE __log_LinkConfiguration |
1ef45fa4 | 41 | #define SC_LOG_HANDLE_TYPE static |
942cecd7 | 42 | #include "SCNetworkConfigurationInternal.h" |
dbf6a266 A |
43 | #include <SystemConfiguration/SCDPlugin.h> // for _SCDPluginExecCommand |
44 | ||
6bb65964 | 45 | |
942cecd7 A |
46 | static CFMutableDictionaryRef baseSettings = NULL; |
47 | static CFStringRef interfacesKey = NULL; | |
48 | static SCDynamicStoreRef store = NULL; | |
49 | static CFRunLoopSourceRef rls = NULL; | |
50 | static CFMutableDictionaryRef wantSettings = NULL; | |
51 | ||
52 | ||
53 | #pragma mark - | |
54 | #pragma mark Logging | |
55 | ||
56 | ||
57 | static os_log_t | |
f715d946 | 58 | __log_LinkConfiguration(void) |
942cecd7 A |
59 | { |
60 | static os_log_t log = NULL; | |
61 | ||
62 | if (log == NULL) { | |
63 | log = os_log_create("com.apple.SystemConfiguration", "LinkConfiguration"); | |
64 | } | |
dbf6a266 | 65 | |
942cecd7 A |
66 | return log; |
67 | } | |
dbf6a266 | 68 | |
dbf6a266 | 69 | |
6bb65964 A |
70 | #pragma mark - |
71 | #pragma mark Capabilities | |
72 | ||
73 | ||
74 | #define CAPABILITIES_KEY CFSTR("_CAPABILITIES_") | |
75 | ||
76 | ||
77 | __private_extern__ | |
78 | Boolean | |
79 | _SCNetworkInterfaceSetCapabilities(SCNetworkInterfaceRef interface, | |
80 | CFDictionaryRef options) | |
81 | { | |
82 | CFDictionaryRef baseOptions; | |
83 | int cap_base; | |
84 | int cap_current; | |
85 | int cap_requested; | |
86 | CFStringRef interfaceName; | |
87 | ||
88 | #ifdef SIOCSIFCAP | |
89 | struct ifreq ifr; | |
90 | int ret; | |
91 | int sock; | |
92 | #endif // SIOCSIFCAP | |
93 | ||
94 | interfaceName = SCNetworkInterfaceGetBSDName(interface); | |
95 | if (interfaceName == NULL) { | |
96 | /* if no BSD interface name */ | |
97 | return FALSE; | |
98 | } | |
99 | ||
100 | cap_current = __SCNetworkInterfaceCreateCapabilities(interface, -1, NULL); | |
101 | if (cap_current == -1) { | |
102 | /* could not get current capabilities */ | |
103 | return FALSE; | |
104 | } | |
105 | ||
106 | // get base capabilities | |
107 | cap_base = cap_current; | |
108 | baseOptions = CFDictionaryGetValue(baseSettings, interfaceName); | |
109 | if (baseOptions != NULL) { | |
110 | CFNumberRef num; | |
111 | ||
112 | num = CFDictionaryGetValue(baseOptions, CAPABILITIES_KEY); | |
113 | if (num != NULL) { | |
114 | CFNumberGetValue(num, kCFNumberIntType, &cap_base); | |
115 | } | |
116 | } | |
117 | ||
118 | cap_requested = __SCNetworkInterfaceCreateCapabilities(interface, cap_base, options); | |
119 | ||
120 | #ifdef SIOCSIFCAP | |
121 | if (cap_requested == cap_current) { | |
122 | /* if current setting is as requested */ | |
123 | return TRUE; | |
124 | } | |
125 | ||
afb19109 | 126 | memset((char *)&ifr, 0, sizeof(ifr)); |
6bb65964 A |
127 | (void)_SC_cfstring_to_cstring(interfaceName, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII); |
128 | ifr.ifr_curcap = cap_current; | |
129 | ifr.ifr_reqcap = cap_requested; | |
130 | ||
131 | sock = socket(AF_INET, SOCK_DGRAM, 0); | |
132 | if (sock == -1) { | |
9de8ab86 | 133 | SC_log(LOG_ERR, "socket() failed: %s", strerror(errno)); |
6bb65964 A |
134 | return FALSE; |
135 | } | |
136 | ||
137 | ret = ioctl(sock, SIOCSIFCAP, (caddr_t)&ifr); | |
138 | (void)close(sock); | |
139 | if (ret == -1) { | |
1ef45fa4 | 140 | SC_log(LOG_ERR, "%@: ioctl(SIOCSIFCAP) failed: %s", interfaceName, strerror(errno)); |
6bb65964 A |
141 | return FALSE; |
142 | } | |
143 | #endif // SIOCSIFCAP | |
144 | ||
145 | return TRUE; | |
146 | } | |
147 | ||
148 | ||
149 | #pragma mark - | |
150 | #pragma mark Media options | |
d6c893b2 A |
151 | |
152 | ||
153 | static CFDictionaryRef | |
154 | __copyMediaOptions(CFDictionaryRef options) | |
155 | { | |
156 | CFMutableDictionaryRef requested = NULL; | |
157 | CFTypeRef val; | |
158 | ||
159 | if (!isA_CFDictionary(options)) { | |
160 | return NULL; | |
161 | } | |
162 | ||
163 | val = CFDictionaryGetValue(options, kSCPropNetEthernetMediaSubType); | |
164 | if (isA_CFString(val)) { | |
165 | requested = CFDictionaryCreateMutable(NULL, | |
166 | 0, | |
167 | &kCFTypeDictionaryKeyCallBacks, | |
168 | &kCFTypeDictionaryValueCallBacks); | |
169 | CFDictionaryAddValue(requested, kSCPropNetEthernetMediaSubType, val); | |
170 | } else { | |
171 | /* if garbage */; | |
172 | return NULL; | |
173 | } | |
174 | ||
175 | val = CFDictionaryGetValue(options, kSCPropNetEthernetMediaOptions); | |
176 | if (isA_CFArray(val)) { | |
177 | CFDictionaryAddValue(requested, kSCPropNetEthernetMediaOptions, val); | |
178 | } else { | |
179 | /* if garbage */; | |
180 | CFRelease(requested); | |
181 | return NULL; | |
182 | } | |
183 | ||
184 | return requested; | |
185 | } | |
dbf6a266 A |
186 | |
187 | ||
188 | __private_extern__ | |
189 | Boolean | |
edebe297 A |
190 | _SCNetworkInterfaceSetMediaOptions(SCNetworkInterfaceRef interface, |
191 | CFDictionaryRef options) | |
dbf6a266 A |
192 | { |
193 | CFArrayRef available = NULL; | |
194 | CFDictionaryRef current = NULL; | |
195 | struct ifmediareq ifm; | |
196 | struct ifreq ifr; | |
edebe297 | 197 | CFStringRef interfaceName; |
dbf6a266 A |
198 | Boolean ok = FALSE; |
199 | int newOptions; | |
d6c893b2 | 200 | CFDictionaryRef requested; |
dbf6a266 | 201 | int sock = -1; |
dbf6a266 | 202 | |
6bb65964 A |
203 | if (!isA_SCNetworkInterface(interface)) { |
204 | _SCErrorSet(kSCStatusInvalidArgument); | |
205 | return FALSE; | |
206 | } | |
207 | ||
edebe297 A |
208 | interfaceName = SCNetworkInterfaceGetBSDName(interface); |
209 | if (interfaceName == NULL) { | |
210 | /* if no BSD interface name */ | |
9de8ab86 | 211 | SC_log(LOG_INFO, "no BSD interface name for %@", interface); |
6bb65964 | 212 | _SCErrorSet(kSCStatusInvalidArgument); |
edebe297 A |
213 | return FALSE; |
214 | } | |
215 | ||
dbf6a266 | 216 | /* get current & available options */ |
edebe297 | 217 | if (!SCNetworkInterfaceCopyMediaOptions(interface, ¤t, NULL, &available, FALSE)) { |
d6c893b2 | 218 | /* could not get current media options */ |
9de8ab86 | 219 | SC_log(LOG_INFO, "no media options for %@", interfaceName); |
dbf6a266 A |
220 | return FALSE; |
221 | } | |
222 | ||
223 | /* extract just the dictionary key/value pairs of interest */ | |
d6c893b2 A |
224 | requested = __copyMediaOptions(options); |
225 | if (requested == NULL) { | |
226 | CFDictionaryRef baseOptions; | |
dbf6a266 | 227 | |
d6c893b2 | 228 | /* get base options */ |
edebe297 | 229 | baseOptions = CFDictionaryGetValue(baseSettings, interfaceName); |
d6c893b2 | 230 | requested = __copyMediaOptions(baseOptions); |
dbf6a266 | 231 | } |
d6c893b2 A |
232 | if (requested == NULL) { |
233 | /* get base options */ | |
234 | requested = __copyMediaOptions(current); | |
dbf6a266 | 235 | } |
d6c893b2 A |
236 | if (requested == NULL) { |
237 | /* if no media options to set */ | |
dbf6a266 A |
238 | goto done; |
239 | } | |
240 | ||
d6c893b2 | 241 | if ((current != NULL) && CFEqual(current, requested)) { |
dbf6a266 A |
242 | /* if current settings are as requested */ |
243 | ok = TRUE; | |
244 | goto done; | |
245 | } | |
246 | ||
247 | if (!CFArrayContainsValue(available, CFRangeMake(0, CFArrayGetCount(available)), requested)) { | |
248 | /* if requested settings not currently available */ | |
9de8ab86 | 249 | SC_log(LOG_INFO, "requested media settings unavailable for %@", interfaceName); |
dbf6a266 A |
250 | goto done; |
251 | } | |
252 | ||
6bb65964 | 253 | newOptions = __SCNetworkInterfaceCreateMediaOptions(interface, requested); |
dbf6a266 A |
254 | if (newOptions == -1) { |
255 | /* since we have just validated, this should never happen */ | |
256 | goto done; | |
257 | } | |
258 | ||
259 | sock = socket(AF_INET, SOCK_DGRAM, 0); | |
edebe297 | 260 | if (sock == -1) { |
9de8ab86 | 261 | SC_log(LOG_ERR, "socket() failed: %s", strerror(errno)); |
dbf6a266 A |
262 | goto done; |
263 | } | |
264 | ||
afb19109 | 265 | memset((char *)&ifm, 0, sizeof(ifm)); |
edebe297 | 266 | (void)_SC_cfstring_to_cstring(interfaceName, ifm.ifm_name, sizeof(ifm.ifm_name), kCFStringEncodingASCII); |
dbf6a266 | 267 | |
965d9e25 A |
268 | if (ioctl(sock, SIOCGIFXMEDIA, (caddr_t)&ifm) == -1) { |
269 | SC_log(LOG_ERR, "%@: ioctl(SIOCGIFXMEDIA) failed: %s", interfaceName, strerror(errno)); | |
dbf6a266 A |
270 | goto done; |
271 | } | |
272 | ||
afb19109 A |
273 | memset((char *)&ifr, 0, sizeof(ifr)); |
274 | memcpy(ifr.ifr_name, ifm.ifm_name, sizeof(ifr.ifr_name)); | |
dbf6a266 A |
275 | ifr.ifr_media = ifm.ifm_current & ~(IFM_NMASK|IFM_TMASK|IFM_OMASK|IFM_GMASK); |
276 | ifr.ifr_media |= newOptions; | |
277 | ||
9de8ab86 A |
278 | SC_log(LOG_INFO, "old media settings: 0x%8.8x (0x%8.8x)", ifm.ifm_current, ifm.ifm_active); |
279 | SC_log(LOG_INFO, "new media settings: 0x%8.8x", ifr.ifr_media); | |
dbf6a266 | 280 | |
edebe297 | 281 | if (ioctl(sock, SIOCSIFMEDIA, (caddr_t)&ifr) == -1) { |
1ef45fa4 | 282 | SC_log(LOG_ERR, "%@: ioctl(SIOCSIFMEDIA) failed: %s", interfaceName, strerror(errno)); |
dbf6a266 A |
283 | goto done; |
284 | } | |
285 | ||
286 | ok = TRUE; | |
287 | ||
288 | done : | |
289 | ||
edebe297 A |
290 | if (available != NULL) CFRelease(available); |
291 | if (current != NULL) CFRelease(current); | |
292 | if (requested != NULL) CFRelease(requested); | |
293 | if (sock != -1) (void)close(sock); | |
dbf6a266 A |
294 | |
295 | return ok; | |
296 | } | |
297 | ||
298 | ||
6bb65964 A |
299 | #pragma mark - |
300 | #pragma mark MTU | |
301 | ||
302 | ||
dbf6a266 A |
303 | #ifndef USE_SIOCSIFMTU |
304 | static void | |
305 | ifconfig_exit(pid_t pid, int status, struct rusage *rusage, void *context) | |
306 | { | |
1ef45fa4 A |
307 | #pragma unused(pid) |
308 | #pragma unused(rusage) | |
dbf6a266 A |
309 | char *if_name = (char *)context; |
310 | ||
311 | if (WIFEXITED(status)) { | |
312 | if (WEXITSTATUS(status) != 0) { | |
9de8ab86 A |
313 | SC_log(LOG_NOTICE, "ifconfig %s failed, exit status = %d", |
314 | if_name, | |
315 | WEXITSTATUS(status)); | |
dbf6a266 A |
316 | } |
317 | } else if (WIFSIGNALED(status)) { | |
9de8ab86 A |
318 | SC_log(LOG_NOTICE, "ifconfig %s: terminated w/signal = %d", |
319 | if_name, | |
320 | WTERMSIG(status)); | |
dbf6a266 | 321 | } else { |
9de8ab86 A |
322 | SC_log(LOG_NOTICE, "ifconfig %s: exit status = %d", |
323 | if_name, | |
324 | status); | |
dbf6a266 A |
325 | } |
326 | ||
327 | CFAllocatorDeallocate(NULL, if_name); | |
328 | return; | |
329 | } | |
330 | #endif /* !USE_SIOCSIFMTU */ | |
331 | ||
332 | ||
333 | __private_extern__ | |
334 | Boolean | |
edebe297 A |
335 | _SCNetworkInterfaceSetMTU(SCNetworkInterfaceRef interface, |
336 | CFDictionaryRef options) | |
dbf6a266 | 337 | { |
942cecd7 A |
338 | CFArrayRef bridge_members = NULL; |
339 | Boolean bridge_updated = FALSE; | |
340 | CFStringRef interfaceName; | |
341 | SCNetworkInterfacePrivateRef interfacePrivate = (SCNetworkInterfacePrivateRef)interface; | |
342 | CFStringRef interfaceType; | |
343 | int mtu_cur = -1; | |
344 | int mtu_max = -1; | |
345 | int mtu_min = -1; | |
346 | Boolean ok = TRUE; | |
347 | int requested; | |
348 | CFNumberRef val; | |
dbf6a266 | 349 | |
edebe297 A |
350 | interfaceName = SCNetworkInterfaceGetBSDName(interface); |
351 | if (interfaceName == NULL) { | |
352 | /* if no BSD interface name */ | |
353 | return FALSE; | |
354 | } | |
355 | ||
356 | if (!SCNetworkInterfaceCopyMTU(interface, &mtu_cur, &mtu_min, &mtu_max)) { | |
dbf6a266 A |
357 | /* could not get current MTU */ |
358 | return FALSE; | |
359 | } | |
360 | ||
d6c893b2 A |
361 | val = NULL; |
362 | if (isA_CFDictionary(options)) { | |
363 | val = CFDictionaryGetValue(options, kSCPropNetEthernetMTU); | |
364 | val = isA_CFNumber(val); | |
365 | } | |
366 | if (val == NULL) { | |
367 | CFDictionaryRef baseOptions; | |
368 | ||
369 | /* get base MTU */ | |
edebe297 | 370 | baseOptions = CFDictionaryGetValue(baseSettings, interfaceName); |
d6c893b2 A |
371 | if (baseOptions != NULL) { |
372 | val = CFDictionaryGetValue(baseOptions, kSCPropNetEthernetMTU); | |
dbf6a266 | 373 | } |
d6c893b2 A |
374 | } |
375 | if (val != NULL) { | |
376 | CFNumberGetValue(val, kCFNumberIntType, &requested); | |
dbf6a266 A |
377 | } else { |
378 | requested = mtu_cur; | |
379 | } | |
380 | ||
381 | if (requested == mtu_cur) { | |
382 | /* if current setting is as requested */ | |
383 | return TRUE; | |
384 | } | |
385 | ||
386 | if (((mtu_min >= 0) && (requested < mtu_min)) || | |
387 | ((mtu_max >= 0) && (requested > mtu_max))) { | |
388 | /* if requested MTU outside of the valid range */ | |
389 | return FALSE; | |
390 | } | |
391 | ||
942cecd7 A |
392 | interfaceType = SCNetworkInterfaceGetInterfaceType(interface); |
393 | if (CFEqual(interfaceType, kSCNetworkInterfaceTypeBridge)) { | |
394 | bridge_members = SCBridgeInterfaceGetMemberInterfaces(interface); | |
395 | if ((bridge_members != NULL) && (CFArrayGetCount(bridge_members) == 0)) { | |
396 | /* if no members */ | |
397 | bridge_members = NULL; | |
398 | } | |
399 | if (bridge_members != NULL) { | |
400 | SCNetworkInterfaceRef member0; | |
401 | ||
402 | /* temporarily, remove all bridge members */ | |
403 | CFRetain(bridge_members); | |
404 | ok = SCBridgeInterfaceSetMemberInterfaces(interface, NULL); | |
405 | if (!ok) { | |
406 | goto done; | |
407 | } | |
408 | ||
409 | /* and update the (bridge) configuration */ | |
410 | ok = _SCBridgeInterfaceUpdateConfiguration(interfacePrivate->prefs); | |
411 | if (!ok) { | |
412 | goto done; | |
413 | } | |
414 | ||
415 | /* switch from updating the MTU of the bridge to the first member */ | |
416 | member0 = CFArrayGetValueAtIndex(bridge_members, 0); | |
417 | interfaceName = SCNetworkInterfaceGetBSDName(member0); | |
418 | ||
419 | bridge_updated = TRUE; | |
420 | } | |
421 | } | |
422 | ||
dbf6a266 A |
423 | #ifdef USE_SIOCSIFMTU |
424 | { | |
425 | struct ifreq ifr; | |
426 | int ret; | |
427 | int sock; | |
428 | ||
afb19109 | 429 | memset((char *)&ifr, 0, sizeof(ifr)); |
edebe297 | 430 | (void)_SC_cfstring_to_cstring(interfaceName, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII); |
dbf6a266 A |
431 | ifr.ifr_mtu = requested; |
432 | ||
433 | sock = socket(AF_INET, SOCK_DGRAM, 0); | |
edebe297 | 434 | if (sock == -1) { |
9de8ab86 | 435 | SC_log(LOG_ERR, "socket() failed: %s", strerror(errno)); |
942cecd7 A |
436 | ok = FALSE; |
437 | goto done; | |
dbf6a266 A |
438 | } |
439 | ||
440 | ret = ioctl(sock, SIOCSIFMTU, (caddr_t)&ifr); | |
441 | (void)close(sock); | |
442 | if (ret == -1) { | |
1ef45fa4 | 443 | SC_log(LOG_ERR, "%@: ioctl(SIOCSIFMTU) failed: %s", interfaceName, strerror(errno)); |
942cecd7 A |
444 | ok = FALSE; |
445 | goto done; | |
dbf6a266 A |
446 | } |
447 | } | |
448 | #else /* !USE_SIOCSIFMTU */ | |
449 | { | |
450 | char *ifconfig_argv[] = { "ifconfig", NULL, "mtu", NULL, NULL }; | |
451 | pid_t pid; | |
452 | ||
edebe297 | 453 | ifconfig_argv[1] = _SC_cfstring_to_cstring(interfaceName, NULL, 0, kCFStringEncodingASCII); |
dbf6a266 A |
454 | (void)asprintf(&ifconfig_argv[3], "%d", requested); |
455 | ||
456 | pid = _SCDPluginExecCommand(ifconfig_exit, // callout, | |
457 | ifconfig_argv[1], // context | |
458 | 0, // uid | |
459 | 0, // gid | |
460 | "/sbin/ifconfig", // path | |
461 | ifconfig_argv // argv | |
462 | ); | |
463 | ||
464 | // CFAllocatorDeallocate(NULL, ifconfig_argv[1]); // released in ifconfig_exit() | |
465 | free(ifconfig_argv[3]); | |
466 | ||
467 | if (pid <= 0) { | |
942cecd7 A |
468 | ok = FALSE; |
469 | goto done; | |
dbf6a266 A |
470 | } |
471 | } | |
472 | #endif /* !USE_SIOCSIFMTU */ | |
473 | ||
942cecd7 A |
474 | done : |
475 | ||
476 | if (bridge_members != NULL) { | |
477 | /* restore bridge members */ | |
478 | (void) SCBridgeInterfaceSetMemberInterfaces(interface, bridge_members); | |
479 | CFRelease(bridge_members); | |
480 | ||
481 | if (bridge_updated) { | |
482 | /* and update the (bridge) configuration */ | |
483 | (void) _SCBridgeInterfaceUpdateConfiguration(interfacePrivate->prefs); | |
484 | } | |
485 | } | |
486 | ||
487 | return ok; | |
dbf6a266 A |
488 | } |
489 | ||
490 | ||
6bb65964 A |
491 | #pragma mark - |
492 | #pragma mark Update link configuration | |
493 | ||
494 | ||
dbf6a266 A |
495 | /* |
496 | * Function: parse_component | |
497 | * Purpose: | |
498 | * Given a string 'key' and a string prefix 'prefix', | |
499 | * return the next component in the slash '/' separated | |
500 | * key. | |
501 | * | |
502 | * Examples: | |
503 | * 1. key = "a/b/c" prefix = "a/" | |
504 | * returns "b" | |
505 | * 2. key = "a/b/c" prefix = "a/b/" | |
506 | * returns "c" | |
507 | */ | |
17d3ee29 | 508 | static CF_RETURNS_RETAINED CFStringRef |
dbf6a266 A |
509 | parse_component(CFStringRef key, CFStringRef prefix) |
510 | { | |
511 | CFMutableStringRef comp; | |
512 | CFRange range; | |
513 | ||
942cecd7 | 514 | if (!CFStringHasPrefix(key, prefix)) { |
dbf6a266 A |
515 | return NULL; |
516 | } | |
517 | comp = CFStringCreateMutableCopy(NULL, 0, key); | |
518 | CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix))); | |
519 | range = CFStringFind(comp, CFSTR("/"), 0); | |
520 | if (range.location == kCFNotFound) { | |
521 | return comp; | |
522 | } | |
523 | range.length = CFStringGetLength(comp) - range.location; | |
524 | CFStringDelete(comp, range); | |
525 | return comp; | |
526 | } | |
527 | ||
528 | ||
edebe297 A |
529 | static void updateLink(CFStringRef interfaceName, CFDictionaryRef options); |
530 | ||
531 | ||
dbf6a266 | 532 | static void |
edebe297 | 533 | updateInterfaces(CFArrayRef newInterfaces) |
dbf6a266 | 534 | { |
edebe297 A |
535 | CFIndex i; |
536 | CFIndex n_old; | |
537 | CFIndex n_new; | |
538 | static CFArrayRef oldInterfaces = NULL; | |
dbf6a266 | 539 | |
edebe297 A |
540 | n_old = (oldInterfaces != NULL) ? CFArrayGetCount(oldInterfaces) : 0; |
541 | n_new = CFArrayGetCount(newInterfaces); | |
542 | ||
543 | for (i = 0; i < n_new; i++) { | |
544 | CFStringRef interfaceName; | |
545 | ||
546 | interfaceName = CFArrayGetValueAtIndex(newInterfaces, i); | |
547 | ||
548 | if ((n_old == 0) || | |
549 | !CFArrayContainsValue(oldInterfaces, | |
550 | CFRangeMake(0, n_old), | |
551 | interfaceName)) { | |
552 | CFDictionaryRef options; | |
553 | ||
554 | // if new interface | |
555 | options = CFDictionaryGetValue(wantSettings, interfaceName); | |
556 | updateLink(interfaceName, options); | |
557 | } | |
dbf6a266 A |
558 | } |
559 | ||
edebe297 A |
560 | if (oldInterfaces != NULL) CFRelease(oldInterfaces); |
561 | oldInterfaces = CFRetain(newInterfaces); | |
562 | } | |
563 | ||
564 | ||
565 | static void | |
566 | updateLink(CFStringRef interfaceName, CFDictionaryRef options) | |
567 | { | |
568 | SCNetworkInterfaceRef interface; | |
569 | ||
570 | /* retain requested configuration */ | |
571 | if (options != NULL) { | |
572 | CFDictionarySetValue(wantSettings, interfaceName, options); | |
573 | } else { | |
574 | CFDictionaryRemoveValue(wantSettings, interfaceName); | |
575 | } | |
576 | ||
577 | /* apply requested configuration */ | |
578 | interface = _SCNetworkInterfaceCreateWithBSDName(NULL, interfaceName, | |
579 | kIncludeAllVirtualInterfaces); | |
580 | if (interface == NULL) { | |
581 | return; | |
dbf6a266 A |
582 | } |
583 | ||
edebe297 A |
584 | if (options != NULL) { |
585 | if (!CFDictionaryContainsKey(baseSettings, interfaceName)) { | |
6bb65964 | 586 | int cur_cap = -1; |
dbf6a266 A |
587 | CFDictionaryRef cur_media = NULL; |
588 | CFMutableDictionaryRef new_media = NULL; | |
589 | int cur_mtu = -1; | |
dbf6a266 | 590 | |
d6c893b2 | 591 | /* preserve current media options */ |
edebe297 | 592 | if (SCNetworkInterfaceCopyMediaOptions(interface, &cur_media, NULL, NULL, FALSE)) { |
d6c893b2 A |
593 | if (cur_media != NULL) { |
594 | new_media = CFDictionaryCreateMutableCopy(NULL, 0, cur_media); | |
595 | CFRelease(cur_media); | |
596 | } | |
dbf6a266 A |
597 | } |
598 | ||
d6c893b2 | 599 | /* preserve current MTU */ |
edebe297 | 600 | if (SCNetworkInterfaceCopyMTU(interface, &cur_mtu, NULL, NULL)) { |
d6c893b2 A |
601 | if (cur_mtu != -1) { |
602 | CFNumberRef num; | |
603 | ||
604 | if (new_media == NULL) { | |
605 | new_media = CFDictionaryCreateMutable(NULL, | |
606 | 0, | |
607 | &kCFTypeDictionaryKeyCallBacks, | |
608 | &kCFTypeDictionaryValueCallBacks); | |
609 | } | |
610 | ||
611 | num = CFNumberCreate(NULL, kCFNumberIntType, &cur_mtu); | |
612 | CFDictionaryAddValue(new_media, kSCPropNetEthernetMTU, num); | |
613 | CFRelease(num); | |
614 | } | |
dbf6a266 A |
615 | } |
616 | ||
6bb65964 A |
617 | /* preserve capabilities */ |
618 | cur_cap = __SCNetworkInterfaceCreateCapabilities(interface, -1, NULL); | |
619 | if (cur_cap != -1) { | |
620 | CFNumberRef num; | |
621 | ||
622 | if (new_media == NULL) { | |
623 | new_media = CFDictionaryCreateMutable(NULL, | |
624 | 0, | |
625 | &kCFTypeDictionaryKeyCallBacks, | |
626 | &kCFTypeDictionaryValueCallBacks); | |
627 | } | |
628 | ||
629 | num = CFNumberCreate(NULL, kCFNumberIntType, &cur_cap); | |
630 | CFDictionaryAddValue(new_media, CAPABILITIES_KEY, num); | |
631 | CFRelease(num); | |
632 | } | |
633 | ||
d6c893b2 | 634 | if (new_media != NULL) { |
edebe297 | 635 | CFDictionarySetValue(baseSettings, interfaceName, new_media); |
d6c893b2 | 636 | CFRelease(new_media); |
dbf6a266 | 637 | } |
dbf6a266 A |
638 | } |
639 | ||
640 | /* establish new settings */ | |
6bb65964 | 641 | (void)_SCNetworkInterfaceSetCapabilities(interface, options); |
edebe297 A |
642 | (void)_SCNetworkInterfaceSetMediaOptions(interface, options); |
643 | (void)_SCNetworkInterfaceSetMTU (interface, options); | |
dbf6a266 A |
644 | } else { |
645 | /* no requested settings */ | |
edebe297 A |
646 | options = CFDictionaryGetValue(baseSettings, interfaceName); |
647 | if (options != NULL) { | |
dbf6a266 | 648 | /* restore original settings */ |
6bb65964 | 649 | (void)_SCNetworkInterfaceSetCapabilities(interface, options); |
edebe297 A |
650 | (void)_SCNetworkInterfaceSetMediaOptions(interface, options); |
651 | (void)_SCNetworkInterfaceSetMTU (interface, options); | |
652 | CFDictionaryRemoveValue(baseSettings, interfaceName); | |
dbf6a266 A |
653 | } |
654 | } | |
655 | ||
edebe297 | 656 | CFRelease(interface); |
dbf6a266 A |
657 | return; |
658 | } | |
659 | ||
660 | ||
661 | static void | |
662 | linkConfigChangedCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg) | |
663 | { | |
1ef45fa4 | 664 | #pragma unused(arg) |
edebe297 | 665 | CFDictionaryRef changes; |
dbf6a266 A |
666 | CFIndex i; |
667 | CFIndex n; | |
edebe297 A |
668 | static CFStringRef prefix = NULL; |
669 | ||
670 | if (prefix == NULL) { | |
671 | prefix = SCDynamicStoreKeyCreate(NULL, | |
672 | CFSTR("%@/%@/%@/"), | |
673 | kSCDynamicStoreDomainSetup, | |
674 | kSCCompNetwork, | |
675 | kSCCompInterface); | |
676 | } | |
dbf6a266 | 677 | |
edebe297 | 678 | changes = SCDynamicStoreCopyMultiple(store, changedKeys, NULL); |
dbf6a266 | 679 | |
9de8ab86 | 680 | n = (changes != NULL) ? CFArrayGetCount(changedKeys) : 0; |
dbf6a266 A |
681 | for (i = 0; i < n; i++) { |
682 | CFStringRef key; | |
edebe297 | 683 | CFDictionaryRef info; |
dbf6a266 A |
684 | |
685 | key = CFArrayGetValueAtIndex(changedKeys, i); | |
edebe297 A |
686 | info = CFDictionaryGetValue(changes, key); |
687 | ||
688 | if (CFEqual(key, interfacesKey)) { | |
9de8ab86 A |
689 | if (isA_CFDictionary(info) != NULL) { |
690 | CFArrayRef interfaces; | |
edebe297 | 691 | |
9de8ab86 A |
692 | interfaces = CFDictionaryGetValue(info, kSCPropNetInterfaces); |
693 | if (isA_CFArray(interfaces)) { | |
694 | updateInterfaces(interfaces); | |
695 | } | |
edebe297 A |
696 | } |
697 | } else { | |
698 | CFStringRef interfaceName; | |
699 | ||
700 | interfaceName = parse_component(key, prefix); | |
701 | if (interfaceName != NULL) { | |
702 | updateLink(interfaceName, info); | |
703 | CFRelease(interfaceName); | |
704 | } | |
705 | } | |
dbf6a266 A |
706 | } |
707 | ||
9de8ab86 A |
708 | if (changes != NULL) { |
709 | CFRelease(changes); | |
710 | } | |
711 | ||
dbf6a266 A |
712 | return; |
713 | } | |
714 | ||
715 | ||
716 | __private_extern__ | |
717 | void | |
718 | load_LinkConfiguration(CFBundleRef bundle, Boolean bundleVerbose) | |
719 | { | |
1ef45fa4 | 720 | #pragma unused(bundleVerbose) |
dbf6a266 | 721 | CFStringRef key; |
edebe297 A |
722 | CFMutableArrayRef keys = NULL; |
723 | Boolean ok; | |
dbf6a266 A |
724 | CFMutableArrayRef patterns = NULL; |
725 | ||
9de8ab86 A |
726 | SC_log(LOG_DEBUG, "load() called"); |
727 | SC_log(LOG_DEBUG, " bundle ID = %@", CFBundleGetIdentifier(bundle)); | |
dbf6a266 A |
728 | |
729 | /* initialize a few globals */ | |
730 | ||
731 | baseSettings = CFDictionaryCreateMutable(NULL, | |
732 | 0, | |
733 | &kCFTypeDictionaryKeyCallBacks, | |
734 | &kCFTypeDictionaryValueCallBacks); | |
edebe297 A |
735 | wantSettings = CFDictionaryCreateMutable(NULL, |
736 | 0, | |
737 | &kCFTypeDictionaryKeyCallBacks, | |
738 | &kCFTypeDictionaryValueCallBacks); | |
dbf6a266 A |
739 | |
740 | /* open a "configd" store to allow cache updates */ | |
741 | store = SCDynamicStoreCreate(NULL, | |
742 | CFSTR("Link Configuraton plug-in"), | |
743 | linkConfigChangedCallback, | |
744 | NULL); | |
edebe297 | 745 | if (store == NULL) { |
9de8ab86 | 746 | SC_log(LOG_ERR, "SCDynamicStoreCreate() failed: %s", SCErrorString(SCError())); |
dbf6a266 A |
747 | goto error; |
748 | } | |
749 | ||
750 | /* establish notification keys and patterns */ | |
edebe297 | 751 | keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
dbf6a266 A |
752 | patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
753 | ||
edebe297 A |
754 | /* ...watch for a change in the list of network interfaces */ |
755 | interfacesKey = SCDynamicStoreKeyCreateNetworkInterface(NULL, | |
756 | kSCDynamicStoreDomainState); | |
757 | CFArrayAppendValue(keys, interfacesKey); | |
758 | ||
d6c893b2 A |
759 | /* ...watch for (per-interface) AirPort configuration changes */ |
760 | key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, | |
761 | kSCDynamicStoreDomainSetup, | |
762 | kSCCompAnyRegex, | |
763 | kSCEntNetAirPort); | |
764 | CFArrayAppendValue(patterns, key); | |
765 | CFRelease(key); | |
766 | ||
dbf6a266 A |
767 | /* ...watch for (per-interface) Ethernet configuration changes */ |
768 | key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, | |
769 | kSCDynamicStoreDomainSetup, | |
770 | kSCCompAnyRegex, | |
771 | kSCEntNetEthernet); | |
772 | CFArrayAppendValue(patterns, key); | |
773 | CFRelease(key); | |
774 | ||
f715d946 | 775 | #if TARGET_OS_OSX |
d6c893b2 A |
776 | /* ...watch for (per-interface) FireWire configuration changes */ |
777 | key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, | |
778 | kSCDynamicStoreDomainSetup, | |
779 | kSCCompAnyRegex, | |
780 | kSCEntNetFireWire); | |
781 | CFArrayAppendValue(patterns, key); | |
782 | CFRelease(key); | |
f715d946 | 783 | #endif // TARGET_OS_OSX |
d6c893b2 | 784 | |
dbf6a266 | 785 | /* register the keys/patterns */ |
edebe297 A |
786 | ok = SCDynamicStoreSetNotificationKeys(store, keys, patterns); |
787 | CFRelease(keys); | |
788 | CFRelease(patterns); | |
789 | if (!ok) { | |
9de8ab86 A |
790 | SC_log(LOG_NOTICE, "SCDynamicStoreSetNotificationKeys() failed: %s", |
791 | SCErrorString(SCError())); | |
dbf6a266 A |
792 | goto error; |
793 | } | |
794 | ||
795 | rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); | |
edebe297 | 796 | if (rls == NULL) { |
9de8ab86 A |
797 | SC_log(LOG_NOTICE, "SCDynamicStoreCreateRunLoopSource() failed: %s", |
798 | SCErrorString(SCError())); | |
dbf6a266 A |
799 | goto error; |
800 | } | |
801 | ||
802 | CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); | |
dbf6a266 A |
803 | return; |
804 | ||
805 | error : | |
806 | ||
edebe297 A |
807 | if (baseSettings != NULL) CFRelease(baseSettings); |
808 | if (wantSettings != NULL) CFRelease(wantSettings); | |
809 | if (store != NULL) CFRelease(store); | |
dbf6a266 A |
810 | return; |
811 | } | |
812 | ||
813 | ||
814 | #ifdef MAIN | |
6bb65964 A |
815 | |
816 | ||
817 | #pragma mark - | |
818 | #pragma mark Standalone test code | |
819 | ||
820 | ||
dbf6a266 A |
821 | int |
822 | main(int argc, char **argv) | |
823 | { | |
6bb65964 A |
824 | SCPreferencesRef prefs; |
825 | ||
59647b27 | 826 | _sc_log = kSCLogDestinationFile; |
dbf6a266 A |
827 | _sc_verbose = (argc > 1) ? TRUE : FALSE; |
828 | ||
6bb65964 A |
829 | prefs = SCPreferencesCreate(NULL, CFSTR("linkconfig"), NULL); |
830 | if (prefs != NULL) { | |
831 | SCNetworkSetRef set; | |
832 | ||
833 | set = SCNetworkSetCopyCurrent(prefs); | |
834 | if (set != NULL) { | |
835 | CFMutableSetRef seen; | |
836 | CFArrayRef services; | |
837 | ||
838 | services = SCNetworkSetCopyServices(set); | |
839 | if (services != NULL) { | |
840 | CFIndex i; | |
841 | CFIndex n; | |
842 | ||
843 | seen = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); | |
844 | ||
845 | n = CFArrayGetCount(services); | |
846 | for (i = 0; i < n; i++) { | |
847 | SCNetworkInterfaceRef interface; | |
848 | SCNetworkServiceRef service; | |
849 | ||
850 | service = CFArrayGetValueAtIndex(services, i); | |
851 | interface = SCNetworkServiceGetInterface(service); | |
852 | if ((interface != NULL) && | |
5e9ce69e | 853 | !CFSetContainsValue(seen, interface)) { |
6bb65964 A |
854 | CFDictionaryRef capabilities; |
855 | ||
856 | capabilities = SCNetworkInterfaceCopyCapability(interface, NULL); | |
857 | if (capabilities != NULL) { | |
858 | int cap_current; | |
859 | int cap_requested; | |
860 | CFDictionaryRef options; | |
861 | ||
862 | options = SCNetworkInterfaceGetConfiguration(interface); | |
863 | cap_current = __SCNetworkInterfaceCreateCapabilities(interface, -1, NULL); | |
864 | cap_requested = __SCNetworkInterfaceCreateCapabilities(interface, cap_current, options); | |
865 | ||
866 | SCPrint(TRUE, stdout, | |
867 | CFSTR("%sinterface = %@, current = %p, requested = %p\n%@\n"), | |
868 | (i == 0) ? "" : "\n", | |
869 | SCNetworkInterfaceGetBSDName(interface), | |
870 | (void *)(uintptr_t)cap_current, | |
871 | (void *)(uintptr_t)cap_requested, | |
872 | capabilities); | |
873 | CFRelease(capabilities); | |
874 | } | |
875 | ||
876 | CFSetAddValue(seen, interface); | |
877 | } | |
878 | } | |
879 | ||
880 | CFRelease(seen); | |
881 | CFRelease(services); | |
882 | } | |
883 | ||
884 | CFRelease(set); | |
885 | } | |
886 | ||
887 | CFRelease(prefs); | |
888 | } | |
889 | ||
dbf6a266 A |
890 | load_LinkConfiguration(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE); |
891 | CFRunLoopRun(); | |
892 | /* not reached */ | |
893 | exit(0); | |
894 | return 0; | |
895 | } | |
896 | #endif |