]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/LinkConfiguration.c
4c92666b15878903373865f3a3d796835edc8f41
[apple/configd.git] / SystemConfiguration.fproj / LinkConfiguration.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 <unistd.h>
33 #define KERNEL_PRIVATE
34 #include <sys/ioctl.h>
35 #undef KERNEL_PRIVATE
36 #include <sys/socket.h>
37 #include <net/ethernet.h>
38 #include <net/if.h>
39 #include <net/if_vlan_var.h>
40 #include <net/if_media.h>
41 #include <net/if_types.h>
42
43 #include <SystemConfiguration/SystemConfiguration.h>
44 #include <SystemConfiguration/SCPrivate.h> // for SCLog()
45 #include <SystemConfiguration/SCValidation.h>
46
47 #include <IOKit/IOKitLib.h>
48 #include <IOKit/network/IONetworkInterface.h>
49 #include <IOKit/network/IONetworkController.h>
50 #include "dy_framework.h"
51
52
53 static const struct ifmedia_description ifm_subtype_shared_descriptions[] =
54 IFM_SUBTYPE_SHARED_DESCRIPTIONS;
55
56 static const struct ifmedia_description ifm_subtype_ethernet_descriptions[] =
57 IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
58
59 static const struct ifmedia_description ifm_subtype_ieee80211_descriptions[] =
60 IFM_SUBTYPE_IEEE80211_DESCRIPTIONS;
61
62 static const struct ifmedia_description ifm_shared_option_descriptions[] =
63 IFM_SHARED_OPTION_DESCRIPTIONS;
64
65 static const struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
66 IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
67
68 static const struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] =
69 IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS;
70
71
72 static void
73 __freeMediaList(struct ifmediareq *ifm)
74 {
75 if (ifm->ifm_ulist != NULL) CFAllocatorDeallocate(NULL, ifm->ifm_ulist);
76 CFAllocatorDeallocate(NULL, ifm);
77 return;
78 }
79
80
81 static struct ifmediareq *
82 __copyMediaList(CFStringRef interface)
83 {
84 struct ifmediareq *ifm;
85 Boolean ok = FALSE;
86 int sock = -1;
87
88 ifm = (struct ifmediareq *)CFAllocatorAllocate(NULL, sizeof(struct ifmediareq), 0);
89 bzero((void *)ifm, sizeof(*ifm));
90
91 if (_SC_cfstring_to_cstring(interface, ifm->ifm_name, sizeof(ifm->ifm_name), kCFStringEncodingASCII) == NULL) {
92 SCLog(TRUE, LOG_ERR, CFSTR("could not convert inteface name"));
93 goto done;
94 }
95
96 sock = socket(AF_INET, SOCK_DGRAM, 0);
97 if (sock == -1) {
98 SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
99 goto done;
100 }
101
102 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)ifm) == -1) {
103 // SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno));
104 goto done;
105 }
106
107 if (ifm->ifm_count > 0) {
108 ifm->ifm_ulist = (int *)CFAllocatorAllocate(NULL, ifm->ifm_count * sizeof(int), 0);
109 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)ifm) == -1) {
110 SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(SIOCGIFMEDIA) failed: %s"), strerror(errno));
111 goto done;
112 }
113 }
114
115 ok = TRUE;
116
117 done :
118
119 if (sock != -1) (void)close(sock);
120 if (!ok) {
121 __freeMediaList(ifm);
122 ifm = NULL;
123 }
124 return ifm;
125 }
126
127
128 static CFDictionaryRef
129 __createMediaDictionary(int media_options, Boolean filter)
130 {
131 CFMutableDictionaryRef dict = NULL;
132 int i;
133 const struct ifmedia_description *option_descriptions = NULL;
134 CFMutableArrayRef options = NULL;
135 const struct ifmedia_description *subtype_descriptions = NULL;
136 CFStringRef val;
137
138 switch (IFM_TYPE(media_options)) {
139 case IFM_ETHER :
140 option_descriptions = ifm_subtype_ethernet_option_descriptions;
141 subtype_descriptions = ifm_subtype_ethernet_descriptions;
142 break;
143 case IFM_IEEE80211 :
144 option_descriptions = ifm_subtype_ieee80211_option_descriptions;
145 subtype_descriptions = ifm_subtype_ieee80211_descriptions;
146 break;
147 default :
148 return NULL;
149 }
150
151 if (filter && (IFM_SUBTYPE(media_options) == IFM_NONE)) {
152 return NULL; /* filter */
153 }
154
155 dict = CFDictionaryCreateMutable(NULL,
156 0,
157 &kCFTypeDictionaryKeyCallBacks,
158 &kCFTypeDictionaryValueCallBacks);
159
160 /* subtype */
161
162 val = NULL;
163 for (i = 0; !val && ifm_subtype_shared_descriptions[i].ifmt_string; i++) {
164 if (IFM_SUBTYPE(media_options) == ifm_subtype_shared_descriptions[i].ifmt_word) {
165 val = CFStringCreateWithCString(NULL,
166 ifm_subtype_shared_descriptions[i].ifmt_string,
167 kCFStringEncodingASCII);
168 break;
169 }
170 }
171
172 if (subtype_descriptions != NULL) {
173 for (i = 0; !val && subtype_descriptions[i].ifmt_string; i++) {
174 if (IFM_SUBTYPE(media_options) == subtype_descriptions[i].ifmt_word) {
175 val = CFStringCreateWithCString(NULL,
176 subtype_descriptions[i].ifmt_string,
177 kCFStringEncodingASCII);
178 break;
179 }
180 }
181 }
182
183 if (val) {
184 CFDictionaryAddValue(dict, kSCPropNetEthernetMediaSubType, val);
185 CFRelease(val);
186 }
187
188 /* options */
189
190 options = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
191
192 while (IFM_OPTIONS(media_options) != 0) {
193 if (filter && (IFM_OPTIONS(media_options) & IFM_LOOP)) {
194 media_options &= ~IFM_LOOP; /* filter */
195 continue;
196 }
197
198 val = NULL;
199 for (i = 0; !val && ifm_shared_option_descriptions[i].ifmt_string; i++) {
200 if (IFM_OPTIONS(media_options) & ifm_shared_option_descriptions[i].ifmt_word) {
201 val = CFStringCreateWithCString(NULL,
202 ifm_shared_option_descriptions[i].ifmt_string,
203 kCFStringEncodingASCII);
204 media_options &= ~ifm_shared_option_descriptions[i].ifmt_word;
205 break;
206 }
207 }
208
209 if (option_descriptions != NULL) {
210 for (i = 0; !val && option_descriptions[i].ifmt_string; i++) {
211 if (IFM_OPTIONS(media_options) & option_descriptions[i].ifmt_word) {
212 val = CFStringCreateWithCString(NULL,
213 option_descriptions[i].ifmt_string,
214 kCFStringEncodingASCII);
215 media_options &= ~option_descriptions[i].ifmt_word;
216 break;
217 }
218 }
219 }
220
221 if (val) {
222 CFArrayAppendValue(options, val);
223 CFRelease(val);
224 }
225 }
226
227 CFDictionaryAddValue(dict, kSCPropNetEthernetMediaOptions, options);
228 CFRelease(options);
229
230 return dict;
231 }
232
233
234 int
235 __createMediaOptions(CFStringRef interface, CFDictionaryRef media_options)
236 {
237 CFIndex i;
238 struct ifmediareq *ifm;
239 int ifm_new = -1;
240 Boolean match;
241 CFIndex n;
242 const struct ifmedia_description *option_descriptions = NULL;
243 CFArrayRef options;
244 char *str;
245 const struct ifmedia_description *subtype_descriptions = NULL;
246 CFStringRef val;
247
248 /* set type */
249
250 ifm = __copyMediaList(interface);
251 if (ifm != NULL) {
252 if (ifm->ifm_count > 0) {
253 ifm_new = IFM_TYPE(ifm->ifm_ulist[0]);
254 }
255 __freeMediaList(ifm);
256 }
257
258 if (ifm_new == -1) {
259 // if we cannot determine the media type for the interface
260 return -1;
261 }
262
263 switch (IFM_TYPE(ifm_new)) {
264 case IFM_ETHER :
265 option_descriptions = ifm_subtype_ethernet_option_descriptions;
266 subtype_descriptions = ifm_subtype_ethernet_descriptions;
267 break;
268 case IFM_IEEE80211 :
269 option_descriptions = ifm_subtype_ieee80211_option_descriptions;
270 subtype_descriptions = ifm_subtype_ieee80211_descriptions;
271 break;
272 }
273
274 /* set subtype */
275
276 val = CFDictionaryGetValue(media_options, kSCPropNetEthernetMediaSubType);
277 if (!isA_CFString(val)) {
278 return -1;
279 }
280
281 str = _SC_cfstring_to_cstring(val, NULL, 0, kCFStringEncodingASCII);
282 if (str == NULL) {
283 return -1;
284 }
285
286 match = FALSE;
287 for (i = 0; !match && ifm_subtype_shared_descriptions[i].ifmt_string; i++) {
288 if (strcasecmp(str, ifm_subtype_shared_descriptions[i].ifmt_string) == 0) {
289 ifm_new |= ifm_subtype_shared_descriptions[i].ifmt_word;
290 match = TRUE;
291 break;
292 }
293 }
294
295 if (subtype_descriptions != NULL) {
296 for (i = 0; !match && subtype_descriptions[i].ifmt_string; i++) {
297 if (strcasecmp(str, subtype_descriptions[i].ifmt_string) == 0) {
298 ifm_new |= subtype_descriptions[i].ifmt_word;
299 match = TRUE;
300 break;
301 }
302 }
303 }
304
305 CFAllocatorDeallocate(NULL, str);
306
307 if (!match) {
308 return -1; /* if no subtype */
309 }
310
311 /* set options */
312
313 options = CFDictionaryGetValue(media_options, kSCPropNetEthernetMediaOptions);
314 if (!isA_CFArray(options)) {
315 return -1;
316 }
317
318 n = CFArrayGetCount(options);
319 for (i = 0; i < n; i++) {
320 CFIndex j;
321
322 val = CFArrayGetValueAtIndex(options, i);
323 if (!isA_CFString(val)) {
324 return -1;
325 }
326
327 str = _SC_cfstring_to_cstring(val, NULL, 0, kCFStringEncodingASCII);
328 if (str == NULL) {
329 return -1;
330 }
331
332
333 match = FALSE;
334 for (j = 0; !match && ifm_shared_option_descriptions[j].ifmt_string; j++) {
335 if (strcasecmp(str, ifm_shared_option_descriptions[j].ifmt_string) == 0) {
336 ifm_new |= ifm_shared_option_descriptions[j].ifmt_word;
337 match = TRUE;
338 break;
339 }
340 }
341
342 if (option_descriptions != NULL) {
343 for (j = 0; !match && option_descriptions[j].ifmt_string; j++) {
344 if (strcasecmp(str, option_descriptions[j].ifmt_string) == 0) {
345 ifm_new |= option_descriptions[j].ifmt_word;
346 match = TRUE;
347 break;
348 }
349 }
350 }
351
352 CFAllocatorDeallocate(NULL, str);
353
354 if (!match) {
355 return -1; /* if no option */
356 }
357 }
358
359 return ifm_new;
360 }
361
362
363 Boolean
364 NetworkInterfaceCopyMediaOptions(CFStringRef interface,
365 CFDictionaryRef *current,
366 CFDictionaryRef *active,
367 CFArrayRef *available,
368 Boolean filter)
369 {
370 int i;
371 struct ifmediareq *ifm;
372
373 ifm = __copyMediaList(interface);
374 if (ifm == NULL) {
375 return FALSE;
376 }
377
378 if (active) *active = NULL;
379 if (current) *current = NULL;
380 if (available) {
381 CFMutableArrayRef media_options;
382
383 media_options = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
384 for (i = 0; i < ifm->ifm_count; i++) {
385 CFDictionaryRef options;
386
387 options = __createMediaDictionary(ifm->ifm_ulist[i], filter);
388 if (!options) {
389 continue;
390 }
391
392 if (active && (*active == NULL) && (ifm->ifm_active == ifm->ifm_ulist[i])) {
393 *active = CFRetain(options);
394 }
395
396 if (current && (*current == NULL) && (ifm->ifm_current == ifm->ifm_ulist[i])) {
397 *current = CFRetain(options);
398 }
399
400 if (!CFArrayContainsValue(media_options, CFRangeMake(0, CFArrayGetCount(media_options)), options)) {
401 CFArrayAppendValue(media_options, options);
402 }
403
404 CFRelease(options);
405 }
406 *available = (CFArrayRef)media_options;
407 }
408
409 if (active && (*active == NULL)) {
410 *active = __createMediaDictionary(ifm->ifm_active, FALSE);
411 }
412
413 if (current && (*current == NULL)) {
414 if (active && (ifm->ifm_active == ifm->ifm_current)) {
415 if (*active) *current = CFRetain(active);
416 } else {
417 *current = __createMediaDictionary(ifm->ifm_current, FALSE);
418 }
419 }
420
421 __freeMediaList(ifm);
422 return TRUE;
423 }
424
425
426 CFArrayRef
427 NetworkInterfaceCopyMediaSubTypes(CFArrayRef available)
428 {
429 CFIndex i;
430 CFIndex n;
431 CFMutableArrayRef subTypes;
432
433 if (!isA_CFArray(available)) {
434 return NULL;
435 }
436
437 subTypes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
438
439 n = CFArrayGetCount(available);
440 for (i = 0; i < n; i++) {
441 CFDictionaryRef options;
442 CFStringRef subType;
443
444 options = CFArrayGetValueAtIndex(available, i);
445 if (!isA_CFDictionary(options)) {
446 continue;
447 }
448
449 subType = CFDictionaryGetValue(options, kSCPropNetEthernetMediaSubType);
450 if (!isA_CFString(subType)) {
451 continue;
452 }
453
454 if (!CFArrayContainsValue(subTypes, CFRangeMake(0, CFArrayGetCount(subTypes)), subType)) {
455 CFArrayAppendValue(subTypes, subType);
456 }
457 }
458
459 if (CFArrayGetCount(subTypes) == 0) {
460 CFRelease(subTypes);
461 subTypes = NULL;
462 }
463
464 return subTypes;
465 }
466
467
468 CFArrayRef
469 NetworkInterfaceCopyMediaSubTypeOptions(CFArrayRef available,
470 CFStringRef subType)
471 {
472 CFIndex i;
473 CFIndex n;
474 CFMutableArrayRef subTypeOptions;
475
476 if (!isA_CFArray(available)) {
477 return NULL;
478 }
479
480 subTypeOptions = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
481
482 n = CFArrayGetCount(available);
483 for (i = 0; i < n; i++) {
484 CFDictionaryRef options;
485 CFArrayRef mediaOptions;
486 CFStringRef mediaSubType;
487
488 options = CFArrayGetValueAtIndex(available, i);
489 if (!isA_CFDictionary(options)) {
490 continue;
491 }
492
493 mediaSubType = CFDictionaryGetValue(options, kSCPropNetEthernetMediaSubType);
494 if (!isA_CFString(mediaSubType) || !CFEqual(subType, mediaSubType)) {
495 continue;
496 }
497
498 mediaOptions = CFDictionaryGetValue(options, kSCPropNetEthernetMediaOptions);
499 if (!isA_CFArray(mediaOptions)) {
500 continue;
501 }
502
503 if (!CFArrayContainsValue(subTypeOptions, CFRangeMake(0, CFArrayGetCount(subTypeOptions)), mediaOptions)) {
504 CFArrayAppendValue(subTypeOptions, mediaOptions);
505 }
506 }
507
508 if (CFArrayGetCount(subTypeOptions) == 0) {
509 CFRelease(subTypeOptions);
510 subTypeOptions = NULL;
511 }
512
513 return subTypeOptions;
514 }
515
516
517 static Boolean
518 __getMTULimits(char ifr_name[IFNAMSIZ],
519 int *mtu_min,
520 int *mtu_max)
521 {
522 int ifType = 0;
523 io_iterator_t io_iter = 0;
524 io_registry_entry_t io_interface = 0;
525 io_registry_entry_t io_controller = 0;
526 kern_return_t kr;
527 static mach_port_t masterPort = MACH_PORT_NULL;
528 CFMutableDictionaryRef matchingDict;
529
530 /* look for a matching interface in the IORegistry */
531
532 if (masterPort == MACH_PORT_NULL) {
533 kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
534 if (kr != KERN_SUCCESS) {
535 return FALSE;
536 }
537 }
538
539 matchingDict = IOBSDNameMatching(masterPort, 0, ifr_name);
540 if (matchingDict) {
541 /* Note: IOServiceGetMatchingServices consumes a reference on the 'matchingDict' */
542 kr = IOServiceGetMatchingServices(masterPort, matchingDict, &io_iter);
543 if ((kr == KERN_SUCCESS) && io_iter) {
544 /* should only have a single match */
545 io_interface = IOIteratorNext(io_iter);
546 }
547 if (io_iter) IOObjectRelease(io_iter);
548 }
549
550 if (io_interface) {
551 CFNumberRef num;
552
553 /*
554 * found an interface, get the interface type
555 */
556 num = IORegistryEntryCreateCFProperty(io_interface, CFSTR(kIOInterfaceType), NULL, kNilOptions);
557 if (num) {
558 if (isA_CFNumber(num)) {
559 CFNumberGetValue(num, kCFNumberIntType, &ifType);
560 }
561 CFRelease(num);
562 }
563
564 /*
565 * ...and the property we are REALLY interested is in the controller,
566 * which is the parent of the interface object.
567 */
568 (void)IORegistryEntryGetParentEntry(io_interface, kIOServicePlane, &io_controller);
569 IOObjectRelease(io_interface);
570 } else {
571 /* if no matching interface */
572 return FALSE;
573 }
574
575 if (io_controller) {
576 CFNumberRef num;
577
578 num = IORegistryEntryCreateCFProperty(io_controller, CFSTR(kIOMaxPacketSize), NULL, kNilOptions);
579 if (num) {
580 if (isA_CFNumber(num)) {
581 int value;
582
583 /*
584 * Get the value and subtract the FCS bytes and Ethernet header
585 * sizes from the maximum frame size reported by the controller
586 * to get the MTU size. The 14 byte media header can be found
587 * in the registry, but not the size for the trailing FCS bytes.
588 */
589 CFNumberGetValue(num, kCFNumberIntType, &value);
590
591 if (ifType == IFT_ETHER) {
592 value -= (ETHER_HDR_LEN + ETHER_CRC_LEN);
593 }
594
595 if (mtu_min) *mtu_min = IF_MINMTU;
596 if (mtu_max) *mtu_max = value;
597 }
598 CFRelease(num);
599 }
600
601 IOObjectRelease(io_controller);
602 }
603
604 return TRUE;
605 }
606
607
608 Boolean
609 NetworkInterfaceCopyMTU(CFStringRef interface,
610 int *mtu_cur,
611 int *mtu_min,
612 int *mtu_max)
613 {
614 struct ifreq ifr;
615 Boolean ok = FALSE;
616 int sock = -1;
617
618 bzero((void *)&ifr, sizeof(ifr));
619 if (_SC_cfstring_to_cstring(interface, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII) == NULL) {
620 SCLog(TRUE, LOG_ERR, CFSTR("could not convert interface name"));
621 goto done;
622 }
623
624 sock = socket(AF_INET, SOCK_DGRAM, 0);
625 if (sock < 0) {
626 SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno));
627 goto done;
628 }
629
630 if (ioctl(sock, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
631 // SCLog(TRUE, LOG_DEBUG, CFSTR("ioctl(SIOCGIFMTU) failed: %s"), strerror(errno));
632 goto done;
633 }
634
635 if (mtu_cur) *mtu_cur = ifr.ifr_mtu;
636 if (mtu_min) *mtu_min = ifr.ifr_mtu;
637 if (mtu_max) *mtu_max = ifr.ifr_mtu;
638
639 /* get valid MTU range */
640
641 if (mtu_min != NULL || mtu_max != NULL) {
642 if (ioctl(sock, SIOCGIFDEVMTU, (caddr_t)&ifr) == 0) {
643 struct ifdevmtu * devmtu_p;
644
645 devmtu_p = &ifr.ifr_devmtu;
646 if (mtu_min != NULL) {
647 *mtu_min = (devmtu_p->ifdm_min > IF_MINMTU)
648 ? devmtu_p->ifdm_min : IF_MINMTU;
649 }
650 if (mtu_max != NULL) {
651 *mtu_max = devmtu_p->ifdm_max;
652 }
653 }
654 else {
655 (void)__getMTULimits(ifr.ifr_name, mtu_min, mtu_max);
656 }
657 }
658
659 ok = TRUE;
660
661 done :
662
663 if (sock >= 0) (void)close(sock);
664
665 return ok;
666 }