2 * Copyright (c) 2002-2007 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * October 21, 2000 Allan Nathanson <ajn@apple.com>
34 //#include <sys/fcntl.h>
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
39 #include <net/if_media.h>
41 #include <SystemConfiguration/SystemConfiguration.h>
42 #include <SystemConfiguration/SCPrivate.h>
43 #include <SystemConfiguration/SCValidation.h>
44 #include <SystemConfiguration/LinkConfiguration.h>
45 #include <SystemConfiguration/SCDPlugin.h> // for _SCDPluginExecCommand
48 static CFMutableDictionaryRef baseSettings
= NULL
;
49 static CFStringRef interfacesKey
= NULL
;
50 static SCDynamicStoreRef store
= NULL
;
51 static CFRunLoopSourceRef rls
= NULL
;
52 static CFMutableDictionaryRef wantSettings
= NULL
;
54 static Boolean _verbose
= FALSE
;
57 /* in SystemConfiguration/LinkConfiguration.c */
59 __createMediaOptions(CFStringRef interfaceName
, CFDictionaryRef media_options
);
62 static CFDictionaryRef
63 __copyMediaOptions(CFDictionaryRef options
)
65 CFMutableDictionaryRef requested
= NULL
;
68 if (!isA_CFDictionary(options
)) {
72 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaSubType
);
73 if (isA_CFString(val
)) {
74 requested
= CFDictionaryCreateMutable(NULL
,
76 &kCFTypeDictionaryKeyCallBacks
,
77 &kCFTypeDictionaryValueCallBacks
);
78 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaSubType
, val
);
84 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaOptions
);
85 if (isA_CFArray(val
)) {
86 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaOptions
, val
);
99 _SCNetworkInterfaceSetMediaOptions(SCNetworkInterfaceRef interface
,
100 CFDictionaryRef options
)
102 CFArrayRef available
= NULL
;
103 CFDictionaryRef current
= NULL
;
104 struct ifmediareq ifm
;
106 CFStringRef interfaceName
;
109 CFDictionaryRef requested
;
112 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
113 if (interfaceName
== NULL
) {
114 /* if no BSD interface name */
115 SCLog(_verbose
, LOG_INFO
, CFSTR("no BSD interface name for %@"), interface
);
119 /* get current & available options */
120 if (!SCNetworkInterfaceCopyMediaOptions(interface
, ¤t
, NULL
, &available
, FALSE
)) {
121 /* could not get current media options */
122 SCLog(_verbose
, LOG_INFO
, CFSTR("no media options for %@"), interfaceName
);
126 /* extract just the dictionary key/value pairs of interest */
127 requested
= __copyMediaOptions(options
);
128 if (requested
== NULL
) {
129 CFDictionaryRef baseOptions
;
131 /* get base options */
132 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
133 requested
= __copyMediaOptions(baseOptions
);
135 if (requested
== NULL
) {
136 /* get base options */
137 requested
= __copyMediaOptions(current
);
139 if (requested
== NULL
) {
140 /* if no media options to set */
144 if ((current
!= NULL
) && CFEqual(current
, requested
)) {
145 /* if current settings are as requested */
150 if (!CFArrayContainsValue(available
, CFRangeMake(0, CFArrayGetCount(available
)), requested
)) {
151 /* if requested settings not currently available */
152 SCLog(_verbose
, LOG_INFO
, CFSTR("requested media settings unavailable for %@"), interfaceName
);
156 newOptions
= __createMediaOptions(interfaceName
, requested
);
157 if (newOptions
== -1) {
158 /* since we have just validated, this should never happen */
162 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
164 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
168 bzero((char *)&ifm
, sizeof(ifm
));
169 (void)_SC_cfstring_to_cstring(interfaceName
, ifm
.ifm_name
, sizeof(ifm
.ifm_name
), kCFStringEncodingASCII
);
171 if (ioctl(sock
, SIOCGIFMEDIA
, (caddr_t
)&ifm
) == -1) {
172 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno
));
176 bzero((char *)&ifr
, sizeof(ifr
));
177 bcopy(ifm
.ifm_name
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
));
178 ifr
.ifr_media
= ifm
.ifm_current
& ~(IFM_NMASK
|IFM_TMASK
|IFM_OMASK
|IFM_GMASK
);
179 ifr
.ifr_media
|= newOptions
;
181 SCLog(_verbose
, LOG_INFO
, CFSTR("old media settings: 0x%8.8x (0x%8.8x)"), ifm
.ifm_current
, ifm
.ifm_active
);
182 SCLog(_verbose
, LOG_INFO
, CFSTR("new media settings: 0x%8.8x"), ifr
.ifr_media
);
184 if (ioctl(sock
, SIOCSIFMEDIA
, (caddr_t
)&ifr
) == -1) {
185 SCLog(TRUE
, LOG_DEBUG
, CFSTR("%@: ioctl(SIOCSIFMEDIA) failed: %s"), interfaceName
, strerror(errno
));
193 if (available
!= NULL
) CFRelease(available
);
194 if (current
!= NULL
) CFRelease(current
);
195 if (requested
!= NULL
) CFRelease(requested
);
196 if (sock
!= -1) (void)close(sock
);
202 #ifndef USE_SIOCSIFMTU
204 ifconfig_exit(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
206 char *if_name
= (char *)context
;
208 if (WIFEXITED(status
)) {
209 if (WEXITSTATUS(status
) != 0) {
211 CFSTR("ifconfig %s failed, exit status = %d"),
213 WEXITSTATUS(status
));
215 } else if (WIFSIGNALED(status
)) {
216 SCLog(TRUE
, LOG_DEBUG
,
217 CFSTR("ifconfig %s: terminated w/signal = %d"),
221 SCLog(TRUE
, LOG_DEBUG
,
222 CFSTR("ifconfig %s: exit status = %d"),
227 CFAllocatorDeallocate(NULL
, if_name
);
230 #endif /* !USE_SIOCSIFMTU */
235 _SCNetworkInterfaceSetMTU(SCNetworkInterfaceRef interface
,
236 CFDictionaryRef options
)
238 CFStringRef interfaceName
;
245 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
246 if (interfaceName
== NULL
) {
247 /* if no BSD interface name */
251 if (!SCNetworkInterfaceCopyMTU(interface
, &mtu_cur
, &mtu_min
, &mtu_max
)) {
252 /* could not get current MTU */
257 if (isA_CFDictionary(options
)) {
258 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMTU
);
259 val
= isA_CFNumber(val
);
262 CFDictionaryRef baseOptions
;
265 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
266 if (baseOptions
!= NULL
) {
267 val
= CFDictionaryGetValue(baseOptions
, kSCPropNetEthernetMTU
);
271 CFNumberGetValue(val
, kCFNumberIntType
, &requested
);
276 if (requested
== mtu_cur
) {
277 /* if current setting is as requested */
281 if (((mtu_min
>= 0) && (requested
< mtu_min
)) ||
282 ((mtu_max
>= 0) && (requested
> mtu_max
))) {
283 /* if requested MTU outside of the valid range */
287 #ifdef USE_SIOCSIFMTU
293 bzero((char *)&ifr
, sizeof(ifr
));
294 (void)_SC_cfstring_to_cstring(interfaceName
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
), kCFStringEncodingASCII
);
295 ifr
.ifr_mtu
= requested
;
297 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
299 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
303 ret
= ioctl(sock
, SIOCSIFMTU
, (caddr_t
)&ifr
);
306 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCSIFMTU) failed: %s"), strerror(errno
));
310 #else /* !USE_SIOCSIFMTU */
312 char *ifconfig_argv
[] = { "ifconfig", NULL
, "mtu", NULL
, NULL
};
315 ifconfig_argv
[1] = _SC_cfstring_to_cstring(interfaceName
, NULL
, 0, kCFStringEncodingASCII
);
316 (void)asprintf(&ifconfig_argv
[3], "%d", requested
);
318 pid
= _SCDPluginExecCommand(ifconfig_exit
, // callout,
319 ifconfig_argv
[1], // context
322 "/sbin/ifconfig", // path
323 ifconfig_argv
// argv
326 // CFAllocatorDeallocate(NULL, ifconfig_argv[1]); // released in ifconfig_exit()
327 free(ifconfig_argv
[3]);
333 #endif /* !USE_SIOCSIFMTU */
340 * Function: parse_component
342 * Given a string 'key' and a string prefix 'prefix',
343 * return the next component in the slash '/' separated
347 * 1. key = "a/b/c" prefix = "a/"
349 * 2. key = "a/b/c" prefix = "a/b/"
353 parse_component(CFStringRef key
, CFStringRef prefix
)
355 CFMutableStringRef comp
;
358 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
361 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
362 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
363 range
= CFStringFind(comp
, CFSTR("/"), 0);
364 if (range
.location
== kCFNotFound
) {
367 range
.length
= CFStringGetLength(comp
) - range
.location
;
368 CFStringDelete(comp
, range
);
373 static void updateLink(CFStringRef interfaceName
, CFDictionaryRef options
);
377 updateInterfaces(CFArrayRef newInterfaces
)
382 static CFArrayRef oldInterfaces
= NULL
;
384 n_old
= (oldInterfaces
!= NULL
) ? CFArrayGetCount(oldInterfaces
) : 0;
385 n_new
= CFArrayGetCount(newInterfaces
);
387 for (i
= 0; i
< n_new
; i
++) {
388 CFStringRef interfaceName
;
390 interfaceName
= CFArrayGetValueAtIndex(newInterfaces
, i
);
393 !CFArrayContainsValue(oldInterfaces
,
394 CFRangeMake(0, n_old
),
396 CFDictionaryRef options
;
399 options
= CFDictionaryGetValue(wantSettings
, interfaceName
);
400 updateLink(interfaceName
, options
);
404 if (oldInterfaces
!= NULL
) CFRelease(oldInterfaces
);
405 oldInterfaces
= CFRetain(newInterfaces
);
410 updateLink(CFStringRef interfaceName
, CFDictionaryRef options
)
412 SCNetworkInterfaceRef interface
;
414 /* retain requested configuration */
415 if (options
!= NULL
) {
416 CFDictionarySetValue(wantSettings
, interfaceName
, options
);
418 CFDictionaryRemoveValue(wantSettings
, interfaceName
);
421 /* apply requested configuration */
422 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, interfaceName
,
423 kIncludeAllVirtualInterfaces
);
424 if (interface
== NULL
) {
428 if (options
!= NULL
) {
429 if (!CFDictionaryContainsKey(baseSettings
, interfaceName
)) {
430 CFDictionaryRef cur_media
= NULL
;
431 CFMutableDictionaryRef new_media
= NULL
;
434 /* preserve current media options */
435 if (SCNetworkInterfaceCopyMediaOptions(interface
, &cur_media
, NULL
, NULL
, FALSE
)) {
436 if (cur_media
!= NULL
) {
437 new_media
= CFDictionaryCreateMutableCopy(NULL
, 0, cur_media
);
438 CFRelease(cur_media
);
442 /* preserve current MTU */
443 if (SCNetworkInterfaceCopyMTU(interface
, &cur_mtu
, NULL
, NULL
)) {
447 if (new_media
== NULL
) {
448 new_media
= CFDictionaryCreateMutable(NULL
,
450 &kCFTypeDictionaryKeyCallBacks
,
451 &kCFTypeDictionaryValueCallBacks
);
454 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cur_mtu
);
455 CFDictionaryAddValue(new_media
, kSCPropNetEthernetMTU
, num
);
460 if (new_media
!= NULL
) {
461 CFDictionarySetValue(baseSettings
, interfaceName
, new_media
);
462 CFRelease(new_media
);
466 /* establish new settings */
467 (void)_SCNetworkInterfaceSetMediaOptions(interface
, options
);
468 (void)_SCNetworkInterfaceSetMTU (interface
, options
);
470 /* no requested settings */
471 options
= CFDictionaryGetValue(baseSettings
, interfaceName
);
472 if (options
!= NULL
) {
473 /* restore original settings */
474 (void)_SCNetworkInterfaceSetMediaOptions(interface
, options
);
475 (void)_SCNetworkInterfaceSetMTU (interface
, options
);
476 CFDictionaryRemoveValue(baseSettings
, interfaceName
);
480 CFRelease(interface
);
486 linkConfigChangedCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *arg
)
488 CFDictionaryRef changes
;
491 static CFStringRef prefix
= NULL
;
493 if (prefix
== NULL
) {
494 prefix
= SCDynamicStoreKeyCreate(NULL
,
496 kSCDynamicStoreDomainSetup
,
501 changes
= SCDynamicStoreCopyMultiple(store
, changedKeys
, NULL
);
503 n
= CFArrayGetCount(changedKeys
);
504 for (i
= 0; i
< n
; i
++) {
506 CFDictionaryRef info
;
508 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
509 info
= CFDictionaryGetValue(changes
, key
);
511 if (CFEqual(key
, interfacesKey
)) {
512 CFArrayRef interfaces
;
514 interfaces
= CFDictionaryGetValue(info
, kSCPropNetInterfaces
);
515 if (isA_CFArray(interfaces
)) {
516 updateInterfaces(interfaces
);
519 CFStringRef interfaceName
;
521 interfaceName
= parse_component(key
, prefix
);
522 if (interfaceName
!= NULL
) {
523 updateLink(interfaceName
, info
);
524 CFRelease(interfaceName
);
537 load_LinkConfiguration(CFBundleRef bundle
, Boolean bundleVerbose
)
540 CFMutableArrayRef keys
= NULL
;
542 CFMutableArrayRef patterns
= NULL
;
548 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
549 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
551 /* initialize a few globals */
553 baseSettings
= CFDictionaryCreateMutable(NULL
,
555 &kCFTypeDictionaryKeyCallBacks
,
556 &kCFTypeDictionaryValueCallBacks
);
557 wantSettings
= CFDictionaryCreateMutable(NULL
,
559 &kCFTypeDictionaryKeyCallBacks
,
560 &kCFTypeDictionaryValueCallBacks
);
562 /* open a "configd" store to allow cache updates */
563 store
= SCDynamicStoreCreate(NULL
,
564 CFSTR("Link Configuraton plug-in"),
565 linkConfigChangedCallback
,
568 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
572 /* establish notification keys and patterns */
573 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
574 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
576 /* ...watch for a change in the list of network interfaces */
577 interfacesKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
578 kSCDynamicStoreDomainState
);
579 CFArrayAppendValue(keys
, interfacesKey
);
581 /* ...watch for (per-interface) AirPort configuration changes */
582 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
583 kSCDynamicStoreDomainSetup
,
586 CFArrayAppendValue(patterns
, key
);
589 /* ...watch for (per-interface) Ethernet configuration changes */
590 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
591 kSCDynamicStoreDomainSetup
,
594 CFArrayAppendValue(patterns
, key
);
597 /* ...watch for (per-interface) FireWire configuration changes */
598 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
599 kSCDynamicStoreDomainSetup
,
602 CFArrayAppendValue(patterns
, key
);
605 /* register the keys/patterns */
606 ok
= SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
611 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
612 SCErrorString(SCError()));
616 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
619 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
620 SCErrorString(SCError()));
624 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
629 if (baseSettings
!= NULL
) CFRelease(baseSettings
);
630 if (wantSettings
!= NULL
) CFRelease(wantSettings
);
631 if (store
!= NULL
) CFRelease(store
);
638 main(int argc
, char **argv
)
641 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
643 load_LinkConfiguration(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);