]> git.saurik.com Git - apple/configd.git/blob - scutil.tproj/net_interface.c
configd-135.tar.gz
[apple/configd.git] / scutil.tproj / net_interface.c
1 /*
2 * Copyright (c) 2004, 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * August 5, 2004 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31
32 #include "scutil.h"
33 #include "net.h"
34
35 #include <SystemConfiguration/LinkConfiguration.h>
36
37
38 /* -------------------- */
39
40
41 static CFArrayRef
42 _copy_interfaces()
43 {
44 CFMutableArrayRef interfaces;
45 CFArrayRef real_interfaces;
46
47 real_interfaces = SCNetworkInterfaceCopyAll();
48 if (real_interfaces == NULL) {
49 SCPrint(TRUE, stdout, CFSTR("%s\n"), SCErrorString(SCError()));
50 return NULL;
51 }
52
53 interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
54
55 // include real interfaces
56 CFArrayAppendArray(interfaces,
57 real_interfaces,
58 CFRangeMake(0, CFArrayGetCount(real_interfaces)));
59 CFRelease(real_interfaces);
60
61 // include pseudo interfaces
62 CFArrayAppendValue(interfaces, kSCNetworkInterfaceIPv4);
63
64 // include interfaces that we have created
65 if (new_interfaces != NULL) {
66 CFArrayAppendArray(interfaces,
67 new_interfaces,
68 CFRangeMake(0, CFArrayGetCount(new_interfaces)));
69 }
70
71 return (CFArrayRef)interfaces;
72 }
73
74
75 __private_extern__
76 SCNetworkInterfaceRef
77 _find_interface(char *match)
78 {
79 Boolean allowIndex = TRUE;
80 CFIndex i;
81 CFIndex n;
82 CFStringRef select_name = NULL;
83 SCNetworkInterfaceRef selected = NULL;
84
85 if (strcasecmp(match, "$child") == 0) {
86 if (net_interface == NULL) {
87 SCPrint(TRUE, stdout, CFSTR("interface not selected\n"));
88 goto done;
89 }
90
91 selected = SCNetworkInterfaceGetInterface(net_interface);
92 if(selected == NULL) {
93 SCPrint(TRUE, stdout, CFSTR("no child interface\n"));
94 }
95
96 goto done;
97 } else if (strcasecmp(match, "$service") == 0) {
98 if (net_service == NULL) {
99 SCPrint(TRUE, stdout, CFSTR("service not selected\n"));
100 goto done;
101 }
102
103 selected = SCNetworkServiceGetInterface(net_service);
104 if(selected == NULL) {
105 SCPrint(TRUE, stdout, CFSTR("no interface for service\n"));
106 }
107
108 goto done;
109 }
110
111 if (interfaces == NULL) {
112 interfaces = _copy_interfaces();
113 if (interfaces == NULL) {
114 return NULL;
115 }
116 allowIndex = FALSE;
117 }
118
119 // try to select the interface by its display name
120
121 select_name = CFStringCreateWithCString(NULL, match, kCFStringEncodingUTF8);
122
123 n = CFArrayGetCount(interfaces);
124 for (i = 0; i < n; i++) {
125 SCNetworkInterfaceRef interface;
126 CFStringRef interfaceName;
127
128 interface = CFArrayGetValueAtIndex(interfaces, i);
129 interfaceName = SCNetworkInterfaceGetLocalizedDisplayName(interface);
130 if ((interfaceName != NULL) && CFEqual(select_name, interfaceName)) {
131 if (selected == NULL) {
132 selected = interface;
133 } else {
134 // if multiple interfaces match
135 selected = NULL;
136 SCPrint(TRUE, stdout, CFSTR("multiple interfaces match\n"));
137 goto done;
138 }
139 }
140 }
141
142 if (selected != NULL) {
143 goto done;
144 }
145
146 // try to select the interface by its BSD name
147
148 for (i = 0; i < n; i++) {
149 SCNetworkInterfaceRef interface;
150 CFStringRef bsd_name = NULL;
151
152 interface = CFArrayGetValueAtIndex(interfaces, i);
153 while ((interface != NULL) && (bsd_name == NULL)) {
154 bsd_name = SCNetworkInterfaceGetBSDName(interface);
155 if (bsd_name == NULL) {
156 interface = SCNetworkInterfaceGetInterface(interface);
157 }
158 }
159
160 if ((bsd_name != NULL) && CFEqual(select_name, bsd_name)) {
161 if (selected == NULL) {
162 selected = interface;
163 } else {
164 // if multiple interfaces match
165 selected = NULL;
166 SCPrint(TRUE, stdout, CFSTR("multiple interfaces match\n"));
167 goto done;
168 }
169 }
170 }
171
172 if (selected != NULL) {
173 goto done;
174 }
175
176 // try to select the interface by its interface type
177
178 for (i = 0; i < n; i++) {
179 SCNetworkInterfaceRef interface;
180 CFStringRef interfaceType;
181
182 interface = CFArrayGetValueAtIndex(interfaces, i);
183 interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
184 if (CFEqual(select_name, interfaceType)) {
185 if (selected == NULL) {
186 selected = interface;
187 } else {
188 // if multiple interfaces match
189 selected = NULL;
190 SCPrint(TRUE, stdout, CFSTR("multiple interfaces match\n"));
191 goto done;
192 }
193 }
194 }
195
196 if (selected != NULL) {
197 goto done;
198 }
199
200 if (allowIndex) {
201 char *end;
202 char *str = match;
203 long val;
204
205 // try to select the interface by its index
206
207 errno = 0;
208 val = strtol(str, &end, 10);
209 if ((*str != '\0') &&
210 ((*end == '\0') || (*end == '.')) &&
211 (errno == 0)) {
212 if ((val > 0) && (val <= n)) {
213 selected = CFArrayGetValueAtIndex(interfaces, val - 1);
214
215 if (*end == '.') {
216 str = end + 1;
217 val = strtol(str, &end, 10);
218 if ((*str != '\0') && (*end == '\0') && (errno == 0)) {
219 while (val-- > 0) {
220 selected = SCNetworkInterfaceGetInterface(selected);
221 if (selected == NULL) {
222 break;
223 }
224 }
225 }
226 }
227 }
228 }
229 }
230
231 if (selected != NULL) {
232 goto done;
233 }
234
235 SCPrint(TRUE, stdout, CFSTR("no match\n"));
236
237 done :
238
239 if (select_name != NULL) CFRelease(select_name);
240 return selected;
241 }
242
243
244 /* -------------------- */
245
246
247 __private_extern__
248 void
249 create_interface(int argc, char **argv)
250 {
251 SCNetworkInterfaceRef interface;
252 CFStringRef interfaceName;
253 CFStringRef interfaceType;
254 SCNetworkInterfaceRef new_interface;
255
256 if (argc < 1) {
257 SCPrint(TRUE, stdout, CFSTR("what interface type?\n"));
258 return;
259 }
260
261 interfaceType = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
262
263 if (CFEqual(interfaceType, kSCNetworkInterfaceTypeVLAN)) {
264 // xxxxx
265 SCPrint(TRUE, stdout, CFSTR("vlan creation not yet supported\n"));
266 goto done;
267 } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeBond)) {
268 // xxxxx
269 SCPrint(TRUE, stdout, CFSTR("bond creation not yet supported\n"));
270 goto done;
271 } else {
272 if (argc < 2) {
273 if (net_interface == NULL) {
274 SCPrint(TRUE, stdout, CFSTR("no network interface selected\n"));
275 goto done;
276 }
277
278 interface = net_interface;
279 } else {
280 interface = _find_interface(argv[1]);
281 }
282
283 if (interface == NULL) {
284 return;
285 }
286
287 new_interface = SCNetworkInterfaceCreateWithInterface(interface, interfaceType);
288 if (new_interface == NULL) {
289 SCPrint(TRUE, stdout, CFSTR("%s\n"), SCErrorString(SCError()));
290 goto done;
291 }
292 }
293
294 if (new_interfaces == NULL) {
295 new_interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
296 }
297 CFArrayAppendValue(new_interfaces, new_interface);
298
299 if (net_interface != NULL) CFRelease(net_interface);
300 net_interface = new_interface;
301
302 interfaceName = SCNetworkInterfaceGetLocalizedDisplayName(net_interface);
303 if (interfaceName == NULL) {
304 interfaceName = SCNetworkInterfaceGetBSDName(net_interface);
305 }
306 if (interfaceName == NULL) {
307 interfaceName = SCNetworkInterfaceGetInterfaceType(net_interface);
308 }
309 SCPrint(TRUE, stdout, CFSTR("interface \"%@\" created and selected\n"), interfaceName);
310
311 done :
312
313 CFRelease(interfaceType);
314 return;
315 }
316
317
318 /* -------------------- */
319
320
321 __private_extern__
322 void
323 select_interface(int argc, char **argv)
324 {
325 SCNetworkInterfaceRef interface;
326
327 interface = _find_interface(argv[0]);
328
329 if (interface != NULL) {
330 CFStringRef interfaceName;
331
332 if (net_interface != NULL) CFRelease(net_interface);
333 net_interface = CFRetain(interface);
334
335 interfaceName = SCNetworkInterfaceGetLocalizedDisplayName(interface);
336 if (interfaceName == NULL) {
337 interfaceName = SCNetworkInterfaceGetBSDName(interface);
338 }
339 if (interfaceName == NULL) {
340 interfaceName = SCNetworkInterfaceGetInterfaceType(interface);
341 }
342
343 SCPrint(TRUE, stdout, CFSTR("interface \"%@\" selected\n"), interfaceName);
344 }
345
346 return;
347 }
348
349
350 /* -------------------- */
351
352
353 __private_extern__
354 void
355 _show_interface(SCNetworkInterfaceRef interface, CFStringRef prefix, Boolean showChild)
356 {
357 CFDictionaryRef configuration;
358 CFStringRef if_bsd_name;
359 CFStringRef if_localized_name;
360 CFStringRef if_mac_address;
361 CFStringRef if_type;
362 CFArrayRef supported;
363
364 if_localized_name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
365 if (if_localized_name != NULL) {
366 SCPrint(TRUE, stdout, CFSTR("%@ name = %@\n"), prefix, if_localized_name);
367 }
368
369 if_bsd_name = SCNetworkInterfaceGetBSDName(interface);
370 if (if_bsd_name != NULL) {
371 SCPrint(TRUE, stdout, CFSTR("%@ interface name = %@\n"), prefix, if_bsd_name);
372 }
373
374 if_type = SCNetworkInterfaceGetInterfaceType(interface);
375 SCPrint(TRUE, stdout, CFSTR("%@ type = %@\n"), prefix, if_type);
376
377 if_mac_address = SCNetworkInterfaceGetHardwareAddressString(interface);
378 if (if_mac_address != NULL) {
379 SCPrint(TRUE, stdout, CFSTR("%@ address = %@\n"), prefix, if_mac_address);
380 }
381
382 configuration = SCNetworkInterfaceGetConfiguration(interface);
383 if ((configuration != NULL) &&
384 CFDictionaryContainsKey(configuration, kSCResvInactive)) {
385 configuration = NULL;
386 }
387
388 if (if_bsd_name != NULL) {
389 CFArrayRef available;
390 CFDictionaryRef active;
391 int mtu_cur;
392 int mtu_min;
393 int mtu_max;
394
395 if (NetworkInterfaceCopyMTU(if_bsd_name, &mtu_cur, &mtu_min, &mtu_max)) {
396 char isCurrent = '*';
397
398 if (configuration != NULL) {
399 int mtu_req;
400 CFNumberRef num;
401
402 num = CFDictionaryGetValue(configuration, kSCPropNetEthernetMTU);
403 if (isA_CFNumber(num)) {
404 CFNumberGetValue(num, kCFNumberIntType, &mtu_req);
405 if (mtu_cur != mtu_req) {
406 mtu_cur = mtu_req;
407 isCurrent = ' ';
408 }
409 }
410 }
411
412 SCPrint(TRUE, stdout, CFSTR("%@ mtu %c = %ld (%ld < n < %ld)\n"),
413 prefix,
414 isCurrent,
415 mtu_cur,
416 mtu_min,
417 mtu_max);
418 }
419
420 if (NetworkInterfaceCopyMediaOptions(if_bsd_name, NULL, &active, &available, TRUE)) {
421 char isCurrent = ' ';
422 CFArrayRef options = NULL;
423 CFArrayRef options_req = NULL;
424 CFStringRef subtype = NULL;
425 CFStringRef subtype_req = NULL;
426
427 if (configuration != NULL) {
428 subtype_req = CFDictionaryGetValue(configuration, kSCPropNetEthernetMediaSubType);
429 options_req = CFDictionaryGetValue(configuration, kSCPropNetEthernetMediaOptions);
430 }
431
432 if (subtype_req == NULL) {
433 subtype_req = CFSTR("autoselect");
434 }
435
436 if (active != NULL) {
437 subtype = CFDictionaryGetValue(active, kSCPropNetEthernetMediaSubType);
438 options = CFDictionaryGetValue(active, kSCPropNetEthernetMediaOptions);
439 }
440
441 if (subtype != NULL) {
442 if (((subtype_req != NULL) &&
443 CFEqual(subtype, subtype_req)) &&
444 ((options == options_req) ||
445 ((options != NULL) &&
446 (options_req != NULL) &&
447 CFEqual(options, options_req)))
448 ) {
449 isCurrent = '*';
450 } else if ((subtype_req == NULL) ||
451 ((subtype_req != NULL) &&
452 CFEqual(subtype_req, CFSTR("autoselect")))) {
453 // if requested subtype not specified or "autoselect"
454 isCurrent = '*';
455 }
456 }
457
458 if (subtype_req != NULL) {
459 SCPrint(TRUE, stdout, CFSTR("%@ media %c = %@"),
460 prefix,
461 isCurrent,
462 subtype_req);
463
464 if ((options_req != NULL) &&
465 (CFArrayGetCount(options_req) > 0)) {
466 CFStringRef options_str;
467
468 options_str = CFStringCreateByCombiningStrings(NULL, options_req, CFSTR(","));
469 SCPrint(TRUE, stdout, CFSTR(" <%@>"), options_str);
470 CFRelease(options_str);
471 }
472
473 SCPrint(TRUE, stdout, CFSTR("\n"));
474 }
475
476 SCPrint(TRUE, stdout, CFSTR("\n"));
477
478 if (available != NULL) {
479 CFIndex i;
480 CFIndex n_subtypes;
481 CFArrayRef subtypes;
482
483 subtypes = NetworkInterfaceCopyMediaSubTypes(available);
484 n_subtypes = (subtypes != NULL) ? CFArrayGetCount(subtypes) : 0;
485 for (i = 0; i < n_subtypes; i++) {
486 CFIndex j;
487 CFIndex n_subtype_options;
488 CFStringRef subtype;
489 CFArrayRef subtype_options;
490
491 subtype = CFArrayGetValueAtIndex(subtypes, i);
492 subtype_options = NetworkInterfaceCopyMediaSubTypeOptions(available, subtype);
493 n_subtype_options = (subtype_options != NULL) ? CFArrayGetCount(subtype_options) : 0;
494 for (j = 0; j < n_subtype_options; j++) {
495 char isCurrent = ' ';
496 CFArrayRef options;
497
498 options = CFArrayGetValueAtIndex(subtype_options, j);
499
500 if (((subtype_req != NULL) &&
501 CFEqual(subtype, subtype_req)) &&
502 ((options == options_req) ||
503 ((options != NULL) &&
504 (options_req != NULL) &&
505 CFEqual(options, options_req)))
506 ) {
507 isCurrent = '*';
508 }
509
510 SCPrint(TRUE, stdout, CFSTR("%@ %s %c = %@"),
511 prefix,
512 ((i == 0) && (j == 0)) ? "supported media" : " ",
513 isCurrent,
514 subtype);
515
516 if ((options != NULL) &&
517 (CFArrayGetCount(options) > 0)) {
518 CFStringRef options_str;
519
520 options_str = CFStringCreateByCombiningStrings(NULL, options, CFSTR(","));
521 SCPrint(TRUE, stdout, CFSTR(" <%@>"), options_str);
522 CFRelease(options_str);
523 }
524
525 SCPrint(TRUE, stdout, CFSTR("\n"));
526 }
527 CFRelease(subtype_options);
528 }
529 }
530 } else {
531 SCPrint(TRUE, stdout, CFSTR("\n"));
532 }
533 }
534
535 supported = SCNetworkInterfaceGetSupportedInterfaceTypes(interface);
536 SCPrint(TRUE, stdout, CFSTR("%@ supported interfaces = "), prefix);
537 if (supported != NULL) {
538 CFIndex i;
539 CFIndex n = CFArrayGetCount(supported);
540
541 for (i = 0; i < n; i++) {
542 SCPrint(TRUE, stdout, CFSTR("%s%@"),
543 (i == 0) ? "" : ", ",
544 CFArrayGetValueAtIndex(supported, i));
545 }
546 }
547 SCPrint(TRUE, stdout, CFSTR("\n"));
548
549 supported = SCNetworkInterfaceGetSupportedProtocolTypes(interface);
550 SCPrint(TRUE, stdout, CFSTR("%@ supported protocols = "), prefix);
551 if (supported != NULL) {
552 CFIndex i;
553 CFIndex n = CFArrayGetCount(supported);
554
555 for (i = 0; i < n; i++) {
556 SCPrint(TRUE, stdout, CFSTR("%s%@"),
557 (i == 0) ? "" : ", ",
558 CFArrayGetValueAtIndex(supported, i));
559 }
560 }
561 SCPrint(TRUE, stdout, CFSTR("\n"));
562
563 if (configuration != NULL) {
564 CFMutableDictionaryRef effective;
565
566 effective = CFDictionaryCreateMutableCopy(NULL, 0, configuration);
567
568 // remove known (and already reported) interface configuration keys
569 if (CFDictionaryContainsKey(effective, kSCResvInactive)) {
570 CFDictionaryRemoveAllValues(effective);
571 }
572 CFDictionaryRemoveValue(effective, kSCPropNetEthernetMTU);
573 CFDictionaryRemoveValue(effective, kSCPropNetEthernetMediaSubType);
574 CFDictionaryRemoveValue(effective, kSCPropNetEthernetMediaOptions);
575
576 if (CFDictionaryGetCount(effective) > 0) {
577 SCPrint(TRUE, stdout, CFSTR("\n%@ per-interface configuration\n"), prefix);
578 _show_entity(configuration, prefix);
579 }
580
581 CFRelease(effective);
582 }
583
584 if (_sc_debug) {
585 SCPrint(TRUE, stdout, CFSTR("\n%@\n"), interface);
586 }
587
588 interface = SCNetworkInterfaceGetInterface(interface);
589 if (interface != NULL) {
590 CFStringRef newPrefix;
591
592 newPrefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ "), prefix);
593 SCPrint(TRUE, stdout, CFSTR("\n%@child interface\n"), newPrefix);
594 _show_interface(interface, newPrefix, showChild);
595 CFRelease(newPrefix);
596 }
597
598 return;
599 }
600
601
602 /* -------------------- */
603
604
605 static Boolean
606 validateMediaOptions(CFStringRef interfaceName, CFMutableDictionaryRef newConfiguration)
607 {
608 Boolean ok = TRUE;
609 CFNumberRef mtu;
610 CFArrayRef options;
611 CFStringRef subtype;
612
613 mtu = CFDictionaryGetValue(newConfiguration, kSCPropNetEthernetMTU);
614 if (isA_CFNumber(mtu)) {
615 int mtu_max;
616 int mtu_min;
617 int mtu_val;
618
619 if (!NetworkInterfaceCopyMTU(interfaceName, NULL, &mtu_min, &mtu_max)) {
620 SCPrint(TRUE, stdout, CFSTR("cannot set MTU\n"));
621 return FALSE;
622 }
623
624 if (!CFNumberGetValue(mtu, kCFNumberIntType, &mtu_val) ||
625 (mtu_val < mtu_min) ||
626 (mtu_val > mtu_max)) {
627 SCPrint(TRUE, stdout, CFSTR("mtu out of range\n"));
628 return FALSE;
629 }
630 }
631
632 subtype = CFDictionaryGetValue(newConfiguration, kSCPropNetEthernetMediaSubType);
633 options = CFDictionaryGetValue(newConfiguration, kSCPropNetEthernetMediaOptions);
634
635 if (subtype != NULL) {
636 CFArrayRef available = NULL;
637 CFArrayRef config_options = options;
638 CFArrayRef subtypes = NULL;
639 CFArrayRef subtype_options = NULL;
640
641 ok = FALSE;
642
643 if (options == NULL) {
644 config_options = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
645 }
646
647 if (interfaceName == NULL) {
648 SCPrint(TRUE, stdout, CFSTR("media type / options not available\n"));
649 goto checked;
650 }
651
652 if (!NetworkInterfaceCopyMediaOptions(interfaceName, NULL, NULL, &available, FALSE)) {
653 SCPrint(TRUE, stdout, CFSTR("media type / options not available\n"));
654 goto checked;
655 }
656
657 if (available == NULL) {
658 goto checked;
659 }
660
661 subtypes = NetworkInterfaceCopyMediaSubTypes(available);
662 if ((subtypes == NULL) ||
663 !CFArrayContainsValue(subtypes,
664 CFRangeMake(0, CFArrayGetCount(subtypes)),
665 subtype)) {
666 SCPrint(TRUE, stdout, CFSTR("media type not valid\n"));
667 goto checked;
668 }
669
670 subtype_options = NetworkInterfaceCopyMediaSubTypeOptions(available, subtype);
671 if ((subtype_options == NULL) ||
672 !CFArrayContainsValue(subtype_options,
673 CFRangeMake(0, CFArrayGetCount(subtype_options)),
674 config_options)) {
675 SCPrint(TRUE, stdout, CFSTR("media options not valid for \"%@\"\n"), subtype);
676 goto checked;
677 }
678
679 if (options == NULL) {
680 CFDictionarySetValue(newConfiguration, kSCPropNetEthernetMediaOptions, config_options);
681 }
682
683 ok = TRUE;
684
685 checked :
686
687 if (available != NULL) CFRelease(available);
688 if (subtypes != NULL) CFRelease(subtypes);
689 if (subtype_options != NULL) CFRelease(subtype_options);
690 if (options == NULL) CFRelease(config_options);
691 } else {
692 if (options != NULL) {
693 SCPrint(TRUE, stdout, CFSTR("media type and options must both be specified\n"));
694 return FALSE;
695 }
696 }
697
698 return ok;
699 }
700
701
702 /* -------------------- */
703
704
705 __private_extern__
706 void
707 show_interfaces(int argc, char **argv)
708 {
709 CFIndex i;
710 CFIndex n;
711
712 if (interfaces != NULL) CFRelease(interfaces);
713 interfaces = _copy_interfaces();
714 if (interfaces == NULL) {
715 return;
716 }
717
718 n = CFArrayGetCount(interfaces);
719 for (i = 0; i < n; i++) {
720 CFIndex childIndex = 0;
721 SCNetworkInterfaceRef interface;
722
723 interface = CFArrayGetValueAtIndex(interfaces, i);
724 do {
725 CFStringRef interfaceName;
726 char isSelected;
727
728 interfaceName = SCNetworkInterfaceGetLocalizedDisplayName(interface);
729 if (interfaceName == NULL) {
730 interfaceName = SCNetworkInterfaceGetBSDName(interface);
731 }
732 if (interfaceName == NULL) {
733 interfaceName = SCNetworkInterfaceGetInterfaceType(interface);
734 }
735
736 isSelected = ' ';
737 if ((net_interface != NULL) && CFEqual(interface, net_interface)) {
738 isSelected = '>';
739 }
740
741 if (childIndex == 0) {
742 SCPrint(TRUE, stdout, CFSTR("%c%2d: %@\n"),
743 isSelected,
744 i + 1,
745 interfaceName);
746 } else {
747 SCPrint(TRUE, stdout, CFSTR("%c%2d.%d: %@\n"),
748 isSelected,
749 i + 1,
750 childIndex,
751 interfaceName);
752 }
753
754 interface = SCNetworkInterfaceGetInterface(interface);
755 childIndex++;
756 } while (interface != NULL);
757 }
758
759 return;
760 }
761
762
763 /* -------------------- */
764
765
766 static Boolean
767 set_interface_bond(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
768 {
769 // xxxxx ("+device", "-device")
770 SCPrint(TRUE, stdout, CFSTR("bond interface management not yet supported\n"));
771 return FALSE;
772 }
773
774
775 /* -------------------- */
776
777
778 static options airportOptions[] = {
779 { "mtu" , NULL, isNumber , &kSCPropNetEthernetMTU , NULL, NULL },
780 { "media" , NULL, isString , &kSCPropNetEthernetMediaSubType, NULL, NULL },
781 { "mediaopt" , NULL, isStringArray, &kSCPropNetEthernetMediaOptions, NULL, NULL },
782
783 { "?" , NULL , isHelp , NULL , NULL,
784 "\nAirPort configuration commands\n\n"
785 " set interface [mtu n] [media type] [mediaopts opts]\n"
786 }
787 };
788 #define N_AIRPORT_OPTIONS (sizeof(airportOptions) / sizeof(airportOptions[0]))
789
790
791 static Boolean
792 set_interface_airport(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
793 {
794 CFStringRef interfaceName;
795 Boolean ok;
796
797 interfaceName = SCNetworkInterfaceGetBSDName(net_interface);
798 if (interfaceName == NULL) {
799 SCPrint(TRUE, stdout, CFSTR("no BSD interface\n"));
800 return FALSE;
801 }
802
803 ok = _process_options(airportOptions, N_AIRPORT_OPTIONS, argc, argv, newConfiguration);
804 if (ok) {
805 // validate configuration
806 if (!validateMediaOptions(interfaceName, newConfiguration)) {
807 return FALSE;
808 }
809 }
810
811 return ok;
812 }
813
814
815 /* -------------------- */
816
817
818 static options ethernetOptions[] = {
819 { "mtu" , NULL, isNumber , &kSCPropNetEthernetMTU , NULL, NULL },
820 { "media" , NULL, isString , &kSCPropNetEthernetMediaSubType, NULL, NULL },
821 { "mediaopt" , NULL, isStringArray, &kSCPropNetEthernetMediaOptions, NULL, NULL },
822
823 { "?" , NULL , isHelp , NULL , NULL,
824 "\nEthernet configuration commands\n\n"
825 " set interface [mtu n] [media type] [mediaopts opts]\n"
826 }
827 };
828 #define N_ETHERNET_OPTIONS (sizeof(ethernetOptions) / sizeof(ethernetOptions[0]))
829
830
831 static Boolean
832 set_interface_ethernet(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
833 {
834 CFStringRef interfaceName;
835 Boolean ok;
836
837 interfaceName = SCNetworkInterfaceGetBSDName(net_interface);
838 if (interfaceName == NULL) {
839 SCPrint(TRUE, stdout, CFSTR("no BSD interface\n"));
840 return FALSE;
841 }
842
843 ok = _process_options(ethernetOptions, N_ETHERNET_OPTIONS, argc, argv, newConfiguration);
844 if (ok) {
845 // validate configuration
846 if (!validateMediaOptions(interfaceName, newConfiguration)) {
847 return FALSE;
848 }
849 }
850
851 return ok;
852 }
853
854
855 /* -------------------- */
856
857
858 static options firewireOptions[] = {
859 { "mtu" , NULL, isNumber , &kSCPropNetEthernetMTU , NULL, NULL },
860 { "media" , NULL, isString , &kSCPropNetEthernetMediaSubType, NULL, NULL },
861 { "mediaopt" , NULL, isStringArray, &kSCPropNetEthernetMediaOptions, NULL, NULL },
862
863 { "?" , NULL , isHelp , NULL , NULL,
864 "\nFireWire configuration commands\n\n"
865 " set interface [mtu n] [media type] [mediaopts opts]\n"
866 }
867 };
868 #define N_FIREWIRE_OPTIONS (sizeof(firewireOptions) / sizeof(firewireOptions[0]))
869
870
871 static Boolean
872 set_interface_firewire(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
873 {
874 CFStringRef interfaceName;
875 Boolean ok;
876
877 interfaceName = SCNetworkInterfaceGetBSDName(net_interface);
878 if (interfaceName == NULL) {
879 SCPrint(TRUE, stdout, CFSTR("no BSD interface\n"));
880 return FALSE;
881 }
882
883 ok = _process_options(firewireOptions, N_FIREWIRE_OPTIONS, argc, argv, newConfiguration);
884 if (ok) {
885 // validate configuration
886 if (!validateMediaOptions(interfaceName, newConfiguration)) {
887 return FALSE;
888 }
889 }
890
891 return ok;
892 }
893
894
895 /* -------------------- */
896
897
898 static selections modemDialSelections[] = {
899 { CFSTR("ignore"), &kSCValNetModemDialModeIgnoreDialTone , 0 },
900 { CFSTR("manual"), &kSCValNetModemDialModeManual , 0 },
901 { CFSTR("wait") , &kSCValNetModemDialModeWaitForDialTone, 0 },
902 { NULL , NULL , 0 }
903 };
904
905 static options modemOptions[] = {
906 { "ConnectionScript" , "script", isString , &kSCPropNetModemConnectionScript , NULL, NULL },
907 { "DialMode" , "mode" , isChooseOne, &kSCPropNetModemDialMode , NULL, (void *)modemDialSelections },
908 { "CallWaiting" , NULL , isBoolean , &kSCPropNetModemHoldEnabled , NULL, NULL },
909 { "CallWaitingAlert" , NULL , isBoolean , &kSCPropNetModemHoldCallWaitingAudibleAlert, NULL, NULL },
910 { "CallWaitingDisconnectOnAnswer", NULL , isBoolean , &kSCPropNetModemHoldDisconnectOnAnswer , NULL, NULL },
911 { "DataCompression" , NULL , isBoolean , &kSCPropNetModemDataCompression , NULL, NULL },
912 { "ErrorCorrection" , NULL , isBoolean , &kSCPropNetModemErrorCorrection , NULL, NULL },
913 { "HoldReminder" , NULL , isBoolean , &kSCPropNetModemHoldReminder , NULL, NULL },
914 { "HoldReminderTime" , "time" , isNumber , &kSCPropNetModemHoldReminderTime , NULL, NULL },
915 { "PulseDial" , NULL , isBoolean , &kSCPropNetModemPulseDial , NULL, NULL },
916 { "Speaker" , NULL , isBoolean , &kSCPropNetModemSpeaker , NULL, NULL },
917
918 { "?" , NULL , isHelp , NULL , NULL,
919 "\nModem configuration commands\n\n"
920 " set interface [ConnectionScript connection-script]\n"
921 " set interface [CallWaiting {enable|disable}]\n"
922 " set interface [CallWaitingAlert {enable|disable}]\n"
923 " set interface [CallWaitingDisconnectOnAnswer {enable|disable}]\n"
924 " set interface [DialMode {ignore|wait}]\n"
925 " set interface [DataCompression {enable|disable}]\n"
926 " set interface [ErrorCorrection {enable|disable}]\n"
927 " set interface [HoldReminder {enable|disable}]\n"
928 " set interface [HoldReminderTime n]\n"
929 " set interface [PulseDial {enable|disable}]\n"
930 " set interface [Speaker {enable|disable}]"
931 }
932 };
933 #define N_MODEM_OPTIONS (sizeof(modemOptions) / sizeof(modemOptions[0]))
934
935
936 static Boolean
937 set_interface_modem(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
938 {
939 Boolean ok;
940
941 ok = _process_options(modemOptions, N_MODEM_OPTIONS, argc, argv, newConfiguration);
942 return ok;
943 }
944
945
946 /* -------------------- */
947
948
949 static int
950 __doPPPAuthPW(CFStringRef key, const char *description, void *info, int argc, char **argv, CFMutableDictionaryRef newConfiguration)
951 {
952 if (argc < 1) {
953 SCPrint(TRUE, stdout, CFSTR("PPP password not specified\n"));
954 return -1;
955 }
956
957 if (strlen(argv[0]) > 0) {
958 CFStringRef encryptionType;
959
960 encryptionType = CFDictionaryGetValue(newConfiguration, kSCPropNetPPPAuthPasswordEncryption);
961 if (encryptionType == NULL) {
962 CFIndex n;
963 CFMutableDataRef pw;
964 CFStringRef str;
965
966 str = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
967 n = CFStringGetLength(str);
968 pw = CFDataCreateMutable(NULL, n * sizeof(UniChar));
969 CFDataSetLength(pw, n * sizeof(UniChar));
970 CFStringGetCharacters(str,
971 CFRangeMake(0, n),
972 (UniChar *)CFDataGetMutableBytePtr(pw));
973 CFRelease(str);
974
975 CFDictionarySetValue(newConfiguration, key, pw);
976 CFRelease(pw);
977 } else {
978 SCPrint(TRUE, stdout, CFSTR("PPP password type \"%@\" not supported\n"), encryptionType);
979 return -1;
980 }
981 } else {
982 CFDictionaryRemoveValue(newConfiguration, key);
983 }
984
985 return 1;
986 }
987
988
989 static int
990 __doPPPAuthPWType(CFStringRef key, const char *description, void *info, int argc, char **argv, CFMutableDictionaryRef newConfiguration)
991 {
992 if (argc < 1) {
993 SCPrint(TRUE, stdout, CFSTR("PPP password type mode not specified\n"));
994 return -1;
995 }
996
997 if (strlen(argv[0]) > 0) {
998 if (strcasecmp(argv[0], "keychain") == 0) {
999 CFDictionarySetValue(newConfiguration, key, kSCValNetPPPAuthPasswordEncryptionKeychain);
1000 } else {
1001 SCPrint(TRUE, stdout, CFSTR("invalid password type\n"));
1002 return -1;
1003 }
1004 } else {
1005 CFDictionaryRemoveValue(newConfiguration, key);
1006 }
1007
1008 // encryption type changed, reset password
1009 CFDictionaryRemoveValue(newConfiguration, kSCPropNetPPPAuthPassword);
1010
1011 return 1;
1012 }
1013
1014
1015 static selections authPromptSelections[] = {
1016 { CFSTR("before"), &kSCValNetPPPAuthPromptBefore, 0 },
1017 { CFSTR("after") , &kSCValNetPPPAuthPromptAfter , 0 },
1018 { NULL , NULL , 0 }
1019 };
1020
1021
1022 static selections authProtocolSelections[] = {
1023 { CFSTR("CHAP") , &kSCValNetPPPAuthProtocolCHAP , 0 },
1024 { CFSTR("EAP") , &kSCValNetPPPAuthProtocolEAP , 0 },
1025 { CFSTR("MSCHAP1"), &kSCValNetPPPAuthProtocolMSCHAP1, 0 },
1026 { CFSTR("MSCHAP2"), &kSCValNetPPPAuthProtocolMSCHAP2, 0 },
1027 { CFSTR("PAP") , &kSCValNetPPPAuthProtocolPAP , 0 },
1028 { NULL , NULL , 0 }
1029 };
1030
1031
1032 static options pppOptions[] = {
1033 { "ACSP" , NULL , isBoolean , &kSCPropNetPPPACSPEnabled , NULL , NULL },
1034 { "ConnectTime" , "?time" , isNumber , &kSCPropNetPPPConnectTime , NULL , NULL },
1035 { "DialOnDemand" , NULL , isBoolean , &kSCPropNetPPPDialOnDemand , NULL , NULL },
1036 { "DisconnectOnFastUserSwitch", NULL , isBoolean , &kSCPropNetPPPDisconnectOnFastUserSwitch, NULL , NULL },
1037 { "DisconnectOnIdle" , NULL , isBoolean , &kSCPropNetPPPDisconnectOnIdle , NULL , NULL },
1038 { "DisconnectOnIdleTimer" , "timeout" , isNumber , &kSCPropNetPPPDisconnectOnIdleTimer , NULL , NULL },
1039 { "DisconnectOnLogout" , NULL , isBoolean , &kSCPropNetPPPDisconnectOnLogout , NULL , NULL },
1040 { "DisconnectOnSleep" , NULL , isBoolean , &kSCPropNetPPPDisconnectOnSleep , NULL , NULL },
1041 { "DisconnectTime" , "?time" , isNumber , &kSCPropNetPPPDisconnectTime , NULL , NULL },
1042 { "IdleReminder" , NULL , isBoolean , &kSCPropNetPPPIdleReminder , NULL , NULL },
1043 { "IdleReminderTimer" , "time" , isNumber , &kSCPropNetPPPIdleReminderTimer , NULL , NULL },
1044 { "Logfile" , "path" , isString , &kSCPropNetPPPLogfile , NULL , NULL },
1045 { "Plugins" , "plugin" , isStringArray , &kSCPropNetPPPPlugins , NULL , NULL },
1046 { "RetryConnectTime" , "time" , isNumber , &kSCPropNetPPPRetryConnectTime , NULL , NULL },
1047 { "SessionTimer" , "time" , isNumber , &kSCPropNetPPPSessionTimer , NULL , NULL },
1048 { "UseSessionTimer" , NULL , isBoolean , &kSCPropNetPPPUseSessionTimer , NULL , NULL },
1049 { "VerboseLogging" , NULL , isBoolean , &kSCPropNetPPPVerboseLogging , NULL , NULL },
1050
1051 // --- Auth: ---
1052 { "AuthEAPPlugins" , "plugin" , isStringArray , &kSCPropNetPPPAuthEAPPlugins , NULL , NULL },
1053 { "AuthName" , "account" , isString , &kSCPropNetPPPAuthName , NULL , NULL },
1054 { "Account" , "account" , isString , &kSCPropNetPPPAuthName , NULL , NULL },
1055 { "AuthPassword" , "password" , isOther , &kSCPropNetPPPAuthPassword , __doPPPAuthPW , NULL },
1056 { "Password" , "password" , isOther , &kSCPropNetPPPAuthPassword , __doPPPAuthPW , NULL },
1057 { "AuthPasswordEncryption" , "type" , isOther , &kSCPropNetPPPAuthPasswordEncryption , __doPPPAuthPWType, NULL },
1058 { "AuthPrompt" , "before/after", isChooseOne , &kSCPropNetPPPAuthPrompt , NULL , (void *)authPromptSelections },
1059 { "AuthProtocol" , "protocol" , isChooseMultiple , &kSCPropNetPPPAuthProtocol , NULL , (void *)authProtocolSelections },
1060
1061 // --- Comm: ---
1062 { "CommRemoteAddress" , "phone#" , isString , &kSCPropNetPPPCommRemoteAddress , NULL , NULL },
1063 { "CommAlternateRemoteAddress", "phone#" , isString , &kSCPropNetPPPCommAlternateRemoteAddress, NULL , NULL },
1064 { "CommConnectDelay" , "time" , isNumber , &kSCPropNetPPPCommConnectDelay , NULL , NULL },
1065 { "CommDisplayTerminalWindow" , NULL , isBoolean , &kSCPropNetPPPCommDisplayTerminalWindow , NULL , NULL },
1066 { "CommRedialCount" , "retry count" , isNumber , &kSCPropNetPPPCommRedialCount , NULL , NULL },
1067 { "CommRedialEnabled" , NULL , isBoolean , &kSCPropNetPPPCommRedialEnabled , NULL , NULL },
1068 { "CommRedialInterval" , "retry delay" , isNumber , &kSCPropNetPPPCommRedialInterval , NULL , NULL },
1069 { "CommTerminalScript" , "script" , isString , &kSCPropNetPPPCommTerminalScript , NULL , NULL },
1070 { "CommUseTerminalScript" , NULL , isBoolean , &kSCPropNetPPPCommUseTerminalScript , NULL , NULL },
1071
1072 // --- CCP: ---
1073 { "CCPEnabled" , NULL , isBoolean , &kSCPropNetPPPCCPEnabled , NULL , NULL },
1074 { "CCPMPPE40Enabled" , NULL , isBoolean , &kSCPropNetPPPCCPMPPE40Enabled , NULL , NULL },
1075 { "CCPMPPE128Enabled" , NULL , isBoolean , &kSCPropNetPPPCCPMPPE128Enabled , NULL , NULL },
1076
1077 // --- IPCP: ---
1078 { "IPCPCompressionVJ" , NULL , isBoolean , &kSCPropNetPPPIPCPCompressionVJ , NULL , NULL },
1079 { "IPCPUsePeerDNS" , NULL , isBoolean , &kSCPropNetPPPIPCPUsePeerDNS , NULL , NULL },
1080
1081 // --- LCP: ---
1082 { "LCPEchoEnabled" , NULL , isBoolean , &kSCPropNetPPPLCPEchoEnabled , NULL , NULL },
1083 { "LCPEchoFailure" , NULL , isNumber , &kSCPropNetPPPLCPEchoFailure , NULL , NULL },
1084 { "LCPEchoInterval" , NULL , isNumber , &kSCPropNetPPPLCPEchoInterval , NULL , NULL },
1085 { "LCPCompressionACField" , NULL , isBoolean , &kSCPropNetPPPLCPCompressionACField , NULL , NULL },
1086 { "LCPCompressionPField" , NULL , isBoolean , &kSCPropNetPPPLCPCompressionPField , NULL , NULL },
1087 { "LCPMRU" , NULL , isNumber , &kSCPropNetPPPLCPMRU , NULL , NULL },
1088 { "LCPMTU" , NULL , isNumber , &kSCPropNetPPPLCPMTU , NULL , NULL },
1089 { "LCPReceiveACCM" , NULL , isNumber , &kSCPropNetPPPLCPReceiveACCM , NULL , NULL },
1090 { "LCPTransmitACCM" , NULL , isNumber , &kSCPropNetPPPLCPTransmitACCM , NULL , NULL },
1091
1092 // --- Help ---
1093 { "?" , NULL , isHelp , NULL , NULL ,
1094 "\nPPP configuration commands\n\n"
1095 " set interface [Account account]\n"
1096 " set interface [Password password]\n"
1097 " set interface [Number telephone-number]\n"
1098 " set interface [AlternateNumber telephone-number]\n"
1099 " set interface [IdleReminder {enable|disable}]\n"
1100 " set interface [IdleReminderTimer time-in-seconds]\n"
1101 " set interface [DisconnectOnIdle {enable|disable}]\n"
1102 " set interface [DisconnectOnIdleTimer time-in-seconds]\n"
1103 " set interface [DisconnectOnLogout {enable|disable}]"
1104 }
1105 };
1106 #define N_PPP_OPTIONS (sizeof(pppOptions) / sizeof(pppOptions[0]))
1107
1108
1109 static Boolean
1110 set_interface_ppp(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
1111 {
1112 Boolean ok;
1113
1114 ok = _process_options(pppOptions, N_PPP_OPTIONS, argc, argv, newConfiguration);
1115 return ok;
1116 }
1117
1118
1119 /* -------------------- */
1120
1121
1122 static Boolean
1123 set_interface_vlan(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
1124 {
1125 // xxxxx ("device", "tag")
1126 SCPrint(TRUE, stdout, CFSTR("vlan interface management not yet supported\n"));
1127 return FALSE;
1128 }
1129
1130
1131 /* -------------------- */
1132
1133
1134 __private_extern__
1135 void
1136 set_interface(int argc, char **argv)
1137 {
1138 CFDictionaryRef configuration;
1139 CFStringRef interfaceType;
1140 CFMutableDictionaryRef newConfiguration = NULL;
1141 Boolean ok = FALSE;
1142
1143 if (net_interface == NULL) {
1144 SCPrint(TRUE, stdout, CFSTR("interface not selected\n"));
1145 return;
1146 }
1147
1148 if (argc < 1) {
1149 SCPrint(TRUE, stdout, CFSTR("set what?\n"));
1150 return;
1151 }
1152
1153 configuration = SCNetworkInterfaceGetConfiguration(net_interface);
1154 if (configuration == NULL) {
1155 newConfiguration = CFDictionaryCreateMutable(NULL,
1156 0,
1157 &kCFTypeDictionaryKeyCallBacks,
1158 &kCFTypeDictionaryValueCallBacks);
1159 } else {
1160 newConfiguration = CFDictionaryCreateMutableCopy(NULL, 0, configuration);
1161 CFDictionaryRemoveValue(newConfiguration, kSCResvInactive);
1162 }
1163
1164 interfaceType = SCNetworkInterfaceGetInterfaceType(net_interface);
1165
1166 if (CFEqual(interfaceType, kSCNetworkInterfaceTypeBond)) {
1167 ok = set_interface_bond(argc, argv, newConfiguration);
1168 } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeEthernet)) {
1169 ok = set_interface_ethernet(argc, argv, newConfiguration);
1170 } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeFireWire)) {
1171 ok = set_interface_firewire(argc, argv, newConfiguration);
1172 } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeModem)) {
1173 ok = set_interface_modem(argc, argv, newConfiguration);
1174 } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeIEEE80211)) {
1175 ok = set_interface_airport(argc, argv, newConfiguration);
1176 } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) {
1177 ok = set_interface_ppp(argc, argv, newConfiguration);
1178 } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeVLAN)) {
1179 ok = set_interface_vlan(argc, argv, newConfiguration);
1180 } else {
1181 SCPrint(TRUE, stdout, CFSTR("this interfaces configuration cannot be changed\n"));
1182 }
1183
1184 if (!ok) {
1185 goto done;
1186 }
1187
1188 if (((configuration == NULL) && (CFDictionaryGetCount(newConfiguration) > 0)) ||
1189 ((configuration != NULL) && !CFEqual(configuration, newConfiguration))) {
1190 if (!SCNetworkInterfaceSetConfiguration(net_interface, newConfiguration)) {
1191 if (SCError() == kSCStatusNoKey) {
1192 SCPrint(TRUE, stdout, CFSTR("could not update per-service interface configuration\n"));
1193 } else {
1194 SCPrint(TRUE, stdout, CFSTR("%s\n"), SCErrorString(SCError()));
1195 }
1196 goto done;
1197 }
1198
1199 net_changed = TRUE;
1200 }
1201
1202 done :
1203
1204 if (newConfiguration != NULL) CFRelease(newConfiguration);
1205 return;
1206 }
1207
1208
1209 /* -------------------- */
1210
1211
1212 __private_extern__
1213 void
1214 show_interface(int argc, char **argv)
1215 {
1216 SCNetworkInterfaceRef interface;
1217
1218 if (argc == 1) {
1219 interface = _find_interface(argv[0]);
1220 } else {
1221 if (net_interface != NULL) {
1222 interface = net_interface;
1223 } else {
1224 SCPrint(TRUE, stdout, CFSTR("interface not selected\n"));
1225 return;
1226 }
1227 }
1228
1229 if (interface != NULL) {
1230 _show_interface(interface, CFSTR(""), TRUE);
1231 }
1232
1233 return;
1234 }
1235
1236
1237 /* -------------------- */
1238
1239
1240 __private_extern__
1241 CFStringRef
1242 _interface_description(SCNetworkInterfaceRef interface)
1243 {
1244 CFMutableStringRef description;
1245 CFStringRef if_bsd_name;
1246 CFStringRef if_type;
1247
1248 description = CFStringCreateMutable(NULL, 0);
1249
1250 if_type = SCNetworkInterfaceGetInterfaceType(interface);
1251 CFStringAppend(description, if_type);
1252
1253 if_bsd_name = SCNetworkInterfaceGetBSDName(interface);
1254 if (if_bsd_name != NULL) {
1255 CFStringAppendFormat(description, NULL, CFSTR(" (%@)"), if_bsd_name);
1256 }
1257
1258 interface = SCNetworkInterfaceGetInterface(interface);
1259 while ((interface != NULL) &&
1260 !CFEqual(interface, kSCNetworkInterfaceIPv4)) {
1261 CFStringRef childDescription;
1262
1263 childDescription = _interface_description(interface);
1264 CFStringAppendFormat(description, NULL, CFSTR(" / %@"), childDescription);
1265 CFRelease(childDescription);
1266
1267 interface = SCNetworkInterfaceGetInterface(interface);
1268 }
1269
1270 return description;
1271 }