2 * Copyright (c) 2002-2018 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 #ifndef kSCEntNetIdleRoute
37 #define kSCEntNetIdleRoute CFSTR("IdleRoute")
38 #endif /* kSCEntNetIdleRoute */
41 create_interface_cfstring(const char * if_name
)
43 CFStringRef interface
;
45 interface
= CFStringCreateWithCString(NULL
, if_name
,
46 kCFStringEncodingUTF8
);
51 create_interface_key(CFStringRef interface
)
55 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
56 kSCDynamicStoreDomainState
,
63 static CFMutableDictionaryRef
64 copy_mutable_dictionary(CFDictionaryRef dict
)
66 CFMutableDictionaryRef newDict
;
68 if (isA_CFDictionary(dict
) != NULL
) {
69 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
72 newDict
= CFDictionaryCreateMutable(NULL
,
74 &kCFTypeDictionaryKeyCallBacks
,
75 &kCFTypeDictionaryValueCallBacks
);
80 static CFMutableDictionaryRef
81 copy_entity(CFStringRef key
)
84 CFMutableDictionaryRef newDict
= NULL
;
86 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
87 newDict
= copy_mutable_dictionary(dict
);
96 interface_update_status(const char *if_name
,
97 CFStringRef interface
,
98 CFBooleanRef active
, boolean_t attach
,
99 CFBooleanRef expensive
, boolean_t only_if_different
)
101 CFStringRef key
= NULL
;
102 CFMutableDictionaryRef newDict
;
103 CFDictionaryRef oldDict
;
105 key
= create_interface_key(interface
);
106 oldDict
= cache_SCDynamicStoreCopyValue(store
, key
);
107 if (oldDict
!= NULL
&& isA_CFDictionary(oldDict
) == NULL
) {
111 newDict
= copy_mutable_dictionary(oldDict
);
113 /* if new status available, update cache */
114 if (active
!= NULL
) {
115 CFDictionarySetValue(newDict
, kSCPropNetLinkActive
, active
);
117 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkActive
);
121 /* the interface was attached, remove stale state */
122 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkDetaching
);
125 if ((expensive
!= NULL
) && CFBooleanGetValue(expensive
)) {
126 CFDictionarySetValue(newDict
, kSCPropNetLinkExpensive
, expensive
);
128 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkExpensive
);
131 /* update the SCDynamicStore */
132 if (CFDictionaryGetCount(newDict
) > 0) {
134 if (!only_if_different
136 || !CFEqual(oldDict
, newDict
)) {
137 SC_log(LOG_DEBUG
, "Update interface link status: %s: %@", if_name
, newDict
);
138 cache_SCDynamicStoreSetValue(store
, key
, newDict
);
141 /* remove the value */
142 if (oldDict
!= NULL
) {
143 SC_log(LOG_DEBUG
, "Update interface link status: %s: <removed>", if_name
);
145 cache_SCDynamicStoreRemoveValue(store
, key
);
150 if (oldDict
!= NULL
) {
157 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
159 create_linkquality_key(CFStringRef interface
)
163 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
164 kSCDynamicStoreDomainState
,
166 kSCEntNetLinkQuality
);
173 interface_update_quality_metric(const char *if_name
,
177 CFStringRef interface
;
178 CFMutableDictionaryRef newDict
;
180 interface
= create_interface_cfstring(if_name
);
181 key
= create_linkquality_key(interface
);
182 newDict
= copy_entity(key
);
184 if (quality
!= IFNET_LQM_THRESH_UNKNOWN
) {
185 CFNumberRef linkquality
;
187 linkquality
= CFNumberCreate(NULL
, kCFNumberIntType
, &quality
);
188 CFDictionarySetValue(newDict
, kSCPropNetLinkQuality
, linkquality
);
189 CFRelease(linkquality
);
191 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkQuality
);
195 if (CFDictionaryGetCount(newDict
) > 0) {
196 SC_log(LOG_DEBUG
, "Update interface link quality: %s: %@", if_name
, newDict
);
197 cache_SCDynamicStoreSetValue(store
, key
, newDict
);
199 SC_log(LOG_DEBUG
, "Update interface link quality: %s: <unknown>", if_name
);
200 cache_SCDynamicStoreRemoveValue(store
, key
);
203 CFRelease(interface
);
212 link_update_quality_metric(const char *if_name
)
215 int quality
= IFNET_LQM_THRESH_UNKNOWN
;
218 sock
= dgram_socket(AF_INET
);
223 bzero((char *)&ifr
, sizeof(ifr
));
224 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s", if_name
);
226 if (ioctl(sock
, SIOCGIFLINKQUALITYMETRIC
, (caddr_t
)&ifr
) != -1) {
227 quality
= ifr
.ifr_link_quality_metric
;
232 interface_update_quality_metric(if_name
, quality
);
240 #endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
245 create_link_issues_key(CFStringRef interface
)
249 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
250 kSCDynamicStoreDomainState
,
252 kSCEntNetLinkIssues
);
259 interface_update_link_issues(const char *if_name
,
267 CFStringRef interface
;
270 CFMutableDictionaryRef newDict
;
273 interface
= create_interface_cfstring(if_name
);
274 key
= create_link_issues_key(interface
);
276 newDict
= copy_entity(key
);
278 modidData
= CFDataCreate(NULL
, modid
, modid_size
);
279 CFDictionarySetValue(newDict
, kSCPropNetLinkIssuesModuleID
, modidData
);
280 CFRelease(modidData
);
282 if (info_size
!= 0) {
283 infoData
= CFDataCreate(NULL
, info
, info_size
);
284 CFDictionarySetValue(newDict
, kSCPropNetLinkIssuesInfo
, infoData
);
287 CFDictionaryRemoveValue(newDict
, kSCPropNetLinkIssuesInfo
);
290 timeStamp
= CFDateCreate(NULL
, timestamp
);
291 CFDictionarySetValue(newDict
, kSCPropNetLinkIssuesTimeStamp
, timeStamp
);
292 CFRelease(timeStamp
);
294 SC_log(LOG_DEBUG
, "Update interface link issues: %s: %@", if_name
, newDict
);
295 cache_SCDynamicStoreSetValue(store
, key
, newDict
);
296 CFRelease(interface
);
301 #endif /* KEV_DL_ISSUES */
306 interface_detaching(const char *if_name
)
308 CFStringRef interface
;
310 CFMutableDictionaryRef newDict
;
312 SC_log(LOG_DEBUG
, "Detach interface: %s", if_name
);
314 interface
= create_interface_cfstring(if_name
);
315 key
= create_interface_key(interface
);
316 newDict
= copy_entity(key
);
317 CFDictionarySetValue(newDict
, kSCPropNetLinkDetaching
,
319 cache_SCDynamicStoreSetValue(store
, key
, newDict
);
320 CFRelease(interface
);
327 create_nat64_key(CFStringRef interface
)
331 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
332 kSCDynamicStoreDomainState
,
340 interface_remove(const char *if_name
)
342 CFStringRef interface
;
345 SC_log(LOG_DEBUG
, "Remove interface: %s", if_name
);
347 interface
= create_interface_cfstring(if_name
);
349 key
= create_interface_key(interface
);
350 cache_SCDynamicStoreRemoveValue(store
, key
);
353 key
= create_nat64_key(interface
);
354 cache_SCDynamicStoreRemoveValue(store
, key
);
357 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
358 key
= create_linkquality_key(interface
);
359 cache_SCDynamicStoreRemoveValue(store
, key
);
361 #endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
364 key
= create_link_issues_key(interface
);
365 cache_SCDynamicStoreRemoveValue(store
, key
);
367 #endif /* KEV_DL_ISSUES */
369 CFRelease(interface
);
375 S_link_update_status(const char *if_name
, CFStringRef interface
, boolean_t attach
, boolean_t only_if_different
)
377 CFBooleanRef active
= NULL
;
378 CFBooleanRef expensive
= NULL
;
379 struct ifmediareq ifm
;
382 sock
= dgram_socket(AF_INET
);
388 bzero((char *)&ifm
, sizeof(ifm
));
389 (void) strlcpy(ifm
.ifm_name
, if_name
, sizeof(ifm
.ifm_name
));
391 if (ioctl(sock
, SIOCGIFMEDIA
, (caddr_t
)&ifm
) == -1) {
392 /* if media status not available for this interface */
396 if (ifm
.ifm_count
== 0) {
401 if (!(ifm
.ifm_status
& IFM_AVALID
)) {
402 /* if active bit not valid */
406 if (ifm
.ifm_status
& IFM_ACTIVE
) {
407 active
= kCFBooleanTrue
;
409 active
= kCFBooleanFalse
;
414 if ((active
== NULL
) || CFBooleanGetValue(active
)) {
416 * if link status not available or active (link UP),
419 expensive
= interface_update_expensive(if_name
);
423 interface_update_status(if_name
, interface
, active
, attach
, expensive
, only_if_different
);
430 link_update_status(const char *if_name
, boolean_t attach
, boolean_t only_if_different
)
432 CFStringRef interface
;
434 interface
= create_interface_cfstring(if_name
);
435 S_link_update_status(if_name
, interface
, attach
, only_if_different
);
436 CFRelease(interface
);
442 link_update_status_if_missing(const char * if_name
)
444 CFStringRef interface
;
446 CFDictionaryRef dict
;
448 interface
= create_interface_cfstring(if_name
);
449 key
= create_interface_key(interface
);
450 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
452 /* it's already present, don't update */
456 S_link_update_status(if_name
, interface
, FALSE
, FALSE
);
457 dict
= cache_SCDynamicStoreCopyValue(store
, key
);
459 /* our action made it appear */
460 messages_add_msg_with_arg("added missing link status", if_name
);
464 CFRelease(interface
);
471 interfaceListCopy(void)
473 CFStringRef cacheKey
;
474 CFDictionaryRef dict
;
475 CFMutableArrayRef ret_ifList
= NULL
;
477 cacheKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
478 kSCDynamicStoreDomainState
);
479 dict
= cache_SCDynamicStoreCopyValue(store
, cacheKey
);
482 if (isA_CFDictionary(dict
) != NULL
) {
485 ifList
= CFDictionaryGetValue(dict
, kSCPropNetInterfaces
);
486 if (isA_CFArray(ifList
) != NULL
) {
487 ret_ifList
= CFArrayCreateMutableCopy(NULL
, 0, ifList
);
492 if (ret_ifList
== NULL
) {
493 ret_ifList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
501 interfaceListUpdate(CFArrayRef ifList
)
503 CFStringRef cacheKey
;
504 CFDictionaryRef dict
;
506 cacheKey
= SCDynamicStoreKeyCreateNetworkInterface(NULL
,
507 kSCDynamicStoreDomainState
);
508 dict
= cache_SCDynamicStoreCopyValue(store
, cacheKey
);
509 if (dict
!= NULL
&& isA_CFDictionary(dict
) == NULL
) {
514 dict
= CFDictionaryCreate(NULL
,
515 (const void * *)&kSCPropNetInterfaces
,
516 (const void * *)&ifList
,
518 &kCFTypeDictionaryKeyCallBacks
,
519 &kCFTypeDictionaryValueCallBacks
);
520 cache_SCDynamicStoreSetValue(store
, cacheKey
, dict
);
525 CFMutableDictionaryRef newDict
;
527 newDict
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
529 CFDictionarySetValue(newDict
, kSCPropNetInterfaces
, ifList
);
530 cache_SCDynamicStoreSetValue(store
, cacheKey
, newDict
);
540 interfaceListAddInterface(CFMutableArrayRef ifList
, const char * if_name
)
542 Boolean added
= FALSE
;
543 CFStringRef interface
;
545 interface
= create_interface_cfstring(if_name
);
546 if (!CFArrayContainsValue(ifList
,
547 CFRangeMake(0, CFArrayGetCount(ifList
)),
549 /* interface was added, prime the link-specific values */
551 CFArrayAppendValue(ifList
, interface
);
552 link_update_status(if_name
, TRUE
, FALSE
);
553 #ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED
554 link_update_quality_metric(if_name
);
555 #endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */
558 /* only update the link status if it is different */
559 link_update_status(if_name
, FALSE
, TRUE
);
561 CFRelease(interface
);
567 interfaceListRemoveInterface(CFMutableArrayRef ifList
, const char * if_name
)
569 CFStringRef interface
;
572 interface
= create_interface_cfstring(if_name
);
573 where
= CFArrayGetFirstIndexOfValue(ifList
,
574 CFRangeMake(0, CFArrayGetCount(ifList
)),
576 CFRelease(interface
);
577 if (where
!= kCFNotFound
) {
578 CFArrayRemoveValueAtIndex(ifList
, where
);
579 interface_remove(if_name
);
581 return (where
!= kCFNotFound
);
587 link_add(const char *if_name
)
589 CFMutableArrayRef ifList
;
591 ifList
= interfaceListCopy();
592 if (interfaceListAddInterface(ifList
, if_name
)) {
593 /* interface was added, update the global list */
594 messages_add_msg_with_arg("link_add", if_name
);
595 interfaceListUpdate(ifList
);
596 config_new_interface(if_name
);
605 link_remove(const char *if_name
)
607 CFMutableArrayRef ifList
;
609 ifList
= interfaceListCopy();
610 if (interfaceListRemoveInterface(ifList
, if_name
)) {
611 /* interface was removed, update the global list */
612 interfaceListUpdate(ifList
);
619 #ifdef KEV_DL_IF_IDLE_ROUTE_REFCNT
620 #define INVALID_SOCKET_REF -1
623 socket_reference_count(const char* if_name
) {
625 int ref
= INVALID_SOCKET_REF
;
628 s
= dgram_socket(AF_INET
);
633 bzero((char *)&ifr
, sizeof(ifr
));
634 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s", if_name
);
636 if (ioctl(s
, SIOCGIFGETRTREFCNT
, (caddr_t
)&ifr
) != -1) {
637 ref
= ifr
.ifr_route_refcnt
;
639 ref
= INVALID_SOCKET_REF
;
648 interface_update_idle_state(const char *if_name
)
650 CFStringRef interface
;
654 /* only update the SCDynamicStore if the idle ref count is still 0 */
655 ref
= socket_reference_count(if_name
);
660 interface
= create_interface_cfstring(if_name
);
661 key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
662 kSCDynamicStoreDomainState
,
666 SC_log(LOG_DEBUG
, "Post interface idle: %s", if_name
);
667 cache_SCDynamicStoreNotifyValue(store
, key
);
669 CFRelease(interface
);
672 #endif // KEV_DL_IF_IDLE_ROUTE_REFCNT