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