1/*
2 * Copyright (c) 2002-2007, 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 * October 21, 2000		Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31
32#include <stdio.h>
33#include <unistd.h>
34#include <sys/ioctl.h>
35#include <sys/socket.h>
36#include <sys/wait.h>
37#include <net/if.h>
38#include <net/if_media.h>
39
40#include <SystemConfiguration/SystemConfiguration.h>
41#include <SystemConfiguration/SCPrivate.h>
42#include <SystemConfiguration/SCValidation.h>
43#include <SystemConfiguration/SCDPlugin.h>		// for _SCDPluginExecCommand
44
45#include "SCNetworkConfigurationInternal.h"
46
47
48static CFMutableDictionaryRef	baseSettings	= NULL;
49static CFStringRef		interfacesKey	= NULL;
50static SCDynamicStoreRef	store		= NULL;
51static CFRunLoopSourceRef	rls		= NULL;
52static CFMutableDictionaryRef	wantSettings	= NULL;
53
54static Boolean			_verbose	= FALSE;
55
56
57#pragma mark -
58#pragma mark Capabilities
59
60
61#define	CAPABILITIES_KEY	CFSTR("_CAPABILITIES_")
62
63
64__private_extern__
65Boolean
66_SCNetworkInterfaceSetCapabilities(SCNetworkInterfaceRef	interface,
67				   CFDictionaryRef		options)
68{
69	CFDictionaryRef	baseOptions;
70	int		cap_base;
71	int		cap_current;
72	int		cap_requested;
73	CFStringRef	interfaceName;
74
75#ifdef	SIOCSIFCAP
76	struct ifreq	ifr;
77	int		ret;
78	int		sock;
79#endif	// SIOCSIFCAP
80
81	interfaceName = SCNetworkInterfaceGetBSDName(interface);
82	if (interfaceName == NULL) {
83		/* if no BSD interface name */
84		return FALSE;
85	}
86
87	cap_current = __SCNetworkInterfaceCreateCapabilities(interface, -1, NULL);
88	if (cap_current == -1) {
89		/* could not get current capabilities */
90		return FALSE;
91	}
92
93	// get base capabilities
94	cap_base = cap_current;
95	baseOptions = CFDictionaryGetValue(baseSettings, interfaceName);
96	if (baseOptions != NULL) {
97		CFNumberRef	num;
98
99		num = CFDictionaryGetValue(baseOptions, CAPABILITIES_KEY);
100		if (num != NULL) {
101			CFNumberGetValue(num, kCFNumberIntType, &cap_base);
102		}
103	}
104
105	cap_requested = __SCNetworkInterfaceCreateCapabilities(interface, cap_base, options);
106
107#ifdef	SIOCSIFCAP
108	if (cap_requested == cap_current) {
109		/* if current setting is as requested */
110		return TRUE;
111	}
112
113	bzero((char *)&ifr, sizeof(ifr));
114	(void)_SC_cfstring_to_cstring(interfaceName, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII);
115	ifr.ifr_curcap = cap_current;
116	ifr.ifr_reqcap = cap_requested;
117
118	sock = socket(AF_INET, SOCK_DGRAM, 0);
119	if (sock == -1) {
120		SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
121		return FALSE;
122	}
123
124	ret = ioctl(sock, SIOCSIFCAP, (caddr_t)&ifr);
125	(void)close(sock);
126	if (ret == -1) {
127		SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(SIOCSIFCAP) failed: %s"), strerror(errno));
128		return FALSE;
129	}
130#endif	// SIOCSIFCAP
131
132	return TRUE;
133}
134
135
136#pragma mark -
137#pragma mark Media options
138
139
140static CFDictionaryRef
141__copyMediaOptions(CFDictionaryRef options)
142{
143	CFMutableDictionaryRef	requested	= NULL;
144	CFTypeRef		val;
145
146	if (!isA_CFDictionary(options)) {
147		return NULL;
148	}
149
150	val = CFDictionaryGetValue(options, kSCPropNetEthernetMediaSubType);
151	if (isA_CFString(val)) {
152		requested = CFDictionaryCreateMutable(NULL,
153						      0,
154						      &kCFTypeDictionaryKeyCallBacks,
155						      &kCFTypeDictionaryValueCallBacks);
156		CFDictionaryAddValue(requested, kSCPropNetEthernetMediaSubType, val);
157	} else {
158		/* if garbage */;
159		return NULL;
160	}
161
162	val = CFDictionaryGetValue(options, kSCPropNetEthernetMediaOptions);
163	if (isA_CFArray(val)) {
164		CFDictionaryAddValue(requested, kSCPropNetEthernetMediaOptions, val);
165	} else {
166		/* if garbage */;
167		CFRelease(requested);
168		return NULL;
169	}
170
171	return requested;
172}
173
174
175__private_extern__
176Boolean
177_SCNetworkInterfaceSetMediaOptions(SCNetworkInterfaceRef	interface,
178				   CFDictionaryRef		options)
179{
180	CFArrayRef		available	= NULL;
181	CFDictionaryRef		current		= NULL;
182	struct ifmediareq	ifm;
183	struct ifreq		ifr;
184	CFStringRef		interfaceName;
185	Boolean			ok		= FALSE;
186	int			newOptions;
187	CFDictionaryRef		requested;
188	int			sock		= -1;
189
190	if (!isA_SCNetworkInterface(interface)) {
191		_SCErrorSet(kSCStatusInvalidArgument);
192		return FALSE;
193	}
194
195	interfaceName = SCNetworkInterfaceGetBSDName(interface);
196	if (interfaceName == NULL) {
197		/* if no BSD interface name */
198		SCLog(_verbose, LOG_INFO, CFSTR("no BSD interface name for %@"), interface);
199		_SCErrorSet(kSCStatusInvalidArgument);
200		return FALSE;
201	}
202
203	/* get current & available options */
204	if (!SCNetworkInterfaceCopyMediaOptions(interface, &current, NULL, &available, FALSE)) {
205		/* could not get current media options */
206		SCLog(_verbose, LOG_INFO, CFSTR("no media options for %@"), interfaceName);
207		return FALSE;
208	}
209
210	/* extract just the dictionary key/value pairs of interest */
211	requested = __copyMediaOptions(options);
212	if (requested == NULL) {
213		CFDictionaryRef	baseOptions;
214
215		/* get base options */
216		baseOptions = CFDictionaryGetValue(baseSettings, interfaceName);
217		requested = __copyMediaOptions(baseOptions);
218	}
219	if (requested == NULL) {
220		/* get base options */
221		requested = __copyMediaOptions(current);
222	}
223	if (requested == NULL) {
224		/* if no media options to set */
225		goto done;
226	}
227
228	if ((current != NULL) && CFEqual(current, requested)) {
229		/* if current settings are as requested */
230		ok = TRUE;
231		goto done;
232	}
233
234	if (!CFArrayContainsValue(available, CFRangeMake(0, CFArrayGetCount(available)), requested)) {
235		/* if requested settings not currently available */
236		SCLog(_verbose, LOG_INFO, CFSTR("requested media settings unavailable for %@"), interfaceName);
237		goto done;
238	}
239
240	newOptions = __SCNetworkInterfaceCreateMediaOptions(interface, requested);
241	if (newOptions == -1) {
242		/* since we have just validated, this should never happen */
243		goto done;
244	}
245
246	sock = socket(AF_INET, SOCK_DGRAM, 0);
247	if (sock == -1) {
248		SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
249		goto done;
250	}
251
252	bzero((char *)&ifm, sizeof(ifm));
253	(void)_SC_cfstring_to_cstring(interfaceName, ifm.ifm_name, sizeof(ifm.ifm_name), kCFStringEncodingASCII);
254
255	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifm) == -1) {
256		SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno));
257		goto done;
258	}
259
260	bzero((char *)&ifr, sizeof(ifr));
261	bcopy(ifm.ifm_name, ifr.ifr_name, sizeof(ifr.ifr_name));
262	ifr.ifr_media =  ifm.ifm_current & ~(IFM_NMASK|IFM_TMASK|IFM_OMASK|IFM_GMASK);
263	ifr.ifr_media |= newOptions;
264
265	SCLog(_verbose, LOG_INFO, CFSTR("old media settings: 0x%8.8x (0x%8.8x)"), ifm.ifm_current, ifm.ifm_active);
266	SCLog(_verbose, LOG_INFO, CFSTR("new media settings: 0x%8.8x"), ifr.ifr_media);
267
268	if (ioctl(sock, SIOCSIFMEDIA, (caddr_t)&ifr) == -1) {
269		SCLog(TRUE, LOG_DEBUG, CFSTR("%@: ioctl(SIOCSIFMEDIA) failed: %s"), interfaceName, strerror(errno));
270		goto done;
271	}
272
273	ok = TRUE;
274
275    done :
276
277	if (available != NULL)	CFRelease(available);
278	if (current != NULL)	CFRelease(current);
279	if (requested != NULL)	CFRelease(requested);
280	if (sock != -1)	(void)close(sock);
281
282	return ok;
283}
284
285
286#pragma mark -
287#pragma mark MTU
288
289
290#ifndef	USE_SIOCSIFMTU
291static void
292ifconfig_exit(pid_t pid, int status, struct rusage *rusage, void *context)
293{
294	char	*if_name	= (char *)context;
295
296	if (WIFEXITED(status)) {
297		if (WEXITSTATUS(status) != 0) {
298			SCLog(TRUE, LOG_ERR,
299			      CFSTR("ifconfig %s failed, exit status = %d"),
300			      if_name,
301			      WEXITSTATUS(status));
302		}
303	} else if (WIFSIGNALED(status)) {
304		SCLog(TRUE, LOG_DEBUG,
305		      CFSTR("ifconfig %s: terminated w/signal = %d"),
306		      if_name,
307		      WTERMSIG(status));
308	} else {
309		SCLog(TRUE, LOG_DEBUG,
310		      CFSTR("ifconfig %s: exit status = %d"),
311		      if_name,
312		      status);
313	}
314
315	CFAllocatorDeallocate(NULL, if_name);
316	return;
317}
318#endif	/* !USE_SIOCSIFMTU */
319
320
321__private_extern__
322Boolean
323_SCNetworkInterfaceSetMTU(SCNetworkInterfaceRef	interface,
324			  CFDictionaryRef	options)
325{
326	CFStringRef	interfaceName;
327	int		mtu_cur		= -1;
328	int		mtu_max		= -1;
329	int		mtu_min		= -1;
330	int		requested;
331	CFNumberRef	val;
332
333	interfaceName = SCNetworkInterfaceGetBSDName(interface);
334	if (interfaceName == NULL) {
335		/* if no BSD interface name */
336		return FALSE;
337	}
338
339	if (!SCNetworkInterfaceCopyMTU(interface, &mtu_cur, &mtu_min, &mtu_max)) {
340		/* could not get current MTU */
341		return FALSE;
342	}
343
344	val = NULL;
345	if (isA_CFDictionary(options)) {
346		val = CFDictionaryGetValue(options, kSCPropNetEthernetMTU);
347		val = isA_CFNumber(val);
348	}
349	if (val == NULL) {
350		CFDictionaryRef	baseOptions;
351
352		/* get base MTU */
353		baseOptions = CFDictionaryGetValue(baseSettings, interfaceName);
354		if (baseOptions != NULL) {
355			val = CFDictionaryGetValue(baseOptions, kSCPropNetEthernetMTU);
356		}
357	}
358	if (val != NULL) {
359		CFNumberGetValue(val, kCFNumberIntType, &requested);
360	} else {
361		requested = mtu_cur;
362	}
363
364	if (requested == mtu_cur) {
365		/* if current setting is as requested */
366		return TRUE;
367	}
368
369	if (((mtu_min >= 0) && (requested < mtu_min)) ||
370	    ((mtu_max >= 0) && (requested > mtu_max))) {
371		/* if requested MTU outside of the valid range */
372		return FALSE;
373	}
374
375#ifdef	USE_SIOCSIFMTU
376{
377	struct ifreq	ifr;
378	int		ret;
379	int		sock;
380
381	bzero((char *)&ifr, sizeof(ifr));
382	(void)_SC_cfstring_to_cstring(interfaceName, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII);
383	ifr.ifr_mtu = requested;
384
385	sock = socket(AF_INET, SOCK_DGRAM, 0);
386	if (sock == -1) {
387		SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
388		return FALSE;
389	}
390
391	ret = ioctl(sock, SIOCSIFMTU, (caddr_t)&ifr);
392	(void)close(sock);
393	if (ret == -1) {
394		SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(SIOCSIFMTU) failed: %s"), strerror(errno));
395		return FALSE;
396	}
397}
398#else	/* !USE_SIOCSIFMTU */
399{
400	char	*ifconfig_argv[] = { "ifconfig", NULL, "mtu", NULL, NULL };
401	pid_t	pid;
402
403	ifconfig_argv[1] = _SC_cfstring_to_cstring(interfaceName, NULL, 0, kCFStringEncodingASCII);
404	(void)asprintf(&ifconfig_argv[3], "%d", requested);
405
406	pid = _SCDPluginExecCommand(ifconfig_exit,	// callout,
407				    ifconfig_argv[1],	// context
408				    0,			// uid
409				    0,			// gid
410				    "/sbin/ifconfig",	// path
411				    ifconfig_argv	// argv
412				   );
413
414//	CFAllocatorDeallocate(NULL, ifconfig_argv[1]);	// released in ifconfig_exit()
415	free(ifconfig_argv[3]);
416
417	if (pid <= 0) {
418		return FALSE;
419	}
420}
421#endif	/* !USE_SIOCSIFMTU */
422
423	return TRUE;
424}
425
426
427#pragma mark -
428#pragma mark Update link configuration
429
430
431/*
432 * Function: parse_component
433 * Purpose:
434 *   Given a string 'key' and a string prefix 'prefix',
435 *   return the next component in the slash '/' separated
436 *   key.
437 *
438 * Examples:
439 * 1. key = "a/b/c" prefix = "a/"
440 *    returns "b"
441 * 2. key = "a/b/c" prefix = "a/b/"
442 *    returns "c"
443 */
444static CF_RETURNS_RETAINED CFStringRef
445parse_component(CFStringRef key, CFStringRef prefix)
446{
447	CFMutableStringRef	comp;
448	CFRange			range;
449
450	if (CFStringHasPrefix(key, prefix) == FALSE) {
451		return NULL;
452	}
453	comp = CFStringCreateMutableCopy(NULL, 0, key);
454	CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
455	range = CFStringFind(comp, CFSTR("/"), 0);
456	if (range.location == kCFNotFound) {
457		return comp;
458	}
459	range.length = CFStringGetLength(comp) - range.location;
460	CFStringDelete(comp, range);
461	return comp;
462}
463
464
465static void updateLink(CFStringRef interfaceName, CFDictionaryRef options);
466
467
468static void
469updateInterfaces(CFArrayRef newInterfaces)
470{
471	CFIndex			i;
472	CFIndex			n_old;
473	CFIndex			n_new;
474	static CFArrayRef	oldInterfaces	= NULL;
475
476	n_old = (oldInterfaces != NULL) ? CFArrayGetCount(oldInterfaces) : 0;
477	n_new = CFArrayGetCount(newInterfaces);
478
479	for (i = 0; i < n_new; i++) {
480		CFStringRef	interfaceName;
481
482		interfaceName = CFArrayGetValueAtIndex(newInterfaces, i);
483
484		if ((n_old == 0) ||
485		    !CFArrayContainsValue(oldInterfaces,
486					  CFRangeMake(0, n_old),
487					  interfaceName)) {
488			CFDictionaryRef	options;
489
490			// if new interface
491			options = CFDictionaryGetValue(wantSettings, interfaceName);
492			updateLink(interfaceName, options);
493		}
494	}
495
496	if (oldInterfaces != NULL) CFRelease(oldInterfaces);
497	oldInterfaces = CFRetain(newInterfaces);
498}
499
500
501static void
502updateLink(CFStringRef interfaceName, CFDictionaryRef options)
503{
504	SCNetworkInterfaceRef	interface;
505
506	/* retain requested configuration */
507	if (options != NULL) {
508		CFDictionarySetValue(wantSettings, interfaceName, options);
509	} else {
510		CFDictionaryRemoveValue(wantSettings, interfaceName);
511	}
512
513	/* apply requested configuration */
514	interface = _SCNetworkInterfaceCreateWithBSDName(NULL, interfaceName,
515							 kIncludeAllVirtualInterfaces);
516	if (interface == NULL) {
517		return;
518	}
519
520	if (options != NULL) {
521		if (!CFDictionaryContainsKey(baseSettings, interfaceName)) {
522			int			cur_cap		= -1;
523			CFDictionaryRef		cur_media	= NULL;
524			CFMutableDictionaryRef	new_media	= NULL;
525			int			cur_mtu		= -1;
526
527			/* preserve current media options */
528			if (SCNetworkInterfaceCopyMediaOptions(interface, &cur_media, NULL, NULL, FALSE)) {
529				if (cur_media != NULL) {
530					new_media = CFDictionaryCreateMutableCopy(NULL, 0, cur_media);
531					CFRelease(cur_media);
532				}
533			}
534
535			/* preserve current MTU */
536			if (SCNetworkInterfaceCopyMTU(interface, &cur_mtu, NULL, NULL)) {
537				if (cur_mtu != -1) {
538					CFNumberRef	num;
539
540					if (new_media == NULL) {
541						new_media = CFDictionaryCreateMutable(NULL,
542										      0,
543										      &kCFTypeDictionaryKeyCallBacks,
544										      &kCFTypeDictionaryValueCallBacks);
545					}
546
547					num = CFNumberCreate(NULL, kCFNumberIntType, &cur_mtu);
548					CFDictionaryAddValue(new_media, kSCPropNetEthernetMTU, num);
549					CFRelease(num);
550				}
551			}
552
553			/* preserve capabilities */
554			cur_cap = __SCNetworkInterfaceCreateCapabilities(interface, -1, NULL);
555			if (cur_cap != -1) {
556				CFNumberRef	num;
557
558				if (new_media == NULL) {
559					new_media = CFDictionaryCreateMutable(NULL,
560									      0,
561									      &kCFTypeDictionaryKeyCallBacks,
562									      &kCFTypeDictionaryValueCallBacks);
563				}
564
565				num = CFNumberCreate(NULL, kCFNumberIntType, &cur_cap);
566				CFDictionaryAddValue(new_media, CAPABILITIES_KEY, num);
567				CFRelease(num);
568			}
569
570			if (new_media != NULL) {
571				CFDictionarySetValue(baseSettings, interfaceName, new_media);
572				CFRelease(new_media);
573			}
574		}
575
576		/* establish new settings */
577		(void)_SCNetworkInterfaceSetCapabilities(interface, options);
578		(void)_SCNetworkInterfaceSetMediaOptions(interface, options);
579		(void)_SCNetworkInterfaceSetMTU         (interface, options);
580	} else {
581		/* no requested settings */
582		options = CFDictionaryGetValue(baseSettings, interfaceName);
583		if (options != NULL) {
584			/* restore original settings */
585			(void)_SCNetworkInterfaceSetCapabilities(interface, options);
586			(void)_SCNetworkInterfaceSetMediaOptions(interface, options);
587			(void)_SCNetworkInterfaceSetMTU         (interface, options);
588			CFDictionaryRemoveValue(baseSettings, interfaceName);
589		}
590	}
591
592	CFRelease(interface);
593	return;
594}
595
596
597static void
598linkConfigChangedCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg)
599{
600	CFDictionaryRef		changes;
601	CFIndex			i;
602	CFIndex			n;
603	static CFStringRef	prefix		= NULL;
604
605	if (prefix == NULL) {
606		prefix = SCDynamicStoreKeyCreate(NULL,
607						 CFSTR("%@/%@/%@/"),
608						 kSCDynamicStoreDomainSetup,
609						 kSCCompNetwork,
610						 kSCCompInterface);
611	}
612
613	changes = SCDynamicStoreCopyMultiple(store, changedKeys, NULL);
614
615	n = CFArrayGetCount(changedKeys);
616	for (i = 0; i < n; i++) {
617		CFStringRef	key;
618		CFDictionaryRef	info;
619
620		key  = CFArrayGetValueAtIndex(changedKeys, i);
621		info = CFDictionaryGetValue(changes, key);
622
623		if (CFEqual(key, interfacesKey)) {
624			CFArrayRef	interfaces;
625
626			interfaces = CFDictionaryGetValue(info, kSCPropNetInterfaces);
627			if (isA_CFArray(interfaces)) {
628				updateInterfaces(interfaces);
629			}
630		} else {
631			CFStringRef	interfaceName;
632
633			interfaceName = parse_component(key, prefix);
634			if (interfaceName != NULL) {
635				updateLink(interfaceName, info);
636				CFRelease(interfaceName);
637			}
638		}
639	}
640
641	CFRelease(changes);
642
643	return;
644}
645
646
647__private_extern__
648void
649load_LinkConfiguration(CFBundleRef bundle, Boolean bundleVerbose)
650{
651	CFStringRef		key;
652	CFMutableArrayRef	keys		= NULL;
653	Boolean			ok;
654	CFMutableArrayRef	patterns	= NULL;
655
656	if (bundleVerbose) {
657		_verbose = TRUE;
658	}
659
660	SCLog(_verbose, LOG_DEBUG, CFSTR("load() called"));
661	SCLog(_verbose, LOG_DEBUG, CFSTR("  bundle ID = %@"), CFBundleGetIdentifier(bundle));
662
663	/* initialize a few globals */
664
665	baseSettings = CFDictionaryCreateMutable(NULL,
666						 0,
667						 &kCFTypeDictionaryKeyCallBacks,
668						 &kCFTypeDictionaryValueCallBacks);
669	wantSettings = CFDictionaryCreateMutable(NULL,
670						 0,
671						 &kCFTypeDictionaryKeyCallBacks,
672						 &kCFTypeDictionaryValueCallBacks);
673
674	/* open a "configd" store to allow cache updates */
675	store = SCDynamicStoreCreate(NULL,
676				     CFSTR("Link Configuraton plug-in"),
677				     linkConfigChangedCallback,
678				     NULL);
679	if (store == NULL) {
680		SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
681		goto error;
682	}
683
684	/* establish notification keys and patterns */
685	keys     = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
686	patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
687
688	/* ...watch for a change in the list of network interfaces */
689	interfacesKey = SCDynamicStoreKeyCreateNetworkInterface(NULL,
690								kSCDynamicStoreDomainState);
691	CFArrayAppendValue(keys, interfacesKey);
692
693	/* ...watch for (per-interface) AirPort configuration changes */
694	key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
695							    kSCDynamicStoreDomainSetup,
696							    kSCCompAnyRegex,
697							    kSCEntNetAirPort);
698	CFArrayAppendValue(patterns, key);
699	CFRelease(key);
700
701	/* ...watch for (per-interface) Ethernet configuration changes */
702	key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
703							    kSCDynamicStoreDomainSetup,
704							    kSCCompAnyRegex,
705							    kSCEntNetEthernet);
706	CFArrayAppendValue(patterns, key);
707	CFRelease(key);
708
709	/* ...watch for (per-interface) FireWire configuration changes */
710	key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
711							    kSCDynamicStoreDomainSetup,
712							    kSCCompAnyRegex,
713							    kSCEntNetFireWire);
714	CFArrayAppendValue(patterns, key);
715	CFRelease(key);
716
717	/* register the keys/patterns */
718	ok = SCDynamicStoreSetNotificationKeys(store, keys, patterns);
719	CFRelease(keys);
720	CFRelease(patterns);
721	if (!ok) {
722		SCLog(TRUE, LOG_ERR,
723		      CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
724		      SCErrorString(SCError()));
725		goto error;
726	}
727
728	rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
729	if (rls == NULL) {
730		SCLog(TRUE, LOG_ERR,
731		      CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
732		      SCErrorString(SCError()));
733		goto error;
734	}
735
736	CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
737	return;
738
739    error :
740
741	if (baseSettings != NULL)	CFRelease(baseSettings);
742	if (wantSettings != NULL)	CFRelease(wantSettings);
743	if (store != NULL) 		CFRelease(store);
744	return;
745}
746
747
748#ifdef	MAIN
749
750
751#pragma mark -
752#pragma mark Standalone test code
753
754
755int
756main(int argc, char **argv)
757{
758	SCPreferencesRef	prefs;
759
760	_sc_log     = FALSE;
761	_sc_verbose = (argc > 1) ? TRUE : FALSE;
762
763	prefs = SCPreferencesCreate(NULL, CFSTR("linkconfig"), NULL);
764	if (prefs != NULL) {
765		SCNetworkSetRef	set;
766
767		set = SCNetworkSetCopyCurrent(prefs);
768		if (set != NULL) {
769			CFMutableSetRef	seen;
770			CFArrayRef	services;
771
772			services = SCNetworkSetCopyServices(set);
773			if (services != NULL) {
774				CFIndex		i;
775				CFIndex		n;
776
777				seen = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
778
779				n = CFArrayGetCount(services);
780				for (i = 0; i < n; i++) {
781					SCNetworkInterfaceRef	interface;
782					SCNetworkServiceRef	service;
783
784					service = CFArrayGetValueAtIndex(services, i);
785					interface = SCNetworkServiceGetInterface(service);
786					if ((interface != NULL) &&
787					    !CFSetContainsValue(seen, interface)) {
788						CFDictionaryRef	capabilities;
789
790						capabilities = SCNetworkInterfaceCopyCapability(interface, NULL);
791						if (capabilities != NULL) {
792							int		cap_current;
793							int		cap_requested;
794							CFDictionaryRef	options;
795
796							options = SCNetworkInterfaceGetConfiguration(interface);
797							cap_current   = __SCNetworkInterfaceCreateCapabilities(interface, -1, NULL);
798							cap_requested = __SCNetworkInterfaceCreateCapabilities(interface, cap_current, options);
799
800							SCPrint(TRUE, stdout,
801								CFSTR("%sinterface = %@, current = %p, requested = %p\n%@\n"),
802								(i == 0) ? "" : "\n",
803								SCNetworkInterfaceGetBSDName(interface),
804								(void *)(uintptr_t)cap_current,
805								(void *)(uintptr_t)cap_requested,
806								capabilities);
807							CFRelease(capabilities);
808						}
809
810						CFSetAddValue(seen, interface);
811					}
812				}
813
814				CFRelease(seen);
815				CFRelease(services);
816			}
817
818			CFRelease(set);
819		}
820
821		CFRelease(prefs);
822	}
823
824	load_LinkConfiguration(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
825	CFRunLoopRun();
826	/* not reached */
827	exit(0);
828	return 0;
829}
830#endif
831