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