2 * Copyright (c) 2002-2007, 2011, 2013 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/SCDPlugin.h> // for _SCDPluginExecCommand
45 #include "SCNetworkConfigurationInternal.h"
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
;
58 #pragma mark Capabilities
61 #define CAPABILITIES_KEY CFSTR("_CAPABILITIES_")
66 _SCNetworkInterfaceSetCapabilities(SCNetworkInterfaceRef interface
,
67 CFDictionaryRef options
)
69 CFDictionaryRef baseOptions
;
73 CFStringRef interfaceName
;
81 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
82 if (interfaceName
== NULL
) {
83 /* if no BSD interface name */
87 cap_current
= __SCNetworkInterfaceCreateCapabilities(interface
, -1, NULL
);
88 if (cap_current
== -1) {
89 /* could not get current capabilities */
93 // get base capabilities
94 cap_base
= cap_current
;
95 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
96 if (baseOptions
!= NULL
) {
99 num
= CFDictionaryGetValue(baseOptions
, CAPABILITIES_KEY
);
101 CFNumberGetValue(num
, kCFNumberIntType
, &cap_base
);
105 cap_requested
= __SCNetworkInterfaceCreateCapabilities(interface
, cap_base
, options
);
108 if (cap_requested
== cap_current
) {
109 /* if current setting is as requested */
113 bzero((char *)&ifr
, sizeof(ifr
));
114 (void)_SC_cfstring_to_cstring(interfaceName
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
), kCFStringEncodingASCII
);
115 ifr
.ifr_curcap
= cap_current
;
116 ifr
.ifr_reqcap
= cap_requested
;
118 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
120 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
124 ret
= ioctl(sock
, SIOCSIFCAP
, (caddr_t
)&ifr
);
127 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCSIFCAP) failed: %s"), strerror(errno
));
137 #pragma mark Media options
140 static CFDictionaryRef
141 __copyMediaOptions(CFDictionaryRef options
)
143 CFMutableDictionaryRef requested
= NULL
;
146 if (!isA_CFDictionary(options
)) {
150 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaSubType
);
151 if (isA_CFString(val
)) {
152 requested
= CFDictionaryCreateMutable(NULL
,
154 &kCFTypeDictionaryKeyCallBacks
,
155 &kCFTypeDictionaryValueCallBacks
);
156 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaSubType
, val
);
162 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaOptions
);
163 if (isA_CFArray(val
)) {
164 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaOptions
, val
);
167 CFRelease(requested
);
177 _SCNetworkInterfaceSetMediaOptions(SCNetworkInterfaceRef interface
,
178 CFDictionaryRef options
)
180 CFArrayRef available
= NULL
;
181 CFDictionaryRef current
= NULL
;
182 struct ifmediareq ifm
;
184 CFStringRef interfaceName
;
187 CFDictionaryRef requested
;
190 if (!isA_SCNetworkInterface(interface
)) {
191 _SCErrorSet(kSCStatusInvalidArgument
);
195 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
196 if (interfaceName
== NULL
) {
197 /* if no BSD interface name */
198 SCLog(_verbose
, LOG_INFO
, CFSTR("no BSD interface name for %@"), interface
);
199 _SCErrorSet(kSCStatusInvalidArgument
);
203 /* get current & available options */
204 if (!SCNetworkInterfaceCopyMediaOptions(interface
, ¤t
, NULL
, &available
, FALSE
)) {
205 /* could not get current media options */
206 SCLog(_verbose
, LOG_INFO
, CFSTR("no media options for %@"), interfaceName
);
210 /* extract just the dictionary key/value pairs of interest */
211 requested
= __copyMediaOptions(options
);
212 if (requested
== NULL
) {
213 CFDictionaryRef baseOptions
;
215 /* get base options */
216 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
217 requested
= __copyMediaOptions(baseOptions
);
219 if (requested
== NULL
) {
220 /* get base options */
221 requested
= __copyMediaOptions(current
);
223 if (requested
== NULL
) {
224 /* if no media options to set */
228 if ((current
!= NULL
) && CFEqual(current
, requested
)) {
229 /* if current settings are as requested */
234 if (!CFArrayContainsValue(available
, CFRangeMake(0, CFArrayGetCount(available
)), requested
)) {
235 /* if requested settings not currently available */
236 SCLog(_verbose
, LOG_INFO
, CFSTR("requested media settings unavailable for %@"), interfaceName
);
240 newOptions
= __SCNetworkInterfaceCreateMediaOptions(interface
, requested
);
241 if (newOptions
== -1) {
242 /* since we have just validated, this should never happen */
246 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
248 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
252 bzero((char *)&ifm
, sizeof(ifm
));
253 (void)_SC_cfstring_to_cstring(interfaceName
, ifm
.ifm_name
, sizeof(ifm
.ifm_name
), kCFStringEncodingASCII
);
255 if (ioctl(sock
, SIOCGIFMEDIA
, (caddr_t
)&ifm
) == -1) {
256 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno
));
260 bzero((char *)&ifr
, sizeof(ifr
));
261 bcopy(ifm
.ifm_name
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
));
262 ifr
.ifr_media
= ifm
.ifm_current
& ~(IFM_NMASK
|IFM_TMASK
|IFM_OMASK
|IFM_GMASK
);
263 ifr
.ifr_media
|= newOptions
;
265 SCLog(_verbose
, LOG_INFO
, CFSTR("old media settings: 0x%8.8x (0x%8.8x)"), ifm
.ifm_current
, ifm
.ifm_active
);
266 SCLog(_verbose
, LOG_INFO
, CFSTR("new media settings: 0x%8.8x"), ifr
.ifr_media
);
268 if (ioctl(sock
, SIOCSIFMEDIA
, (caddr_t
)&ifr
) == -1) {
269 SCLog(TRUE
, LOG_DEBUG
, CFSTR("%@: ioctl(SIOCSIFMEDIA) failed: %s"), interfaceName
, strerror(errno
));
277 if (available
!= NULL
) CFRelease(available
);
278 if (current
!= NULL
) CFRelease(current
);
279 if (requested
!= NULL
) CFRelease(requested
);
280 if (sock
!= -1) (void)close(sock
);
290 #ifndef USE_SIOCSIFMTU
292 ifconfig_exit(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
294 char *if_name
= (char *)context
;
296 if (WIFEXITED(status
)) {
297 if (WEXITSTATUS(status
) != 0) {
299 CFSTR("ifconfig %s failed, exit status = %d"),
301 WEXITSTATUS(status
));
303 } else if (WIFSIGNALED(status
)) {
304 SCLog(TRUE
, LOG_DEBUG
,
305 CFSTR("ifconfig %s: terminated w/signal = %d"),
309 SCLog(TRUE
, LOG_DEBUG
,
310 CFSTR("ifconfig %s: exit status = %d"),
315 CFAllocatorDeallocate(NULL
, if_name
);
318 #endif /* !USE_SIOCSIFMTU */
323 _SCNetworkInterfaceSetMTU(SCNetworkInterfaceRef interface
,
324 CFDictionaryRef options
)
326 CFStringRef interfaceName
;
333 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
334 if (interfaceName
== NULL
) {
335 /* if no BSD interface name */
339 if (!SCNetworkInterfaceCopyMTU(interface
, &mtu_cur
, &mtu_min
, &mtu_max
)) {
340 /* could not get current MTU */
345 if (isA_CFDictionary(options
)) {
346 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMTU
);
347 val
= isA_CFNumber(val
);
350 CFDictionaryRef baseOptions
;
353 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
354 if (baseOptions
!= NULL
) {
355 val
= CFDictionaryGetValue(baseOptions
, kSCPropNetEthernetMTU
);
359 CFNumberGetValue(val
, kCFNumberIntType
, &requested
);
364 if (requested
== mtu_cur
) {
365 /* if current setting is as requested */
369 if (((mtu_min
>= 0) && (requested
< mtu_min
)) ||
370 ((mtu_max
>= 0) && (requested
> mtu_max
))) {
371 /* if requested MTU outside of the valid range */
375 #ifdef USE_SIOCSIFMTU
381 bzero((char *)&ifr
, sizeof(ifr
));
382 (void)_SC_cfstring_to_cstring(interfaceName
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
), kCFStringEncodingASCII
);
383 ifr
.ifr_mtu
= requested
;
385 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
387 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
391 ret
= ioctl(sock
, SIOCSIFMTU
, (caddr_t
)&ifr
);
394 SCLog(TRUE
, LOG_DEBUG
, CFSTR("ioctl(SIOCSIFMTU) failed: %s"), strerror(errno
));
398 #else /* !USE_SIOCSIFMTU */
400 char *ifconfig_argv
[] = { "ifconfig", NULL
, "mtu", NULL
, NULL
};
403 ifconfig_argv
[1] = _SC_cfstring_to_cstring(interfaceName
, NULL
, 0, kCFStringEncodingASCII
);
404 (void)asprintf(&ifconfig_argv
[3], "%d", requested
);
406 pid
= _SCDPluginExecCommand(ifconfig_exit
, // callout,
407 ifconfig_argv
[1], // context
410 "/sbin/ifconfig", // path
411 ifconfig_argv
// argv
414 // CFAllocatorDeallocate(NULL, ifconfig_argv[1]); // released in ifconfig_exit()
415 free(ifconfig_argv
[3]);
421 #endif /* !USE_SIOCSIFMTU */
428 #pragma mark Update link configuration
432 * Function: parse_component
434 * Given a string 'key' and a string prefix 'prefix',
435 * return the next component in the slash '/' separated
439 * 1. key = "a/b/c" prefix = "a/"
441 * 2. key = "a/b/c" prefix = "a/b/"
444 static CF_RETURNS_RETAINED CFStringRef
445 parse_component(CFStringRef key
, CFStringRef prefix
)
447 CFMutableStringRef comp
;
450 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
453 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
454 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
455 range
= CFStringFind(comp
, CFSTR("/"), 0);
456 if (range
.location
== kCFNotFound
) {
459 range
.length
= CFStringGetLength(comp
) - range
.location
;
460 CFStringDelete(comp
, range
);
465 static void updateLink(CFStringRef interfaceName
, CFDictionaryRef options
);
469 updateInterfaces(CFArrayRef newInterfaces
)
474 static CFArrayRef oldInterfaces
= NULL
;
476 n_old
= (oldInterfaces
!= NULL
) ? CFArrayGetCount(oldInterfaces
) : 0;
477 n_new
= CFArrayGetCount(newInterfaces
);
479 for (i
= 0; i
< n_new
; i
++) {
480 CFStringRef interfaceName
;
482 interfaceName
= CFArrayGetValueAtIndex(newInterfaces
, i
);
485 !CFArrayContainsValue(oldInterfaces
,
486 CFRangeMake(0, n_old
),
488 CFDictionaryRef options
;
491 options
= CFDictionaryGetValue(wantSettings
, interfaceName
);
492 updateLink(interfaceName
, options
);
496 if (oldInterfaces
!= NULL
) CFRelease(oldInterfaces
);
497 oldInterfaces
= CFRetain(newInterfaces
);
502 updateLink(CFStringRef interfaceName
, CFDictionaryRef options
)
504 SCNetworkInterfaceRef interface
;
506 /* retain requested configuration */
507 if (options
!= NULL
) {
508 CFDictionarySetValue(wantSettings
, interfaceName
, options
);
510 CFDictionaryRemoveValue(wantSettings
, interfaceName
);
513 /* apply requested configuration */
514 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, interfaceName
,
515 kIncludeAllVirtualInterfaces
);
516 if (interface
== NULL
) {
520 if (options
!= NULL
) {
521 if (!CFDictionaryContainsKey(baseSettings
, interfaceName
)) {
523 CFDictionaryRef cur_media
= NULL
;
524 CFMutableDictionaryRef new_media
= NULL
;
527 /* preserve current media options */
528 if (SCNetworkInterfaceCopyMediaOptions(interface
, &cur_media
, NULL
, NULL
, FALSE
)) {
529 if (cur_media
!= NULL
) {
530 new_media
= CFDictionaryCreateMutableCopy(NULL
, 0, cur_media
);
531 CFRelease(cur_media
);
535 /* preserve current MTU */
536 if (SCNetworkInterfaceCopyMTU(interface
, &cur_mtu
, NULL
, NULL
)) {
540 if (new_media
== NULL
) {
541 new_media
= CFDictionaryCreateMutable(NULL
,
543 &kCFTypeDictionaryKeyCallBacks
,
544 &kCFTypeDictionaryValueCallBacks
);
547 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cur_mtu
);
548 CFDictionaryAddValue(new_media
, kSCPropNetEthernetMTU
, num
);
553 /* preserve capabilities */
554 cur_cap
= __SCNetworkInterfaceCreateCapabilities(interface
, -1, NULL
);
558 if (new_media
== NULL
) {
559 new_media
= CFDictionaryCreateMutable(NULL
,
561 &kCFTypeDictionaryKeyCallBacks
,
562 &kCFTypeDictionaryValueCallBacks
);
565 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cur_cap
);
566 CFDictionaryAddValue(new_media
, CAPABILITIES_KEY
, num
);
570 if (new_media
!= NULL
) {
571 CFDictionarySetValue(baseSettings
, interfaceName
, new_media
);
572 CFRelease(new_media
);
576 /* establish new settings */
577 (void)_SCNetworkInterfaceSetCapabilities(interface
, options
);
578 (void)_SCNetworkInterfaceSetMediaOptions(interface
, options
);
579 (void)_SCNetworkInterfaceSetMTU (interface
, options
);
581 /* no requested settings */
582 options
= CFDictionaryGetValue(baseSettings
, interfaceName
);
583 if (options
!= NULL
) {
584 /* restore original settings */
585 (void)_SCNetworkInterfaceSetCapabilities(interface
, options
);
586 (void)_SCNetworkInterfaceSetMediaOptions(interface
, options
);
587 (void)_SCNetworkInterfaceSetMTU (interface
, options
);
588 CFDictionaryRemoveValue(baseSettings
, interfaceName
);
592 CFRelease(interface
);
598 linkConfigChangedCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *arg
)
600 CFDictionaryRef changes
;
603 static CFStringRef prefix
= NULL
;
605 if (prefix
== NULL
) {
606 prefix
= SCDynamicStoreKeyCreate(NULL
,
608 kSCDynamicStoreDomainSetup
,
613 changes
= SCDynamicStoreCopyMultiple(store
, changedKeys
, NULL
);
615 n
= CFArrayGetCount(changedKeys
);
616 for (i
= 0; i
< n
; i
++) {
618 CFDictionaryRef info
;
620 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
621 info
= CFDictionaryGetValue(changes
, key
);
623 if (CFEqual(key
, interfacesKey
)) {
624 CFArrayRef interfaces
;
626 interfaces
= CFDictionaryGetValue(info
, kSCPropNetInterfaces
);
627 if (isA_CFArray(interfaces
)) {
628 updateInterfaces(interfaces
);
631 CFStringRef interfaceName
;
633 interfaceName
= parse_component(key
, prefix
);
634 if (interfaceName
!= NULL
) {
635 updateLink(interfaceName
, info
);
636 CFRelease(interfaceName
);
649 load_LinkConfiguration(CFBundleRef bundle
, Boolean bundleVerbose
)
652 CFMutableArrayRef keys
= NULL
;
654 CFMutableArrayRef patterns
= NULL
;
660 SCLog(_verbose
, LOG_DEBUG
, CFSTR("load() called"));
661 SCLog(_verbose
, LOG_DEBUG
, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle
));
663 /* initialize a few globals */
665 baseSettings
= CFDictionaryCreateMutable(NULL
,
667 &kCFTypeDictionaryKeyCallBacks
,
668 &kCFTypeDictionaryValueCallBacks
);
669 wantSettings
= CFDictionaryCreateMutable(NULL
,
671 &kCFTypeDictionaryKeyCallBacks
,
672 &kCFTypeDictionaryValueCallBacks
);
674 /* open a "configd" store to allow cache updates */
675 store
= SCDynamicStoreCreate(NULL
,
676 CFSTR("Link Configuraton plug-in"),
677 linkConfigChangedCallback
,
680 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
684 /* establish notification keys and patterns */
685 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
686 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
688 /* ...watch for a change in the list of network interfaces */
689 interfacesKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
690 kSCDynamicStoreDomainState
);
691 CFArrayAppendValue(keys
, interfacesKey
);
693 /* ...watch for (per-interface) AirPort configuration changes */
694 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
695 kSCDynamicStoreDomainSetup
,
698 CFArrayAppendValue(patterns
, key
);
701 /* ...watch for (per-interface) Ethernet configuration changes */
702 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
703 kSCDynamicStoreDomainSetup
,
706 CFArrayAppendValue(patterns
, key
);
709 /* ...watch for (per-interface) FireWire configuration changes */
710 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
711 kSCDynamicStoreDomainSetup
,
714 CFArrayAppendValue(patterns
, key
);
717 /* register the keys/patterns */
718 ok
= SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
723 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
724 SCErrorString(SCError()));
728 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
731 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
732 SCErrorString(SCError()));
736 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
741 if (baseSettings
!= NULL
) CFRelease(baseSettings
);
742 if (wantSettings
!= NULL
) CFRelease(wantSettings
);
743 if (store
!= NULL
) CFRelease(store
);
752 #pragma mark Standalone test code
756 main(int argc
, char **argv
)
758 SCPreferencesRef prefs
;
761 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
763 prefs
= SCPreferencesCreate(NULL
, CFSTR("linkconfig"), NULL
);
767 set
= SCNetworkSetCopyCurrent(prefs
);
769 CFMutableSetRef seen
;
772 services
= SCNetworkSetCopyServices(set
);
773 if (services
!= NULL
) {
777 seen
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
779 n
= CFArrayGetCount(services
);
780 for (i
= 0; i
< n
; i
++) {
781 SCNetworkInterfaceRef interface
;
782 SCNetworkServiceRef service
;
784 service
= CFArrayGetValueAtIndex(services
, i
);
785 interface
= SCNetworkServiceGetInterface(service
);
786 if ((interface
!= NULL
) &&
787 !CFSetContainsValue(seen
, interface
)) {
788 CFDictionaryRef capabilities
;
790 capabilities
= SCNetworkInterfaceCopyCapability(interface
, NULL
);
791 if (capabilities
!= NULL
) {
794 CFDictionaryRef options
;
796 options
= SCNetworkInterfaceGetConfiguration(interface
);
797 cap_current
= __SCNetworkInterfaceCreateCapabilities(interface
, -1, NULL
);
798 cap_requested
= __SCNetworkInterfaceCreateCapabilities(interface
, cap_current
, options
);
800 SCPrint(TRUE
, stdout
,
801 CFSTR("%sinterface = %@, current = %p, requested = %p\n%@\n"),
802 (i
== 0) ? "" : "\n",
803 SCNetworkInterfaceGetBSDName(interface
),
804 (void *)(uintptr_t)cap_current
,
805 (void *)(uintptr_t)cap_requested
,
807 CFRelease(capabilities
);
810 CFSetAddValue(seen
, interface
);
824 load_LinkConfiguration(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);