2 * Copyright (c) 2002-2005 Apple Computer, 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 SCDynamicStoreRef store
= NULL
;
50 static CFRunLoopSourceRef rls
= NULL
;
52 static Boolean _verbose
= FALSE
;
55 /* in SystemConfiguration/LinkConfiguration.c */
57 __createMediaOptions(CFStringRef interface
, CFDictionaryRef media_options
);
60 static CFDictionaryRef
61 __copyMediaOptions(CFDictionaryRef options
)
63 CFMutableDictionaryRef requested
= NULL
;
66 if (!isA_CFDictionary(options
)) {
70 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaSubType
);
71 if (isA_CFString(val
)) {
72 requested
= CFDictionaryCreateMutable(NULL
,
74 &kCFTypeDictionaryKeyCallBacks
,
75 &kCFTypeDictionaryValueCallBacks
);
76 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaSubType
, val
);
82 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaOptions
);
83 if (isA_CFArray(val
)) {
84 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaOptions
, val
);
97 _NetworkInterfaceSetMediaOptions(CFStringRef interface
,
98 CFDictionaryRef options
)
100 CFArrayRef available
= NULL
;
101 CFDictionaryRef current
= NULL
;
102 struct ifmediareq ifm
;
106 CFDictionaryRef requested
;
109 /* get current & available options */
110 if (!NetworkInterfaceCopyMediaOptions(interface
, ¤t
, NULL
, &available
, FALSE
)) {
111 /* could not get current media options */
115 /* extract just the dictionary key/value pairs of interest */
116 requested
= __copyMediaOptions(options
);
117 if (requested
== NULL
) {
118 CFDictionaryRef baseOptions
;
120 /* get base options */
121 baseOptions
= CFDictionaryGetValue(baseSettings
, interface
);
122 requested
= __copyMediaOptions(baseOptions
);
124 if (requested
== NULL
) {
125 /* get base options */
126 requested
= __copyMediaOptions(current
);
128 if (requested
== NULL
) {
129 /* if no media options to set */
133 if ((current
!= NULL
) && CFEqual(current
, requested
)) {
134 /* if current settings are as requested */
139 if (!CFArrayContainsValue(available
, CFRangeMake(0, CFArrayGetCount(available
)), requested
)) {
140 /* if requested settings not currently available */
141 SCLog(TRUE
, LOG_DEBUG
, CFSTR("requested media settings unavailable"));
145 newOptions
= __createMediaOptions(interface
, requested
);
146 if (newOptions
== -1) {
147 /* since we have just validated, this should never happen */
151 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
153 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
157 bzero((char *)&ifm
, sizeof(ifm
));
158 (void)_SC_cfstring_to_cstring(interface
, ifm
.ifm_name
, sizeof(ifm
.ifm_name
), kCFStringEncodingASCII
);
160 if (ioctl(sock
, SIOCGIFMEDIA
, (caddr_t
)&ifm
) < 0) {
161 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno
));
165 bzero((char *)&ifr
, sizeof(ifr
));
166 bcopy(ifm
.ifm_name
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
));
167 ifr
.ifr_media
= ifm
.ifm_current
& ~(IFM_NMASK
|IFM_TMASK
|IFM_OMASK
|IFM_GMASK
);
168 ifr
.ifr_media
|= newOptions
;
170 //SCLog(TRUE, LOG_INFO, CFSTR("old media settings: 0x%8.8x (0x%8.8x)"), ifm.ifm_current, ifm.ifm_active);
171 //SCLog(TRUE, LOG_INFO, CFSTR("new media settings: 0x%8.8x"), ifr.ifr_media);
173 if (ioctl(sock
, SIOCSIFMEDIA
, (caddr_t
)&ifr
) < 0) {
174 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCSIFMEDIA) failed: %s"), strerror(errno
));
182 if (available
) CFRelease(available
);
183 if (current
) CFRelease(current
);
184 if (requested
) CFRelease(requested
);
185 if (sock
>= 0) (void)close(sock
);
191 #ifndef USE_SIOCSIFMTU
193 ifconfig_exit(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
195 char *if_name
= (char *)context
;
197 if (WIFEXITED(status
)) {
198 if (WEXITSTATUS(status
) != 0) {
200 CFSTR("ifconfig %s failed, exit status = %d"),
202 WEXITSTATUS(status
));
204 } else if (WIFSIGNALED(status
)) {
205 SCLog(TRUE
, LOG_DEBUG
,
206 CFSTR("ifconfig %s: terminated w/signal = %d"),
210 SCLog(TRUE
, LOG_DEBUG
,
211 CFSTR("ifconfig %s: exit status = %d"),
216 CFAllocatorDeallocate(NULL
, if_name
);
219 #endif /* !USE_SIOCSIFMTU */
224 _NetworkInterfaceSetMTU(CFStringRef interface
,
225 CFDictionaryRef options
)
233 if (!NetworkInterfaceCopyMTU(interface
, &mtu_cur
, &mtu_min
, &mtu_max
)) {
234 /* could not get current MTU */
239 if (isA_CFDictionary(options
)) {
240 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMTU
);
241 val
= isA_CFNumber(val
);
244 CFDictionaryRef baseOptions
;
247 baseOptions
= CFDictionaryGetValue(baseSettings
, interface
);
248 if (baseOptions
!= NULL
) {
249 val
= CFDictionaryGetValue(baseOptions
, kSCPropNetEthernetMTU
);
253 CFNumberGetValue(val
, kCFNumberIntType
, &requested
);
258 if (requested
== mtu_cur
) {
259 /* if current setting is as requested */
263 if (((mtu_min
>= 0) && (requested
< mtu_min
)) ||
264 ((mtu_max
>= 0) && (requested
> mtu_max
))) {
265 /* if requested MTU outside of the valid range */
269 #ifdef USE_SIOCSIFMTU
275 bzero((char *)&ifr
, sizeof(ifr
));
276 (void)_SC_cfstring_to_cstring(interface
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
), kCFStringEncodingASCII
);
277 ifr
.ifr_mtu
= requested
;
279 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
281 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
285 ret
= ioctl(sock
, SIOCSIFMTU
, (caddr_t
)&ifr
);
288 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCSIFMTU) failed: %s"), strerror(errno
));
292 #else /* !USE_SIOCSIFMTU */
294 char *ifconfig_argv
[] = { "ifconfig", NULL
, "mtu", NULL
, NULL
};
297 ifconfig_argv
[1] = _SC_cfstring_to_cstring(interface
, NULL
, 0, kCFStringEncodingASCII
);
298 (void)asprintf(&ifconfig_argv
[3], "%d", requested
);
300 pid
= _SCDPluginExecCommand(ifconfig_exit
, // callout,
301 ifconfig_argv
[1], // context
304 "/sbin/ifconfig", // path
305 ifconfig_argv
// argv
308 // CFAllocatorDeallocate(NULL, ifconfig_argv[1]); // released in ifconfig_exit()
309 free(ifconfig_argv
[3]);
315 #endif /* !USE_SIOCSIFMTU */
322 * Function: parse_component
324 * Given a string 'key' and a string prefix 'prefix',
325 * return the next component in the slash '/' separated
329 * 1. key = "a/b/c" prefix = "a/"
331 * 2. key = "a/b/c" prefix = "a/b/"
335 parse_component(CFStringRef key
, CFStringRef prefix
)
337 CFMutableStringRef comp
;
340 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
343 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
344 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
345 range
= CFStringFind(comp
, CFSTR("/"), 0);
346 if (range
.location
== kCFNotFound
) {
349 range
.length
= CFStringGetLength(comp
) - range
.location
;
350 CFStringDelete(comp
, range
);
356 updateLink(CFStringRef ifKey
, CFDictionaryRef options
)
358 CFStringRef interface
= NULL
;
359 static CFStringRef prefix
= NULL
;
362 prefix
= SCDynamicStoreKeyCreate(NULL
,
364 kSCDynamicStoreDomainSetup
,
369 interface
= parse_component(ifKey
, prefix
);
375 if (!CFDictionaryContainsKey(baseSettings
, interface
)) {
376 CFDictionaryRef cur_media
= NULL
;
377 CFMutableDictionaryRef new_media
= NULL
;
380 /* preserve current media options */
381 if (NetworkInterfaceCopyMediaOptions(interface
, &cur_media
, NULL
, NULL
, FALSE
)) {
382 if (cur_media
!= NULL
) {
383 new_media
= CFDictionaryCreateMutableCopy(NULL
, 0, cur_media
);
384 CFRelease(cur_media
);
388 /* preserve current MTU */
389 if (NetworkInterfaceCopyMTU(interface
, &cur_mtu
, NULL
, NULL
)) {
393 if (new_media
== NULL
) {
394 new_media
= CFDictionaryCreateMutable(NULL
,
396 &kCFTypeDictionaryKeyCallBacks
,
397 &kCFTypeDictionaryValueCallBacks
);
400 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cur_mtu
);
401 CFDictionaryAddValue(new_media
, kSCPropNetEthernetMTU
, num
);
406 if (new_media
!= NULL
) {
407 CFDictionarySetValue(baseSettings
, interface
, new_media
);
408 CFRelease(new_media
);
412 /* establish new settings */
413 (void)_NetworkInterfaceSetMediaOptions(interface
, options
);
414 (void)_NetworkInterfaceSetMTU (interface
, options
);
416 /* no requested settings */
417 options
= CFDictionaryGetValue(baseSettings
, interface
);
419 /* restore original settings */
420 (void)_NetworkInterfaceSetMediaOptions(interface
, options
);
421 (void)_NetworkInterfaceSetMTU (interface
, options
);
422 CFDictionaryRemoveValue(baseSettings
, interface
);
428 if (interface
) CFRelease(interface
);
434 linkConfigChangedCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *arg
)
438 CFDictionaryRef linkInfo
;
440 linkInfo
= SCDynamicStoreCopyMultiple(store
, changedKeys
, NULL
);
442 n
= CFArrayGetCount(changedKeys
);
443 for (i
= 0; i
< n
; i
++) {
445 CFDictionaryRef link
;
447 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
448 link
= CFDictionaryGetValue(linkInfo
, key
);
449 updateLink(key
, link
);
460 load_LinkConfiguration(CFBundleRef bundle
, Boolean bundleVerbose
)
463 CFMutableArrayRef patterns
= NULL
;
469 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
470 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
472 /* initialize a few globals */
474 baseSettings
= CFDictionaryCreateMutable(NULL
,
476 &kCFTypeDictionaryKeyCallBacks
,
477 &kCFTypeDictionaryValueCallBacks
);
479 /* open a "configd" store to allow cache updates */
480 store
= SCDynamicStoreCreate(NULL
,
481 CFSTR("Link Configuraton plug-in"),
482 linkConfigChangedCallback
,
485 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
489 /* establish notification keys and patterns */
491 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
493 /* ...watch for (per-interface) AirPort configuration changes */
494 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
495 kSCDynamicStoreDomainSetup
,
498 CFArrayAppendValue(patterns
, key
);
501 /* ...watch for (per-interface) Ethernet configuration changes */
502 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
503 kSCDynamicStoreDomainSetup
,
506 CFArrayAppendValue(patterns
, key
);
509 /* ...watch for (per-interface) FireWire configuration changes */
510 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
511 kSCDynamicStoreDomainSetup
,
514 CFArrayAppendValue(patterns
, key
);
517 /* register the keys/patterns */
518 if (!SCDynamicStoreSetNotificationKeys(store
, NULL
, patterns
)) {
520 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
521 SCErrorString(SCError()));
525 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
528 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
529 SCErrorString(SCError()));
533 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
540 if (baseSettings
) CFRelease(baseSettings
);
541 if (store
) CFRelease(store
);
542 if (patterns
) CFRelease(patterns
);
549 main(int argc
, char **argv
)
552 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
554 load_LinkConfiguration(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);