2 * Copyright (c) 2002-2020 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 * August 5, 2002 Allan Nathanson <ajn@apple.com>
28 * - split code out from eventmon.c
36 create_interface_cfstring(const char * if_name
)
38 CFStringRef interface
;
40 interface
= CFStringCreateWithCString(NULL
, if_name
,
41 kCFStringEncodingUTF8
);
46 create_interface_key(CFStringRef interface
)
50 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
51 kSCDynamicStoreDomainState
,
58 static CFMutableDictionaryRef
59 copy_mutable_dictionary(CFDictionaryRef dict
)
61 CFMutableDictionaryRef newDict
;
63 if (isA_CFDictionary(dict
) != NULL
) {
64 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
67 newDict
= CFDictionaryCreateMutable(NULL
,
69 &kCFTypeDictionaryKeyCallBacks
,
70 &kCFTypeDictionaryValueCallBacks
);
75 static CFMutableDictionaryRef
76 copy_entity(CFStringRef key
)
79 CFMutableDictionaryRef newDict
= NULL
;
81 dict
= SCDynamicStoreCopyValue(store
, key
);
82 newDict
= copy_mutable_dictionary(dict
);
91 interface_update_status(const char *if_name
,
92 CFStringRef interface
,
93 CFBooleanRef active
, boolean_t attach
,
94 CFBooleanRef expensive
, boolean_t only_if_different
)
96 CFStringRef key
= NULL
;
97 CFMutableDictionaryRef newDict
;
98 CFDictionaryRef oldDict
;
100 key
= create_interface_key(interface
);
101 oldDict
= SCDynamicStoreCopyValue(store
, key
);
102 if (oldDict
!= NULL
&& isA_CFDictionary(oldDict
) == NULL
) {
106 newDict
= copy_mutable_dictionary(oldDict
);
108 /* if new status available, update cache */
109 if (active
!= NULL
) {
110 CFDictionarySetValue(newDict
, kSCPropNetLinkActive
, active
);
112 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkActive
);
116 /* the interface was attached, remove stale state */
117 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkDetaching
);
120 if ((expensive
!= NULL
) && CFBooleanGetValue(expensive
)) {
121 CFDictionarySetValue(newDict
, kSCPropNetLinkExpensive
, expensive
);
123 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkExpensive
);
126 /* update the SCDynamicStore */
127 if (CFDictionaryGetCount(newDict
) > 0) {
129 if (!only_if_different
131 || !CFEqual(oldDict
, newDict
)) {
132 SC_log(LOG_DEBUG
, "Update interface link status: %s: %@", if_name
, newDict
);
133 SCDynamicStoreSetValue(store
, key
, newDict
);
136 /* remove the value */
137 if (!only_if_different
138 || oldDict
!= NULL
) {
139 SC_log(LOG_DEBUG
, "Update interface link status: %s: <removed>", if_name
);
140 SCDynamicStoreRemoveValue(store
, key
);
146 if (oldDict
!= NULL
) {
153 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
155 create_linkquality_key(CFStringRef interface
)
159 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
160 kSCDynamicStoreDomainState
,
162 kSCEntNetLinkQuality
);
169 interface_update_quality_metric(const char *if_name
,
173 CFStringRef interface
;
174 CFMutableDictionaryRef newDict
;
176 interface
= create_interface_cfstring(if_name
);
177 key
= create_linkquality_key(interface
);
178 newDict
= copy_entity(key
);
180 if (quality
!= IFNET_LQM_THRESH_UNKNOWN
) {
181 CFNumberRef linkquality
;
183 linkquality
= CFNumberCreate(NULL
, kCFNumberIntType
, &quality
);
184 CFDictionarySetValue(newDict
, kSCPropNetLinkQuality
, linkquality
);
185 CFRelease(linkquality
);
187 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkQuality
);
191 if (CFDictionaryGetCount(newDict
) > 0) {
192 SC_log(LOG_DEBUG
, "Update interface link quality: %s: %@", if_name
, newDict
);
193 SCDynamicStoreSetValue(store
, key
, newDict
);
195 SC_log(LOG_DEBUG
, "Update interface link quality: %s: <unknown>", if_name
);
196 SCDynamicStoreRemoveValue(store
, key
);
199 CFRelease(interface
);
208 link_update_quality_metric(const char *if_name
)
211 int quality
= IFNET_LQM_THRESH_UNKNOWN
;
214 sock
= dgram_socket(AF_INET
);
219 memset((char *)&ifr
, 0, sizeof(ifr
));
220 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s", if_name
);
222 if (ioctl(sock
, SIOCGIFLINKQUALITYMETRIC
, (caddr_t
)&ifr
) != -1) {
223 quality
= ifr
.ifr_link_quality_metric
;
228 interface_update_quality_metric(if_name
, quality
);
236 #endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
241 create_link_issues_key(CFStringRef interface
)
245 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
246 kSCDynamicStoreDomainState
,
248 kSCEntNetLinkIssues
);
255 interface_update_link_issues(const char *if_name
,
263 CFStringRef interface
;
266 CFMutableDictionaryRef newDict
;
269 interface
= create_interface_cfstring(if_name
);
270 key
= create_link_issues_key(interface
);
272 newDict
= copy_entity(key
);
274 modidData
= CFDataCreate(NULL
, modid
, modid_size
);
275 CFDictionarySetValue(newDict
, kSCPropNetLinkIssuesModuleID
, modidData
);
276 CFRelease(modidData
);
278 if (info_size
!= 0) {
279 infoData
= CFDataCreate(NULL
, info
, info_size
);
280 CFDictionarySetValue(newDict
, kSCPropNetLinkIssuesInfo
, infoData
);
283 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkIssuesInfo
);
286 timeStamp
= CFDateCreate(NULL
, timestamp
);
287 CFDictionarySetValue(newDict
, kSCPropNetLinkIssuesTimeStamp
, timeStamp
);
288 CFRelease(timeStamp
);
290 SC_log(LOG_DEBUG
, "Update interface link issues: %s: %@", if_name
, newDict
);
291 SCDynamicStoreSetValue(store
, key
, newDict
);
292 CFRelease(interface
);
297 #endif /* KEV_DL_ISSUES */
302 interface_detaching(const char *if_name
)
304 CFStringRef interface
;
306 CFMutableDictionaryRef newDict
;
308 SC_log(LOG_DEBUG
, "Detach interface: %s", if_name
);
310 interface
= create_interface_cfstring(if_name
);
311 key
= create_interface_key(interface
);
312 newDict
= copy_entity(key
);
313 CFDictionarySetValue(newDict
, kSCPropNetLinkDetaching
,
315 SCDynamicStoreSetValue(store
, key
, newDict
);
316 CFRelease(interface
);
323 create_nat64_key(CFStringRef interface
)
327 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
328 kSCDynamicStoreDomainState
,
336 interface_remove(const char *if_name
)
338 CFStringRef interface
;
341 SC_log(LOG_DEBUG
, "Remove interface: %s", if_name
);
343 interface
= create_interface_cfstring(if_name
);
345 key
= create_interface_key(interface
);
346 SCDynamicStoreRemoveValue(store
, key
);
349 key
= create_nat64_key(interface
);
350 SCDynamicStoreRemoveValue(store
, key
);
353 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
354 key
= create_linkquality_key(interface
);
355 SCDynamicStoreRemoveValue(store
, key
);
357 #endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
360 key
= create_link_issues_key(interface
);
361 SCDynamicStoreRemoveValue(store
, key
);
363 #endif /* KEV_DL_ISSUES */
365 CFRelease(interface
);
371 S_link_update_status(const char *if_name
, CFStringRef interface
, boolean_t attach
, boolean_t only_if_different
)
373 CFBooleanRef active
= NULL
;
374 CFBooleanRef expensive
= NULL
;
375 struct ifmediareq ifm
;
378 sock
= dgram_socket(AF_INET
);
384 memset((char *)&ifm
, 0, sizeof(ifm
));
385 (void) strlcpy(ifm
.ifm_name
, if_name
, sizeof(ifm
.ifm_name
));
387 if (ioctl(sock
, SIOCGIFXMEDIA
, (caddr_t
)&ifm
) == -1) {
388 /* if media status not available for this interface */
392 if (ifm
.ifm_count
== 0) {
397 if (!(ifm
.ifm_status
& IFM_AVALID
)) {
398 /* if active bit not valid */
402 if (ifm
.ifm_status
& IFM_ACTIVE
) {
403 active
= kCFBooleanTrue
;
405 active
= kCFBooleanFalse
;
410 if ((active
== NULL
) || CFBooleanGetValue(active
)) {
412 * if link status not available or active (link UP),
415 expensive
= interface_update_expensive(if_name
);
419 interface_update_status(if_name
, interface
, active
, attach
, expensive
, only_if_different
);
426 link_update_status(const char *if_name
, boolean_t attach
, boolean_t only_if_different
)
428 CFStringRef interface
;
430 interface
= create_interface_cfstring(if_name
);
431 S_link_update_status(if_name
, interface
, attach
, only_if_different
);
432 CFRelease(interface
);
438 link_update_status_if_missing(const char * if_name
)
440 CFStringRef interface
;
442 CFDictionaryRef dict
;
444 interface
= create_interface_cfstring(if_name
);
445 key
= create_interface_key(interface
);
446 dict
= SCDynamicStoreCopyValue(store
, key
);
448 /* it's already present, don't update */
452 S_link_update_status(if_name
, interface
, FALSE
, FALSE
);
453 dict
= SCDynamicStoreCopyValue(store
, key
);
455 /* our action made it appear */
456 messages_add_msg_with_arg("added missing link status", if_name
);
460 CFRelease(interface
);
467 interfaceListCopy(void)
469 CFStringRef cacheKey
;
470 CFDictionaryRef dict
;
471 CFMutableArrayRef ret_ifList
= NULL
;
473 cacheKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
474 kSCDynamicStoreDomainState
);
475 dict
= SCDynamicStoreCopyValue(store
, cacheKey
);
478 if (isA_CFDictionary(dict
) != NULL
) {
481 ifList
= CFDictionaryGetValue(dict
, kSCPropNetInterfaces
);
482 if (isA_CFArray(ifList
) != NULL
) {
483 ret_ifList
= CFArrayCreateMutableCopy(NULL
, 0, ifList
);
488 if (ret_ifList
== NULL
) {
489 ret_ifList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
497 interfaceListUpdate(CFArrayRef ifList
)
499 CFStringRef cacheKey
;
500 CFDictionaryRef dict
;
502 cacheKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
503 kSCDynamicStoreDomainState
);
504 dict
= SCDynamicStoreCopyValue(store
, cacheKey
);
505 if (dict
!= NULL
&& isA_CFDictionary(dict
) == NULL
) {
510 dict
= CFDictionaryCreate(NULL
,
511 (const void * *)&kSCPropNetInterfaces
,
512 (const void * *)&ifList
,
514 &kCFTypeDictionaryKeyCallBacks
,
515 &kCFTypeDictionaryValueCallBacks
);
516 SCDynamicStoreSetValue(store
, cacheKey
, dict
);
521 CFMutableDictionaryRef newDict
;
523 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
525 CFDictionarySetValue(newDict
, kSCPropNetInterfaces
, ifList
);
526 SCDynamicStoreSetValue(store
, cacheKey
, newDict
);
536 interfaceListAddInterface(CFMutableArrayRef ifList
, const char * if_name
)
538 Boolean added
= FALSE
;
539 CFStringRef interface
;
541 interface
= create_interface_cfstring(if_name
);
542 if (!CFArrayContainsValue(ifList
,
543 CFRangeMake(0, CFArrayGetCount(ifList
)),
545 /* interface was added, prime the link-specific values */
547 CFArrayAppendValue(ifList
, interface
);
548 link_update_status(if_name
, TRUE
, FALSE
);
549 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
550 link_update_quality_metric(if_name
);
551 #endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
554 /* only update the link status if it is different */
555 link_update_status(if_name
, FALSE
, TRUE
);
557 CFRelease(interface
);
563 interfaceListRemoveInterface(CFMutableArrayRef ifList
, const char * if_name
)
565 CFStringRef interface
;
568 interface
= create_interface_cfstring(if_name
);
569 where
= CFArrayGetFirstIndexOfValue(ifList
,
570 CFRangeMake(0, CFArrayGetCount(ifList
)),
572 CFRelease(interface
);
573 if (where
!= kCFNotFound
) {
574 CFArrayRemoveValueAtIndex(ifList
, where
);
575 interface_remove(if_name
);
577 return (where
!= kCFNotFound
);
583 link_add(const char *if_name
)
585 CFMutableArrayRef ifList
;
587 ifList
= interfaceListCopy();
588 if (interfaceListAddInterface(ifList
, if_name
)) {
589 /* interface was added, update the global list */
590 messages_add_msg_with_arg("link_add", if_name
);
591 interfaceListUpdate(ifList
);
592 config_new_interface(if_name
);
601 link_remove(const char *if_name
)
603 CFMutableArrayRef ifList
;
605 ifList
= interfaceListCopy();
606 if (interfaceListRemoveInterface(ifList
, if_name
)) {
607 /* interface was removed, update the global list */
608 interfaceListUpdate(ifList
);
617 interface_update_delegation(const char *if_name
)
619 CFStringRef interface
;
622 interface
= create_interface_cfstring(if_name
);
623 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
624 kSCDynamicStoreDomainState
,
626 kSCEntNetInterfaceDelegation
);
627 SC_log(LOG_DEBUG
, "Post interface delegation change: %s", if_name
);
628 SCDynamicStoreNotifyValue(store
, key
);
630 CFRelease(interface
);
635 #ifdef KEV_DL_IF_IDLE_ROUTE_REFCNT
636 #define INVALID_SOCKET_REF -1
639 socket_reference_count(const char* if_name
) {
641 int ref
= INVALID_SOCKET_REF
;
644 s
= dgram_socket(AF_INET
);
649 memset((char *)&ifr
, 0, sizeof(ifr
));
650 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s", if_name
);
652 if (ioctl(s
, SIOCGIFGETRTREFCNT
, (caddr_t
)&ifr
) != -1) {
653 ref
= ifr
.ifr_route_refcnt
;
655 ref
= INVALID_SOCKET_REF
;
664 interface_update_idle_state(const char *if_name
)
666 CFStringRef interface
;
670 /* only update the SCDynamicStore if the idle ref count is still 0 */
671 ref
= socket_reference_count(if_name
);
676 interface
= create_interface_cfstring(if_name
);
677 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
678 kSCDynamicStoreDomainState
,
682 SC_log(LOG_DEBUG
, "Post interface idle: %s", if_name
);
683 SCDynamicStoreNotifyValue(store
, key
);
685 CFRelease(interface
);
688 #endif // KEV_DL_IF_IDLE_ROUTE_REFCNT