]> git.saurik.com Git - apple/configd.git/blobdiff - scutil.tproj/net_interface.c
configd-130.tar.gz
[apple/configd.git] / scutil.tproj / net_interface.c
diff --git a/scutil.tproj/net_interface.c b/scutil.tproj/net_interface.c
new file mode 100644 (file)
index 0000000..a7bb46c
--- /dev/null
@@ -0,0 +1,1186 @@
+/*
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * Modification History
+ *
+ * August 5, 2004                      Allan Nathanson <ajn@apple.com>
+ * - initial revision
+ */
+
+
+#include "scutil.h"
+#include "net.h"
+
+#include <SystemConfiguration/LinkConfiguration.h>
+
+
+/* -------------------- */
+
+
+static CFArrayRef
+_copy_interfaces()
+{
+       CFMutableArrayRef       interfaces;
+       CFArrayRef              real_interfaces;
+
+       real_interfaces = SCNetworkInterfaceCopyAll();
+       if (real_interfaces == NULL) {
+               SCPrint(TRUE, stdout, CFSTR("%s\n"), SCErrorString(SCError()));
+               return NULL;
+       }
+
+       interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+       // include real interfaces
+       CFArrayAppendArray(interfaces,
+                          real_interfaces,
+                          CFRangeMake(0, CFArrayGetCount(real_interfaces)));
+       CFRelease(real_interfaces);
+
+       // include pseudo interfaces
+       CFArrayAppendValue(interfaces, kSCNetworkInterfaceIPv4);
+
+       // include interfaces that we have created
+       if (new_interfaces != NULL) {
+               CFArrayAppendArray(interfaces,
+                                  new_interfaces,
+                                  CFRangeMake(0, CFArrayGetCount(new_interfaces)));
+       }
+
+       return (CFArrayRef)interfaces;
+}
+
+
+__private_extern__
+SCNetworkInterfaceRef
+_find_interface(char *match)
+{
+       Boolean                 allowIndex      = TRUE;
+       CFIndex                 i;
+       CFIndex                 n;
+       CFStringRef             select_name     = NULL;
+       SCNetworkInterfaceRef   selected        = NULL;
+
+       if (strcasecmp(match, "$child") == 0) {
+               if (net_interface == NULL) {
+                       SCPrint(TRUE, stdout, CFSTR("interface not selected\n"));
+                       goto done;
+               }
+
+               selected = SCNetworkInterfaceGetInterface(net_interface);
+               if(selected == NULL) {
+                       SCPrint(TRUE, stdout, CFSTR("no child interface\n"));
+               }
+
+               goto done;
+       } else if (strcasecmp(match, "$service") == 0) {
+               if (net_service == NULL) {
+                       SCPrint(TRUE, stdout, CFSTR("service not selected\n"));
+                       goto done;
+               }
+
+               selected = SCNetworkServiceGetInterface(net_service);
+               if(selected == NULL) {
+                       SCPrint(TRUE, stdout, CFSTR("no interface for service\n"));
+               }
+
+               goto done;
+       }
+
+       if (interfaces == NULL) {
+               interfaces = _copy_interfaces();
+               if (interfaces == NULL) {
+                       return NULL;
+               }
+               allowIndex = FALSE;
+       }
+
+       // try to select the interface by its display name
+
+       select_name = CFStringCreateWithCString(NULL, match, kCFStringEncodingUTF8);
+
+       n = CFArrayGetCount(interfaces);
+       for (i = 0; i < n; i++) {
+               SCNetworkInterfaceRef   interface;
+               CFStringRef             interfaceName;
+
+               interface = CFArrayGetValueAtIndex(interfaces, i);
+               interfaceName = SCNetworkInterfaceGetLocalizedDisplayName(interface);
+               if ((interfaceName != NULL) && CFEqual(select_name, interfaceName)) {
+                       if (selected == NULL) {
+                               selected = interface;
+                       } else {
+                               // if multiple interfaces match
+                               selected = NULL;
+                               SCPrint(TRUE, stdout, CFSTR("multiple interfaces match\n"));
+                               goto done;
+                       }
+               }
+       }
+
+       if (selected != NULL) {
+               goto done;
+       }
+
+       // try to select the interface by its BSD name
+
+       for (i = 0; i < n; i++) {
+               SCNetworkInterfaceRef   interface;
+               CFStringRef             bsd_name        = NULL;
+
+               interface = CFArrayGetValueAtIndex(interfaces, i);
+                while ((interface != NULL) && (bsd_name == NULL)) {
+                        bsd_name = SCNetworkInterfaceGetBSDName(interface);
+                        if (bsd_name == NULL) {
+                                interface = SCNetworkInterfaceGetInterface(interface);
+                        }
+                }
+
+               if ((bsd_name != NULL) && CFEqual(select_name, bsd_name)) {
+                       if (selected == NULL) {
+                               selected = interface;
+                       } else {
+                               // if multiple interfaces match
+                               selected = NULL;
+                               SCPrint(TRUE, stdout, CFSTR("multiple interfaces match\n"));
+                               goto done;
+                       }
+               }
+       }
+
+       if (selected != NULL) {
+               goto done;
+       }
+
+       // try to select the interface by its interface type
+
+       for (i = 0; i < n; i++) {
+               SCNetworkInterfaceRef   interface;
+               CFStringRef             interfaceType;
+
+               interface = CFArrayGetValueAtIndex(interfaces, i);
+               interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
+               if (CFEqual(select_name, interfaceType)) {
+                       if (selected == NULL) {
+                               selected = interface;
+                       } else {
+                               // if multiple interfaces match
+                               selected = NULL;
+                               SCPrint(TRUE, stdout, CFSTR("multiple interfaces match\n"));
+                               goto done;
+                       }
+               }
+       }
+
+       if (selected != NULL) {
+               goto done;
+       }
+
+       if (allowIndex) {
+               char    *end;
+               char    *str    = match;
+               long    val;
+
+               // try to select the interface by its index
+
+               errno = 0;
+               val = strtol(str, &end, 10);
+               if ((*str != '\0') &&
+                   ((*end == '\0') || (*end == '.')) &&
+                   (errno == 0)) {
+                       if ((val > 0) && (val <= n)) {
+                               selected = CFArrayGetValueAtIndex(interfaces, val - 1);
+
+                               if (*end == '.') {
+                                       str = end + 1;
+                                       val = strtol(str, &end, 10);
+                                       if ((*str != '\0') && (*end == '\0') && (errno == 0)) {
+                                               while (val-- > 0) {
+                                                       selected = SCNetworkInterfaceGetInterface(selected);
+                                                       if (selected == NULL) {
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (selected != NULL) {
+               goto done;
+       }
+
+       SCPrint(TRUE, stdout, CFSTR("no match\n"));
+
+    done :
+
+       if (select_name != NULL) CFRelease(select_name);
+       return selected;
+}
+
+
+/* -------------------- */
+
+
+__private_extern__
+void
+create_interface(int argc, char **argv)
+{
+       SCNetworkInterfaceRef   interface;
+       CFStringRef             interfaceName;
+       CFStringRef             interfaceType;
+       SCNetworkInterfaceRef   new_interface;
+
+       if (argc < 1) {
+               SCPrint(TRUE, stdout, CFSTR("what interface type?\n"));
+               return;
+       }
+
+       interfaceType = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+
+       if (CFEqual(interfaceType, kSCNetworkInterfaceTypeVLAN)) {
+// xxxxx
+SCPrint(TRUE, stdout, CFSTR("vlan creation not yet supported\n"));
+goto done;
+       } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeBond)) {
+// xxxxx
+SCPrint(TRUE, stdout, CFSTR("bond creation not yet supported\n"));
+goto done;
+       } else {
+               if (argc < 2) {
+                       if (net_interface == NULL) {
+                               SCPrint(TRUE, stdout, CFSTR("no network interface selected\n"));
+                               goto done;
+                       }
+
+                       interface = net_interface;
+               } else {
+                       interface = _find_interface(argv[1]);
+               }
+
+               if (interface == NULL) {
+                       return;
+               }
+
+               new_interface = SCNetworkInterfaceCreateWithInterface(interface, interfaceType);
+               if (new_interface == NULL) {
+                       SCPrint(TRUE, stdout, CFSTR("%s\n"), SCErrorString(SCError()));
+                       goto done;
+               }
+       }
+
+       if (new_interfaces == NULL) {
+               new_interfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       }
+       CFArrayAppendValue(new_interfaces, new_interface);
+
+       if (net_interface != NULL) CFRelease(net_interface);
+       net_interface = new_interface;
+
+       interfaceName = SCNetworkInterfaceGetLocalizedDisplayName(net_interface);
+       if (interfaceName == NULL) {
+               interfaceName = SCNetworkInterfaceGetBSDName(net_interface);
+       }
+       if (interfaceName == NULL) {
+               interfaceName = SCNetworkInterfaceGetInterfaceType(net_interface);
+       }
+       SCPrint(TRUE, stdout, CFSTR("interface \"%@\" created and selected\n"), interfaceName);
+
+    done :
+
+       CFRelease(interfaceType);
+       return;
+}
+
+
+/* -------------------- */
+
+
+__private_extern__
+void
+select_interface(int argc, char **argv)
+{
+       SCNetworkInterfaceRef   interface;
+
+       interface = _find_interface(argv[0]);
+
+       if (interface != NULL) {
+               CFStringRef     interfaceName;
+
+               if (net_interface != NULL) CFRelease(net_interface);
+               net_interface = CFRetain(interface);
+
+               interfaceName = SCNetworkInterfaceGetLocalizedDisplayName(interface);
+               if (interfaceName == NULL) {
+                       interfaceName = SCNetworkInterfaceGetBSDName(interface);
+               }
+               if (interfaceName == NULL) {
+                       interfaceName = SCNetworkInterfaceGetInterfaceType(interface);
+               }
+
+               SCPrint(TRUE, stdout, CFSTR("interface \"%@\" selected\n"), interfaceName);
+       }
+
+       return;
+}
+
+
+/* -------------------- */
+
+
+__private_extern__
+void
+_show_interface(SCNetworkInterfaceRef interface, CFStringRef prefix, Boolean showChild)
+{
+       CFDictionaryRef configuration;
+       CFStringRef     if_bsd_name;
+       CFStringRef     if_localized_name;
+       CFStringRef     if_mac_address;
+       CFStringRef     if_type;
+       CFArrayRef      supported;
+
+       if_localized_name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
+       if (if_localized_name != NULL) {
+               SCPrint(TRUE, stdout, CFSTR("%@  name                 = %@\n"), prefix, if_localized_name);
+       }
+
+       if_bsd_name = SCNetworkInterfaceGetBSDName(interface);
+       if (if_bsd_name != NULL) {
+               SCPrint(TRUE, stdout, CFSTR("%@  interface name       = %@\n"), prefix, if_bsd_name);
+       }
+
+       if_type = SCNetworkInterfaceGetInterfaceType(interface);
+       SCPrint(TRUE, stdout, CFSTR("%@  type                 = %@\n"), prefix, if_type);
+
+       if_mac_address = SCNetworkInterfaceGetHardwareAddressString(interface);
+       if (if_mac_address != NULL) {
+               SCPrint(TRUE, stdout, CFSTR("%@  address              = %@\n"), prefix, if_mac_address);
+       }
+
+       configuration = SCNetworkInterfaceGetConfiguration(interface);
+       if ((configuration != NULL) &&
+           CFDictionaryContainsKey(configuration, kSCResvInactive)) {
+               configuration = NULL;
+       }
+
+       if (if_bsd_name != NULL) {
+               CFArrayRef      available;
+               CFDictionaryRef active;
+               int             mtu_cur;
+               int             mtu_min;
+               int             mtu_max;
+
+               if (NetworkInterfaceCopyMTU(if_bsd_name, &mtu_cur, &mtu_min, &mtu_max)) {
+                       char    isCurrent       = '*';
+
+                       if (configuration != NULL) {
+                               int             mtu_req;
+                               CFNumberRef     num;
+
+                               num = CFDictionaryGetValue(configuration, kSCPropNetEthernetMTU);
+                               if (isA_CFNumber(num)) {
+                                       CFNumberGetValue(num, kCFNumberIntType, &mtu_req);
+                                       if (mtu_cur != mtu_req) {
+                                               mtu_cur = mtu_req;
+                                               isCurrent = ' ';
+                                       }
+                               }
+                       }
+
+                       SCPrint(TRUE, stdout, CFSTR("%@  mtu                %c = %ld (%ld < n < %ld)\n"),
+                               prefix,
+                               isCurrent,
+                               mtu_cur,
+                               mtu_min,
+                               mtu_max);
+               }
+
+               if (NetworkInterfaceCopyMediaOptions(if_bsd_name, NULL, &active, &available, TRUE)) {
+                       char            isCurrent       = ' ';
+                       CFArrayRef      options         = NULL;
+                       CFArrayRef      options_req     = NULL;
+                       CFStringRef     subtype         = NULL;
+                       CFStringRef     subtype_req     = NULL;
+
+                       if (configuration != NULL) {
+                               subtype_req = CFDictionaryGetValue(configuration, kSCPropNetEthernetMediaSubType);
+                               options_req = CFDictionaryGetValue(configuration, kSCPropNetEthernetMediaOptions);
+                       }
+
+                       if (subtype_req == NULL) {
+                               subtype_req = CFSTR("autoselect");
+                       }
+
+                       if (active != NULL) {
+                               subtype = CFDictionaryGetValue(active, kSCPropNetEthernetMediaSubType);
+                               options = CFDictionaryGetValue(active, kSCPropNetEthernetMediaOptions);
+                       }
+
+                       if (subtype != NULL) {
+                               if (((subtype_req != NULL) &&
+                                    CFEqual(subtype, subtype_req)) &&
+                                   ((options == options_req) ||
+                                    ((options != NULL) &&
+                                     (options_req != NULL) &&
+                                     CFEqual(options, options_req)))
+                                  ) {
+                                       isCurrent = '*';
+                               } else if ((subtype_req == NULL) ||
+                                          ((subtype_req != NULL) &&
+                                           CFEqual(subtype_req, CFSTR("autoselect")))) {
+                                       // if requested subtype not specified or "autoselect"
+                                       isCurrent = '*';
+                               }
+                       }
+
+                       if (subtype_req != NULL) {
+                               SCPrint(TRUE, stdout, CFSTR("%@  media              %c = %@"),
+                                       prefix,
+                                       isCurrent,
+                                       subtype_req);
+
+                               if ((options_req != NULL) &&
+                                   (CFArrayGetCount(options_req) > 0)) {
+                                       CFStringRef     options_str;
+
+                                       options_str = CFStringCreateByCombiningStrings(NULL, options_req, CFSTR(","));
+                                       SCPrint(TRUE, stdout, CFSTR(" <%@>"), options_str);
+                                       CFRelease(options_str);
+                               }
+
+                               SCPrint(TRUE, stdout, CFSTR("\n"));
+                       }
+
+                       SCPrint(TRUE, stdout, CFSTR("\n"));
+
+                       if (available != NULL) {
+                               CFIndex         i;
+                               CFIndex         n_subtypes;
+                               CFArrayRef      subtypes;
+
+                               subtypes   = NetworkInterfaceCopyMediaSubTypes(available);
+                               n_subtypes = (subtypes != NULL) ? CFArrayGetCount(subtypes) : 0;
+                               for (i = 0; i < n_subtypes; i++) {
+                                       CFIndex         j;
+                                       CFIndex         n_subtype_options;
+                                       CFStringRef     subtype;
+                                       CFArrayRef      subtype_options;
+
+                                       subtype = CFArrayGetValueAtIndex(subtypes, i);
+                                       subtype_options = NetworkInterfaceCopyMediaSubTypeOptions(available, subtype);
+                                       n_subtype_options = (subtype_options != NULL) ? CFArrayGetCount(subtype_options) : 0;
+                                       for (j = 0; j < n_subtype_options; j++) {
+                                               char            isCurrent       = ' ';
+                                               CFArrayRef      options;
+
+                                               options = CFArrayGetValueAtIndex(subtype_options, j);
+
+                                               if (((subtype_req != NULL) &&
+                                                    CFEqual(subtype, subtype_req)) &&
+                                                   ((options == options_req) ||
+                                                    ((options != NULL) &&
+                                                     (options_req != NULL) &&
+                                                     CFEqual(options, options_req)))
+                                                  ) {
+                                                       isCurrent = '*';
+                                               }
+
+                                               SCPrint(TRUE, stdout, CFSTR("%@  %s    %c = %@"),
+                                                       prefix,
+                                                       ((i == 0) && (j == 0)) ? "supported media" : "               ",
+                                                       isCurrent,
+                                                       subtype);
+
+                                               if ((options != NULL) &&
+                                                   (CFArrayGetCount(options) > 0)) {
+                                                       CFStringRef     options_str;
+
+                                                       options_str = CFStringCreateByCombiningStrings(NULL, options, CFSTR(","));
+                                                       SCPrint(TRUE, stdout, CFSTR(" <%@>"), options_str);
+                                                       CFRelease(options_str);
+                                               }
+
+                                               SCPrint(TRUE, stdout, CFSTR("\n"));
+                                       }
+                                       CFRelease(subtype_options);
+                               }
+                       }
+               } else {
+                       SCPrint(TRUE, stdout, CFSTR("\n"));
+               }
+       }
+
+       supported = SCNetworkInterfaceGetSupportedInterfaceTypes(interface);
+       SCPrint(TRUE, stdout, CFSTR("%@  supported interfaces = "), prefix);
+       if (supported != NULL) {
+               CFIndex i;
+               CFIndex n       = CFArrayGetCount(supported);
+
+               for (i = 0; i < n; i++) {
+                       SCPrint(TRUE, stdout, CFSTR("%s%@"),
+                               (i == 0) ? "" : ", ",
+                               CFArrayGetValueAtIndex(supported, i));
+               }
+       }
+       SCPrint(TRUE, stdout, CFSTR("\n"));
+
+       supported = SCNetworkInterfaceGetSupportedProtocolTypes(interface);
+       SCPrint(TRUE, stdout, CFSTR("%@  supported protocols  = "), prefix);
+       if (supported != NULL) {
+               CFIndex i;
+               CFIndex n       = CFArrayGetCount(supported);
+
+               for (i = 0; i < n; i++) {
+                       SCPrint(TRUE, stdout, CFSTR("%s%@"),
+                               (i == 0) ? "" : ", ",
+                               CFArrayGetValueAtIndex(supported, i));
+               }
+       }
+       SCPrint(TRUE, stdout, CFSTR("\n"));
+
+       if (configuration != NULL) {
+               CFMutableDictionaryRef  effective;
+
+               effective = CFDictionaryCreateMutableCopy(NULL, 0, configuration);
+
+               // remove known (and already reported) interface configuration keys
+               if (CFDictionaryContainsKey(effective, kSCResvInactive)) {
+                       CFDictionaryRemoveAllValues(effective);
+               }
+               CFDictionaryRemoveValue(effective, kSCPropNetEthernetMTU);
+               CFDictionaryRemoveValue(effective, kSCPropNetEthernetMediaSubType);
+               CFDictionaryRemoveValue(effective, kSCPropNetEthernetMediaOptions);
+
+               if (CFDictionaryGetCount(effective) > 0) {
+                       SCPrint(TRUE, stdout, CFSTR("\n%@  per-interface configuration\n"), prefix);
+                       _show_entity(configuration, prefix);
+               }
+
+               CFRelease(effective);
+       }
+
+       if (_sc_debug) {
+               SCPrint(TRUE, stdout, CFSTR("\n%@\n"), interface);
+       }
+
+       interface = SCNetworkInterfaceGetInterface(interface);
+       if (interface != NULL) {
+               CFStringRef     newPrefix;
+
+               newPrefix = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@  "), prefix);
+               SCPrint(TRUE, stdout, CFSTR("\n%@child interface\n"), newPrefix);
+               _show_interface(interface, newPrefix, showChild);
+               CFRelease(newPrefix);
+       }
+
+       return;
+}
+
+
+/* -------------------- */
+
+
+__private_extern__
+void
+show_interfaces(int argc, char **argv)
+{
+       CFIndex         i;
+       CFIndex         n;
+
+       if (interfaces != NULL) CFRelease(interfaces);
+       interfaces = _copy_interfaces();
+       if (interfaces == NULL) {
+               return;
+       }
+
+       n = CFArrayGetCount(interfaces);
+       for (i = 0; i < n; i++) {
+               CFIndex                 childIndex      = 0;
+               SCNetworkInterfaceRef   interface;
+
+               interface = CFArrayGetValueAtIndex(interfaces, i);
+               do {
+                       CFStringRef     interfaceName;
+                       char            isSelected;
+
+                       interfaceName = SCNetworkInterfaceGetLocalizedDisplayName(interface);
+                       if (interfaceName == NULL) {
+                               interfaceName = SCNetworkInterfaceGetBSDName(interface);
+                       }
+                       if (interfaceName == NULL) {
+                               interfaceName = SCNetworkInterfaceGetInterfaceType(interface);
+                       }
+
+                       isSelected = ' ';
+                       if ((net_interface != NULL) && CFEqual(interface, net_interface)) {
+                               isSelected = '>';
+                       }
+
+                       if (childIndex == 0) {
+                               SCPrint(TRUE, stdout, CFSTR("%c%2d: %@\n"),
+                                       isSelected,
+                                       i + 1,
+                                       interfaceName);
+                       } else {
+                               SCPrint(TRUE, stdout, CFSTR("%c%2d.%d: %@\n"),
+                                       isSelected,
+                                       i + 1,
+                                       childIndex,
+                                       interfaceName);
+                       }
+
+                       interface = SCNetworkInterfaceGetInterface(interface);
+                       childIndex++;
+               } while (interface != NULL);
+       }
+
+       return;
+}
+
+
+/* -------------------- */
+
+
+static Boolean
+set_interface_bond(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
+{
+// xxxxx ("+device", "-device")
+SCPrint(TRUE, stdout, CFSTR("bond interface management not yet supported\n"));
+       return FALSE;
+}
+
+
+/* -------------------- */
+
+
+static Boolean
+set_interface_airport(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
+{
+SCPrint(TRUE, stdout, CFSTR("airport interface management not yet supported\n"));
+       return FALSE;
+}
+
+
+/* -------------------- */
+
+
+static options ethernetOptions[] = {
+       { "mtu"       , NULL, isNumber     , &kSCPropNetEthernetMTU         , NULL, NULL },
+       { "media"     , NULL, isString     , &kSCPropNetEthernetMediaSubType, NULL, NULL },
+       { "mediaopt"  , NULL, isStringArray, &kSCPropNetEthernetMediaOptions, NULL, NULL },
+
+       { "?"         , NULL , isHelp     , NULL                            , NULL,
+           "\nEthernet configuration commands\n\n"
+           " set interface [mtu n] [media type] [mediaopts opts]\n"
+       }
+};
+#define        N_ETHERNET_OPTIONS      (sizeof(ethernetOptions) / sizeof(ethernetOptions[0]))
+
+
+static Boolean
+set_interface_ethernet(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
+{
+       CFStringRef     interfaceName;
+       Boolean         ok;
+
+       interfaceName = SCNetworkInterfaceGetBSDName(net_interface);
+       if (interfaceName == NULL) {
+               SCPrint(TRUE, stdout, CFSTR("no BSD interface\n"));
+               return FALSE;
+       }
+
+       ok = _process_options(ethernetOptions, N_ETHERNET_OPTIONS, argc, argv, newConfiguration);
+       if (ok) {
+               CFNumberRef     mtu;
+               CFArrayRef      options;
+               CFStringRef     subtype;
+
+               // validate configuration
+
+               mtu = CFDictionaryGetValue(newConfiguration, kSCPropNetEthernetMTU);
+               if (isA_CFNumber(mtu)) {
+                       int     mtu_max;
+                       int     mtu_min;
+                       int     mtu_val;
+
+                       if (!NetworkInterfaceCopyMTU(interfaceName, NULL, &mtu_min, &mtu_max)) {
+                               SCPrint(TRUE, stdout, CFSTR("cannot set MTU\n"));
+                               return FALSE;
+                       }
+
+                       if (!CFNumberGetValue(mtu, kCFNumberIntType, &mtu_val) ||
+                           (mtu_val < mtu_min) ||
+                           (mtu_val > mtu_max)) {
+                               SCPrint(TRUE, stdout, CFSTR("mtu out of range\n"));
+                               return FALSE;
+                       }
+               }
+
+               subtype = CFDictionaryGetValue(newConfiguration, kSCPropNetEthernetMediaSubType);
+               options = CFDictionaryGetValue(newConfiguration, kSCPropNetEthernetMediaOptions);
+
+               if (subtype != NULL) {
+                       CFArrayRef      available       = NULL;
+                       CFArrayRef      config_options  = options;
+                       CFArrayRef      subtypes        = NULL;
+                       CFArrayRef      subtype_options = NULL;
+
+                       ok = FALSE;
+
+                       if (options == NULL) {
+                               config_options = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
+                       }
+
+                       if (interfaceName == NULL) {
+                               SCPrint(TRUE, stdout, CFSTR("media type / options not available\n"));
+                               goto checked;
+                       }
+
+                       if (!NetworkInterfaceCopyMediaOptions(interfaceName, NULL, NULL, &available, FALSE)) {
+                               SCPrint(TRUE, stdout, CFSTR("media type / options not available\n"));
+                               goto checked;
+                       }
+
+                       if (available == NULL) {
+                               goto checked;
+                       }
+
+                       subtypes = NetworkInterfaceCopyMediaSubTypes(available);
+                       if ((subtypes == NULL) ||
+                           !CFArrayContainsValue(subtypes,
+                                                CFRangeMake(0, CFArrayGetCount(subtypes)),
+                                                subtype)) {
+                               SCPrint(TRUE, stdout, CFSTR("media type not valid\n"));
+                               goto checked;
+                       }
+
+                       subtype_options = NetworkInterfaceCopyMediaSubTypeOptions(available, subtype);
+                       if ((subtype_options == NULL) ||
+                           !CFArrayContainsValue(subtype_options,
+                                                 CFRangeMake(0, CFArrayGetCount(subtype_options)),
+                                                 config_options)) {
+                               SCPrint(TRUE, stdout, CFSTR("media options not valid for \"%@\"\n"), subtype);
+                               goto checked;
+                       }
+
+                       if (options == NULL) {
+                               CFDictionarySetValue(newConfiguration, kSCPropNetEthernetMediaOptions, config_options);
+                       }
+
+                       ok = TRUE;
+
+                   checked :
+
+                       if (available       != NULL)    CFRelease(available);
+                       if (subtypes        != NULL)    CFRelease(subtypes);
+                       if (subtype_options != NULL)    CFRelease(subtype_options);
+                       if (options         == NULL)    CFRelease(config_options);
+               } else {
+                       if (options != NULL) {
+                               SCPrint(TRUE, stdout, CFSTR("media type and options must both be specified\n"));
+                               return FALSE;
+                       }
+               }
+       }
+
+       return ok;
+}
+
+
+/* -------------------- */
+
+
+static selections modemDialSelections[] = {
+       { CFSTR("ignore"), &kSCValNetModemDialModeIgnoreDialTone , 0 },
+       { CFSTR("manual"), &kSCValNetModemDialModeManual         , 0 },
+       { CFSTR("wait")  , &kSCValNetModemDialModeWaitForDialTone, 0 },
+       { NULL           , NULL                                  , 0 }
+};
+
+static options modemOptions[] = {
+       { "ConnectionScript"             , "script", isString   , &kSCPropNetModemConnectionScript           , NULL, NULL                        },
+       { "DialMode"                     , "mode"  , isChooseOne, &kSCPropNetModemDialMode                   , NULL, (void *)modemDialSelections },
+       { "CallWaiting"                  , NULL    , isBoolean  , &kSCPropNetModemHoldEnabled                , NULL, NULL                        },
+       { "CallWaitingAlert"             , NULL    , isBoolean  , &kSCPropNetModemHoldCallWaitingAudibleAlert, NULL, NULL                        },
+       { "CallWaitingDisconnectOnAnswer", NULL    , isBoolean  , &kSCPropNetModemHoldDisconnectOnAnswer     , NULL, NULL                        },
+       { "DataCompression"              , NULL    , isBoolean  , &kSCPropNetModemDataCompression            , NULL, NULL                        },
+       { "ErrorCorrection"              , NULL    , isBoolean  , &kSCPropNetModemErrorCorrection            , NULL, NULL                        },
+       { "HoldReminder"                 , NULL    , isBoolean  , &kSCPropNetModemHoldReminder               , NULL, NULL                        },
+       { "HoldReminderTime"             , "time"  , isNumber   , &kSCPropNetModemHoldReminderTime           , NULL, NULL                        },
+       { "PulseDial"                    , NULL    , isBoolean  , &kSCPropNetModemPulseDial                  , NULL, NULL                        },
+       { "Speaker"                      , NULL    , isBoolean  , &kSCPropNetModemSpeaker                    , NULL, NULL                        },
+
+       { "?"                            , NULL    , isHelp     , NULL                                       , NULL,
+           "\nModem configuration commands\n\n"
+           " set interface [ConnectionScript connection-script]\n"
+           " set interface [CallWaiting {enable|disable}]\n"
+           " set interface [CallWaitingAlert {enable|disable}]\n"
+           " set interface [CallWaitingDisconnectOnAnswer {enable|disable}]\n"
+           " set interface [DialMode {ignore|wait}]\n"
+           " set interface [DataCompression {enable|disable}]\n"
+           " set interface [ErrorCorrection {enable|disable}]\n"
+           " set interface [HoldReminder {enable|disable}]\n"
+           " set interface [HoldReminderTime n]\n"
+           " set interface [PulseDial {enable|disable}]\n"
+           " set interface [Speaker {enable|disable}]"
+       }
+};
+#define        N_MODEM_OPTIONS (sizeof(modemOptions) / sizeof(modemOptions[0]))
+
+
+static Boolean
+set_interface_modem(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
+{
+       Boolean ok;
+
+       ok = _process_options(modemOptions, N_MODEM_OPTIONS, argc, argv, newConfiguration);
+       return ok;
+}
+
+
+/* -------------------- */
+
+
+static int
+__doPPPAuthPW(CFStringRef key, const char *description, void *info, int argc, char **argv, CFMutableDictionaryRef newConfiguration)
+{
+       if (argc < 1) {
+               SCPrint(TRUE, stdout, CFSTR("PPP password not specified\n"));
+               return -1;
+       }
+
+       if (strlen(argv[0]) > 0) {
+               CFStringRef     encryptionType;
+
+               encryptionType = CFDictionaryGetValue(newConfiguration, kSCPropNetPPPAuthPasswordEncryption);
+               if (encryptionType == NULL) {
+                       CFIndex                 n;
+                       CFMutableDataRef        pw;
+                       CFStringRef             str;
+
+                       str = CFStringCreateWithCString(NULL, argv[0], kCFStringEncodingUTF8);
+                       n = CFStringGetLength(str);
+                       pw = CFDataCreateMutable(NULL, n * sizeof(UniChar));
+                       CFDataSetLength(pw, n * sizeof(UniChar));
+                       CFStringGetCharacters(str,
+                                             CFRangeMake(0, n),
+                                             (UniChar *)CFDataGetMutableBytePtr(pw));
+                       CFRelease(str);
+
+                       CFDictionarySetValue(newConfiguration, key, pw);
+                       CFRelease(pw);
+               } else {
+                       SCPrint(TRUE, stdout, CFSTR("PPP password type \"%@\" not supported\n"), encryptionType);
+                       return -1;
+               }
+       } else {
+               CFDictionaryRemoveValue(newConfiguration, key);
+       }
+
+       return 1;
+}
+
+
+static int
+__doPPPAuthPWType(CFStringRef key, const char *description, void *info, int argc, char **argv, CFMutableDictionaryRef newConfiguration)
+{
+       if (argc < 1) {
+               SCPrint(TRUE, stdout, CFSTR("PPP password type mode not specified\n"));
+               return -1;
+       }
+
+       if (strlen(argv[0]) > 0) {
+               if (strcasecmp(argv[0], "keychain") == 0) {
+                       CFDictionarySetValue(newConfiguration, key, kSCValNetPPPAuthPasswordEncryptionKeychain);
+               } else {
+                       SCPrint(TRUE, stdout, CFSTR("invalid password type\n"));
+                       return -1;
+               }
+       } else {
+               CFDictionaryRemoveValue(newConfiguration, key);
+       }
+
+       // encryption type changed, reset password
+       CFDictionaryRemoveValue(newConfiguration, kSCPropNetPPPAuthPassword);
+
+       return 1;
+}
+
+
+static selections authPromptSelections[] = {
+       { CFSTR("before"), &kSCValNetPPPAuthPromptBefore, 0 },
+       { CFSTR("after") , &kSCValNetPPPAuthPromptAfter , 0 },
+       { NULL    , NULL                                , 0 }
+};
+
+
+static selections authProtocolSelections[] = {
+       { CFSTR("CHAP")   , &kSCValNetPPPAuthProtocolCHAP   , 0 },
+       { CFSTR("EAP")    , &kSCValNetPPPAuthProtocolEAP    , 0 },
+       { CFSTR("MSCHAP1"), &kSCValNetPPPAuthProtocolMSCHAP1, 0 },
+       { CFSTR("MSCHAP2"), &kSCValNetPPPAuthProtocolMSCHAP2, 0 },
+       { CFSTR("PAP")    , &kSCValNetPPPAuthProtocolPAP    , 0 },
+       { NULL            , NULL                            , 0 }
+};
+
+
+static options pppOptions[] = {
+       { "ACSP"                      , NULL          , isBoolean        , &kSCPropNetPPPACSPEnabled               , NULL             , NULL                           },
+       { "ConnectTime"               , "?time"       , isNumber         , &kSCPropNetPPPConnectTime               , NULL             , NULL                           },
+       { "DialOnDemand"              , NULL          , isBoolean        , &kSCPropNetPPPDialOnDemand              , NULL             , NULL                           },
+       { "DisconnectOnFastUserSwitch", NULL          , isBoolean        , &kSCPropNetPPPDisconnectOnFastUserSwitch, NULL             , NULL                           },
+       { "DisconnectOnIdle"          , NULL          , isBoolean        , &kSCPropNetPPPDisconnectOnIdle          , NULL             , NULL                           },
+       { "DisconnectOnIdleTimer"     , "timeout"     , isNumber         , &kSCPropNetPPPDisconnectOnIdleTimer     , NULL             , NULL                           },
+       { "DisconnectOnLogout"        , NULL          , isBoolean        , &kSCPropNetPPPDisconnectOnLogout        , NULL             , NULL                           },
+       { "DisconnectOnSleep"         , NULL          , isBoolean        , &kSCPropNetPPPDisconnectOnSleep         , NULL             , NULL                           },
+       { "DisconnectTime"            , "?time"       , isNumber         , &kSCPropNetPPPDisconnectTime            , NULL             , NULL                           },
+       { "IdleReminder"              , NULL          , isBoolean        , &kSCPropNetPPPIdleReminder              , NULL             , NULL                           },
+       { "IdleReminderTimer"         , "time"        , isNumber         , &kSCPropNetPPPIdleReminderTimer         , NULL             , NULL                           },
+       { "Logfile"                   , "path"        , isString         , &kSCPropNetPPPLogfile                   , NULL             , NULL                           },
+       { "Plugins"                   , "plugin"      , isStringArray    , &kSCPropNetPPPPlugins                   , NULL             , NULL                           },
+       { "RetryConnectTime"          , "time"        , isNumber         , &kSCPropNetPPPRetryConnectTime          , NULL             , NULL                           },
+       { "SessionTimer"              , "time"        , isNumber         , &kSCPropNetPPPSessionTimer              , NULL             , NULL                           },
+       { "UseSessionTimer"           , NULL          , isBoolean        , &kSCPropNetPPPUseSessionTimer           , NULL             , NULL                           },
+       { "VerboseLogging"            , NULL          , isBoolean        , &kSCPropNetPPPVerboseLogging            , NULL             , NULL                           },
+
+       // --- Auth: ---
+       { "AuthEAPPlugins"            , "plugin"      , isStringArray    , &kSCPropNetPPPAuthEAPPlugins            , NULL             , NULL                           },
+       { "AuthName"                  , "account"     , isString         , &kSCPropNetPPPAuthName                  , NULL             , NULL                           },
+       {   "Account"                 , "account"     , isString         , &kSCPropNetPPPAuthName                  , NULL             , NULL                           },
+       { "AuthPassword"              , "password"    , isOther          , &kSCPropNetPPPAuthPassword              , __doPPPAuthPW    , NULL                           },
+       {   "Password"                , "password"    , isOther          , &kSCPropNetPPPAuthPassword              , __doPPPAuthPW    , NULL                           },
+       { "AuthPasswordEncryption"    , "type"        , isOther          , &kSCPropNetPPPAuthPasswordEncryption    , __doPPPAuthPWType, NULL                           },
+       { "AuthPrompt"                , "before/after", isChooseOne      , &kSCPropNetPPPAuthPrompt                , NULL             , (void *)authPromptSelections   },
+       { "AuthProtocol"              , "protocol"    , isChooseMultiple , &kSCPropNetPPPAuthProtocol              , NULL             , (void *)authProtocolSelections },
+
+       // --- Comm: ---
+       { "CommRemoteAddress"         , "phone#"      , isString         , &kSCPropNetPPPCommRemoteAddress         , NULL             , NULL                           },
+       { "CommAlternateRemoteAddress", "phone#"      , isString         , &kSCPropNetPPPCommAlternateRemoteAddress, NULL             , NULL                           },
+       { "CommConnectDelay"          , "time"        , isNumber         , &kSCPropNetPPPCommConnectDelay          , NULL             , NULL                           },
+       { "CommDisplayTerminalWindow" , NULL          , isBoolean        , &kSCPropNetPPPCommDisplayTerminalWindow , NULL             , NULL                           },
+       { "CommRedialCount"           , "retry count" , isNumber         , &kSCPropNetPPPCommRedialCount           , NULL             , NULL                           },
+       { "CommRedialEnabled"         , NULL          , isBoolean        , &kSCPropNetPPPCommRedialEnabled         , NULL             , NULL                           },
+       { "CommRedialInterval"        , "retry delay" , isNumber         , &kSCPropNetPPPCommRedialInterval        , NULL             , NULL                           },
+       { "CommTerminalScript"        , "script"      , isString         , &kSCPropNetPPPCommTerminalScript        , NULL             , NULL                           },
+       { "CommUseTerminalScript"     , NULL          , isBoolean        , &kSCPropNetPPPCommUseTerminalScript     , NULL             , NULL                           },
+
+       // --- CCP: ---
+       { "CCPEnabled"                , NULL          , isBoolean        , &kSCPropNetPPPCCPEnabled                , NULL             , NULL                           },
+       { "CCPMPPE40Enabled"          , NULL          , isBoolean        , &kSCPropNetPPPCCPMPPE40Enabled          , NULL             , NULL                           },
+       { "CCPMPPE128Enabled"         , NULL          , isBoolean        , &kSCPropNetPPPCCPMPPE128Enabled         , NULL             , NULL                           },
+
+       // --- IPCP: ---
+       { "IPCPCompressionVJ"         , NULL          , isBoolean        , &kSCPropNetPPPIPCPCompressionVJ         , NULL             , NULL                           },
+       { "IPCPUsePeerDNS"            , NULL          , isBoolean        , &kSCPropNetPPPIPCPUsePeerDNS            , NULL             , NULL                           },
+
+       // --- LCP: ---
+       { "LCPEchoEnabled"            , NULL          , isBoolean        , &kSCPropNetPPPLCPEchoEnabled            , NULL             , NULL                           },
+       { "LCPEchoFailure"            , NULL          , isNumber         , &kSCPropNetPPPLCPEchoFailure            , NULL             , NULL                           },
+       { "LCPEchoInterval"           , NULL          , isNumber         , &kSCPropNetPPPLCPEchoInterval           , NULL             , NULL                           },
+       { "LCPCompressionACField"     , NULL          , isBoolean        , &kSCPropNetPPPLCPCompressionACField     , NULL             , NULL                           },
+       { "LCPCompressionPField"      , NULL          , isBoolean        , &kSCPropNetPPPLCPCompressionPField      , NULL             , NULL                           },
+       { "LCPMRU"                    , NULL          , isNumber         , &kSCPropNetPPPLCPMRU                    , NULL             , NULL                           },
+       { "LCPMTU"                    , NULL          , isNumber         , &kSCPropNetPPPLCPMTU                    , NULL             , NULL                           },
+       { "LCPReceiveACCM"            , NULL          , isNumber         , &kSCPropNetPPPLCPReceiveACCM            , NULL             , NULL                           },
+       { "LCPTransmitACCM"           , NULL          , isNumber         , &kSCPropNetPPPLCPTransmitACCM           , NULL             , NULL                           },
+
+       // --- Help ---
+       { "?"                         , NULL          , isHelp           , NULL                                    , NULL             ,
+           "\nPPP configuration commands\n\n"
+           " set interface [Account account]\n"
+           " set interface [Password password]\n"
+           " set interface [Number telephone-number]\n"
+           " set interface [AlternateNumber telephone-number]\n"
+           " set interface [IdleReminder {enable|disable}]\n"
+           " set interface [IdleReminderTimer time-in-seconds]\n"
+           " set interface [DisconnectOnIdle {enable|disable}]\n"
+           " set interface [DisconnectOnIdleTimer time-in-seconds]\n"
+           " set interface [DisconnectOnLogout {enable|disable}]"
+       }
+};
+#define        N_PPP_OPTIONS   (sizeof(pppOptions) / sizeof(pppOptions[0]))
+
+
+static Boolean
+set_interface_ppp(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
+{
+       Boolean ok;
+
+       ok = _process_options(pppOptions, N_PPP_OPTIONS, argc, argv, newConfiguration);
+       return ok;
+}
+
+
+/* -------------------- */
+
+
+static Boolean
+set_interface_vlan(int argc, char **argv, CFMutableDictionaryRef newConfiguration)
+{
+// xxxxx ("device", "tag")
+SCPrint(TRUE, stdout, CFSTR("vlan interface management not yet supported\n"));
+       return FALSE;
+}
+
+
+/* -------------------- */
+
+
+__private_extern__
+void
+set_interface(int argc, char **argv)
+{
+       CFDictionaryRef         configuration;
+       CFStringRef             interfaceType;
+       CFMutableDictionaryRef  newConfiguration        = NULL;
+       Boolean                 ok                      = FALSE;
+
+       if (net_interface == NULL) {
+               SCPrint(TRUE, stdout, CFSTR("interface not selected\n"));
+               return;
+       }
+
+       if (argc < 1) {
+               SCPrint(TRUE, stdout, CFSTR("set what?\n"));
+               return;
+       }
+
+       configuration = SCNetworkInterfaceGetConfiguration(net_interface);
+       if (configuration == NULL) {
+               newConfiguration = CFDictionaryCreateMutable(NULL,
+                                                            0,
+                                                            &kCFTypeDictionaryKeyCallBacks,
+                                                            &kCFTypeDictionaryValueCallBacks);
+       } else {
+               newConfiguration = CFDictionaryCreateMutableCopy(NULL, 0, configuration);
+               CFDictionaryRemoveValue(newConfiguration, kSCResvInactive);
+       }
+
+       interfaceType = SCNetworkInterfaceGetInterfaceType(net_interface);
+
+       if (CFEqual(interfaceType, kSCNetworkInterfaceTypeBond)) {
+               ok = set_interface_bond(argc, argv, newConfiguration);
+       } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeEthernet)) {
+               ok = set_interface_ethernet(argc, argv, newConfiguration);
+       } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeModem)) {
+               ok = set_interface_modem(argc, argv, newConfiguration);
+       } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeIEEE80211)) {
+               ok = set_interface_airport(argc, argv, newConfiguration);
+       } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) {
+               ok = set_interface_ppp(argc, argv, newConfiguration);
+       } else if (CFEqual(interfaceType, kSCNetworkInterfaceTypeVLAN)) {
+               ok = set_interface_vlan(argc, argv, newConfiguration);
+       } else {
+               SCPrint(TRUE, stdout, CFSTR("this interfaces configuration cannot be changed\n"));
+       }
+
+       if (!ok) {
+               goto done;
+       }
+
+       if (((configuration == NULL) && (CFDictionaryGetCount(newConfiguration) > 0)) ||
+           ((configuration != NULL) && !CFEqual(configuration, newConfiguration))) {
+               if (!SCNetworkInterfaceSetConfiguration(net_interface, newConfiguration)) {
+                       if (SCError() == kSCStatusNoKey) {
+                               SCPrint(TRUE, stdout, CFSTR("could not update per-service interface configuration\n"));
+                       } else {
+                               SCPrint(TRUE, stdout, CFSTR("%s\n"), SCErrorString(SCError()));
+                       }
+                       goto done;
+               }
+
+               net_changed = TRUE;
+       }
+
+    done :
+
+       if (newConfiguration != NULL) CFRelease(newConfiguration);
+       return;
+}
+
+
+/* -------------------- */
+
+
+__private_extern__
+void
+show_interface(int argc, char **argv)
+{
+       SCNetworkInterfaceRef   interface;
+
+       if (argc == 1) {
+               interface = _find_interface(argv[0]);
+       } else {
+               if (net_interface != NULL) {
+                       interface = net_interface;
+               } else {
+                       SCPrint(TRUE, stdout, CFSTR("interface not selected\n"));
+                       return;
+               }
+       }
+
+       if (interface != NULL) {
+               _show_interface(interface, CFSTR(""), TRUE);
+       }
+
+       return;
+}
+
+
+/* -------------------- */
+
+
+__private_extern__
+CFStringRef
+_interface_description(SCNetworkInterfaceRef interface)
+{
+       CFMutableStringRef      description;
+       CFStringRef             if_bsd_name;
+       CFStringRef             if_type;
+
+       description = CFStringCreateMutable(NULL, 0);
+
+       if_type = SCNetworkInterfaceGetInterfaceType(interface);
+       CFStringAppend(description, if_type);
+
+       if_bsd_name = SCNetworkInterfaceGetBSDName(interface);
+       if (if_bsd_name != NULL) {
+               CFStringAppendFormat(description, NULL, CFSTR(" (%@)"), if_bsd_name);
+       }
+
+       interface = SCNetworkInterfaceGetInterface(interface);
+       while ((interface != NULL) &&
+              !CFEqual(interface, kSCNetworkInterfaceIPv4)) {
+               CFStringRef     childDescription;
+
+               childDescription = _interface_description(interface);
+               CFStringAppendFormat(description, NULL, CFSTR(" / %@"), childDescription);
+               CFRelease(childDescription);
+
+               interface = SCNetworkInterfaceGetInterface(interface);
+       }
+
+       return description;
+}