1/* 2 * Copyright (c) 2002-2006, 2009, 2011, 2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * Modification History 26 * 27 * August 5, 2002 Allan Nathanson <ajn@apple.com> 28 * - split code out from eventmon.c 29 */ 30 31#include "eventmon.h" 32#include "cache.h" 33#include "ev_dlil.h" 34 35#ifndef kSCEntNetIdleRoute 36#define kSCEntNetIdleRoute CFSTR("IdleRoute") 37#endif /* kSCEntNetIdleRoute */ 38 39static CFStringRef 40create_interface_key(const char * if_name) 41{ 42 CFStringRef interface; 43 CFStringRef key; 44 45 interface = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingMacRoman); 46 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 47 kSCDynamicStoreDomainState, 48 interface, 49 kSCEntNetLink); 50 CFRelease(interface); 51 return (key); 52} 53 54 55static CFMutableDictionaryRef 56copy_entity(CFStringRef key) 57{ 58 CFDictionaryRef dict; 59 CFMutableDictionaryRef newDict = NULL; 60 61 dict = cache_SCDynamicStoreCopyValue(store, key); 62 if (dict != NULL) { 63 if (isA_CFDictionary(dict) != NULL) { 64 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 65 } 66 CFRelease(dict); 67 } 68 if (newDict == NULL) { 69 newDict = CFDictionaryCreateMutable(NULL, 70 0, 71 &kCFTypeDictionaryKeyCallBacks, 72 &kCFTypeDictionaryValueCallBacks); 73 } 74 return (newDict); 75} 76 77 78static void 79interface_update_status(const char *if_name, CFBooleanRef active, 80 boolean_t attach) 81{ 82 CFStringRef key = NULL; 83 CFMutableDictionaryRef newDict = NULL; 84 85 key = create_interface_key(if_name); 86 newDict = copy_entity(key); 87 /* if new status available, update cache */ 88 if (active == NULL) { 89 CFDictionaryRemoveValue(newDict, kSCPropNetLinkActive); 90 } else { 91 CFDictionarySetValue(newDict, kSCPropNetLinkActive, active); 92 } 93 if (attach == TRUE) { 94 /* the interface was attached, remove stale state */ 95 CFDictionaryRemoveValue(newDict, kSCPropNetLinkDetaching); 96 } 97 98 /* update status */ 99 if (CFDictionaryGetCount(newDict) > 0) { 100 cache_SCDynamicStoreSetValue(store, key, newDict); 101 } else { 102 cache_SCDynamicStoreRemoveValue(store, key); 103 } 104 105 CFRelease(key); 106 CFRelease(newDict); 107 return; 108} 109 110 111#ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED 112static CFStringRef 113create_linkquality_key(const char * if_name) 114{ 115 CFStringRef interface; 116 CFStringRef key; 117 118 interface = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingMacRoman); 119 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 120 kSCDynamicStoreDomainState, 121 interface, 122 kSCEntNetLinkQuality); 123 CFRelease(interface); 124 return (key); 125} 126 127 128__private_extern__ 129void 130interface_update_quality_metric(const char *if_name, 131 int quality) 132{ 133 CFStringRef key = NULL; 134 CFMutableDictionaryRef newDict = NULL; 135 CFNumberRef linkquality = NULL; 136 137 key = create_linkquality_key(if_name); 138 newDict = copy_entity(key); 139 140 if (quality != IFNET_LQM_THRESH_UNKNOWN) { 141 linkquality = CFNumberCreate(NULL, kCFNumberIntType, &quality); 142 CFDictionarySetValue(newDict, kSCPropNetLinkQuality, linkquality); 143 CFRelease(linkquality); 144 } else { 145 CFDictionaryRemoveValue(newDict, kSCPropNetLinkQuality); 146 } 147 148 /* update status */ 149 if (CFDictionaryGetCount(newDict) > 0) { 150 cache_SCDynamicStoreSetValue(store, key, newDict); 151 } else { 152 cache_SCDynamicStoreRemoveValue(store, key); 153 } 154 155 CFRelease(key); 156 CFRelease(newDict); 157 return; 158} 159 160 161static 162void 163link_update_quality_metric(const char *if_name) 164{ 165 struct ifreq ifr; 166 int quality = IFNET_LQM_THRESH_UNKNOWN; 167 int sock; 168 169 sock = dgram_socket(AF_INET); 170 if (sock == -1) { 171 SCLog(TRUE, LOG_NOTICE, CFSTR("socket_get_link_quality: socket open failed, %s"), strerror(errno)); 172 goto done; 173 } 174 175 bzero((char *)&ifr, sizeof(ifr)); 176 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", if_name); 177 178 if (ioctl(sock, SIOCGIFLINKQUALITYMETRIC, (caddr_t)&ifr) != -1) { 179 quality = ifr.ifr_link_quality_metric; 180 } 181 182done: 183 interface_update_quality_metric(if_name, quality); 184 if (sock != -1) 185 close(sock); 186 return; 187 188} 189#endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */ 190 191 192#ifdef KEV_DL_ISSUES 193static CFStringRef 194create_link_issues_key(const char * if_name) 195{ 196 CFStringRef interface; 197 CFStringRef key; 198 199 interface = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingMacRoman); 200 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 201 kSCDynamicStoreDomainState, 202 interface, 203 kSCEntNetLinkIssues); 204 CFRelease(interface); 205 return (key); 206} 207 208 209__private_extern__ 210void 211interface_update_link_issues(const char *if_name, 212 uint64_t timestamp, 213 uint8_t *modid, 214 size_t modid_size, 215 uint8_t *info, 216 size_t info_size) 217{ 218 CFDataRef infoData; 219 CFStringRef key; 220 CFDataRef modidData; 221 CFMutableDictionaryRef newDict; 222 CFDateRef timeStamp; 223 224 key = create_link_issues_key(if_name); 225 226 newDict = copy_entity(key); 227 228 modidData = CFDataCreate(NULL, modid, modid_size); 229 CFDictionarySetValue(newDict, kSCPropNetLinkIssuesModuleID, modidData); 230 CFRelease(modidData); 231 232 if (info_size != 0) { 233 infoData = CFDataCreate(NULL, info, info_size); 234 CFDictionarySetValue(newDict, kSCPropNetLinkIssuesInfo, infoData); 235 CFRelease(infoData); 236 } else { 237 CFDictionaryRemoveValue(newDict, kSCPropNetLinkIssuesInfo); 238 } 239 240 timeStamp = CFDateCreate(NULL, timestamp); 241 CFDictionarySetValue(newDict, kSCPropNetLinkIssuesTimeStamp, timeStamp); 242 CFRelease(timeStamp); 243 244 cache_SCDynamicStoreSetValue(store, key, newDict); 245 CFRelease(newDict); 246 CFRelease(key); 247 return; 248} 249#endif /* KEV_DL_ISSUES */ 250 251 252__private_extern__ 253void 254interface_detaching(const char *if_name) 255{ 256 CFStringRef key; 257 CFMutableDictionaryRef newDict; 258 259 key = create_interface_key(if_name); 260 newDict = copy_entity(key); 261 CFDictionarySetValue(newDict, kSCPropNetLinkDetaching, 262 kCFBooleanTrue); 263 cache_SCDynamicStoreSetValue(store, key, newDict); 264 CFRelease(newDict); 265 CFRelease(key); 266 return; 267} 268 269 270static void 271interface_remove(const char *if_name) 272{ 273 CFStringRef key; 274 275 key = create_interface_key(if_name); 276 cache_SCDynamicStoreRemoveValue(store, key); 277 CFRelease(key); 278 279#ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED 280 key = create_linkquality_key(if_name); 281 cache_SCDynamicStoreRemoveValue(store, key); 282 CFRelease(key); 283#endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */ 284 285#ifdef KEV_DL_ISSUES 286 key = create_link_issues_key(if_name); 287 cache_SCDynamicStoreRemoveValue(store, key); 288 CFRelease(key); 289#endif /* KEV_DL_ISSUES */ 290 291 return; 292} 293 294 295__private_extern__ 296void 297link_update_status(const char *if_name, boolean_t attach) 298{ 299 CFBooleanRef active = NULL; 300 struct ifmediareq ifm; 301 int sock; 302 303 sock = dgram_socket(AF_INET); 304 if (sock == -1) { 305 SCLog(TRUE, LOG_NOTICE, CFSTR("link_update_status: socket open failed, %s"), strerror(errno)); 306 goto done; 307 } 308 bzero((char *)&ifm, sizeof(ifm)); 309 (void) strncpy(ifm.ifm_name, if_name, sizeof(ifm.ifm_name)); 310 311 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifm) == -1) { 312 /* if media status not available for this interface */ 313 goto done; 314 } 315 316 if (ifm.ifm_count == 0) { 317 /* no media types */ 318 goto done; 319 } 320 321 if (!(ifm.ifm_status & IFM_AVALID)) { 322 /* if active bit not valid */ 323 goto done; 324 } 325 326 if (ifm.ifm_status & IFM_ACTIVE) { 327 active = kCFBooleanTrue; 328 } else { 329 active = kCFBooleanFalse; 330 } 331 332 done: 333 interface_update_status(if_name, active, attach); 334 if (sock != -1) 335 close(sock); 336 return; 337} 338 339__private_extern__ 340void 341link_add(const char *if_name) 342{ 343 CFStringRef interface; 344 CFStringRef cacheKey; 345 CFDictionaryRef dict; 346 CFMutableDictionaryRef newDict = NULL; 347 CFArrayRef ifList; 348 CFMutableArrayRef newIFList = NULL; 349 350 interface = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingMacRoman); 351 cacheKey = SCDynamicStoreKeyCreateNetworkInterface(NULL, 352 kSCDynamicStoreDomainState); 353 354 dict = cache_SCDynamicStoreCopyValue(store, cacheKey); 355 if (dict) { 356 if (isA_CFDictionary(dict)) { 357 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 358 ifList = CFDictionaryGetValue(newDict, kSCPropNetInterfaces); 359 if (isA_CFArray(ifList)) { 360 newIFList = CFArrayCreateMutableCopy(NULL, 0, ifList); 361 } 362 } 363 CFRelease(dict); 364 } 365 366 if (!newDict) { 367 newDict = CFDictionaryCreateMutable(NULL, 368 0, 369 &kCFTypeDictionaryKeyCallBacks, 370 &kCFTypeDictionaryValueCallBacks); 371 } 372 373 if (!newIFList) { 374 newIFList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 375 } 376 377 if (CFArrayContainsValue(newIFList, 378 CFRangeMake(0, CFArrayGetCount(newIFList)), 379 interface) == FALSE) { 380 CFArrayAppendValue(newIFList, interface); 381 CFDictionarySetValue(newDict, kSCPropNetInterfaces, newIFList); 382 } 383 cache_SCDynamicStoreSetValue(store, cacheKey, newDict); 384 link_update_status(if_name, TRUE); 385#ifdef KEV_DL_LINK_QUALITY_METRIC_CHANGED 386 link_update_quality_metric(if_name); 387#endif /* KEV_DL_LINK_QUALITY_METRIC_CHANGED */ 388 CFRelease(cacheKey); 389 CFRelease(interface); 390 if (newDict) CFRelease(newDict); 391 if (newIFList) CFRelease(newIFList); 392 393 return; 394} 395 396 397__private_extern__ 398void 399link_remove(const char *if_name) 400{ 401 CFStringRef interface; 402 CFStringRef cacheKey; 403 CFDictionaryRef dict; 404 CFMutableDictionaryRef newDict = NULL; 405 CFArrayRef ifList; 406 CFMutableArrayRef newIFList = NULL; 407 CFIndex i; 408 409 interface = CFStringCreateWithCString(NULL, if_name, kCFStringEncodingMacRoman); 410 cacheKey = SCDynamicStoreKeyCreateNetworkInterface(NULL, 411 kSCDynamicStoreDomainState); 412 413 dict = cache_SCDynamicStoreCopyValue(store, cacheKey); 414 if (dict) { 415 if (isA_CFDictionary(dict)) { 416 newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 417 ifList = CFDictionaryGetValue(newDict, kSCPropNetInterfaces); 418 if (isA_CFArray(ifList)) { 419 newIFList = CFArrayCreateMutableCopy(NULL, 0, ifList); 420 } 421 } 422 CFRelease(dict); 423 } 424 425 if (!newIFList || 426 ((i = CFArrayGetFirstIndexOfValue(newIFList, 427 CFRangeMake(0, CFArrayGetCount(newIFList)), 428 interface)) == kCFNotFound) 429 ) { 430 /* we're not tracking this interface */ 431 goto done; 432 } 433 434 CFArrayRemoveValueAtIndex(newIFList, i); 435 CFDictionarySetValue(newDict, kSCPropNetInterfaces, newIFList); 436 cache_SCDynamicStoreSetValue(store, cacheKey, newDict); 437 438 interface_remove(if_name); 439 440 done: 441 442 CFRelease(cacheKey); 443 CFRelease(interface); 444 if (newDict) CFRelease(newDict); 445 if (newIFList) CFRelease(newIFList); 446 447 return; 448} 449 450 451#ifdef KEV_DL_IF_IDLE_ROUTE_REFCNT 452#define INVALID_SOCKET_REF -1 453static 454int 455socket_reference_count(const char* if_name) { 456 struct ifreq ifr; 457 int ref = INVALID_SOCKET_REF; 458 int s; 459 460 s = dgram_socket(AF_INET); 461 if (s == -1) { 462 return (ref); 463 } 464 465 bzero((char *)&ifr, sizeof(ifr)); 466 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", if_name); 467 468 if (ioctl(s, SIOCGIFGETRTREFCNT, (caddr_t)&ifr) != -1) { 469 ref = ifr.ifr_route_refcnt; 470 } else { 471 ref = INVALID_SOCKET_REF; 472 } 473 close(s); 474 return (ref); 475} 476 477 478__private_extern__ 479void 480interface_update_idle_state(const char *if_name) 481{ 482 CFStringRef if_name_cf; 483 CFStringRef key; 484 int ref; 485 486 /* We will only update the SCDynamicStore if the idle ref count 487 * is still 0 */ 488 ref = socket_reference_count(if_name); 489 if (ref != 0) { 490 return; 491 } 492 493 if_name_cf = CFStringCreateWithCString(NULL, if_name, 494 kCFStringEncodingASCII); 495 496 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 497 kSCDynamicStoreDomainState, 498 if_name_cf, 499 kSCEntNetIdleRoute); 500 501 cache_SCDynamicStoreNotifyValue(store, key); 502 CFRelease(key); 503 CFRelease(if_name_cf); 504 return; 505} 506#endif // KEV_DL_IF_IDLE_ROUTE_REFCNT 507