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