2 * Copyright (c) 2002-2007, 2011 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/ioctl.h>
35 #include <sys/socket.h>
38 #include <net/if_media.h>
40 #include <SystemConfiguration/SystemConfiguration.h>
41 #include <SystemConfiguration/SCPrivate.h>
42 #include <SystemConfiguration/SCValidation.h>
43 #include <SystemConfiguration/LinkConfiguration.h>
44 #include <SystemConfiguration/SCDPlugin.h> // for _SCDPluginExecCommand
46 #include "SCNetworkConfigurationInternal.h"
49 static CFMutableDictionaryRef baseSettings
= NULL
;
50 static CFStringRef interfacesKey
= NULL
;
51 static SCDynamicStoreRef store
= NULL
;
52 static CFRunLoopSourceRef rls
= NULL
;
53 static CFMutableDictionaryRef wantSettings
= NULL
;
55 static Boolean _verbose
= FALSE
;
59 #pragma mark Capabilities
62 #define CAPABILITIES_KEY CFSTR("_CAPABILITIES_")
67 _SCNetworkInterfaceSetCapabilities(SCNetworkInterfaceRef interface
,
68 CFDictionaryRef options
)
70 CFDictionaryRef baseOptions
;
74 CFStringRef interfaceName
;
82 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
83 if (interfaceName
== NULL
) {
84 /* if no BSD interface name */
88 cap_current
= __SCNetworkInterfaceCreateCapabilities(interface
, -1, NULL
);
89 if (cap_current
== -1) {
90 /* could not get current capabilities */
94 // get base capabilities
95 cap_base
= cap_current
;
96 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
97 if (baseOptions
!= NULL
) {
100 num
= CFDictionaryGetValue(baseOptions
, CAPABILITIES_KEY
);
102 CFNumberGetValue(num
, kCFNumberIntType
, &cap_base
);
106 cap_requested
= __SCNetworkInterfaceCreateCapabilities(interface
, cap_base
, options
);
109 if (cap_requested
== cap_current
) {
110 /* if current setting is as requested */
114 bzero((char *)&ifr
, sizeof(ifr
));
115 (void)_SC_cfstring_to_cstring(interfaceName
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
), kCFStringEncodingASCII
);
116 ifr
.ifr_curcap
= cap_current
;
117 ifr
.ifr_reqcap
= cap_requested
;
119 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
121 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
125 ret
= ioctl(sock
, SIOCSIFCAP
, (caddr_t
)&ifr
);
128 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCSIFCAP) failed: %s"), strerror(errno
));
138 #pragma mark Media options
141 static CFDictionaryRef
142 __copyMediaOptions(CFDictionaryRef options
)
144 CFMutableDictionaryRef requested
= NULL
;
147 if (!isA_CFDictionary(options
)) {
151 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaSubType
);
152 if (isA_CFString(val
)) {
153 requested
= CFDictionaryCreateMutable(NULL
,
155 &kCFTypeDictionaryKeyCallBacks
,
156 &kCFTypeDictionaryValueCallBacks
);
157 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaSubType
, val
);
163 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaOptions
);
164 if (isA_CFArray(val
)) {
165 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaOptions
, val
);
168 CFRelease(requested
);
178 _SCNetworkInterfaceSetMediaOptions(SCNetworkInterfaceRef interface
,
179 CFDictionaryRef options
)
181 CFArrayRef available
= NULL
;
182 CFDictionaryRef current
= NULL
;
183 struct ifmediareq ifm
;
185 CFStringRef interfaceName
;
188 CFDictionaryRef requested
;
191 if (!isA_SCNetworkInterface(interface
)) {
192 _SCErrorSet(kSCStatusInvalidArgument
);
196 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
197 if (interfaceName
== NULL
) {
198 /* if no BSD interface name */
199 SCLog(_verbose
, LOG_INFO
, CFSTR("no BSD interface name for %@"), interface
);
200 _SCErrorSet(kSCStatusInvalidArgument
);
204 /* get current & available options */
205 if (!SCNetworkInterfaceCopyMediaOptions(interface
, ¤t
, NULL
, &available
, FALSE
)) {
206 /* could not get current media options */
207 SCLog(_verbose
, LOG_INFO
, CFSTR("no media options for %@"), interfaceName
);
211 /* extract just the dictionary key/value pairs of interest */
212 requested
= __copyMediaOptions(options
);
213 if (requested
== NULL
) {
214 CFDictionaryRef baseOptions
;
216 /* get base options */
217 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
218 requested
= __copyMediaOptions(baseOptions
);
220 if (requested
== NULL
) {
221 /* get base options */
222 requested
= __copyMediaOptions(current
);
224 if (requested
== NULL
) {
225 /* if no media options to set */
229 if ((current
!= NULL
) && CFEqual(current
, requested
)) {
230 /* if current settings are as requested */
235 if (!CFArrayContainsValue(available
, CFRangeMake(0, CFArrayGetCount(available
)), requested
)) {
236 /* if requested settings not currently available */
237 SCLog(_verbose
, LOG_INFO
, CFSTR("requested media settings unavailable for %@"), interfaceName
);
241 newOptions
= __SCNetworkInterfaceCreateMediaOptions(interface
, requested
);
242 if (newOptions
== -1) {
243 /* since we have just validated, this should never happen */
247 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
249 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
253 bzero((char *)&ifm
, sizeof(ifm
));
254 (void)_SC_cfstring_to_cstring(interfaceName
, ifm
.ifm_name
, sizeof(ifm
.ifm_name
), kCFStringEncodingASCII
);
256 if (ioctl(sock
, SIOCGIFMEDIA
, (caddr_t
)&ifm
) == -1) {
257 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno
));
261 bzero((char *)&ifr
, sizeof(ifr
));
262 bcopy(ifm
.ifm_name
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
));
263 ifr
.ifr_media
= ifm
.ifm_current
& ~(IFM_NMASK
|IFM_TMASK
|IFM_OMASK
|IFM_GMASK
);
264 ifr
.ifr_media
|= newOptions
;
266 SCLog(_verbose
, LOG_INFO
, CFSTR("old media settings: 0x%8.8x (0x%8.8x)"), ifm
.ifm_current
, ifm
.ifm_active
);
267 SCLog(_verbose
, LOG_INFO
, CFSTR("new media settings: 0x%8.8x"), ifr
.ifr_media
);
269 if (ioctl(sock
, SIOCSIFMEDIA
, (caddr_t
)&ifr
) == -1) {
270 SCLog(TRUE
, LOG_DEBUG
, CFSTR("%@: ioctl(SIOCSIFMEDIA) failed: %s"), interfaceName
, strerror(errno
));
278 if (available
!= NULL
) CFRelease(available
);
279 if (current
!= NULL
) CFRelease(current
);
280 if (requested
!= NULL
) CFRelease(requested
);
281 if (sock
!= -1) (void)close(sock
);
291 #ifndef USE_SIOCSIFMTU
293 ifconfig_exit(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
295 char *if_name
= (char *)context
;
297 if (WIFEXITED(status
)) {
298 if (WEXITSTATUS(status
) != 0) {
300 CFSTR("ifconfig %s failed, exit status = %d"),
302 WEXITSTATUS(status
));
304 } else if (WIFSIGNALED(status
)) {
305 SCLog(TRUE
, LOG_DEBUG
,
306 CFSTR("ifconfig %s: terminated w/signal = %d"),
310 SCLog(TRUE
, LOG_DEBUG
,
311 CFSTR("ifconfig %s: exit status = %d"),
316 CFAllocatorDeallocate(NULL
, if_name
);
319 #endif /* !USE_SIOCSIFMTU */
324 _SCNetworkInterfaceSetMTU(SCNetworkInterfaceRef interface
,
325 CFDictionaryRef options
)
327 CFStringRef interfaceName
;
334 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
335 if (interfaceName
== NULL
) {
336 /* if no BSD interface name */
340 if (!SCNetworkInterfaceCopyMTU(interface
, &mtu_cur
, &mtu_min
, &mtu_max
)) {
341 /* could not get current MTU */
346 if (isA_CFDictionary(options
)) {
347 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMTU
);
348 val
= isA_CFNumber(val
);
351 CFDictionaryRef baseOptions
;
354 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
355 if (baseOptions
!= NULL
) {
356 val
= CFDictionaryGetValue(baseOptions
, kSCPropNetEthernetMTU
);
360 CFNumberGetValue(val
, kCFNumberIntType
, &requested
);
365 if (requested
== mtu_cur
) {
366 /* if current setting is as requested */
370 if (((mtu_min
>= 0) && (requested
< mtu_min
)) ||
371 ((mtu_max
>= 0) && (requested
> mtu_max
))) {
372 /* if requested MTU outside of the valid range */
376 #ifdef USE_SIOCSIFMTU
382 bzero((char *)&ifr
, sizeof(ifr
));
383 (void)_SC_cfstring_to_cstring(interfaceName
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
), kCFStringEncodingASCII
);
384 ifr
.ifr_mtu
= requested
;
386 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
388 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
392 ret
= ioctl(sock
, SIOCSIFMTU
, (caddr_t
)&ifr
);
395 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCSIFMTU) failed: %s"), strerror(errno
));
399 #else /* !USE_SIOCSIFMTU */
401 char *ifconfig_argv
[] = { "ifconfig", NULL
, "mtu", NULL
, NULL
};
404 ifconfig_argv
[1] = _SC_cfstring_to_cstring(interfaceName
, NULL
, 0, kCFStringEncodingASCII
);
405 (void)asprintf(&ifconfig_argv
[3], "%d", requested
);
407 pid
= _SCDPluginExecCommand(ifconfig_exit
, // callout,
408 ifconfig_argv
[1], // context
411 "/sbin/ifconfig", // path
412 ifconfig_argv
// argv
415 // CFAllocatorDeallocate(NULL, ifconfig_argv[1]); // released in ifconfig_exit()
416 free(ifconfig_argv
[3]);
422 #endif /* !USE_SIOCSIFMTU */
429 #pragma mark Update link configuration
433 * Function: parse_component
435 * Given a string 'key' and a string prefix 'prefix',
436 * return the next component in the slash '/' separated
440 * 1. key = "a/b/c" prefix = "a/"
442 * 2. key = "a/b/c" prefix = "a/b/"
445 static CF_RETURNS_RETAINED CFStringRef
446 parse_component(CFStringRef key
, CFStringRef prefix
)
448 CFMutableStringRef comp
;
451 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
454 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
455 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
456 range
= CFStringFind(comp
, CFSTR("/"), 0);
457 if (range
.location
== kCFNotFound
) {
460 range
.length
= CFStringGetLength(comp
) - range
.location
;
461 CFStringDelete(comp
, range
);
466 static void updateLink(CFStringRef interfaceName
, CFDictionaryRef options
);
470 updateInterfaces(CFArrayRef newInterfaces
)
475 static CFArrayRef oldInterfaces
= NULL
;
477 n_old
= (oldInterfaces
!= NULL
) ? CFArrayGetCount(oldInterfaces
) : 0;
478 n_new
= CFArrayGetCount(newInterfaces
);
480 for (i
= 0; i
< n_new
; i
++) {
481 CFStringRef interfaceName
;
483 interfaceName
= CFArrayGetValueAtIndex(newInterfaces
, i
);
486 !CFArrayContainsValue(oldInterfaces
,
487 CFRangeMake(0, n_old
),
489 CFDictionaryRef options
;
492 options
= CFDictionaryGetValue(wantSettings
, interfaceName
);
493 updateLink(interfaceName
, options
);
497 if (oldInterfaces
!= NULL
) CFRelease(oldInterfaces
);
498 oldInterfaces
= CFRetain(newInterfaces
);
503 updateLink(CFStringRef interfaceName
, CFDictionaryRef options
)
505 SCNetworkInterfaceRef interface
;
507 /* retain requested configuration */
508 if (options
!= NULL
) {
509 CFDictionarySetValue(wantSettings
, interfaceName
, options
);
511 CFDictionaryRemoveValue(wantSettings
, interfaceName
);
514 /* apply requested configuration */
515 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, interfaceName
,
516 kIncludeAllVirtualInterfaces
);
517 if (interface
== NULL
) {
521 if (options
!= NULL
) {
522 if (!CFDictionaryContainsKey(baseSettings
, interfaceName
)) {
524 CFDictionaryRef cur_media
= NULL
;
525 CFMutableDictionaryRef new_media
= NULL
;
528 /* preserve current media options */
529 if (SCNetworkInterfaceCopyMediaOptions(interface
, &cur_media
, NULL
, NULL
, FALSE
)) {
530 if (cur_media
!= NULL
) {
531 new_media
= CFDictionaryCreateMutableCopy(NULL
, 0, cur_media
);
532 CFRelease(cur_media
);
536 /* preserve current MTU */
537 if (SCNetworkInterfaceCopyMTU(interface
, &cur_mtu
, NULL
, NULL
)) {
541 if (new_media
== NULL
) {
542 new_media
= CFDictionaryCreateMutable(NULL
,
544 &kCFTypeDictionaryKeyCallBacks
,
545 &kCFTypeDictionaryValueCallBacks
);
548 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cur_mtu
);
549 CFDictionaryAddValue(new_media
, kSCPropNetEthernetMTU
, num
);
554 /* preserve capabilities */
555 cur_cap
= __SCNetworkInterfaceCreateCapabilities(interface
, -1, NULL
);
559 if (new_media
== NULL
) {
560 new_media
= CFDictionaryCreateMutable(NULL
,
562 &kCFTypeDictionaryKeyCallBacks
,
563 &kCFTypeDictionaryValueCallBacks
);
566 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cur_cap
);
567 CFDictionaryAddValue(new_media
, CAPABILITIES_KEY
, num
);
571 if (new_media
!= NULL
) {
572 CFDictionarySetValue(baseSettings
, interfaceName
, new_media
);
573 CFRelease(new_media
);
577 /* establish new settings */
578 (void)_SCNetworkInterfaceSetCapabilities(interface
, options
);
579 (void)_SCNetworkInterfaceSetMediaOptions(interface
, options
);
580 (void)_SCNetworkInterfaceSetMTU (interface
, options
);
582 /* no requested settings */
583 options
= CFDictionaryGetValue(baseSettings
, interfaceName
);
584 if (options
!= NULL
) {
585 /* restore original settings */
586 (void)_SCNetworkInterfaceSetCapabilities(interface
, options
);
587 (void)_SCNetworkInterfaceSetMediaOptions(interface
, options
);
588 (void)_SCNetworkInterfaceSetMTU (interface
, options
);
589 CFDictionaryRemoveValue(baseSettings
, interfaceName
);
593 CFRelease(interface
);
599 linkConfigChangedCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *arg
)
601 CFDictionaryRef changes
;
604 static CFStringRef prefix
= NULL
;
606 if (prefix
== NULL
) {
607 prefix
= SCDynamicStoreKeyCreate(NULL
,
609 kSCDynamicStoreDomainSetup
,
614 changes
= SCDynamicStoreCopyMultiple(store
, changedKeys
, NULL
);
616 n
= CFArrayGetCount(changedKeys
);
617 for (i
= 0; i
< n
; i
++) {
619 CFDictionaryRef info
;
621 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
622 info
= CFDictionaryGetValue(changes
, key
);
624 if (CFEqual(key
, interfacesKey
)) {
625 CFArrayRef interfaces
;
627 interfaces
= CFDictionaryGetValue(info
, kSCPropNetInterfaces
);
628 if (isA_CFArray(interfaces
)) {
629 updateInterfaces(interfaces
);
632 CFStringRef interfaceName
;
634 interfaceName
= parse_component(key
, prefix
);
635 if (interfaceName
!= NULL
) {
636 updateLink(interfaceName
, info
);
637 CFRelease(interfaceName
);
650 load_LinkConfiguration(CFBundleRef bundle
, Boolean bundleVerbose
)
653 CFMutableArrayRef keys
= NULL
;
655 CFMutableArrayRef patterns
= NULL
;
661 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
662 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
664 /* initialize a few globals */
666 baseSettings
= CFDictionaryCreateMutable(NULL
,
668 &kCFTypeDictionaryKeyCallBacks
,
669 &kCFTypeDictionaryValueCallBacks
);
670 wantSettings
= CFDictionaryCreateMutable(NULL
,
672 &kCFTypeDictionaryKeyCallBacks
,
673 &kCFTypeDictionaryValueCallBacks
);
675 /* open a "configd" store to allow cache updates */
676 store
= SCDynamicStoreCreate(NULL
,
677 CFSTR("Link Configuraton plug-in"),
678 linkConfigChangedCallback
,
681 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
685 /* establish notification keys and patterns */
686 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
687 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
689 /* ...watch for a change in the list of network interfaces */
690 interfacesKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
691 kSCDynamicStoreDomainState
);
692 CFArrayAppendValue(keys
, interfacesKey
);
694 /* ...watch for (per-interface) AirPort configuration changes */
695 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
696 kSCDynamicStoreDomainSetup
,
699 CFArrayAppendValue(patterns
, key
);
702 /* ...watch for (per-interface) Ethernet configuration changes */
703 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
704 kSCDynamicStoreDomainSetup
,
707 CFArrayAppendValue(patterns
, key
);
710 /* ...watch for (per-interface) FireWire configuration changes */
711 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
712 kSCDynamicStoreDomainSetup
,
715 CFArrayAppendValue(patterns
, key
);
718 /* register the keys/patterns */
719 ok
= SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
724 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
725 SCErrorString(SCError()));
729 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
732 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
733 SCErrorString(SCError()));
737 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
742 if (baseSettings
!= NULL
) CFRelease(baseSettings
);
743 if (wantSettings
!= NULL
) CFRelease(wantSettings
);
744 if (store
!= NULL
) CFRelease(store
);
753 #pragma mark Standalone test code
757 main(int argc
, char **argv
)
759 SCPreferencesRef prefs
;
762 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
764 prefs
= SCPreferencesCreate(NULL
, CFSTR("linkconfig"), NULL
);
768 set
= SCNetworkSetCopyCurrent(prefs
);
770 CFMutableSetRef seen
;
773 services
= SCNetworkSetCopyServices(set
);
774 if (services
!= NULL
) {
778 seen
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
780 n
= CFArrayGetCount(services
);
781 for (i
= 0; i
< n
; i
++) {
782 SCNetworkInterfaceRef interface
;
783 SCNetworkServiceRef service
;
785 service
= CFArrayGetValueAtIndex(services
, i
);
786 interface
= SCNetworkServiceGetInterface(service
);
787 if ((interface
!= NULL
) &&
788 !CFSetContainsValue(seen
, interface
)){
789 CFDictionaryRef capabilities
;
791 capabilities
= SCNetworkInterfaceCopyCapability(interface
, NULL
);
792 if (capabilities
!= NULL
) {
795 CFDictionaryRef options
;
797 options
= SCNetworkInterfaceGetConfiguration(interface
);
798 cap_current
= __SCNetworkInterfaceCreateCapabilities(interface
, -1, NULL
);
799 cap_requested
= __SCNetworkInterfaceCreateCapabilities(interface
, cap_current
, options
);
801 SCPrint(TRUE
, stdout
,
802 CFSTR("%sinterface = %@, current = %p, requested = %p\n%@\n"),
803 (i
== 0) ? "" : "\n",
804 SCNetworkInterfaceGetBSDName(interface
),
805 (void *)(uintptr_t)cap_current
,
806 (void *)(uintptr_t)cap_requested
,
808 CFRelease(capabilities
);
811 CFSetAddValue(seen
, interface
);
825 load_LinkConfiguration(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);