2 * Copyright (c) 2002-2019 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 (oldDict
!= NULL
) {
138 SC_log(LOG_DEBUG
, "Update interface link status: %s: <removed>", if_name
);
140 SCDynamicStoreRemoveValue(store
, key
);
145 if (oldDict
!= NULL
) {
152 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
154 create_linkquality_key(CFStringRef interface
)
158 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
159 kSCDynamicStoreDomainState
,
161 kSCEntNetLinkQuality
);
168 interface_update_quality_metric(const char *if_name
,
172 CFStringRef interface
;
173 CFMutableDictionaryRef newDict
;
175 interface
= create_interface_cfstring(if_name
);
176 key
= create_linkquality_key(interface
);
177 newDict
= copy_entity(key
);
179 if (quality
!= IFNET_LQM_THRESH_UNKNOWN
) {
180 CFNumberRef linkquality
;
182 linkquality
= CFNumberCreate(NULL
, kCFNumberIntType
, &quality
);
183 CFDictionarySetValue(newDict
, kSCPropNetLinkQuality
, linkquality
);
184 CFRelease(linkquality
);
186 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkQuality
);
190 if (CFDictionaryGetCount(newDict
) > 0) {
191 SC_log(LOG_DEBUG
, "Update interface link quality: %s: %@", if_name
, newDict
);
192 SCDynamicStoreSetValue(store
, key
, newDict
);
194 SC_log(LOG_DEBUG
, "Update interface link quality: %s: <unknown>", if_name
);
195 SCDynamicStoreRemoveValue(store
, key
);
198 CFRelease(interface
);
207 link_update_quality_metric(const char *if_name
)
210 int quality
= IFNET_LQM_THRESH_UNKNOWN
;
213 sock
= dgram_socket(AF_INET
);
218 memset((char *)&ifr
, 0, sizeof(ifr
));
219 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s", if_name
);
221 if (ioctl(sock
, SIOCGIFLINKQUALITYMETRIC
, (caddr_t
)&ifr
) != -1) {
222 quality
= ifr
.ifr_link_quality_metric
;
227 interface_update_quality_metric(if_name
, quality
);
235 #endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
240 create_link_issues_key(CFStringRef interface
)
244 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
245 kSCDynamicStoreDomainState
,
247 kSCEntNetLinkIssues
);
254 interface_update_link_issues(const char *if_name
,
262 CFStringRef interface
;
265 CFMutableDictionaryRef newDict
;
268 interface
= create_interface_cfstring(if_name
);
269 key
= create_link_issues_key(interface
);
271 newDict
= copy_entity(key
);
273 modidData
= CFDataCreate(NULL
, modid
, modid_size
);
274 CFDictionarySetValue(newDict
, kSCPropNetLinkIssuesModuleID
, modidData
);
275 CFRelease(modidData
);
277 if (info_size
!= 0) {
278 infoData
= CFDataCreate(NULL
, info
, info_size
);
279 CFDictionarySetValue(newDict
, kSCPropNetLinkIssuesInfo
, infoData
);
282 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkIssuesInfo
);
285 timeStamp
= CFDateCreate(NULL
, timestamp
);
286 CFDictionarySetValue(newDict
, kSCPropNetLinkIssuesTimeStamp
, timeStamp
);
287 CFRelease(timeStamp
);
289 SC_log(LOG_DEBUG
, "Update interface link issues: %s: %@", if_name
, newDict
);
290 SCDynamicStoreSetValue(store
, key
, newDict
);
291 CFRelease(interface
);
296 #endif /* KEV_DL_ISSUES */
301 interface_detaching(const char *if_name
)
303 CFStringRef interface
;
305 CFMutableDictionaryRef newDict
;
307 SC_log(LOG_DEBUG
, "Detach interface: %s", if_name
);
309 interface
= create_interface_cfstring(if_name
);
310 key
= create_interface_key(interface
);
311 newDict
= copy_entity(key
);
312 CFDictionarySetValue(newDict
, kSCPropNetLinkDetaching
,
314 SCDynamicStoreSetValue(store
, key
, newDict
);
315 CFRelease(interface
);
322 create_nat64_key(CFStringRef interface
)
326 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
327 kSCDynamicStoreDomainState
,
335 interface_remove(const char *if_name
)
337 CFStringRef interface
;
340 SC_log(LOG_DEBUG
, "Remove interface: %s", if_name
);
342 interface
= create_interface_cfstring(if_name
);
344 key
= create_interface_key(interface
);
345 SCDynamicStoreRemoveValue(store
, key
);
348 key
= create_nat64_key(interface
);
349 SCDynamicStoreRemoveValue(store
, key
);
352 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
353 key
= create_linkquality_key(interface
);
354 SCDynamicStoreRemoveValue(store
, key
);
356 #endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
359 key
= create_link_issues_key(interface
);
360 SCDynamicStoreRemoveValue(store
, key
);
362 #endif /* KEV_DL_ISSUES */
364 CFRelease(interface
);
370 S_link_update_status(const char *if_name
, CFStringRef interface
, boolean_t attach
, boolean_t only_if_different
)
372 CFBooleanRef active
= NULL
;
373 CFBooleanRef expensive
= NULL
;
374 struct ifmediareq ifm
;
377 sock
= dgram_socket(AF_INET
);
383 memset((char *)&ifm
, 0, sizeof(ifm
));
384 (void) strlcpy(ifm
.ifm_name
, if_name
, sizeof(ifm
.ifm_name
));
386 if (ioctl(sock
, SIOCGIFXMEDIA
, (caddr_t
)&ifm
) == -1) {
387 /* if media status not available for this interface */
391 if (ifm
.ifm_count
== 0) {
396 if (!(ifm
.ifm_status
& IFM_AVALID
)) {
397 /* if active bit not valid */
401 if (ifm
.ifm_status
& IFM_ACTIVE
) {
402 active
= kCFBooleanTrue
;
404 active
= kCFBooleanFalse
;
409 if ((active
== NULL
) || CFBooleanGetValue(active
)) {
411 * if link status not available or active (link UP),
414 expensive
= interface_update_expensive(if_name
);
418 interface_update_status(if_name
, interface
, active
, attach
, expensive
, only_if_different
);
425 link_update_status(const char *if_name
, boolean_t attach
, boolean_t only_if_different
)
427 CFStringRef interface
;
429 interface
= create_interface_cfstring(if_name
);
430 S_link_update_status(if_name
, interface
, attach
, only_if_different
);
431 CFRelease(interface
);
437 link_update_status_if_missing(const char * if_name
)
439 CFStringRef interface
;
441 CFDictionaryRef dict
;
443 interface
= create_interface_cfstring(if_name
);
444 key
= create_interface_key(interface
);
445 dict
= SCDynamicStoreCopyValue(store
, key
);
447 /* it's already present, don't update */
451 S_link_update_status(if_name
, interface
, FALSE
, FALSE
);
452 dict
= SCDynamicStoreCopyValue(store
, key
);
454 /* our action made it appear */
455 messages_add_msg_with_arg("added missing link status", if_name
);
459 CFRelease(interface
);
466 interfaceListCopy(void)
468 CFStringRef cacheKey
;
469 CFDictionaryRef dict
;
470 CFMutableArrayRef ret_ifList
= NULL
;
472 cacheKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
473 kSCDynamicStoreDomainState
);
474 dict
= SCDynamicStoreCopyValue(store
, cacheKey
);
477 if (isA_CFDictionary(dict
) != NULL
) {
480 ifList
= CFDictionaryGetValue(dict
, kSCPropNetInterfaces
);
481 if (isA_CFArray(ifList
) != NULL
) {
482 ret_ifList
= CFArrayCreateMutableCopy(NULL
, 0, ifList
);
487 if (ret_ifList
== NULL
) {
488 ret_ifList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
496 interfaceListUpdate(CFArrayRef ifList
)
498 CFStringRef cacheKey
;
499 CFDictionaryRef dict
;
501 cacheKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
502 kSCDynamicStoreDomainState
);
503 dict
= SCDynamicStoreCopyValue(store
, cacheKey
);
504 if (dict
!= NULL
&& isA_CFDictionary(dict
) == NULL
) {
509 dict
= CFDictionaryCreate(NULL
,
510 (const void * *)&kSCPropNetInterfaces
,
511 (const void * *)&ifList
,
513 &kCFTypeDictionaryKeyCallBacks
,
514 &kCFTypeDictionaryValueCallBacks
);
515 SCDynamicStoreSetValue(store
, cacheKey
, dict
);
520 CFMutableDictionaryRef newDict
;
522 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
524 CFDictionarySetValue(newDict
, kSCPropNetInterfaces
, ifList
);
525 SCDynamicStoreSetValue(store
, cacheKey
, newDict
);
535 interfaceListAddInterface(CFMutableArrayRef ifList
, const char * if_name
)
537 Boolean added
= FALSE
;
538 CFStringRef interface
;
540 interface
= create_interface_cfstring(if_name
);
541 if (!CFArrayContainsValue(ifList
,
542 CFRangeMake(0, CFArrayGetCount(ifList
)),
544 /* interface was added, prime the link-specific values */
546 CFArrayAppendValue(ifList
, interface
);
547 link_update_status(if_name
, TRUE
, FALSE
);
548 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
549 link_update_quality_metric(if_name
);
550 #endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
553 /* only update the link status if it is different */
554 link_update_status(if_name
, FALSE
, TRUE
);
556 CFRelease(interface
);
562 interfaceListRemoveInterface(CFMutableArrayRef ifList
, const char * if_name
)
564 CFStringRef interface
;
567 interface
= create_interface_cfstring(if_name
);
568 where
= CFArrayGetFirstIndexOfValue(ifList
,
569 CFRangeMake(0, CFArrayGetCount(ifList
)),
571 CFRelease(interface
);
572 if (where
!= kCFNotFound
) {
573 CFArrayRemoveValueAtIndex(ifList
, where
);
574 interface_remove(if_name
);
576 return (where
!= kCFNotFound
);
582 link_add(const char *if_name
)
584 CFMutableArrayRef ifList
;
586 ifList
= interfaceListCopy();
587 if (interfaceListAddInterface(ifList
, if_name
)) {
588 /* interface was added, update the global list */
589 messages_add_msg_with_arg("link_add", if_name
);
590 interfaceListUpdate(ifList
);
591 config_new_interface(if_name
);
600 link_remove(const char *if_name
)
602 CFMutableArrayRef ifList
;
604 ifList
= interfaceListCopy();
605 if (interfaceListRemoveInterface(ifList
, if_name
)) {
606 /* interface was removed, update the global list */
607 interfaceListUpdate(ifList
);
616 interface_update_delegation(const char *if_name
)
618 CFStringRef interface
;
621 interface
= create_interface_cfstring(if_name
);
622 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
623 kSCDynamicStoreDomainState
,
625 kSCEntNetInterfaceDelegation
);
626 SC_log(LOG_DEBUG
, "Post interface delegation change: %s", if_name
);
627 SCDynamicStoreNotifyValue(store
, key
);
629 CFRelease(interface
);
634 #ifdef KEV_DL_IF_IDLE_ROUTE_REFCNT
635 #define INVALID_SOCKET_REF -1
638 socket_reference_count(const char* if_name
) {
640 int ref
= INVALID_SOCKET_REF
;
643 s
= dgram_socket(AF_INET
);
648 memset((char *)&ifr
, 0, sizeof(ifr
));
649 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s", if_name
);
651 if (ioctl(s
, SIOCGIFGETRTREFCNT
, (caddr_t
)&ifr
) != -1) {
652 ref
= ifr
.ifr_route_refcnt
;
654 ref
= INVALID_SOCKET_REF
;
663 interface_update_idle_state(const char *if_name
)
665 CFStringRef interface
;
669 /* only update the SCDynamicStore if the idle ref count is still 0 */
670 ref
= socket_reference_count(if_name
);
675 interface
= create_interface_cfstring(if_name
);
676 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
677 kSCDynamicStoreDomainState
,
681 SC_log(LOG_DEBUG
, "Post interface idle: %s", if_name
);
682 SCDynamicStoreNotifyValue(store
, key
);
684 CFRelease(interface
);
687 #endif // KEV_DL_IF_IDLE_ROUTE_REFCNT