* Copyright 2006-2015, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Oliver Tappe, zooey@hirschkaefer.de
* Atis Elsts, the.kfx@gmail.com
*/
#include <errno.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sockio.h>
#include <unistd.h>
#include <Message.h>
#include <Messenger.h>
#include <NetworkDevice.h>
#include <NetworkInterface.h>
#include <NetworkRoster.h>
#include <NetServer.h>
extern "C" {
# include <freebsd_network/compat/sys/cdefs.h>
# include <freebsd_network/compat/sys/ioccom.h>
# include <net80211/ieee80211_ioctl.h>
}
#include "MediaTypes.h"
extern const char* __progname;
const char* kProgramName = __progname;
enum preferred_output_format {
PREFER_OUTPUT_MASK,
PREFER_OUTPUT_PREFIX_LENGTH,
};
struct address_family {
int family;
const char* name;
const char* identifiers[4];
preferred_output_format preferred_format;
};
static const address_family kFamilies[] = {
{
AF_INET,
"inet",
{"AF_INET", "inet", "ipv4", NULL},
PREFER_OUTPUT_MASK
},
{
AF_INET6,
"inet6",
{"AF_INET6", "inet6", "ipv6", NULL},
PREFER_OUTPUT_PREFIX_LENGTH
},
{ -1, NULL, {NULL}, PREFER_OUTPUT_MASK }
};
static void
usage(int status)
{
printf("usage: %s [<interface> [<address family>] [<address> [<mask>] | "
"auto-config] [<option/flags>...]]\n"
" %s --delete <interface> [...]\n"
" %s <interface> [scan|join|leave] [<network> "
"[<password>]]\n\n"
"Where <option> can be the following:\n"
" netmask <addr> - networking subnet mask\n"
" prefixlen <number> - subnet mask length in bits\n"
" broadcast <addr> - set broadcast address\n"
" peer <addr> - ppp-peer address\n"
" mtu <bytes> - maximal transfer unit\n"
" metric <number> - metric number to use (defaults to 0)\n"
" media <media> - media type to use (defaults to auto)\n",
kProgramName, kProgramName, kProgramName);
for (int32 i = 0; const char* type = get_media_type_name(i); i++) {
printf("For %s <media> can be one of: ", type);
for (int32 j = 0; const char* subType = get_media_subtype_name(i, j);
j++) {
printf("%s ", subType);
}
printf("\n");
}
printf("And <flags> can be: up, down, [-]promisc, [-]allmulti, [-]bcast, "
"[-]ht, loopback\n"
"If you specify \"auto-config\" instead of an address, it will be "
"configured automatically.\n\n"
"Example:\n"
"\t%s loop 127.0.0.1 255.0.0.0 up\n",
kProgramName);
exit(status);
}
int
get_address_family(const char* argument)
{
for (int32 i = 0; kFamilies[i].family >= 0; i++) {
for (int32 j = 0; kFamilies[i].identifiers[j]; j++) {
if (!strcmp(argument, kFamilies[i].identifiers[j])) {
return kFamilies[i].family;
}
}
}
return AF_UNSPEC;
}
static const address_family*
address_family_for(int family)
{
for (int32 i = 0; kFamilies[i].family >= 0; i++) {
if (kFamilies[i].family == family)
return &kFamilies[i];
}
return &kFamilies[0];
}
If \a family is \c AF_UNSPEC, \a family will be overwritten with the family
of the successfully parsed address.
*/
bool
parse_address(int& family, const char* argument, BNetworkAddress& address)
{
if (argument == NULL)
return false;
status_t status = address.SetTo(family, argument, (uint16)0,
B_NO_ADDRESS_RESOLUTION);
if (status != B_OK)
return false;
if (family == AF_UNSPEC) {
bool supported = false;
for (int32 i = 0; kFamilies[i].family >= 0; i++) {
if (kFamilies[i].family == address.Family()) {
supported = true;
break;
}
}
if (!supported)
return false;
family = address.Family();
}
return true;
}
bool
prefix_length_to_mask(int family, const char* argument, BNetworkAddress& mask)
{
char *end;
uint32 prefixLength = strtoul(argument, &end, 10);
if (end == argument)
return false;
return mask.SetToMask(family, prefixLength) == B_OK;
}
BString
to_string(const BNetworkAddress& address)
{
if (address.IsEmpty())
return "--";
return address.ToString();
}
const char*
get_key_mode(uint32 mode)
{
if ((mode & B_KEY_MODE_WPS) != 0)
return "WPS";
if ((mode & B_KEY_MODE_PSK_SHA256) != 0)
return "PSK-SHA256";
if ((mode & B_KEY_MODE_IEEE802_1X_SHA256) != 0)
return "IEEE 802.1x-SHA256";
if ((mode & B_KEY_MODE_FT_PSK) != 0)
return "FT-PSK";
if ((mode & B_KEY_MODE_FT_IEEE802_1X) != 0)
return "FT-IEEE 802.1x";
if ((mode & B_KEY_MODE_NONE) != 0)
return "-";
if ((mode & B_KEY_MODE_PSK) != 0)
return "PSK";
if ((mode & B_KEY_MODE_IEEE802_1X) != 0)
return "IEEE 802.1x";
return "";
}
const char*
get_cipher(uint32 cipher)
{
if ((cipher & B_NETWORK_CIPHER_AES_128_CMAC) != 0)
return "AES-128-CMAC";
if ((cipher & B_NETWORK_CIPHER_CCMP) != 0)
return "CCMP";
if ((cipher & B_NETWORK_CIPHER_TKIP) != 0)
return "TKIP";
if ((cipher & B_NETWORK_CIPHER_WEP_104) != 0)
return "WEP-104";
if ((cipher & B_NETWORK_CIPHER_WEP_40) != 0)
return "WEP-40";
return "";
}
const char*
get_authentication_mode(uint32 mode, uint32 flags)
{
switch (mode) {
default:
case B_NETWORK_AUTHENTICATION_NONE:
if ((flags & B_NETWORK_IS_ENCRYPTED) != 0)
return "(encrypted)";
return "-";
case B_NETWORK_AUTHENTICATION_WEP:
return "WEP";
case B_NETWORK_AUTHENTICATION_WPA:
return "WPA";
case B_NETWORK_AUTHENTICATION_WPA2:
return "WPA2";
}
}
void
show_wireless_network_header(bool verbose)
{
printf("%-32s %-20s %s %s\n", "name", "address", "signal", "auth");
}
void
show_wireless_network(const wireless_network& network, bool verbose)
{
printf("%-32s %-20s %6u %s\n", network.name,
network.address.ToString().String(),
network.signal_strength / 2,
get_authentication_mode(network.authentication_mode, network.flags));
}
bool
configure_wireless(const char* name, char* const* args, int32 argCount)
{
enum {
NONE,
SCAN,
LIST,
JOIN,
LEAVE,
CONTROL
} mode = NONE;
int controlOption = -1;
int controlValue = -1;
if (!strcmp(args[0], "scan"))
mode = SCAN;
else if (!strcmp(args[0], "list"))
mode = LIST;
else if (!strcmp(args[0], "join"))
mode = JOIN;
else if (!strcmp(args[0], "leave"))
mode = LEAVE;
else if (!strcmp(args[0], "ht")) {
mode = CONTROL;
controlOption = IEEE80211_IOC_HTCONF;
controlValue = 3;
} else if (!strcmp(args[0], "-ht")) {
mode = CONTROL;
controlOption = IEEE80211_IOC_HTCONF;
controlValue = 0;
}
if (mode == NONE)
return false;
BNetworkDevice device(name);
if (!device.Exists()) {
fprintf(stderr, "%s: \"%s\" does not exist!\n", kProgramName, name);
exit(1);
}
if (!device.IsWireless()) {
fprintf(stderr, "%s: \"%s\" is not a WLAN device!\n", kProgramName,
name);
exit(1);
}
args++;
argCount--;
switch (mode) {
case SCAN:
{
status_t status = device.Scan(true, true);
if (status != B_OK) {
fprintf(stderr, "%s: Scan on \"%s\" failed: %s\n", kProgramName,
name, strerror(status));
exit(1);
}
}
case LIST:
{
bool verbose = false;
if (argCount > 0 && !strcmp(args[0], "-v")) {
verbose = true;
args++;
argCount--;
}
show_wireless_network_header(verbose);
if (argCount > 0) {
for (int32 i = 0; i < argCount; i++) {
wireless_network network;
BNetworkAddress link;
status_t status;
if (link.SetTo(AF_LINK, args[i]) == B_OK)
status = device.GetNetwork(link, network);
else
status = device.GetNetwork(args[i], network);
if (status != B_OK) {
fprintf(stderr, "%s: Getting network failed: %s\n",
kProgramName, strerror(status));
} else
show_wireless_network(network, verbose);
}
} else {
uint32 networksCount = 0;
wireless_network* networks = NULL;
status_t status = device.GetNetworks(networks, networksCount);
if (status != B_OK) {
fprintf(stderr, "%s: Getting networks failed: %s\n",
kProgramName, strerror(status));
}
for (uint32 i = 0; i < networksCount; i++)
show_wireless_network(networks[i], verbose);
delete[] networks;
}
break;
}
case JOIN:
{
if (argCount > 2) {
fprintf(stderr, "usage: %s %s join <network> [<password>]\n",
kProgramName, name);
exit(1);
}
const char* password = NULL;
if (argCount == 2)
password = args[1];
BNetworkAddress link;
status_t status;
if (link.SetTo(AF_LINK, args[0]) == B_OK)
status = device.JoinNetwork(link, password);
else
status = device.JoinNetwork(args[0], password);
if (status != B_OK) {
fprintf(stderr, "%s: Joining network failed: %s\n",
kProgramName, strerror(status));
exit(1);
}
break;
}
case LEAVE:
{
if (argCount != 1) {
fprintf(stderr, "usage: %s %s leave <network>\n", kProgramName,
name);
exit(1);
}
BNetworkAddress link;
status_t status;
if (link.SetTo(AF_LINK, args[0]) == B_OK)
status = device.LeaveNetwork(link);
else
status = device.LeaveNetwork(args[0]);
if (status != B_OK) {
fprintf(stderr, "%s: Leaving network failed: %s\n",
kProgramName, strerror(status));
exit(1);
}
break;
}
case CONTROL:
{
ieee80211req request;
memset(&request, 0, sizeof(request));
request.i_type = controlOption;
request.i_val = controlValue;
status_t status = device.Control(SIOCS80211, &request);
if (status != B_OK) {
fprintf(stderr, "%s: Control failed: %s\n", kProgramName,
strerror(status));
exit(1);
}
break;
}
case NONE:
break;
}
return true;
}
void
list_interface_addresses(BNetworkInterface& interface, uint32 flags)
{
int32 count = interface.CountAddresses();
for (int32 i = 0; i < count; i++) {
BNetworkInterfaceAddress address;
if (interface.GetAddressAt(i, address) != B_OK)
break;
const address_family* family
= address_family_for(address.Address().Family());
printf("\t%s addr: %s", family->name,
to_string(address.Address()).String());
if ((flags & IFF_BROADCAST) != 0)
printf(", Bcast: %s", to_string(address.Broadcast()).String());
switch (family->preferred_format) {
case PREFER_OUTPUT_MASK:
printf(", Mask: %s", to_string(address.Mask()).String());
break;
case PREFER_OUTPUT_PREFIX_LENGTH:
printf(", Prefix Length: %zu", address.Mask().PrefixLength());
break;
}
putchar('\n');
}
}
bool
list_interface(const char* name)
{
printf("%s", name);
size_t length = strlen(name);
if (length < 8)
putchar('\t');
else
printf("\n\t");
BNetworkInterface interface(name);
if (!interface.Exists()) {
printf("Interface not found!\n");
return false;
}
BNetworkAddress linkAddress;
status_t status = interface.GetHardwareAddress(linkAddress);
if (status == B_OK) {
const char *type = "unknown";
switch (linkAddress.LinkLevelType()) {
case IFT_ETHER:
type = "Ethernet";
break;
case IFT_LOOP:
type = "Local Loopback";
break;
case IFT_MODEM:
type = "Modem";
break;
}
BString address = linkAddress.ToString();
if (address.Length() == 0)
address = "none";
printf("Hardware type: %s, Address: %s\n", type, address.String());
} else
printf("No link level: %s\n", strerror(status));
int media = interface.Media();
if ((media & IFM_ACTIVE) != 0) {
const char* type = media_type_to_string(media);
if (type != NULL)
printf("\tMedia type: %s\n", type);
}
BNetworkDevice device(name);
if (device.IsWireless()) {
wireless_network network;
bool first = true;
uint32 cookie = 0;
while (device.GetNextAssociatedNetwork(cookie, network) == B_OK) {
if (first) {
printf("\tNetwork: ");
first = false;
} else
printf("\t\t");
printf("%s, Address: %s, %s", network.name,
network.address.ToString().String(),
get_authentication_mode(network.authentication_mode,
network.flags));
const char* keyMode = get_key_mode(network.key_mode);
if (keyMode != NULL)
printf(", %s/%s", keyMode, get_cipher(network.cipher));
putchar('\n');
}
}
uint32 flags = interface.Flags();
list_interface_addresses(interface, flags);
printf("\tMTU: %" B_PRId32 ", Metric: %" B_PRId32, interface.MTU(),
interface.Metric());
if (flags != 0) {
const struct {
int value;
const char *name;
} kFlags[] = {
{IFF_UP, "up"},
{IFF_NOARP, "noarp"},
{IFF_BROADCAST, "broadcast"},
{IFF_LOOPBACK, "loopback"},
{IFF_PROMISC, "promiscuous"},
{IFF_ALLMULTI, "allmulti"},
{IFF_AUTOUP, "autoup"},
{IFF_LINK, "link"},
{IFF_AUTO_CONFIGURED, "auto-configured"},
{IFF_CONFIGURING, "configuring"},
};
bool first = true;
for (uint32 i = 0; i < sizeof(kFlags) / sizeof(kFlags[0]); i++) {
if ((flags & kFlags[i].value) != 0) {
if (first) {
printf(",");
first = false;
}
putchar(' ');
fputs(kFlags[i].name, stdout);
}
}
}
putchar('\n');
ifreq_stats stats;
if (interface.GetStats(stats) == B_OK) {
printf("\tReceive: %d packets, %d errors, %" B_PRId64 " bytes, %d mcasts, %d "
"dropped\n", stats.receive.packets, stats.receive.errors,
stats.receive.bytes, stats.receive.multicast_packets,
stats.receive.dropped);
printf("\tTransmit: %d packets, %d errors, %" B_PRId64 " bytes, %d mcasts, %d "
"dropped\n", stats.send.packets, stats.send.errors,
stats.send.bytes, stats.send.multicast_packets, stats.send.dropped);
printf("\tCollisions: %d\n", stats.collisions);
}
putchar('\n');
return true;
}
void
list_interfaces(const char* name)
{
if (name != NULL) {
list_interface(name);
return;
}
BNetworkRoster& roster = BNetworkRoster::Default();
BNetworkInterface interface;
uint32 cookie = 0;
while (roster.GetNextInterface(&cookie, interface) == B_OK) {
list_interface(interface.Name());
}
}
addresses from the interface named \a name.
If there are no arguments, it will remove the complete interface with all
of its addresses.
*/
void
delete_interface(const char* name, char* const* args, int32 argCount)
{
BNetworkInterface interface(name);
for (int32 i = 0; i < argCount; i++) {
int family = get_address_family(args[i]);
if (family != AF_UNSPEC)
i++;
BNetworkAddress address;
if (!parse_address(family, args[i], address)) {
fprintf(stderr, "%s: Could not parse address \"%s\".\n",
kProgramName, args[i]);
exit(1);
}
status_t status = interface.RemoveAddress(address);
if (status != B_OK) {
fprintf(stderr, "%s: Could not delete address %s from interface %s:"
" %s\n", kProgramName, args[i], name, strerror(status));
}
}
if (argCount == 0) {
BNetworkRoster& roster = BNetworkRoster::Default();
status_t status = roster.RemoveInterface(interface);
if (status != B_OK) {
fprintf(stderr, "%s: Could not delete interface %s: %s\n",
kProgramName, name, strerror(errno));
}
}
}
void
configure_interface(const char* name, char* const* args, int32 argCount)
{
int32 i = 0;
int family = get_address_family(args[i]);
if (family != AF_UNSPEC) {
i++;
int socket = ::socket(family, SOCK_DGRAM, 0);
if (socket < 0) {
fprintf(stderr, "%s: The requested address family is not available.\n",
kProgramName);
exit(1);
}
close(socket);
}
BNetworkAddress address;
BNetworkAddress mask;
if (parse_address(family, args[i], address)) {
i++;
if (parse_address(family, args[i], mask))
i++;
}
BNetworkInterface interface(name);
if (!interface.Exists()) {
BNetworkRoster& roster = BNetworkRoster::Default();
status_t status = roster.AddInterface(interface);
if (status != B_OK) {
fprintf(stderr, "%s: Could not add interface: %s\n", kProgramName,
strerror(status));
exit(1);
}
}
BNetworkAddress broadcast;
BNetworkAddress peer;
int mtu = -1, metric = -1, media = -1;
int addFlags = 0, currentFlags = 0, removeFlags = 0;
bool doAutoConfig = false;
while (i < argCount) {
if (!strcmp(args[i], "peer")) {
if (!parse_address(family, args[i + 1], peer)) {
fprintf(stderr, "%s: Option 'peer' needs valid address "
"parameter\n", kProgramName);
exit(1);
}
i++;
} else if (!strcmp(args[i], "nm") || !strcmp(args[i], "netmask")) {
if (!mask.IsEmpty()) {
fprintf(stderr, "%s: Netmask or prefix length is specified "
"twice\n", kProgramName);
exit(1);
}
if (!parse_address(family, args[i + 1], mask)) {
fprintf(stderr, "%s: Option 'netmask' needs valid address "
"parameter\n", kProgramName);
exit(1);
}
i++;
} else if (!strcmp(args[i], "prefixlen") || !strcmp(args[i], "plen")
|| !strcmp(args[i], "prefix-length")) {
if (!mask.IsEmpty()) {
fprintf(stderr, "%s: Netmask or prefix length is specified "
"twice\n", kProgramName);
exit(1);
}
if (family == AF_UNSPEC)
family = AF_INET;
if (!prefix_length_to_mask(family, args[i + 1], mask)) {
fprintf(stderr, "%s: Option 'prefix-length %s' is invalid for "
"this address family\n", kProgramName, args[i + 1]);
exit(1);
}
i++;
} else if (!strcmp(args[i], "bc") || !strcmp(args[i], "broadcast")) {
if (!broadcast.IsEmpty()) {
fprintf(stderr, "%s: broadcast address is specified twice\n",
kProgramName);
exit(1);
}
if (!parse_address(family, args[i + 1], broadcast)) {
fprintf(stderr, "%s: Option 'broadcast' needs valid address "
"parameter\n", kProgramName);
exit(1);
}
addFlags |= IFF_BROADCAST;
i++;
} else if (!strcmp(args[i], "mtu")) {
mtu = args[i + 1] ? strtol(args[i + 1], NULL, 0) : 0;
if (mtu <= 500) {
fprintf(stderr, "%s: Option 'mtu' expected valid max transfer "
"unit size\n", kProgramName);
exit(1);
}
i++;
} else if (!strcmp(args[i], "metric")) {
if (i + 1 >= argCount) {
fprintf(stderr, "%s: Option 'metric' expected parameter\n",
kProgramName);
exit(1);
}
metric = strtol(args[i + 1], NULL, 0);
i++;
} else if (!strcmp(args[i], "media")) {
media = interface.Media();
if (media < 0) {
fprintf(stderr, "%s: Unable to detect media type\n",
kProgramName);
exit(1);
}
if (i + 1 >= argCount) {
fprintf(stderr, "%s: Option 'media' expected parameter\n",
kProgramName);
exit(1);
}
if (!media_parse_subtype(args[i + 1], IFM_TYPE(media), &media)) {
fprintf(stderr, "%s: Invalid parameter for option 'media': "
"'%s'\n", kProgramName, args[i + 1]);
exit(1);
}
i++;
} else if (!strcmp(args[i], "up") || !strcmp(args[i], "-down")) {
addFlags |= IFF_UP;
} else if (!strcmp(args[i], "down") || !strcmp(args[i], "-up")) {
removeFlags |= IFF_UP;
} else if (!strcmp(args[i], "bcast")) {
addFlags |= IFF_BROADCAST;
} else if (!strcmp(args[i], "-bcast")) {
removeFlags |= IFF_BROADCAST;
} else if (!strcmp(args[i], "promisc")) {
addFlags |= IFF_PROMISC;
} else if (!strcmp(args[i], "-promisc")) {
removeFlags |= IFF_PROMISC;
} else if (!strcmp(args[i], "allmulti")) {
addFlags |= IFF_ALLMULTI;
} else if (!strcmp(args[i], "-allmulti")) {
removeFlags |= IFF_ALLMULTI;
} else if (!strcmp(args[i], "loopback")) {
addFlags |= IFF_LOOPBACK;
} else if (!strcmp(args[i], "auto-config")) {
doAutoConfig = true;
} else
usage(1);
i++;
}
if ((addFlags & removeFlags) != 0) {
fprintf(stderr, "%s: Contradicting flags specified\n", kProgramName);
exit(1);
}
if (doAutoConfig && (!address.IsEmpty() || !mask.IsEmpty()
|| !broadcast.IsEmpty() || !peer.IsEmpty())) {
fprintf(stderr, "%s: Contradicting changes specified\n", kProgramName);
exit(1);
}
if (!address.IsEmpty() || !mask.IsEmpty() || !broadcast.IsEmpty()) {
BNetworkInterfaceAddress interfaceAddress;
interfaceAddress.SetAddress(address);
interfaceAddress.SetMask(mask);
if (!broadcast.IsEmpty())
interfaceAddress.SetBroadcast(broadcast);
else if (!peer.IsEmpty())
interfaceAddress.SetDestination(peer);
status_t status = interface.SetAddress(interfaceAddress);
if (status != B_OK) {
fprintf(stderr, "%s: Setting address failed: %s\n", kProgramName,
strerror(status));
exit(1);
}
}
currentFlags = interface.Flags();
if (!address.IsEmpty() || !mask.IsEmpty() || !broadcast.IsEmpty()
|| !peer.IsEmpty())
removeFlags = IFF_AUTO_CONFIGURED | IFF_CONFIGURING;
if (addFlags || removeFlags) {
status_t status
= interface.SetFlags((currentFlags & ~removeFlags) | addFlags);
if (status != B_OK) {
fprintf(stderr, "%s: Setting flags failed: %s\n", kProgramName,
strerror(status));
}
}
if (mtu != -1) {
status_t status = interface.SetMTU(mtu);
if (status != B_OK) {
fprintf(stderr, "%s: Setting MTU failed: %s\n", kProgramName,
strerror(status));
}
}
if (metric != -1) {
status_t status = interface.SetMetric(metric);
if (status != B_OK) {
fprintf(stderr, "%s: Setting metric failed: %s\n", kProgramName,
strerror(status));
}
}
if (media != -1) {
status_t status = interface.SetMedia(media);
if (status != B_OK) {
fprintf(stderr, "%s: Setting media failed: %s\n", kProgramName,
strerror(status));
}
}
if (doAutoConfig) {
status_t status = interface.AutoConfigure(family);
if (status == B_BAD_PORT_ID) {
fprintf(stderr, "%s: The net_server needs to run for the auto "
"configuration!\n", kProgramName);
} else if (status != B_OK) {
fprintf(stderr, "%s: Auto-configuring failed: %s\n", kProgramName,
strerror(status));
}
}
}
int
main(int argc, char** argv)
{
if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
usage(0);
int socket = ::socket(AF_LINK, SOCK_DGRAM, 0);
if (socket < 0) {
fprintf(stderr, "%s: The networking stack doesn't seem to be "
"available.\n", kProgramName);
return 1;
}
close(socket);
if (argc > 1
&& (!strcmp(argv[1], "--delete")
|| !strcmp(argv[1], "--del")
|| !strcmp(argv[1], "-d")
|| !strcmp(argv[1], "del")
|| !strcmp(argv[1], "delete"))) {
if (argc < 3)
usage(1);
const char* name = argv[2];
delete_interface(name, argv + 3, argc - 3);
return 0;
}
if (argc > 1 && !strcmp(argv[1], "-a")) {
if (argc > 2)
usage(1);
list_interfaces(NULL);
return 0;
}
const char* name = argv[1];
if (argc > 2) {
if (configure_wireless(name, argv + 2, argc - 2))
return 0;
configure_interface(name, argv + 2, argc - 2);
return 0;
}
list_interfaces(name);
return 0;
}