2 * Copyright (c) 2002-2007, 2011, 2013, 2015 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
;
56 #pragma mark Capabilities
59 #define CAPABILITIES_KEY CFSTR("_CAPABILITIES_")
64 _SCNetworkInterfaceSetCapabilities(SCNetworkInterfaceRef interface
,
65 CFDictionaryRef options
)
67 CFDictionaryRef baseOptions
;
71 CFStringRef interfaceName
;
79 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
80 if (interfaceName
== NULL
) {
81 /* if no BSD interface name */
85 cap_current
= __SCNetworkInterfaceCreateCapabilities(interface
, -1, NULL
);
86 if (cap_current
== -1) {
87 /* could not get current capabilities */
91 // get base capabilities
92 cap_base
= cap_current
;
93 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
94 if (baseOptions
!= NULL
) {
97 num
= CFDictionaryGetValue(baseOptions
, CAPABILITIES_KEY
);
99 CFNumberGetValue(num
, kCFNumberIntType
, &cap_base
);
103 cap_requested
= __SCNetworkInterfaceCreateCapabilities(interface
, cap_base
, options
);
106 if (cap_requested
== cap_current
) {
107 /* if current setting is as requested */
111 bzero((char *)&ifr
, sizeof(ifr
));
112 (void)_SC_cfstring_to_cstring(interfaceName
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
), kCFStringEncodingASCII
);
113 ifr
.ifr_curcap
= cap_current
;
114 ifr
.ifr_reqcap
= cap_requested
;
116 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
118 SC_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
122 ret
= ioctl(sock
, SIOCSIFCAP
, (caddr_t
)&ifr
);
125 SC_log(LOG_INFO
, "ioctl(SIOCSIFCAP) failed: %s", strerror(errno
));
135 #pragma mark Media options
138 static CFDictionaryRef
139 __copyMediaOptions(CFDictionaryRef options
)
141 CFMutableDictionaryRef requested
= NULL
;
144 if (!isA_CFDictionary(options
)) {
148 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaSubType
);
149 if (isA_CFString(val
)) {
150 requested
= CFDictionaryCreateMutable(NULL
,
152 &kCFTypeDictionaryKeyCallBacks
,
153 &kCFTypeDictionaryValueCallBacks
);
154 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaSubType
, val
);
160 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMediaOptions
);
161 if (isA_CFArray(val
)) {
162 CFDictionaryAddValue(requested
, kSCPropNetEthernetMediaOptions
, val
);
165 CFRelease(requested
);
175 _SCNetworkInterfaceSetMediaOptions(SCNetworkInterfaceRef interface
,
176 CFDictionaryRef options
)
178 CFArrayRef available
= NULL
;
179 CFDictionaryRef current
= NULL
;
180 struct ifmediareq ifm
;
182 CFStringRef interfaceName
;
185 CFDictionaryRef requested
;
188 if (!isA_SCNetworkInterface(interface
)) {
189 _SCErrorSet(kSCStatusInvalidArgument
);
193 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
194 if (interfaceName
== NULL
) {
195 /* if no BSD interface name */
196 SC_log(LOG_INFO
, "no BSD interface name for %@", interface
);
197 _SCErrorSet(kSCStatusInvalidArgument
);
201 /* get current & available options */
202 if (!SCNetworkInterfaceCopyMediaOptions(interface
, ¤t
, NULL
, &available
, FALSE
)) {
203 /* could not get current media options */
204 SC_log(LOG_INFO
, "no media options for %@", interfaceName
);
208 /* extract just the dictionary key/value pairs of interest */
209 requested
= __copyMediaOptions(options
);
210 if (requested
== NULL
) {
211 CFDictionaryRef baseOptions
;
213 /* get base options */
214 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
215 requested
= __copyMediaOptions(baseOptions
);
217 if (requested
== NULL
) {
218 /* get base options */
219 requested
= __copyMediaOptions(current
);
221 if (requested
== NULL
) {
222 /* if no media options to set */
226 if ((current
!= NULL
) && CFEqual(current
, requested
)) {
227 /* if current settings are as requested */
232 if (!CFArrayContainsValue(available
, CFRangeMake(0, CFArrayGetCount(available
)), requested
)) {
233 /* if requested settings not currently available */
234 SC_log(LOG_INFO
, "requested media settings unavailable for %@", interfaceName
);
238 newOptions
= __SCNetworkInterfaceCreateMediaOptions(interface
, requested
);
239 if (newOptions
== -1) {
240 /* since we have just validated, this should never happen */
244 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
246 SC_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
250 bzero((char *)&ifm
, sizeof(ifm
));
251 (void)_SC_cfstring_to_cstring(interfaceName
, ifm
.ifm_name
, sizeof(ifm
.ifm_name
), kCFStringEncodingASCII
);
253 if (ioctl(sock
, SIOCGIFMEDIA
, (caddr_t
)&ifm
) == -1) {
254 SC_log(LOG_NOTICE
, "ioctl(SIOCGIFMEDIA) failed: %s", strerror(errno
));
258 bzero((char *)&ifr
, sizeof(ifr
));
259 bcopy(ifm
.ifm_name
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
));
260 ifr
.ifr_media
= ifm
.ifm_current
& ~(IFM_NMASK
|IFM_TMASK
|IFM_OMASK
|IFM_GMASK
);
261 ifr
.ifr_media
|= newOptions
;
263 SC_log(LOG_INFO
, "old media settings: 0x%8.8x (0x%8.8x)", ifm
.ifm_current
, ifm
.ifm_active
);
264 SC_log(LOG_INFO
, "new media settings: 0x%8.8x", ifr
.ifr_media
);
266 if (ioctl(sock
, SIOCSIFMEDIA
, (caddr_t
)&ifr
) == -1) {
267 SC_log(LOG_NOTICE
, "%@: ioctl(SIOCSIFMEDIA) failed: %s", interfaceName
, strerror(errno
));
275 if (available
!= NULL
) CFRelease(available
);
276 if (current
!= NULL
) CFRelease(current
);
277 if (requested
!= NULL
) CFRelease(requested
);
278 if (sock
!= -1) (void)close(sock
);
288 #ifndef USE_SIOCSIFMTU
290 ifconfig_exit(pid_t pid
, int status
, struct rusage
*rusage
, void *context
)
292 char *if_name
= (char *)context
;
294 if (WIFEXITED(status
)) {
295 if (WEXITSTATUS(status
) != 0) {
296 SC_log(LOG_NOTICE
, "ifconfig %s failed, exit status = %d",
298 WEXITSTATUS(status
));
300 } else if (WIFSIGNALED(status
)) {
301 SC_log(LOG_NOTICE
, "ifconfig %s: terminated w/signal = %d",
305 SC_log(LOG_NOTICE
, "ifconfig %s: exit status = %d",
310 CFAllocatorDeallocate(NULL
, if_name
);
313 #endif /* !USE_SIOCSIFMTU */
318 _SCNetworkInterfaceSetMTU(SCNetworkInterfaceRef interface
,
319 CFDictionaryRef options
)
321 CFStringRef interfaceName
;
328 interfaceName
= SCNetworkInterfaceGetBSDName(interface
);
329 if (interfaceName
== NULL
) {
330 /* if no BSD interface name */
334 if (!SCNetworkInterfaceCopyMTU(interface
, &mtu_cur
, &mtu_min
, &mtu_max
)) {
335 /* could not get current MTU */
340 if (isA_CFDictionary(options
)) {
341 val
= CFDictionaryGetValue(options
, kSCPropNetEthernetMTU
);
342 val
= isA_CFNumber(val
);
345 CFDictionaryRef baseOptions
;
348 baseOptions
= CFDictionaryGetValue(baseSettings
, interfaceName
);
349 if (baseOptions
!= NULL
) {
350 val
= CFDictionaryGetValue(baseOptions
, kSCPropNetEthernetMTU
);
354 CFNumberGetValue(val
, kCFNumberIntType
, &requested
);
359 if (requested
== mtu_cur
) {
360 /* if current setting is as requested */
364 if (((mtu_min
>= 0) && (requested
< mtu_min
)) ||
365 ((mtu_max
>= 0) && (requested
> mtu_max
))) {
366 /* if requested MTU outside of the valid range */
370 #ifdef USE_SIOCSIFMTU
376 bzero((char *)&ifr
, sizeof(ifr
));
377 (void)_SC_cfstring_to_cstring(interfaceName
, ifr
.ifr_name
, sizeof(ifr
.ifr_name
), kCFStringEncodingASCII
);
378 ifr
.ifr_mtu
= requested
;
380 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
382 SC_log(LOG_ERR
, "socket() failed: %s", strerror(errno
));
386 ret
= ioctl(sock
, SIOCSIFMTU
, (caddr_t
)&ifr
);
389 SC_log(LOG_NOTICE
, "ioctl(SIOCSIFMTU) failed: %s", strerror(errno
));
393 #else /* !USE_SIOCSIFMTU */
395 char *ifconfig_argv
[] = { "ifconfig", NULL
, "mtu", NULL
, NULL
};
398 ifconfig_argv
[1] = _SC_cfstring_to_cstring(interfaceName
, NULL
, 0, kCFStringEncodingASCII
);
399 (void)asprintf(&ifconfig_argv
[3], "%d", requested
);
401 pid
= _SCDPluginExecCommand(ifconfig_exit
, // callout,
402 ifconfig_argv
[1], // context
405 "/sbin/ifconfig", // path
406 ifconfig_argv
// argv
409 // CFAllocatorDeallocate(NULL, ifconfig_argv[1]); // released in ifconfig_exit()
410 free(ifconfig_argv
[3]);
416 #endif /* !USE_SIOCSIFMTU */
423 #pragma mark Update link configuration
427 * Function: parse_component
429 * Given a string 'key' and a string prefix 'prefix',
430 * return the next component in the slash '/' separated
434 * 1. key = "a/b/c" prefix = "a/"
436 * 2. key = "a/b/c" prefix = "a/b/"
439 static CF_RETURNS_RETAINED CFStringRef
440 parse_component(CFStringRef key
, CFStringRef prefix
)
442 CFMutableStringRef comp
;
445 if (CFStringHasPrefix(key
, prefix
) == FALSE
) {
448 comp
= CFStringCreateMutableCopy(NULL
, 0, key
);
449 CFStringDelete(comp
, CFRangeMake(0, CFStringGetLength(prefix
)));
450 range
= CFStringFind(comp
, CFSTR("/"), 0);
451 if (range
.location
== kCFNotFound
) {
454 range
.length
= CFStringGetLength(comp
) - range
.location
;
455 CFStringDelete(comp
, range
);
460 static void updateLink(CFStringRef interfaceName
, CFDictionaryRef options
);
464 updateInterfaces(CFArrayRef newInterfaces
)
469 static CFArrayRef oldInterfaces
= NULL
;
471 n_old
= (oldInterfaces
!= NULL
) ? CFArrayGetCount(oldInterfaces
) : 0;
472 n_new
= CFArrayGetCount(newInterfaces
);
474 for (i
= 0; i
< n_new
; i
++) {
475 CFStringRef interfaceName
;
477 interfaceName
= CFArrayGetValueAtIndex(newInterfaces
, i
);
480 !CFArrayContainsValue(oldInterfaces
,
481 CFRangeMake(0, n_old
),
483 CFDictionaryRef options
;
486 options
= CFDictionaryGetValue(wantSettings
, interfaceName
);
487 updateLink(interfaceName
, options
);
491 if (oldInterfaces
!= NULL
) CFRelease(oldInterfaces
);
492 oldInterfaces
= CFRetain(newInterfaces
);
497 updateLink(CFStringRef interfaceName
, CFDictionaryRef options
)
499 SCNetworkInterfaceRef interface
;
501 /* retain requested configuration */
502 if (options
!= NULL
) {
503 CFDictionarySetValue(wantSettings
, interfaceName
, options
);
505 CFDictionaryRemoveValue(wantSettings
, interfaceName
);
508 /* apply requested configuration */
509 interface
= _SCNetworkInterfaceCreateWithBSDName(NULL
, interfaceName
,
510 kIncludeAllVirtualInterfaces
);
511 if (interface
== NULL
) {
515 if (options
!= NULL
) {
516 if (!CFDictionaryContainsKey(baseSettings
, interfaceName
)) {
518 CFDictionaryRef cur_media
= NULL
;
519 CFMutableDictionaryRef new_media
= NULL
;
522 /* preserve current media options */
523 if (SCNetworkInterfaceCopyMediaOptions(interface
, &cur_media
, NULL
, NULL
, FALSE
)) {
524 if (cur_media
!= NULL
) {
525 new_media
= CFDictionaryCreateMutableCopy(NULL
, 0, cur_media
);
526 CFRelease(cur_media
);
530 /* preserve current MTU */
531 if (SCNetworkInterfaceCopyMTU(interface
, &cur_mtu
, NULL
, NULL
)) {
535 if (new_media
== NULL
) {
536 new_media
= CFDictionaryCreateMutable(NULL
,
538 &kCFTypeDictionaryKeyCallBacks
,
539 &kCFTypeDictionaryValueCallBacks
);
542 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cur_mtu
);
543 CFDictionaryAddValue(new_media
, kSCPropNetEthernetMTU
, num
);
548 /* preserve capabilities */
549 cur_cap
= __SCNetworkInterfaceCreateCapabilities(interface
, -1, NULL
);
553 if (new_media
== NULL
) {
554 new_media
= CFDictionaryCreateMutable(NULL
,
556 &kCFTypeDictionaryKeyCallBacks
,
557 &kCFTypeDictionaryValueCallBacks
);
560 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cur_cap
);
561 CFDictionaryAddValue(new_media
, CAPABILITIES_KEY
, num
);
565 if (new_media
!= NULL
) {
566 CFDictionarySetValue(baseSettings
, interfaceName
, new_media
);
567 CFRelease(new_media
);
571 /* establish new settings */
572 (void)_SCNetworkInterfaceSetCapabilities(interface
, options
);
573 (void)_SCNetworkInterfaceSetMediaOptions(interface
, options
);
574 (void)_SCNetworkInterfaceSetMTU (interface
, options
);
576 /* no requested settings */
577 options
= CFDictionaryGetValue(baseSettings
, interfaceName
);
578 if (options
!= NULL
) {
579 /* restore original settings */
580 (void)_SCNetworkInterfaceSetCapabilities(interface
, options
);
581 (void)_SCNetworkInterfaceSetMediaOptions(interface
, options
);
582 (void)_SCNetworkInterfaceSetMTU (interface
, options
);
583 CFDictionaryRemoveValue(baseSettings
, interfaceName
);
587 CFRelease(interface
);
593 linkConfigChangedCallback(SCDynamicStoreRef store
, CFArrayRef changedKeys
, void *arg
)
595 os_activity_t activity_id
;
596 CFDictionaryRef changes
;
599 static CFStringRef prefix
= NULL
;
601 activity_id
= os_activity_start("processing link configuration changes",
602 OS_ACTIVITY_FLAG_DEFAULT
);
604 if (prefix
== NULL
) {
605 prefix
= SCDynamicStoreKeyCreate(NULL
,
607 kSCDynamicStoreDomainSetup
,
612 changes
= SCDynamicStoreCopyMultiple(store
, changedKeys
, NULL
);
614 n
= (changes
!= NULL
) ? CFArrayGetCount(changedKeys
) : 0;
615 for (i
= 0; i
< n
; i
++) {
617 CFDictionaryRef info
;
619 key
= CFArrayGetValueAtIndex(changedKeys
, i
);
620 info
= CFDictionaryGetValue(changes
, key
);
622 if (CFEqual(key
, interfacesKey
)) {
623 if (isA_CFDictionary(info
) != NULL
) {
624 CFArrayRef interfaces
;
626 interfaces
= CFDictionaryGetValue(info
, kSCPropNetInterfaces
);
627 if (isA_CFArray(interfaces
)) {
628 updateInterfaces(interfaces
);
632 CFStringRef interfaceName
;
634 interfaceName
= parse_component(key
, prefix
);
635 if (interfaceName
!= NULL
) {
636 updateLink(interfaceName
, info
);
637 CFRelease(interfaceName
);
642 if (changes
!= NULL
) {
646 os_activity_end(activity_id
);
654 load_LinkConfiguration(CFBundleRef bundle
, Boolean bundleVerbose
)
657 CFMutableArrayRef keys
= NULL
;
659 CFMutableArrayRef patterns
= NULL
;
661 SC_log(LOG_DEBUG
, "load() called");
662 SC_log(LOG_DEBUG
, " 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 SC_log(LOG_ERR
, "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
);
723 SC_log(LOG_NOTICE
, "SCDynamicStoreSetNotificationKeys() failed: %s",
724 SCErrorString(SCError()));
728 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
730 SC_log(LOG_NOTICE
, "SCDynamicStoreCreateRunLoopSource() failed: %s",
731 SCErrorString(SCError()));
735 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
740 if (baseSettings
!= NULL
) CFRelease(baseSettings
);
741 if (wantSettings
!= NULL
) CFRelease(wantSettings
);
742 if (store
!= NULL
) CFRelease(store
);
751 #pragma mark Standalone test code
755 main(int argc
, char **argv
)
757 SCPreferencesRef prefs
;
760 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
762 prefs
= SCPreferencesCreate(NULL
, CFSTR("linkconfig"), NULL
);
766 set
= SCNetworkSetCopyCurrent(prefs
);
768 CFMutableSetRef seen
;
771 services
= SCNetworkSetCopyServices(set
);
772 if (services
!= NULL
) {
776 seen
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
778 n
= CFArrayGetCount(services
);
779 for (i
= 0; i
< n
; i
++) {
780 SCNetworkInterfaceRef interface
;
781 SCNetworkServiceRef service
;
783 service
= CFArrayGetValueAtIndex(services
, i
);
784 interface
= SCNetworkServiceGetInterface(service
);
785 if ((interface
!= NULL
) &&
786 !CFSetContainsValue(seen
, interface
)) {
787 CFDictionaryRef capabilities
;
789 capabilities
= SCNetworkInterfaceCopyCapability(interface
, NULL
);
790 if (capabilities
!= NULL
) {
793 CFDictionaryRef options
;
795 options
= SCNetworkInterfaceGetConfiguration(interface
);
796 cap_current
= __SCNetworkInterfaceCreateCapabilities(interface
, -1, NULL
);
797 cap_requested
= __SCNetworkInterfaceCreateCapabilities(interface
, cap_current
, options
);
799 SCPrint(TRUE
, stdout
,
800 CFSTR("%sinterface = %@, current = %p, requested = %p\n%@\n"),
801 (i
== 0) ? "" : "\n",
802 SCNetworkInterfaceGetBSDName(interface
),
803 (void *)(uintptr_t)cap_current
,
804 (void *)(uintptr_t)cap_requested
,
806 CFRelease(capabilities
);
809 CFSetAddValue(seen
, interface
);
823 load_LinkConfiguration(CFBundleGetMainBundle(), (argc
> 1) ? TRUE
: FALSE
);