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