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