2 * Copyright (c) 2002-2003 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(CFDictionaryRef media_options
);
62 _NetworkInterfaceSetMediaOptions(CFStringRef interface
,
63 CFDictionaryRef options
)
65 CFArrayRef available
= NULL
;
66 CFDictionaryRef current
= NULL
;
67 struct ifmediareq ifm
;
71 CFMutableDictionaryRef requested
= NULL
;
75 /* get current & available options */
76 if (!NetworkInterfaceCopyMediaOptions(interface
, ¤t
, NULL
, &available
, FALSE
)) {
80 /* extract just the dictionary key/value pairs of interest */
81 requested
= CFDictionaryCreateMutable(NULL
,
83 &kCFTypeDictionaryKeyCallBacks
,
84 &kCFTypeDictionaryValueCallBacks
);
86 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaSubType
);
88 val
= CFDictionaryGetValue(current
, kSCPropNetEthernetMediaSubType
);
90 if (isA_CFString(val
)) {
91 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaSubType
, val
);
97 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaOptions
);
99 val
= CFDictionaryGetValue(current
, kSCPropNetEthernetMediaOptions
);
101 if (isA_CFArray(val
)) {
102 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaOptions
, val
);
108 if (current
&& CFEqual(current
, requested
)) {
109 /* if current settings are as requested */
114 if (!CFArrayContainsValue(available
, CFRangeMake(0, CFArrayGetCount(available
)), requested
)) {
115 /* if requested settings not currently available */
116 SCLog(TRUE
, LOG_DEBUG
, CFSTR("requested media settings unavailable"));
120 newOptions
= __createMediaOptions(requested
);
121 if (newOptions
== -1) {
122 /* since we have just validated, this should never happen */
126 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
128 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
132 bzero((char *)&ifm
, sizeof(ifm
));
133 (void)_SC_cfstring_to_cstring(interface
, ifm
.ifm_name
, sizeof(ifm
.ifm_name
), kCFStringEncodingASCII
);
135 if (ioctl(sock
, SIOCGIFMEDIA
, (caddr_t
)&ifm
) < 0) {
136 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno
));
140 bzero((char *)&ifr
, sizeof(ifr
));
141 bcopy(ifm
.ifm_name
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
));
142 ifr
.ifr_media
= ifm
.ifm_current
& ~(IFM_NMASK
|IFM_TMASK
|IFM_OMASK
|IFM_GMASK
);
143 ifr
.ifr_media
|= newOptions
;
145 //SCLog(TRUE, LOG_INFO, CFSTR("old media settings: 0x%8.8x (0x%8.8x)"), ifm.ifm_current, ifm.ifm_active);
146 //SCLog(TRUE, LOG_INFO, CFSTR("new media settings: 0x%8.8x"), ifr.ifr_media);
148 if (ioctl(sock
, SIOCSIFMEDIA
, (caddr_t
)&ifr
) < 0) {
149 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCSIFMEDIA) failed: %s"), strerror(errno
));
157 if (available
) CFRelease(available
);
158 if (current
) CFRelease(current
);
159 if (requested
) CFRelease(requested
);
160 if (sock
>= 0) (void)close(sock
);
166 #ifndef USE_SIOCSIFMTU
168 ifconfig_exit(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
170 char *if_name
= (char *)context
;
172 if (WIFEXITED(status
)) {
173 if (WEXITSTATUS(status
) != 0) {
175 CFSTR("ifconfig %s failed, exit status = %d"),
177 WEXITSTATUS(status
));
179 } else if (WIFSIGNALED(status
)) {
180 SCLog(TRUE
, LOG_DEBUG
,
181 CFSTR("ifconfig %s: terminated w/signal = %d"),
185 SCLog(TRUE
, LOG_DEBUG
,
186 CFSTR("ifconfig %s: exit status = %d"),
191 CFAllocatorDeallocate(NULL
, if_name
);
194 #endif /* !USE_SIOCSIFMTU */
199 _NetworkInterfaceSetMTU(CFStringRef interface
,
200 CFDictionaryRef options
)
208 if (!NetworkInterfaceCopyMTU(interface
, &mtu_cur
, &mtu_min
, &mtu_max
)) {
209 /* could not get current MTU */
213 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMTU
);
215 if (isA_CFNumber(val
)) {
216 CFNumberGetValue(val
, kCFNumberIntType
, &requested
);
224 if (requested
== mtu_cur
) {
225 /* if current setting is as requested */
229 if (((mtu_min
>= 0) && (requested
< mtu_min
)) ||
230 ((mtu_max
>= 0) && (requested
> mtu_max
))) {
231 /* if requested MTU outside of the valid range */
235 #ifdef USE_SIOCSIFMTU
241 bzero((char *)&ifr
, sizeof(ifr
));
242 (void)_SC_cfstring_to_cstring(interface
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
), kCFStringEncodingASCII
);
243 ifr
.ifr_mtu
= requested
;
245 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
247 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
251 ret
= ioctl(sock
, SIOCSIFMTU
, (caddr_t
)&ifr
);
254 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCSIFMTU) failed: %s"), strerror(errno
));
258 #else /* !USE_SIOCSIFMTU */
260 char *ifconfig_argv
[] = { "ifconfig", NULL
, "mtu", NULL
, NULL
};
263 ifconfig_argv
[1] = _SC_cfstring_to_cstring(interface
, NULL
, 0, kCFStringEncodingASCII
);
264 (void)asprintf(&ifconfig_argv
[3], "%d", requested
);
266 pid
= _SCDPluginExecCommand(ifconfig_exit
, // callout,
267 ifconfig_argv
[1], // context
270 "/sbin/ifconfig", // path
271 ifconfig_argv
// argv
274 // CFAllocatorDeallocate(NULL, ifconfig_argv[1]); // released in ifconfig_exit()
275 free(ifconfig_argv
[3]);
281 #endif /* !USE_SIOCSIFMTU */
288 * Function: parse_component
290 * Given a string 'key' and a string prefix 'prefix',
291 * return the next component in the slash '/' separated
295 * 1. key = "a/b/c" prefix = "a/"
297 * 2. key = "a/b/c" prefix = "a/b/"
301 parse_component(CFStringRef key
, CFStringRef prefix
)
303 CFMutableStringRef comp
;
306 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
309 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
310 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
311 range
= CFStringFind(comp
, CFSTR("/"), 0);
312 if (range
.location
== kCFNotFound
) {
315 range
.length
= CFStringGetLength(comp
) - range
.location
;
316 CFStringDelete(comp
, range
);
322 updateLink(CFStringRef ifKey
, CFDictionaryRef options
)
324 CFStringRef interface
= NULL
;
325 static CFStringRef prefix
= NULL
;
328 prefix
= SCDynamicStoreKeyCreate(NULL
,
330 kSCDynamicStoreDomainSetup
,
335 interface
= parse_component(ifKey
, prefix
);
341 if (!CFDictionaryContainsKey(baseSettings
, interface
)) {
342 CFDictionaryRef cur_media
= NULL
;
343 CFMutableDictionaryRef new_media
= NULL
;
347 if (!NetworkInterfaceCopyMediaOptions(interface
, &cur_media
, NULL
, NULL
, FALSE
)) {
348 /* could not determine current settings */
353 /* could not determine current settings */
357 if (!NetworkInterfaceCopyMTU(interface
, &cur_mtu
, NULL
, NULL
)) {
358 /* could not determine current MTU */
359 CFRelease(cur_media
);
364 /* could not determine current MTU */
365 CFRelease(cur_media
);
369 new_media
= CFDictionaryCreateMutableCopy(NULL
, 0, cur_media
);
370 CFRelease(cur_media
);
372 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cur_mtu
);
373 CFDictionaryAddValue(new_media
, kSCPropNetEthernetMTU
, num
);
376 CFDictionarySetValue(baseSettings
, interface
, new_media
);
377 CFRelease(new_media
);
380 /* establish new settings */
381 (void)_NetworkInterfaceSetMediaOptions(interface
, options
);
382 (void)_NetworkInterfaceSetMTU (interface
, options
);
384 /* no requested settings */
385 options
= CFDictionaryGetValue(baseSettings
, interface
);
387 /* restore original settings */
388 (void)_NetworkInterfaceSetMediaOptions(interface
, options
);
389 (void)_NetworkInterfaceSetMTU (interface
, options
);
390 CFDictionaryRemoveValue(baseSettings
, interface
);
396 if (interface
) CFRelease(interface
);
402 linkConfigChangedCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *arg
)
406 CFDictionaryRef linkInfo
;
408 linkInfo
= SCDynamicStoreCopyMultiple(store
, changedKeys
, NULL
);
410 n
= CFArrayGetCount(changedKeys
);
411 for (i
= 0; i
< n
; i
++) {
413 CFDictionaryRef link
;
415 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
416 link
= CFDictionaryGetValue(linkInfo
, key
);
417 updateLink(key
, link
);
428 load_LinkConfiguration(CFBundleRef bundle
, Boolean bundleVerbose
)
431 CFMutableArrayRef patterns
= NULL
;
437 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
438 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
440 /* initialize a few globals */
442 baseSettings
= CFDictionaryCreateMutable(NULL
,
444 &kCFTypeDictionaryKeyCallBacks
,
445 &kCFTypeDictionaryValueCallBacks
);
447 /* open a "configd" store to allow cache updates */
448 store
= SCDynamicStoreCreate(NULL
,
449 CFSTR("Link Configuraton plug-in"),
450 linkConfigChangedCallback
,
453 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
457 /* establish notification keys and patterns */
459 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
461 /* ...watch for (per-interface) Ethernet configuration changes */
462 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
463 kSCDynamicStoreDomainSetup
,
466 CFArrayAppendValue(patterns
, key
);
469 /* register the keys/patterns */
470 if (!SCDynamicStoreSetNotificationKeys(store
, NULL
, patterns
)) {
472 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
473 SCErrorString(SCError()));
477 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
480 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
481 SCErrorString(SCError()));
485 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
492 if (baseSettings
) CFRelease(baseSettings
);
493 if (store
) CFRelease(store
);
494 if (patterns
) CFRelease(patterns
);
501 main(int argc
, char **argv
)
504 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
506 load_LinkConfiguration(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);