]> git.saurik.com Git - apple/configd.git/blob - Plugins/LinkConfiguration/linkconfig.c
configd-204.tar.gz
[apple/configd.git] / Plugins / LinkConfiguration / linkconfig.c
1 /*
2 * Copyright (c) 2002-2007 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/fcntl.h>
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37 #include <sys/wait.h>
38 #include <net/if.h>
39 #include <net/if_media.h>
40
41 #include <SystemConfiguration/SystemConfiguration.h>
42 #include <SystemConfiguration/SCPrivate.h>
43 #include <SystemConfiguration/SCValidation.h>
44 #include <SystemConfiguration/LinkConfiguration.h>
45 #include <SystemConfiguration/SCDPlugin.h> // for _SCDPluginExecCommand
46
47
48 static CFMutableDictionaryRef baseSettings = NULL;
49 static CFStringRef interfacesKey = NULL;
50 static SCDynamicStoreRef store = NULL;
51 static CFRunLoopSourceRef rls = NULL;
52 static CFMutableDictionaryRef wantSettings = NULL;
53
54 static Boolean _verbose = FALSE;
55
56
57 /* in SystemConfiguration/LinkConfiguration.c */
58 int
59 __createMediaOptions(CFStringRef interfaceName, CFDictionaryRef media_options);
60
61
62 static CFDictionaryRef
63 __copyMediaOptions(CFDictionaryRef options)
64 {
65 CFMutableDictionaryRef requested = NULL;
66 CFTypeRef val;
67
68 if (!isA_CFDictionary(options)) {
69 return NULL;
70 }
71
72 val = CFDictionaryGetValue(options, kSCPropNetEthernetMediaSubType);
73 if (isA_CFString(val)) {
74 requested = CFDictionaryCreateMutable(NULL,
75 0,
76 &kCFTypeDictionaryKeyCallBacks,
77 &kCFTypeDictionaryValueCallBacks);
78 CFDictionaryAddValue(requested, kSCPropNetEthernetMediaSubType, val);
79 } else {
80 /* if garbage */;
81 return NULL;
82 }
83
84 val = CFDictionaryGetValue(options, kSCPropNetEthernetMediaOptions);
85 if (isA_CFArray(val)) {
86 CFDictionaryAddValue(requested, kSCPropNetEthernetMediaOptions, val);
87 } else {
88 /* if garbage */;
89 CFRelease(requested);
90 return NULL;
91 }
92
93 return requested;
94 }
95
96
97 __private_extern__
98 Boolean
99 _SCNetworkInterfaceSetMediaOptions(SCNetworkInterfaceRef interface,
100 CFDictionaryRef options)
101 {
102 CFArrayRef available = NULL;
103 CFDictionaryRef current = NULL;
104 struct ifmediareq ifm;
105 struct ifreq ifr;
106 CFStringRef interfaceName;
107 Boolean ok = FALSE;
108 int newOptions;
109 CFDictionaryRef requested;
110 int sock = -1;
111
112 interfaceName = SCNetworkInterfaceGetBSDName(interface);
113 if (interfaceName == NULL) {
114 /* if no BSD interface name */
115 SCLog(_verbose, LOG_INFO, CFSTR("no BSD interface name for %@"), interface);
116 return FALSE;
117 }
118
119 /* get current & available options */
120 if (!SCNetworkInterfaceCopyMediaOptions(interface, &current, NULL, &available, FALSE)) {
121 /* could not get current media options */
122 SCLog(_verbose, LOG_INFO, CFSTR("no media options for %@"), interfaceName);
123 return FALSE;
124 }
125
126 /* extract just the dictionary key/value pairs of interest */
127 requested = __copyMediaOptions(options);
128 if (requested == NULL) {
129 CFDictionaryRef baseOptions;
130
131 /* get base options */
132 baseOptions = CFDictionaryGetValue(baseSettings, interfaceName);
133 requested = __copyMediaOptions(baseOptions);
134 }
135 if (requested == NULL) {
136 /* get base options */
137 requested = __copyMediaOptions(current);
138 }
139 if (requested == NULL) {
140 /* if no media options to set */
141 goto done;
142 }
143
144 if ((current != NULL) && CFEqual(current, requested)) {
145 /* if current settings are as requested */
146 ok = TRUE;
147 goto done;
148 }
149
150 if (!CFArrayContainsValue(available, CFRangeMake(0, CFArrayGetCount(available)), requested)) {
151 /* if requested settings not currently available */
152 SCLog(_verbose, LOG_INFO, CFSTR("requested media settings unavailable for %@"), interfaceName);
153 goto done;
154 }
155
156 newOptions = __createMediaOptions(interfaceName, requested);
157 if (newOptions == -1) {
158 /* since we have just validated, this should never happen */
159 goto done;
160 }
161
162 sock = socket(AF_INET, SOCK_DGRAM, 0);
163 if (sock == -1) {
164 SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
165 goto done;
166 }
167
168 bzero((char *)&ifm, sizeof(ifm));
169 (void)_SC_cfstring_to_cstring(interfaceName, ifm.ifm_name, sizeof(ifm.ifm_name), kCFStringEncodingASCII);
170
171 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifm) == -1) {
172 SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno));
173 goto done;
174 }
175
176 bzero((char *)&ifr, sizeof(ifr));
177 bcopy(ifm.ifm_name, ifr.ifr_name, sizeof(ifr.ifr_name));
178 ifr.ifr_media = ifm.ifm_current & ~(IFM_NMASK|IFM_TMASK|IFM_OMASK|IFM_GMASK);
179 ifr.ifr_media |= newOptions;
180
181 SCLog(_verbose, LOG_INFO, CFSTR("old media settings: 0x%8.8x (0x%8.8x)"), ifm.ifm_current, ifm.ifm_active);
182 SCLog(_verbose, LOG_INFO, CFSTR("new media settings: 0x%8.8x"), ifr.ifr_media);
183
184 if (ioctl(sock, SIOCSIFMEDIA, (caddr_t)&ifr) == -1) {
185 SCLog(TRUE, LOG_DEBUG, CFSTR("%@: ioctl(SIOCSIFMEDIA) failed: %s"), interfaceName, strerror(errno));
186 goto done;
187 }
188
189 ok = TRUE;
190
191 done :
192
193 if (available != NULL) CFRelease(available);
194 if (current != NULL) CFRelease(current);
195 if (requested != NULL) CFRelease(requested);
196 if (sock != -1) (void)close(sock);
197
198 return ok;
199 }
200
201
202 #ifndef USE_SIOCSIFMTU
203 static void
204 ifconfig_exit(pid_t pid, int status, struct rusage *rusage, void *context)
205 {
206 char *if_name = (char *)context;
207
208 if (WIFEXITED(status)) {
209 if (WEXITSTATUS(status) != 0) {
210 SCLog(TRUE, LOG_ERR,
211 CFSTR("ifconfig %s failed, exit status = %d"),
212 if_name,
213 WEXITSTATUS(status));
214 }
215 } else if (WIFSIGNALED(status)) {
216 SCLog(TRUE, LOG_DEBUG,
217 CFSTR("ifconfig %s: terminated w/signal = %d"),
218 if_name,
219 WTERMSIG(status));
220 } else {
221 SCLog(TRUE, LOG_DEBUG,
222 CFSTR("ifconfig %s: exit status = %d"),
223 if_name,
224 status);
225 }
226
227 CFAllocatorDeallocate(NULL, if_name);
228 return;
229 }
230 #endif /* !USE_SIOCSIFMTU */
231
232
233 __private_extern__
234 Boolean
235 _SCNetworkInterfaceSetMTU(SCNetworkInterfaceRef interface,
236 CFDictionaryRef options)
237 {
238 CFStringRef interfaceName;
239 int mtu_cur = -1;
240 int mtu_max = -1;
241 int mtu_min = -1;
242 int requested;
243 CFNumberRef val;
244
245 interfaceName = SCNetworkInterfaceGetBSDName(interface);
246 if (interfaceName == NULL) {
247 /* if no BSD interface name */
248 return FALSE;
249 }
250
251 if (!SCNetworkInterfaceCopyMTU(interface, &mtu_cur, &mtu_min, &mtu_max)) {
252 /* could not get current MTU */
253 return FALSE;
254 }
255
256 val = NULL;
257 if (isA_CFDictionary(options)) {
258 val = CFDictionaryGetValue(options, kSCPropNetEthernetMTU);
259 val = isA_CFNumber(val);
260 }
261 if (val == NULL) {
262 CFDictionaryRef baseOptions;
263
264 /* get base MTU */
265 baseOptions = CFDictionaryGetValue(baseSettings, interfaceName);
266 if (baseOptions != NULL) {
267 val = CFDictionaryGetValue(baseOptions, kSCPropNetEthernetMTU);
268 }
269 }
270 if (val != NULL) {
271 CFNumberGetValue(val, kCFNumberIntType, &requested);
272 } else {
273 requested = mtu_cur;
274 }
275
276 if (requested == mtu_cur) {
277 /* if current setting is as requested */
278 return TRUE;
279 }
280
281 if (((mtu_min >= 0) && (requested < mtu_min)) ||
282 ((mtu_max >= 0) && (requested > mtu_max))) {
283 /* if requested MTU outside of the valid range */
284 return FALSE;
285 }
286
287 #ifdef USE_SIOCSIFMTU
288 {
289 struct ifreq ifr;
290 int ret;
291 int sock;
292
293 bzero((char *)&ifr, sizeof(ifr));
294 (void)_SC_cfstring_to_cstring(interfaceName, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII);
295 ifr.ifr_mtu = requested;
296
297 sock = socket(AF_INET, SOCK_DGRAM, 0);
298 if (sock == -1) {
299 SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
300 return FALSE;
301 }
302
303 ret = ioctl(sock, SIOCSIFMTU, (caddr_t)&ifr);
304 (void)close(sock);
305 if (ret == -1) {
306 SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(SIOCSIFMTU) failed: %s"), strerror(errno));
307 return FALSE;
308 }
309 }
310 #else /* !USE_SIOCSIFMTU */
311 {
312 char *ifconfig_argv[] = { "ifconfig", NULL, "mtu", NULL, NULL };
313 pid_t pid;
314
315 ifconfig_argv[1] = _SC_cfstring_to_cstring(interfaceName, NULL, 0, kCFStringEncodingASCII);
316 (void)asprintf(&ifconfig_argv[3], "%d", requested);
317
318 pid = _SCDPluginExecCommand(ifconfig_exit, // callout,
319 ifconfig_argv[1], // context
320 0, // uid
321 0, // gid
322 "/sbin/ifconfig", // path
323 ifconfig_argv // argv
324 );
325
326 // CFAllocatorDeallocate(NULL, ifconfig_argv[1]); // released in ifconfig_exit()
327 free(ifconfig_argv[3]);
328
329 if (pid <= 0) {
330 return FALSE;
331 }
332 }
333 #endif /* !USE_SIOCSIFMTU */
334
335 return TRUE;
336 }
337
338
339 /*
340 * Function: parse_component
341 * Purpose:
342 * Given a string 'key' and a string prefix 'prefix',
343 * return the next component in the slash '/' separated
344 * key.
345 *
346 * Examples:
347 * 1. key = "a/b/c" prefix = "a/"
348 * returns "b"
349 * 2. key = "a/b/c" prefix = "a/b/"
350 * returns "c"
351 */
352 static CFStringRef
353 parse_component(CFStringRef key, CFStringRef prefix)
354 {
355 CFMutableStringRef comp;
356 CFRange range;
357
358 if (CFStringHasPrefix(key, prefix) == FALSE) {
359 return NULL;
360 }
361 comp = CFStringCreateMutableCopy(NULL, 0, key);
362 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix)));
363 range = CFStringFind(comp, CFSTR("/"), 0);
364 if (range.location == kCFNotFound) {
365 return comp;
366 }
367 range.length = CFStringGetLength(comp) - range.location;
368 CFStringDelete(comp, range);
369 return comp;
370 }
371
372
373 static void updateLink(CFStringRef interfaceName, CFDictionaryRef options);
374
375
376 static void
377 updateInterfaces(CFArrayRef newInterfaces)
378 {
379 CFIndex i;
380 CFIndex n_old;
381 CFIndex n_new;
382 static CFArrayRef oldInterfaces = NULL;
383
384 n_old = (oldInterfaces != NULL) ? CFArrayGetCount(oldInterfaces) : 0;
385 n_new = CFArrayGetCount(newInterfaces);
386
387 for (i = 0; i < n_new; i++) {
388 CFStringRef interfaceName;
389
390 interfaceName = CFArrayGetValueAtIndex(newInterfaces, i);
391
392 if ((n_old == 0) ||
393 !CFArrayContainsValue(oldInterfaces,
394 CFRangeMake(0, n_old),
395 interfaceName)) {
396 CFDictionaryRef options;
397
398 // if new interface
399 options = CFDictionaryGetValue(wantSettings, interfaceName);
400 updateLink(interfaceName, options);
401 }
402 }
403
404 if (oldInterfaces != NULL) CFRelease(oldInterfaces);
405 oldInterfaces = CFRetain(newInterfaces);
406 }
407
408
409 static void
410 updateLink(CFStringRef interfaceName, CFDictionaryRef options)
411 {
412 SCNetworkInterfaceRef interface;
413
414 /* retain requested configuration */
415 if (options != NULL) {
416 CFDictionarySetValue(wantSettings, interfaceName, options);
417 } else {
418 CFDictionaryRemoveValue(wantSettings, interfaceName);
419 }
420
421 /* apply requested configuration */
422 interface = _SCNetworkInterfaceCreateWithBSDName(NULL, interfaceName,
423 kIncludeAllVirtualInterfaces);
424 if (interface == NULL) {
425 return;
426 }
427
428 if (options != NULL) {
429 if (!CFDictionaryContainsKey(baseSettings, interfaceName)) {
430 CFDictionaryRef cur_media = NULL;
431 CFMutableDictionaryRef new_media = NULL;
432 int cur_mtu = -1;
433
434 /* preserve current media options */
435 if (SCNetworkInterfaceCopyMediaOptions(interface, &cur_media, NULL, NULL, FALSE)) {
436 if (cur_media != NULL) {
437 new_media = CFDictionaryCreateMutableCopy(NULL, 0, cur_media);
438 CFRelease(cur_media);
439 }
440 }
441
442 /* preserve current MTU */
443 if (SCNetworkInterfaceCopyMTU(interface, &cur_mtu, NULL, NULL)) {
444 if (cur_mtu != -1) {
445 CFNumberRef num;
446
447 if (new_media == NULL) {
448 new_media = CFDictionaryCreateMutable(NULL,
449 0,
450 &kCFTypeDictionaryKeyCallBacks,
451 &kCFTypeDictionaryValueCallBacks);
452 }
453
454 num = CFNumberCreate(NULL, kCFNumberIntType, &cur_mtu);
455 CFDictionaryAddValue(new_media, kSCPropNetEthernetMTU, num);
456 CFRelease(num);
457 }
458 }
459
460 if (new_media != NULL) {
461 CFDictionarySetValue(baseSettings, interfaceName, new_media);
462 CFRelease(new_media);
463 }
464 }
465
466 /* establish new settings */
467 (void)_SCNetworkInterfaceSetMediaOptions(interface, options);
468 (void)_SCNetworkInterfaceSetMTU (interface, options);
469 } else {
470 /* no requested settings */
471 options = CFDictionaryGetValue(baseSettings, interfaceName);
472 if (options != NULL) {
473 /* restore original settings */
474 (void)_SCNetworkInterfaceSetMediaOptions(interface, options);
475 (void)_SCNetworkInterfaceSetMTU (interface, options);
476 CFDictionaryRemoveValue(baseSettings, interfaceName);
477 }
478 }
479
480 CFRelease(interface);
481 return;
482 }
483
484
485 static void
486 linkConfigChangedCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg)
487 {
488 CFDictionaryRef changes;
489 CFIndex i;
490 CFIndex n;
491 static CFStringRef prefix = NULL;
492
493 if (prefix == NULL) {
494 prefix = SCDynamicStoreKeyCreate(NULL,
495 CFSTR("%@/%@/%@/"),
496 kSCDynamicStoreDomainSetup,
497 kSCCompNetwork,
498 kSCCompInterface);
499 }
500
501 changes = SCDynamicStoreCopyMultiple(store, changedKeys, NULL);
502
503 n = CFArrayGetCount(changedKeys);
504 for (i = 0; i < n; i++) {
505 CFStringRef key;
506 CFDictionaryRef info;
507
508 key = CFArrayGetValueAtIndex(changedKeys, i);
509 info = CFDictionaryGetValue(changes, key);
510
511 if (CFEqual(key, interfacesKey)) {
512 CFArrayRef interfaces;
513
514 interfaces = CFDictionaryGetValue(info, kSCPropNetInterfaces);
515 if (isA_CFArray(interfaces)) {
516 updateInterfaces(interfaces);
517 }
518 } else {
519 CFStringRef interfaceName;
520
521 interfaceName = parse_component(key, prefix);
522 if (interfaceName != NULL) {
523 updateLink(interfaceName, info);
524 CFRelease(interfaceName);
525 }
526 }
527 }
528
529 CFRelease(changes);
530
531 return;
532 }
533
534
535 __private_extern__
536 void
537 load_LinkConfiguration(CFBundleRef bundle, Boolean bundleVerbose)
538 {
539 CFStringRef key;
540 CFMutableArrayRef keys = NULL;
541 Boolean ok;
542 CFMutableArrayRef patterns = NULL;
543
544 if (bundleVerbose) {
545 _verbose = TRUE;
546 }
547
548 SCLog(_verbose, LOG_DEBUG, CFSTR("load() called"));
549 SCLog(_verbose, LOG_DEBUG, CFSTR(" bundle ID = %@"), CFBundleGetIdentifier(bundle));
550
551 /* initialize a few globals */
552
553 baseSettings = CFDictionaryCreateMutable(NULL,
554 0,
555 &kCFTypeDictionaryKeyCallBacks,
556 &kCFTypeDictionaryValueCallBacks);
557 wantSettings = CFDictionaryCreateMutable(NULL,
558 0,
559 &kCFTypeDictionaryKeyCallBacks,
560 &kCFTypeDictionaryValueCallBacks);
561
562 /* open a "configd" store to allow cache updates */
563 store = SCDynamicStoreCreate(NULL,
564 CFSTR("Link Configuraton plug-in"),
565 linkConfigChangedCallback,
566 NULL);
567 if (store == NULL) {
568 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed: %s"), SCErrorString(SCError()));
569 goto error;
570 }
571
572 /* establish notification keys and patterns */
573 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
574 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
575
576 /* ...watch for a change in the list of network interfaces */
577 interfacesKey = SCDynamicStoreKeyCreateNetworkInterface(NULL,
578 kSCDynamicStoreDomainState);
579 CFArrayAppendValue(keys, interfacesKey);
580
581 /* ...watch for (per-interface) AirPort configuration changes */
582 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
583 kSCDynamicStoreDomainSetup,
584 kSCCompAnyRegex,
585 kSCEntNetAirPort);
586 CFArrayAppendValue(patterns, key);
587 CFRelease(key);
588
589 /* ...watch for (per-interface) Ethernet configuration changes */
590 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
591 kSCDynamicStoreDomainSetup,
592 kSCCompAnyRegex,
593 kSCEntNetEthernet);
594 CFArrayAppendValue(patterns, key);
595 CFRelease(key);
596
597 /* ...watch for (per-interface) FireWire configuration changes */
598 key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
599 kSCDynamicStoreDomainSetup,
600 kSCCompAnyRegex,
601 kSCEntNetFireWire);
602 CFArrayAppendValue(patterns, key);
603 CFRelease(key);
604
605 /* register the keys/patterns */
606 ok = SCDynamicStoreSetNotificationKeys(store, keys, patterns);
607 CFRelease(keys);
608 CFRelease(patterns);
609 if (!ok) {
610 SCLog(TRUE, LOG_ERR,
611 CFSTR("SCDynamicStoreSetNotificationKeys() failed: %s"),
612 SCErrorString(SCError()));
613 goto error;
614 }
615
616 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
617 if (rls == NULL) {
618 SCLog(TRUE, LOG_ERR,
619 CFSTR("SCDynamicStoreCreateRunLoopSource() failed: %s"),
620 SCErrorString(SCError()));
621 goto error;
622 }
623
624 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
625 return;
626
627 error :
628
629 if (baseSettings != NULL) CFRelease(baseSettings);
630 if (wantSettings != NULL) CFRelease(wantSettings);
631 if (store != NULL) CFRelease(store);
632 return;
633 }
634
635
636 #ifdef MAIN
637 int
638 main(int argc, char **argv)
639 {
640 _sc_log = FALSE;
641 _sc_verbose = (argc > 1) ? TRUE : FALSE;
642
643 load_LinkConfiguration(CFBundleGetMainBundle(), (argc > 1) ? TRUE : FALSE);
644 CFRunLoopRun();
645 /* not reached */
646 exit(0);
647 return 0;
648 }
649 #endif