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