]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/LinkConfiguration.c
dc4ef3f062cb933ad6985200d865203eb42e8f51
[apple/configd.git] / SystemConfiguration.fproj / LinkConfiguration.c
1 /*
2 * Copyright (c) 2002-2007, 2010, 2011, 2013, 2015, 2016 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 <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 #include <netinet/in.h>
43 #include <netinet/ip6.h> // for IPV6_MMTU
44
45 #include "SCNetworkConfigurationInternal.h" // for __SCNetworkInterfaceCreatePrivate
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 #pragma mark -
54 #pragma mark Capabilities
55
56
57 // the following table needs to keep the capabilitiy names and values
58 // between the <SystemConfiguration/SCSchemaDefinitionsPrivate.h> and
59 // <net/if.h> headers in sync.
60 static const struct {
61 const CFStringRef *name;
62 Boolean readwrite;
63 int val;
64 } capabilityMappings[] = {
65 #ifdef SIOCGIFCAP
66 { &kSCPropNetEthernetCapabilityRXCSUM, TRUE, IFCAP_RXCSUM }, // can offload checksum on RX
67 { &kSCPropNetEthernetCapabilityTXCSUM, TRUE, IFCAP_TXCSUM }, // can offload checksum on TX
68 { &kSCPropNetEthernetCapabilityVLAN_MTU, FALSE, IFCAP_VLAN_MTU }, // VLAN-compatible MTU
69 { &kSCPropNetEthernetCapabilityVLAN_HWTAGGING, FALSE, IFCAP_VLAN_HWTAGGING }, // hardware VLAN tag support
70 { &kSCPropNetEthernetCapabilityJUMBO_MTU, FALSE, IFCAP_JUMBO_MTU }, // 9000 byte MTU supported
71 { &kSCPropNetEthernetCapabilityTSO, TRUE, IFCAP_TSO }, // can do TCP/TCP6 Segmentation Offload
72 { &kSCPropNetEthernetCapabilityTSO4, FALSE, IFCAP_TSO4 }, // can do TCP Segmentation Offload
73 { &kSCPropNetEthernetCapabilityTSO6, FALSE, IFCAP_TSO6 }, // can do TCP6 Segmentation Offload
74 { &kSCPropNetEthernetCapabilityLRO, TRUE, IFCAP_LRO }, // can do Large Receive Offload
75 { &kSCPropNetEthernetCapabilityAV, TRUE, IFCAP_AV }, // can do 802.1 AV Bridging
76 #endif // SIOCGIFCAP
77 };
78
79
80 static CFIndex
81 findCapability(CFStringRef capability)
82 {
83 CFIndex i;
84
85 for (i = 0; i < sizeof(capabilityMappings) / sizeof(capabilityMappings[0]); i++) {
86 if (CFEqual(capability, *capabilityMappings[i].name)) {
87 return i;
88 }
89 }
90
91 return kCFNotFound;
92 }
93
94
95 static Boolean
96 __getCapabilities(CFStringRef interfaceName,
97 int *current,
98 int *available)
99 {
100 #ifdef SIOCGIFCAP
101 struct ifreq ifr;
102 Boolean ok = FALSE;
103 int sock = -1;
104
105 bzero((void *)&ifr, sizeof(ifr));
106 if (_SC_cfstring_to_cstring(interfaceName, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII) == NULL) {
107 SC_log(LOG_NOTICE, "could not convert interface name");
108 _SCErrorSet(kSCStatusInvalidArgument);
109 return FALSE;
110 }
111
112 sock = socket(AF_INET, SOCK_DGRAM, 0);
113 if (sock == -1) {
114 _SCErrorSet(errno);
115 SC_log(LOG_ERR, "socket() failed: %s", strerror(errno));
116 return FALSE;
117 }
118
119 if (ioctl(sock, SIOCGIFCAP, (caddr_t)&ifr) == -1) {
120 _SCErrorSet(errno);
121 switch (errno) {
122 case EBUSY :
123 case ENXIO :
124 break;
125 default :
126 SC_log(LOG_NOTICE, "ioctl(SIOCGIFCAP) failed: %s", strerror(errno));
127 }
128 goto done;
129 }
130
131 if (current != NULL) *current = ifr.ifr_curcap;
132 if (available != NULL) *available = ifr.ifr_reqcap;
133
134 ok = TRUE;
135
136 done :
137
138 (void)close(sock);
139 return ok;
140 #else // SIOCGIFCAP
141 if (current != NULL) *current = 0;
142 if (available != NULL) *available = 0;
143 return TRUE;
144 #endif // SIOCGIFCAP
145 }
146
147
148 int
149 __SCNetworkInterfaceCreateCapabilities(SCNetworkInterfaceRef interface,
150 int capability_base,
151 CFDictionaryRef capability_options)
152 {
153 int cap_available = 0;
154 int cap_current = capability_base;
155 CFIndex i;
156 CFStringRef interfaceName;
157
158 if (!isA_SCNetworkInterface(interface)) {
159 _SCErrorSet(kSCStatusInvalidArgument);
160 goto done;
161 }
162
163 interfaceName = SCNetworkInterfaceGetBSDName(interface);
164 if (interfaceName == NULL) {
165 _SCErrorSet(kSCStatusInvalidArgument);
166 goto done;
167 }
168
169 if (!__getCapabilities(interfaceName,
170 (capability_base == -1) ? &cap_current : NULL,
171 &cap_available)) {
172 goto done;
173 }
174
175 if (cap_available == 0) {
176 goto done;
177 }
178
179 if (capability_options == NULL) {
180 goto done;
181 }
182
183 for (i = 0; i < sizeof(capabilityMappings) / sizeof(capabilityMappings[0]); i++) {
184 int cap_val;
185 CFTypeRef val;
186
187 if (((cap_available & capabilityMappings[i].val) != 0) &&
188 capabilityMappings[i].readwrite &&
189 CFDictionaryGetValueIfPresent(capability_options,
190 *capabilityMappings[i].name,
191 &val) &&
192 isA_CFNumber(val) &&
193 CFNumberGetValue(val, kCFNumberIntType, &cap_val)) {
194 // update capability
195 if (cap_val != 0) {
196 cap_current |= (cap_available & capabilityMappings[i].val);
197 } else {
198 cap_current &= ~capabilityMappings[i].val;
199 }
200
201 // don't process again
202 cap_available &= ~capabilityMappings[i].val;
203 }
204 }
205
206 done :
207
208 return cap_current;
209 }
210
211
212 CFTypeRef
213 SCNetworkInterfaceCopyCapability(SCNetworkInterfaceRef interface,
214 CFStringRef capability)
215 {
216 int cap_current = 0;
217 int cap_available = 0;
218 int cap_val;
219 CFIndex i;
220 CFStringRef interfaceName;
221 CFTypeRef val = NULL;
222
223 if (!isA_SCNetworkInterface(interface)) {
224 _SCErrorSet(kSCStatusInvalidArgument);
225 return NULL;
226 }
227
228 interfaceName = SCNetworkInterfaceGetBSDName(interface);
229 if (interfaceName == NULL) {
230 _SCErrorSet(kSCStatusInvalidArgument);
231 return NULL;
232 }
233
234 if (!__getCapabilities(interfaceName, &cap_current, &cap_available)) {
235 return NULL;
236 }
237
238 if (capability == NULL) {
239 CFMutableDictionaryRef all = NULL;
240
241 // if ALL capabilities requested
242 for (i = 0; i < sizeof(capabilityMappings) / sizeof(capabilityMappings[0]); i++) {
243 if ((cap_available & capabilityMappings[i].val) == capabilityMappings[i].val) {
244 if (all == NULL) {
245 all = CFDictionaryCreateMutable(NULL,
246 0,
247 &kCFTypeDictionaryKeyCallBacks,
248 &kCFTypeDictionaryValueCallBacks);
249 }
250 cap_val = ((cap_current & capabilityMappings[i].val) == capabilityMappings[i].val) ? 1 : 0;
251 val = CFNumberCreate(NULL, kCFNumberIntType, &cap_val);
252 CFDictionarySetValue(all, *capabilityMappings[i].name, val);
253 CFRelease(val);
254 cap_available &= ~capabilityMappings[i].val;
255 }
256 }
257
258 val = all;
259 } else {
260 i = findCapability(capability);
261 if (i == kCFNotFound) {
262 // if unknown capability
263 _SCErrorSet(kSCStatusInvalidArgument);
264 return NULL;
265 }
266
267 if ((cap_available & capabilityMappings[i].val) != capabilityMappings[i].val) {
268 // if capability not available
269 _SCErrorSet(kSCStatusInvalidArgument);
270 return NULL;
271 }
272
273 cap_val = ((cap_current & capabilityMappings[i].val) == capabilityMappings[i].val) ? 1 : 0;
274 val = CFNumberCreate(NULL, kCFNumberIntType, &cap_val);
275 }
276
277 return val;
278 }
279
280
281 Boolean
282 SCNetworkInterfaceSetCapability(SCNetworkInterfaceRef interface,
283 CFStringRef capability,
284 CFTypeRef newValue)
285 {
286 int cap_available = 0;
287 CFDictionaryRef configuration;
288 CFIndex i;
289 CFStringRef interfaceName;
290 CFMutableDictionaryRef newConfiguration = NULL;
291 Boolean ok = FALSE;
292
293 if (!isA_SCNetworkInterface(interface)) {
294 _SCErrorSet(kSCStatusInvalidArgument);
295 return FALSE;
296 }
297
298 interfaceName = SCNetworkInterfaceGetBSDName(interface);
299 if (interfaceName == NULL) {
300 // if no interface name
301 _SCErrorSet(kSCStatusInvalidArgument);
302 return FALSE;
303 }
304
305 i = findCapability(capability);
306 if (i == kCFNotFound) {
307 // if unknown capability
308 _SCErrorSet(kSCStatusInvalidArgument);
309 return FALSE;
310 }
311
312 if (!capabilityMappings[i].readwrite) {
313 // if not read-write
314 _SCErrorSet(kSCStatusInvalidArgument);
315 return FALSE;
316 }
317
318 if ((newValue != NULL) && !isA_CFNumber(newValue)) {
319 // all values must (for now) be CFNumber[0 or 1]'s
320 _SCErrorSet(kSCStatusInvalidArgument);
321 return FALSE;
322 }
323
324 if (!__getCapabilities(interfaceName, NULL, &cap_available)) {
325 return FALSE;
326 }
327
328 if ((cap_available & capabilityMappings[i].val) == 0) {
329 // if capability not available
330 _SCErrorSet(kSCStatusInvalidArgument);
331 return FALSE;
332 }
333
334 configuration = SCNetworkInterfaceGetConfiguration(interface);
335 if (configuration == NULL) {
336 newConfiguration = CFDictionaryCreateMutable(NULL,
337 0,
338 &kCFTypeDictionaryKeyCallBacks,
339 &kCFTypeDictionaryValueCallBacks);
340 } else {
341 newConfiguration = CFDictionaryCreateMutableCopy(NULL, 0, configuration);
342 CFDictionaryRemoveValue(newConfiguration, kSCResvInactive);
343 }
344
345 if ((newValue != NULL)) {
346 CFDictionarySetValue(newConfiguration, capability, newValue);
347 } else {
348 CFDictionaryRemoveValue(newConfiguration, capability);
349 if (CFDictionaryGetCount(newConfiguration) == 0) {
350 CFRelease(newConfiguration);
351 newConfiguration = NULL;
352 }
353 }
354
355 ok = SCNetworkInterfaceSetConfiguration(interface, newConfiguration);
356 if (newConfiguration != NULL) CFRelease(newConfiguration);
357
358 return ok;
359 }
360
361
362 #pragma mark -
363 #pragma mark Media Options
364
365
366 static const struct ifmedia_description ifm_subtype_shared_descriptions[] =
367 IFM_SUBTYPE_SHARED_DESCRIPTIONS;
368
369 static const struct ifmedia_description ifm_subtype_ethernet_descriptions[] =
370 IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
371
372 static const struct ifmedia_description ifm_subtype_ieee80211_descriptions[] =
373 IFM_SUBTYPE_IEEE80211_DESCRIPTIONS;
374
375 static const struct ifmedia_description ifm_shared_option_descriptions[] =
376 IFM_SHARED_OPTION_DESCRIPTIONS;
377
378 static const struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
379 IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
380
381 static const struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] =
382 IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS;
383
384
385 static void
386 __freeMediaList(struct ifmediareq *ifm)
387 {
388 if (ifm->ifm_ulist != NULL) CFAllocatorDeallocate(NULL, ifm->ifm_ulist);
389 CFAllocatorDeallocate(NULL, ifm);
390 return;
391 }
392
393
394 static struct ifmediareq *
395 __copyMediaList(CFStringRef interfaceName)
396 {
397 struct ifmediareq *ifm;
398 Boolean ok = FALSE;
399 int sock = -1;
400
401 ifm = (struct ifmediareq *)CFAllocatorAllocate(NULL, sizeof(struct ifmediareq), 0);
402 bzero((void *)ifm, sizeof(*ifm));
403
404 if (_SC_cfstring_to_cstring(interfaceName, ifm->ifm_name, sizeof(ifm->ifm_name), kCFStringEncodingASCII) == NULL) {
405 SC_log(LOG_NOTICE, "could not convert interface name");
406 goto done;
407 }
408
409 sock = socket(AF_INET, SOCK_DGRAM, 0);
410 if (sock == -1) {
411 SC_log(LOG_ERR, "socket() failed: %s", strerror(errno));
412 goto done;
413 }
414
415 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)ifm) == -1) {
416 // SC_log(LOG_NOTICE, "ioctl(SIOCGIFMEDIA) failed: %s", strerror(errno));
417 goto done;
418 }
419
420 if (ifm->ifm_count > 0) {
421 ifm->ifm_ulist = (int *)CFAllocatorAllocate(NULL, ifm->ifm_count * sizeof(int), 0);
422 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)ifm) == -1) {
423 SC_log(LOG_NOTICE, "ioctl(SIOCGIFMEDIA) failed: %s", strerror(errno));
424 goto done;
425 }
426 }
427
428 ok = TRUE;
429
430 done :
431
432 if (sock != -1) (void)close(sock);
433 if (!ok) {
434 __freeMediaList(ifm);
435 ifm = NULL;
436 _SCErrorSet(kSCStatusFailed);
437 }
438 return ifm;
439 }
440
441
442 static CFDictionaryRef
443 __createMediaDictionary(int media_options, Boolean filter)
444 {
445 CFMutableDictionaryRef dict = NULL;
446 int i;
447 const struct ifmedia_description *option_descriptions = NULL;
448 CFMutableArrayRef options = NULL;
449 const struct ifmedia_description *subtype_descriptions = NULL;
450 CFStringRef val;
451
452 if (filter &&
453 ((IFM_SUBTYPE(media_options) == IFM_NONE) ||
454 ((IFM_OPTIONS(media_options) & IFM_LOOP) != 0))) {
455 return NULL; /* filter */
456 }
457
458 switch (IFM_TYPE(media_options)) {
459 case IFM_ETHER :
460 option_descriptions = ifm_subtype_ethernet_option_descriptions;
461 subtype_descriptions = ifm_subtype_ethernet_descriptions;
462 break;
463 case IFM_IEEE80211 :
464 option_descriptions = ifm_subtype_ieee80211_option_descriptions;
465 subtype_descriptions = ifm_subtype_ieee80211_descriptions;
466 break;
467 default :
468 return NULL;
469 }
470
471 dict = CFDictionaryCreateMutable(NULL,
472 0,
473 &kCFTypeDictionaryKeyCallBacks,
474 &kCFTypeDictionaryValueCallBacks);
475
476 /* subtype */
477
478 val = NULL;
479 for (i = 0; !val && ifm_subtype_shared_descriptions[i].ifmt_string; i++) {
480 if (IFM_SUBTYPE(media_options) == ifm_subtype_shared_descriptions[i].ifmt_word) {
481 val = CFStringCreateWithCString(NULL,
482 ifm_subtype_shared_descriptions[i].ifmt_string,
483 kCFStringEncodingASCII);
484 break;
485 }
486 }
487
488 if (subtype_descriptions != NULL) {
489 for (i = 0; !val && subtype_descriptions[i].ifmt_string; i++) {
490 if (IFM_SUBTYPE(media_options) == subtype_descriptions[i].ifmt_word) {
491 val = CFStringCreateWithCString(NULL,
492 subtype_descriptions[i].ifmt_string,
493 kCFStringEncodingASCII);
494 break;
495 }
496 }
497 }
498
499 if (val) {
500 CFDictionaryAddValue(dict, kSCPropNetEthernetMediaSubType, val);
501 CFRelease(val);
502 }
503
504 /* options */
505
506 options = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
507
508 for (i = 0; (IFM_OPTIONS(media_options) != 0) && (ifm_shared_option_descriptions[i].ifmt_string != NULL); i++) {
509 if ((media_options & ifm_shared_option_descriptions[i].ifmt_word) != 0) {
510 val = CFStringCreateWithCString(NULL,
511 ifm_shared_option_descriptions[i].ifmt_string,
512 kCFStringEncodingASCII);
513 CFArrayAppendValue(options, val);
514 CFRelease(val);
515
516 media_options &= ~ifm_shared_option_descriptions[i].ifmt_word;
517 }
518 }
519
520 if (option_descriptions != NULL) {
521 for (i = 0; (IFM_OPTIONS(media_options) != 0) && (option_descriptions[i].ifmt_string != NULL); i++) {
522 if ((media_options & option_descriptions[i].ifmt_word) != 0) {
523 val = CFStringCreateWithCString(NULL,
524 option_descriptions[i].ifmt_string,
525 kCFStringEncodingASCII);
526 CFArrayAppendValue(options, val);
527 CFRelease(val);
528
529 media_options &= ~option_descriptions[i].ifmt_word;
530 }
531 }
532 }
533
534 CFDictionaryAddValue(dict, kSCPropNetEthernetMediaOptions, options);
535 CFRelease(options);
536
537 return dict;
538 }
539
540
541 int
542 __SCNetworkInterfaceCreateMediaOptions(SCNetworkInterfaceRef interface, CFDictionaryRef media_options)
543 {
544 CFIndex i;
545 struct ifmediareq *ifm;
546 int ifm_new = -1;
547 CFStringRef interfaceName;
548 Boolean match;
549 CFIndex n;
550 const struct ifmedia_description *option_descriptions = NULL;
551 CFArrayRef options;
552 char *str;
553 const struct ifmedia_description *subtype_descriptions = NULL;
554 CFStringRef val;
555
556 if (!isA_SCNetworkInterface(interface)) {
557 _SCErrorSet(kSCStatusInvalidArgument);
558 return -1;
559 }
560
561 interfaceName = SCNetworkInterfaceGetBSDName(interface);
562 if (interfaceName == NULL) {
563 _SCErrorSet(kSCStatusInvalidArgument);
564 return -1;
565 }
566
567 /* set type */
568
569 ifm = __copyMediaList(interfaceName);
570 if (ifm != NULL) {
571 if (ifm->ifm_count > 0) {
572 ifm_new = IFM_TYPE(ifm->ifm_ulist[0]);
573 }
574 __freeMediaList(ifm);
575 }
576
577 if (ifm_new == -1) {
578 // if we cannot determine the media type for the interface
579 return -1;
580 }
581
582 switch (IFM_TYPE(ifm_new)) {
583 case IFM_ETHER :
584 option_descriptions = ifm_subtype_ethernet_option_descriptions;
585 subtype_descriptions = ifm_subtype_ethernet_descriptions;
586 break;
587 case IFM_IEEE80211 :
588 option_descriptions = ifm_subtype_ieee80211_option_descriptions;
589 subtype_descriptions = ifm_subtype_ieee80211_descriptions;
590 break;
591 }
592
593 /* set subtype */
594
595 val = CFDictionaryGetValue(media_options, kSCPropNetEthernetMediaSubType);
596 if (!isA_CFString(val)) {
597 return -1;
598 }
599
600 str = _SC_cfstring_to_cstring(val, NULL, 0, kCFStringEncodingASCII);
601 if (str == NULL) {
602 return -1;
603 }
604
605 match = FALSE;
606 for (i = 0; !match && ifm_subtype_shared_descriptions[i].ifmt_string; i++) {
607 if (strcasecmp(str, ifm_subtype_shared_descriptions[i].ifmt_string) == 0) {
608 ifm_new |= ifm_subtype_shared_descriptions[i].ifmt_word;
609 match = TRUE;
610 break;
611 }
612 }
613
614 if (subtype_descriptions != NULL) {
615 for (i = 0; !match && subtype_descriptions[i].ifmt_string; i++) {
616 if (strcasecmp(str, subtype_descriptions[i].ifmt_string) == 0) {
617 ifm_new |= subtype_descriptions[i].ifmt_word;
618 match = TRUE;
619 break;
620 }
621 }
622 }
623
624 CFAllocatorDeallocate(NULL, str);
625
626 if (!match) {
627 return -1; /* if no subtype */
628 }
629
630 /* set options */
631
632 options = CFDictionaryGetValue(media_options, kSCPropNetEthernetMediaOptions);
633 if (!isA_CFArray(options)) {
634 return -1;
635 }
636
637 n = CFArrayGetCount(options);
638 for (i = 0; i < n; i++) {
639 CFIndex j;
640
641 val = CFArrayGetValueAtIndex(options, i);
642 if (!isA_CFString(val)) {
643 return -1;
644 }
645
646 str = _SC_cfstring_to_cstring(val, NULL, 0, kCFStringEncodingASCII);
647 if (str == NULL) {
648 return -1;
649 }
650
651
652 match = FALSE;
653 for (j = 0; !match && ifm_shared_option_descriptions[j].ifmt_string; j++) {
654 if (strcasecmp(str, ifm_shared_option_descriptions[j].ifmt_string) == 0) {
655 ifm_new |= ifm_shared_option_descriptions[j].ifmt_word;
656 match = TRUE;
657 break;
658 }
659 }
660
661 if (option_descriptions != NULL) {
662 for (j = 0; !match && option_descriptions[j].ifmt_string; j++) {
663 if (strcasecmp(str, option_descriptions[j].ifmt_string) == 0) {
664 ifm_new |= option_descriptions[j].ifmt_word;
665 match = TRUE;
666 break;
667 }
668 }
669 }
670
671 CFAllocatorDeallocate(NULL, str);
672
673 if (!match) {
674 return -1; /* if no option */
675 }
676 }
677
678 return ifm_new;
679 }
680
681
682 Boolean
683 SCNetworkInterfaceCopyMediaOptions(SCNetworkInterfaceRef interface,
684 CFDictionaryRef *current,
685 CFDictionaryRef *active,
686 CFArrayRef *available,
687 Boolean filter)
688 {
689 int i;
690 struct ifmediareq *ifm;
691 CFStringRef interfaceName;
692
693 if (!isA_SCNetworkInterface(interface)) {
694 _SCErrorSet(kSCStatusInvalidArgument);
695 return FALSE;
696 }
697
698 interfaceName = SCNetworkInterfaceGetBSDName(interface);
699 if (interfaceName == NULL) {
700 _SCErrorSet(kSCStatusInvalidArgument);
701 return FALSE;
702 }
703
704 ifm = __copyMediaList(interfaceName);
705 if (ifm == NULL) {
706 return FALSE;
707 }
708
709 if (active != NULL) *active = NULL;
710 if (current != NULL) *current = NULL;
711 if (available != NULL) {
712 CFMutableArrayRef media_options;
713
714 media_options = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
715 for (i = 0; i < ifm->ifm_count; i++) {
716 CFDictionaryRef options;
717
718 options = __createMediaDictionary(ifm->ifm_ulist[i], filter);
719 if (options == NULL) {
720 continue;
721 }
722
723 if ((active != NULL) && (*active == NULL) && (ifm->ifm_active == ifm->ifm_ulist[i])) {
724 *active = CFRetain(options);
725 }
726
727 if ((current != NULL) && (*current == NULL) && (ifm->ifm_current == ifm->ifm_ulist[i])) {
728 *current = CFRetain(options);
729 }
730
731 if (!CFArrayContainsValue(media_options, CFRangeMake(0, CFArrayGetCount(media_options)), options)) {
732 CFArrayAppendValue(media_options, options);
733 }
734
735 CFRelease(options);
736 }
737 *available = (CFArrayRef)media_options;
738 }
739
740 if ((active != NULL) && (*active == NULL)) {
741 *active = __createMediaDictionary(ifm->ifm_active, FALSE);
742 }
743
744 if ((current != NULL) && (*current == NULL)) {
745 if ((active != NULL) && (ifm->ifm_active == ifm->ifm_current)) {
746 if (*active != NULL) *current = CFRetain(*active);
747 } else {
748 *current = __createMediaDictionary(ifm->ifm_current, FALSE);
749 }
750 }
751
752 __freeMediaList(ifm);
753 return TRUE;
754 }
755
756
757 CFArrayRef
758 SCNetworkInterfaceCopyMediaSubTypes(CFArrayRef available)
759 {
760 CFIndex i;
761 CFIndex n;
762 CFMutableArrayRef subTypes;
763
764 if (!isA_CFArray(available)) {
765 _SCErrorSet(kSCStatusInvalidArgument);
766 return NULL;
767 }
768
769 subTypes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
770
771 n = CFArrayGetCount(available);
772 for (i = 0; i < n; i++) {
773 CFDictionaryRef options;
774 CFStringRef subType;
775
776 options = CFArrayGetValueAtIndex(available, i);
777 if (!isA_CFDictionary(options)) {
778 continue;
779 }
780
781 subType = CFDictionaryGetValue(options, kSCPropNetEthernetMediaSubType);
782 if (!isA_CFString(subType)) {
783 continue;
784 }
785
786 if (!CFArrayContainsValue(subTypes, CFRangeMake(0, CFArrayGetCount(subTypes)), subType)) {
787 CFArrayAppendValue(subTypes, subType);
788 }
789 }
790
791 if (CFArrayGetCount(subTypes) == 0) {
792 CFRelease(subTypes);
793 subTypes = NULL;
794 _SCErrorSet(kSCStatusOK);
795 }
796
797 return subTypes;
798 }
799
800
801 CFArrayRef
802 SCNetworkInterfaceCopyMediaSubTypeOptions(CFArrayRef available,
803 CFStringRef subType)
804 {
805 CFIndex i;
806 CFIndex n;
807 CFMutableArrayRef subTypeOptions;
808
809 if (!isA_CFArray(available)) {
810 _SCErrorSet(kSCStatusInvalidArgument);
811 return NULL;
812 }
813
814 subTypeOptions = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
815
816 n = CFArrayGetCount(available);
817 for (i = 0; i < n; i++) {
818 CFDictionaryRef options;
819 CFArrayRef mediaOptions;
820 CFStringRef mediaSubType;
821
822 options = CFArrayGetValueAtIndex(available, i);
823 if (!isA_CFDictionary(options)) {
824 continue;
825 }
826
827 mediaSubType = CFDictionaryGetValue(options, kSCPropNetEthernetMediaSubType);
828 if (!isA_CFString(mediaSubType) || !CFEqual(subType, mediaSubType)) {
829 continue;
830 }
831
832 mediaOptions = CFDictionaryGetValue(options, kSCPropNetEthernetMediaOptions);
833 if (!isA_CFArray(mediaOptions)) {
834 continue;
835 }
836
837 if (!CFArrayContainsValue(subTypeOptions, CFRangeMake(0, CFArrayGetCount(subTypeOptions)), mediaOptions)) {
838 CFArrayAppendValue(subTypeOptions, mediaOptions);
839 }
840 }
841
842 if (CFArrayGetCount(subTypeOptions) == 0) {
843 CFRelease(subTypeOptions);
844 subTypeOptions = NULL;
845 _SCErrorSet(kSCStatusOK);
846 }
847
848 return subTypeOptions;
849 }
850
851
852 Boolean
853 _SCNetworkInterfaceIsPhysicalEthernet(SCNetworkInterfaceRef interface)
854 {
855 int i;
856 struct ifmediareq *ifm;
857 CFStringRef interfaceName;
858 Boolean realEthernet = FALSE;
859
860 if (!isA_SCNetworkInterface(interface)) {
861 _SCErrorSet(kSCStatusInvalidArgument);
862 return FALSE;
863 }
864
865 interfaceName = SCNetworkInterfaceGetBSDName(interface);
866 if (interfaceName == NULL) {
867 _SCErrorSet(kSCStatusInvalidArgument);
868 return FALSE;
869 }
870
871 ifm = __copyMediaList(interfaceName);
872 if (ifm == NULL) {
873 CFStringRef interfaceType;
874
875 interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
876 if (CFEqual(interfaceType, kSCNetworkInterfaceTypeEthernet) &&
877 !_SCNetworkInterfaceIsTethered(interface) &&
878 !_SCNetworkInterfaceIsBluetoothPAN(interface)) {
879 // if likely physical ethernet interface
880 return TRUE;
881 }
882 return FALSE;
883 }
884 _SCErrorSet(kSCStatusOK);
885 if (IFM_TYPE(ifm->ifm_current) != IFM_ETHER) {
886 goto done;
887 }
888 if (ifm->ifm_count == 1
889 && IFM_SUBTYPE(ifm->ifm_ulist[0]) == IFM_AUTO) {
890 /* only support autoselect, not really ethernet */
891 goto done;
892 }
893 for (i = 0; i < ifm->ifm_count; i++) {
894 if ((ifm->ifm_ulist[i] & IFM_FDX) != 0) {
895 realEthernet = TRUE;
896 break;
897 }
898 }
899 done:
900 __freeMediaList(ifm);
901 return (realEthernet);
902 }
903
904 static Boolean
905 __getIOMTULimits(char ifr_name[IFNAMSIZ],
906 int *mtu_min,
907 int *mtu_max)
908 {
909 int ifType = 0;
910 io_iterator_t io_iter = 0;
911 io_registry_entry_t io_interface = 0;
912 io_registry_entry_t io_controller = 0;
913 kern_return_t kr;
914 static mach_port_t masterPort = MACH_PORT_NULL;
915 CFMutableDictionaryRef matchingDict;
916
917 /* look for a matching interface in the IORegistry */
918
919 if (masterPort == MACH_PORT_NULL) {
920 kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
921 if (kr != KERN_SUCCESS) {
922 return FALSE;
923 }
924 }
925
926 matchingDict = IOBSDNameMatching(masterPort, 0, ifr_name);
927 if (matchingDict) {
928 /* Note: IOServiceGetMatchingServices consumes a reference on the 'matchingDict' */
929 kr = IOServiceGetMatchingServices(masterPort, matchingDict, &io_iter);
930 if ((kr == KERN_SUCCESS) && io_iter) {
931 /* should only have a single match */
932 io_interface = IOIteratorNext(io_iter);
933 }
934 if (io_iter) IOObjectRelease(io_iter);
935 }
936
937 if (io_interface) {
938 CFNumberRef num;
939
940 /*
941 * found an interface, get the interface type
942 */
943 num = IORegistryEntryCreateCFProperty(io_interface, CFSTR(kIOInterfaceType), NULL, kNilOptions);
944 if (num) {
945 if (isA_CFNumber(num)) {
946 CFNumberGetValue(num, kCFNumberIntType, &ifType);
947 }
948 CFRelease(num);
949 }
950
951 /*
952 * ...and the property we are REALLY interested is in the controller,
953 * which is the parent of the interface object.
954 */
955 (void)IORegistryEntryGetParentEntry(io_interface, kIOServicePlane, &io_controller);
956 IOObjectRelease(io_interface);
957 } else {
958 /* if no matching interface */
959 return FALSE;
960 }
961
962 if (io_controller) {
963 CFNumberRef num;
964
965 num = IORegistryEntryCreateCFProperty(io_controller, CFSTR(kIOMaxPacketSize), NULL, kNilOptions);
966 if (num) {
967 if (isA_CFNumber(num)) {
968 int value;
969
970 /*
971 * Get the value and subtract the FCS bytes and Ethernet header
972 * sizes from the maximum frame size reported by the controller
973 * to get the MTU size. The 14 byte media header can be found
974 * in the registry, but not the size for the trailing FCS bytes.
975 */
976 CFNumberGetValue(num, kCFNumberIntType, &value);
977
978 if (ifType == IFT_ETHER) {
979 value -= (ETHER_HDR_LEN + ETHER_CRC_LEN);
980 }
981
982 if (mtu_min) *mtu_min = IF_MINMTU;
983 if (mtu_max) *mtu_max = value;
984 }
985 CFRelease(num);
986 }
987
988 IOObjectRelease(io_controller);
989 }
990
991 return TRUE;
992 }
993
994
995 Boolean
996 SCNetworkInterfaceCopyMTU(SCNetworkInterfaceRef interface,
997 int *mtu_cur,
998 int *mtu_min,
999 int *mtu_max)
1000 {
1001 struct ifreq ifr;
1002 CFStringRef interfaceName;
1003 Boolean ok = FALSE;
1004 int sock = -1;
1005
1006 if (!isA_SCNetworkInterface(interface)) {
1007 _SCErrorSet(kSCStatusInvalidArgument);
1008 return FALSE;
1009 }
1010
1011 interfaceName = SCNetworkInterfaceGetBSDName(interface);
1012 if (interfaceName == NULL) {
1013 _SCErrorSet(kSCStatusInvalidArgument);
1014 return FALSE;
1015 }
1016
1017 bzero((void *)&ifr, sizeof(ifr));
1018 if (_SC_cfstring_to_cstring(interfaceName, ifr.ifr_name, sizeof(ifr.ifr_name), kCFStringEncodingASCII) == NULL) {
1019 SC_log(LOG_NOTICE, "could not convert interface name");
1020 _SCErrorSet(kSCStatusInvalidArgument);
1021 return FALSE;
1022 }
1023
1024 sock = socket(AF_INET, SOCK_DGRAM, 0);
1025 if (sock == -1) {
1026 _SCErrorSet(errno);
1027 SC_log(LOG_ERR, "socket() failed: %s", strerror(errno));
1028 return FALSE;
1029 }
1030
1031 if (ioctl(sock, SIOCGIFMTU, (caddr_t)&ifr) == -1) {
1032 _SCErrorSet(errno);
1033 // SC_log(LOG_NOTICE, "ioctl(SIOCGIFMTU) failed: %s", strerror(errno));
1034 goto done;
1035 }
1036
1037 if (mtu_cur) *mtu_cur = ifr.ifr_mtu;
1038 if (mtu_min) *mtu_min = ifr.ifr_mtu;
1039 if (mtu_max) *mtu_max = ifr.ifr_mtu;
1040
1041 /* get valid MTU range */
1042
1043 if (mtu_min != NULL || mtu_max != NULL) {
1044 if (ioctl(sock, SIOCGIFDEVMTU, (caddr_t)&ifr) == 0) {
1045 struct ifdevmtu * devmtu_p;
1046
1047 devmtu_p = &ifr.ifr_devmtu;
1048 if (mtu_min != NULL) {
1049 *mtu_min = (devmtu_p->ifdm_min > IF_MINMTU)
1050 ? devmtu_p->ifdm_min : IF_MINMTU;
1051 }
1052 if (mtu_max != NULL) {
1053 *mtu_max = devmtu_p->ifdm_max;
1054 }
1055 } else {
1056 ok = __getIOMTULimits(ifr.ifr_name, mtu_min, mtu_max);
1057 if (!ok) {
1058 CFStringRef interfaceType;
1059
1060 interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
1061 if (CFEqual(interfaceType, kSCNetworkInterfaceTypeBridge)) {
1062 CFIndex i;
1063 CFArrayRef members;
1064 CFIndex n;
1065
1066 members = SCBridgeInterfaceGetMemberInterfaces(interface);
1067 n = (members != NULL) ? CFArrayGetCount(members) : 0;
1068 if (n > 1) {
1069 if (mtu_min) *mtu_min = IF_MINMTU;
1070 if (mtu_max) *mtu_max = IF_MAXMTU;
1071 }
1072 for (i = 0; i < n; i++) {
1073 SCNetworkInterfaceRef member;
1074 int member_mtu_min;
1075 int member_mtu_max;
1076
1077 member = CFArrayGetValueAtIndex(members, i);
1078 ok = SCNetworkInterfaceCopyMTU(member, NULL, &member_mtu_min, &member_mtu_max);
1079 if (ok) {
1080 if ((mtu_min != NULL) && (*mtu_min < member_mtu_min)) {
1081 *mtu_min = member_mtu_min; // min MTU needs to be higher
1082 }
1083 if ((mtu_max != NULL) && (*mtu_max > member_mtu_max)) {
1084 *mtu_max = member_mtu_max; // max MTU needs to be lower
1085 }
1086 }
1087 }
1088 }
1089 }
1090 }
1091
1092 if (mtu_min != NULL) {
1093 #if IP_MSS > IPV6_MMTU
1094 if (*mtu_min < IP_MSS) {
1095 /* bump up the minimum MTU */
1096 *mtu_min = IP_MSS/*576*/;
1097 }
1098 #else // IP_MSS > IPV6_MMTU
1099 if (*mtu_min < IPV6_MMTU) {
1100 /* bump up the minimum MTU */
1101 *mtu_min = IPV6_MMTU;
1102 }
1103 #endif // IP_MSS > IPV6_MMTU
1104
1105 if ((mtu_cur != NULL) && (*mtu_min > *mtu_cur)) {
1106 /* min must be <= cur */
1107 *mtu_min = *mtu_cur;
1108 }
1109
1110 if ((mtu_max != NULL) && (*mtu_min > *mtu_max)) {
1111 /* min must be <= max */
1112 *mtu_min = *mtu_max;
1113 }
1114 }
1115 }
1116
1117 ok = TRUE;
1118
1119 done :
1120
1121 (void)close(sock);
1122 return ok;
1123 }
1124
1125
1126 Boolean
1127 SCNetworkInterfaceSetMediaOptions(SCNetworkInterfaceRef interface,
1128 CFStringRef subtype,
1129 CFArrayRef options)
1130 {
1131 CFDictionaryRef configuration;
1132 CFMutableDictionaryRef newConfiguration = NULL;
1133 Boolean ok = FALSE;
1134
1135 if (!isA_SCNetworkInterface(interface)) {
1136 _SCErrorSet(kSCStatusInvalidArgument);
1137 return FALSE;
1138 }
1139
1140 configuration = SCNetworkInterfaceGetConfiguration(interface);
1141 if (configuration == NULL) {
1142 newConfiguration = CFDictionaryCreateMutable(NULL,
1143 0,
1144 &kCFTypeDictionaryKeyCallBacks,
1145 &kCFTypeDictionaryValueCallBacks);
1146 } else {
1147 newConfiguration = CFDictionaryCreateMutableCopy(NULL, 0, configuration);
1148 CFDictionaryRemoveValue(newConfiguration, kSCResvInactive);
1149 }
1150
1151 if (subtype != NULL) {
1152 CFArrayRef available = NULL;
1153 CFArrayRef config_options = options;
1154 CFArrayRef subtypes = NULL;
1155 CFArrayRef subtype_options = NULL;
1156
1157 if (options == NULL) {
1158 config_options = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
1159 }
1160
1161 if (!SCNetworkInterfaceCopyMediaOptions(interface, NULL, NULL, &available, FALSE)) {
1162 SC_log(LOG_INFO, "media type / options not available");
1163 goto checked;
1164 }
1165
1166 if (available == NULL) {
1167 _SCErrorSet(kSCStatusInvalidArgument);
1168 goto checked;
1169 }
1170
1171 subtypes = SCNetworkInterfaceCopyMediaSubTypes(available);
1172 if ((subtypes == NULL) ||
1173 !CFArrayContainsValue(subtypes,
1174 CFRangeMake(0, CFArrayGetCount(subtypes)),
1175 subtype)) {
1176 SC_log(LOG_INFO, "media type not valid");
1177 _SCErrorSet(kSCStatusInvalidArgument);
1178 goto checked;
1179 }
1180
1181 subtype_options = SCNetworkInterfaceCopyMediaSubTypeOptions(available, subtype);
1182 if ((subtype_options == NULL) ||
1183 !CFArrayContainsValue(subtype_options,
1184 CFRangeMake(0, CFArrayGetCount(subtype_options)),
1185 config_options)) {
1186 SC_log(LOG_INFO, "media options not valid for \"%@\"", subtype);
1187 _SCErrorSet(kSCStatusInvalidArgument);
1188 goto checked;
1189 }
1190
1191 CFDictionarySetValue(newConfiguration, kSCPropNetEthernetMediaSubType, subtype);
1192 CFDictionarySetValue(newConfiguration,
1193 kSCPropNetEthernetMediaOptions,
1194 (options != NULL) ? options : config_options);
1195
1196 ok = TRUE;
1197
1198 checked :
1199
1200 if (available != NULL) CFRelease(available);
1201 if (subtypes != NULL) CFRelease(subtypes);
1202 if (subtype_options != NULL) CFRelease(subtype_options);
1203 if (options == NULL) CFRelease(config_options);
1204 } else if (options == NULL) {
1205 CFDictionaryRemoveValue(newConfiguration, kSCPropNetEthernetMediaSubType);
1206 CFDictionaryRemoveValue(newConfiguration, kSCPropNetEthernetMediaOptions);
1207 if (CFDictionaryGetCount(newConfiguration) == 0) {
1208 CFRelease(newConfiguration);
1209 newConfiguration = NULL;
1210 }
1211 ok = TRUE;
1212 } else {
1213 SC_log(LOG_INFO, "media type must be specified with options");
1214 _SCErrorSet(kSCStatusInvalidArgument);
1215 }
1216
1217 if (ok) {
1218 ok = SCNetworkInterfaceSetConfiguration(interface, newConfiguration);
1219 }
1220
1221 if (newConfiguration != NULL) CFRelease(newConfiguration);
1222 return ok;
1223 }
1224
1225
1226 Boolean
1227 SCNetworkInterfaceSetMTU(SCNetworkInterfaceRef interface,
1228 int mtu)
1229 {
1230 CFDictionaryRef configuration;
1231 int mtu_max;
1232 int mtu_min;
1233 CFMutableDictionaryRef newConfiguration = NULL;
1234 Boolean ok = FALSE;
1235
1236 if (!isA_SCNetworkInterface(interface)) {
1237 _SCErrorSet(kSCStatusInvalidArgument);
1238 return FALSE;
1239 }
1240
1241 if (!SCNetworkInterfaceCopyMTU(interface, NULL, &mtu_min, &mtu_max)) {
1242 SC_log(LOG_INFO, "MTU bounds not available");
1243 return FALSE;
1244 }
1245
1246 configuration = SCNetworkInterfaceGetConfiguration(interface);
1247 if (configuration == NULL) {
1248 newConfiguration = CFDictionaryCreateMutable(NULL,
1249 0,
1250 &kCFTypeDictionaryKeyCallBacks,
1251 &kCFTypeDictionaryValueCallBacks);
1252 } else {
1253 newConfiguration = CFDictionaryCreateMutableCopy(NULL, 0, configuration);
1254 CFDictionaryRemoveValue(newConfiguration, kSCResvInactive);
1255 }
1256
1257 if ((mtu >= mtu_min) && (mtu <= mtu_max)) {
1258 CFNumberRef num;
1259
1260 num = CFNumberCreate(NULL, kCFNumberIntType, &mtu);
1261 CFDictionarySetValue(newConfiguration, kSCPropNetEthernetMTU, num);
1262 CFRelease(num);
1263 ok = TRUE;
1264 } else if (mtu == 0) {
1265 CFDictionaryRemoveValue(newConfiguration, kSCPropNetEthernetMTU);
1266 if (CFDictionaryGetCount(newConfiguration) == 0) {
1267 CFRelease(newConfiguration);
1268 newConfiguration = NULL;
1269 }
1270 ok = TRUE;
1271 } else {
1272 SC_log(LOG_INFO, "MTU out of range");
1273 _SCErrorSet(kSCStatusInvalidArgument);
1274 }
1275
1276 if (ok) {
1277 ok = SCNetworkInterfaceSetConfiguration(interface, newConfiguration);
1278 }
1279
1280 if (newConfiguration != NULL) CFRelease(newConfiguration);
1281 return ok;
1282 }