aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2013-11-26 19:35:22 -0600
committerRob Landley <rob@landley.net>2013-11-26 19:35:22 -0600
commit9fff257357fee7a90e7e8b5b44c0abc18d019b64 (patch)
tree828526ccb5879060e3309ebe38a2d28164c50e19
parent617588a7894be9e4f775932ff56e20ebfae97a93 (diff)
downloadtoybox-9fff257357fee7a90e7e8b5b44c0abc18d019b64.tar.gz
Ifconfig cleanup.
-rw-r--r--toys/pending/ifconfig.c281
1 files changed, 117 insertions, 164 deletions
diff --git a/toys/pending/ifconfig.c b/toys/pending/ifconfig.c
index 55a56892..92085d0c 100644
--- a/toys/pending/ifconfig.c
+++ b/toys/pending/ifconfig.c
@@ -38,14 +38,6 @@ GLOBALS(
int sockfd;
)
-typedef struct sockaddr_with_len {
- union {
- struct sockaddr sock;
- struct sockaddr_in sock_in;
- struct sockaddr_in6 sock_in6;
- } sock_u;
-} sockaddr_with_len;
-
//for ipv6 add/del
struct ifreq_inet6 {
struct in6_addr ifrinte6_addr;
@@ -53,91 +45,32 @@ struct ifreq_inet6 {
int ifrinet6_ifindex;
};
-/*
- * use to get the socket address with the given host ip.
- */
-sockaddr_with_len *get_sockaddr(char *host, int port, sa_family_t af)
+// Convert hostname to binary address for AF_INET or AF_INET6
+void get_addrinfo(char *host, sa_family_t af, void *addr)
{
- sockaddr_with_len *swl = NULL;
- in_port_t port_num = htons(port);
- struct addrinfo hints, *result, *rp;
- int status;
- char *s;
-
- if (!strncmp(host, "local:", 6)) {
- struct sockaddr_un *sockun;
-
- swl = xzalloc(sizeof(struct sockaddr_with_len));
- swl->sock_u.sock.sa_family = AF_UNIX;
- sockun = (struct sockaddr_un *)&swl->sock_u.sock;
- xstrncpy(sockun->sun_path, host + 6, sizeof(sockun->sun_path));
-
- return swl;
- }
-
- // [ipv6]:port or exactly one :
-
- if (*host == '[') {
- host++;
- s = strchr(host, ']');
- if (s && !s[1]) s = 0;
- else {
- if (!s || s[1] != ':') error_exit("bad address '%s'", host-1);
- s++;
- }
- } else {
- s = strrchr(host, ':');
- if (strchr(host, ':') != s) s = 0;
- }
-
- if (s++) {
- char *ss;
- unsigned long p = strtoul(s, &ss, 0);
- if (*ss || p > 65535) error_exit("bad port '%s'", s);
- port = p;
- }
+ struct addrinfo hints, *result, *rp = 0;
+ int status, len;
+ char *from;
memset(&hints, 0 , sizeof(struct addrinfo));
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
status = getaddrinfo(host, NULL, &hints, &result);
- if (status) error_exit("bad address '%s' : %s", host, gai_strerror(status));
-
- for (rp = result; rp; rp = rp->ai_next) {
- if (rp->ai_family == AF_INET || rp->ai_family == AF_INET6) {
- swl = xmalloc(sizeof(struct sockaddr_with_len));
- memcpy(&swl->sock_u.sock, rp->ai_addr, rp->ai_addrlen);
- break;
- }
+ if (!status)
+ for (rp = result; rp; rp = rp->ai_next)
+ if (rp->ai_family == af) break;
+ if (!rp) error_exit("bad address '%s' : %s", host, gai_strerror(status));
+
+ // You'd think ipv4 and ipv6 would ahve some basic compatability, but no.
+ len = 4;
+ from = ((char *)rp->ai_addr->sa_data) + 2;
+ if (af == AF_INET6) {
+ len = 16;
+ from += 4;
}
+ memcpy(addr, from, len);
freeaddrinfo(result);
- if (!rp) error_exit("bad host name");
-
- if(swl->sock_u.sock.sa_family == AF_INET)
- swl->sock_u.sock_in.sin_port = port_num;
- else if(swl->sock_u.sock.sa_family == AF_INET6)
- swl->sock_u.sock_in6.sin6_port = port_num;
-
- return swl;
-}
-
-static void set_address(char *host_name, struct ifreq *ifre, int request)
-{
- struct sockaddr_in *sock_in = (struct sockaddr_in *)&ifre->ifr_addr;
- sockaddr_with_len *swl = NULL;
-
- memset(sock_in, 0, sizeof(struct sockaddr_in));
- sock_in->sin_family = AF_INET;
-
- //Default 0.0.0.0
- if(strcmp(host_name, "default") == 0) sock_in->sin_addr.s_addr = INADDR_ANY;
- else {
- swl = get_sockaddr(host_name, 0, AF_INET);
- sock_in->sin_addr = swl->sock_u.sock_in.sin_addr;
- free(swl);
- }
- xioctl(TT.sockfd, request, ifre);
}
static void display_ifconfig(char *name, int always, unsigned long long val[])
@@ -274,7 +207,7 @@ static void display_ifconfig(char *name, int always, unsigned long long val[])
// non-virtual interface
- if(val) {
+ if (val) {
char *label[] = {"RX bytes", "RX packets", "errors", "dropped", "overruns",
"frame", 0, 0, "TX bytes", "TX packets", "errors", "dropped", "overruns",
"collisions", "carrier", 0, "txqueuelen"};
@@ -345,7 +278,7 @@ static void show_iface(char *iface_name)
sl->next = ifaces;
ifaces = sl;
- display_ifconfig(name, toys.optflags & FLAG_a, val);
+ display_ifconfig(sl->str, toys.optflags & FLAG_a, val);
}
}
fclose(fp);
@@ -390,8 +323,6 @@ void ifconfig_main(void)
struct ifreq ifre;
int i;
- if(*argv && (strcmp(*argv, "--help") == 0)) show_help();
-
TT.sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
if(toys.optc < 2) {
show_iface(*argv);
@@ -404,10 +335,12 @@ void ifconfig_main(void)
// Perform operations on interface
while(*++argv) {
+ // Table of known operations
struct argh {
char *name;
int on, off; // set, clear
} try[] = {
+ {0, IFF_UP|IFF_RUNNING, SIOCSIFADDR},
{"up", IFF_UP|IFF_RUNNING, 0},
{"down", 0, IFF_UP},
{"arp", 0, IFF_NOARP},
@@ -436,11 +369,95 @@ void ifconfig_main(void)
s += rev;
- for (i = 0; i < sizeof(try)/sizeof(*try); i++) {
+ // "set hardware address" is oddball enough to special case
+ if (!strcmp(*argv, "hw")) {
+ char *hw_addr, *ptr, *p;
+ struct sockaddr *sock = &ifre.ifr_hwaddr;
+ int count = 6;
+
+ ptr = p = (char *)sock->sa_data;
+ memset(sock, 0, sizeof(struct sockaddr));
+ if (!argv[1]) {
+ if (!strcmp("ether", *++argv)) sock->sa_family = ARPHRD_ETHER;
+ else if (!strcmp("infiniband", *argv)) {
+ sock->sa_family = ARPHRD_INFINIBAND;
+ count = 20;
+ p = ptr = toybuf;
+ }
+ }
+ if (!sock->sa_family || !argv[1]) {
+ toys.exithelp++;
+ error_exit("bad hw '%s'", *argv);
+ }
+ hw_addr = *++argv;
+
+ // Parse and verify address.
+ while (*hw_addr && (p-ptr) < count) {
+ int val, len = 0;
+
+ if (*hw_addr == ':') hw_addr++;
+ sscanf(hw_addr, "%2x%n", &val, &len);
+ if (len != 2) break;
+ hw_addr += len;
+ *p++ = val;
+ }
+
+ if ((p-ptr) != count || *hw_addr)
+ error_exit("bad hw-addr '%s'", hw_addr ? hw_addr : "");
+
+ // the linux kernel's "struct sockaddr" (include/linux/socket.h in the
+ // kernel source) only has 14 bytes of sa_data, and an infiniband address
+ // is 20. So if we go through the ioctl, the kernel will truncate
+ // infiniband addresses, meaning we have to go through sysfs instead.
+ if (sock->sa_family == ARPHRD_INFINIBAND && !strchr(ifre.ifr_name, '/')) {
+ int fd;
+
+ sprintf(toybuf, "/sys/class/net/%s/address", ifre.ifr_name);
+ fd = xopen(toybuf, O_RDWR);
+ xwrite(fd, *argv, strlen(*argv));
+ close(fd);
+ } else xioctl(TT.sockfd, SIOCSIFHWADDR, &ifre);
+ continue;
+
+ // Add/remove ipv6 address to interface
+
+ } else if (!strcmp(*argv, "add") || !strcmp(*argv, "del")) {
+ struct ifreq_inet6 {
+ struct in6_addr addr;
+ unsigned prefix;
+ int index;
+ } ifre6;
+ int plen = 128, fd6 = xsocket(AF_INET6, SOCK_DGRAM, 0);
+ char *prefix;
+
+ if (!argv[1]) {
+ toys.exithelp++;
+ error_exit(*argv);
+ }
+
+ prefix = strchr(argv[1], '/');
+ if (prefix) {
+ plen = atolx_range(prefix+1, 0, 128);
+ *prefix = 0;
+ }
+
+ get_addrinfo(argv[1], AF_INET6, &ifre6.addr);
+ xioctl(fd6, SIOCGIFINDEX, &ifre);
+ ifre6.index = ifre.ifr_ifindex;
+ ifre6.prefix = plen;
+ xioctl(fd6, **(argv++)=='a' ? SIOCSIFADDR : SIOCDIFADDR, &ifre6);
+
+ close(fd6);
+ continue;
+ // Iterate through table to find/perform operation
+ } else for (i = 0; i < sizeof(try)/sizeof(*try); i++) {
struct argh *t = try+i;
int on = t->on, off = t->off;
- if (strcmp(t->name, s)) continue;
+ if (!t->name) {
+ if (isdigit(**argv) || !strcmp(*argv, "default")) argv--;
+ else continue;
+ } else if (strcmp(t->name, s)) continue;
// Is this an SIOCSI entry?
if ((off|0xff) == 0x89ff) {
@@ -456,7 +473,15 @@ void ifconfig_main(void)
poke((on>>16) + (char *)&ifre, l, on&15);
xioctl(TT.sockfd, off, &ifre);
break;
- } else set_address(*argv, &ifre, off);
+ } else if (t->name || !strchr(ifre.ifr_name, ':')) {
+ struct sockaddr_in *si = (struct sockaddr_in *)&ifre.ifr_addr;
+
+ si->sin_family = AF_INET;
+
+ if (!strcmp(*argv, "default")) si->sin_addr.s_addr = INADDR_ANY;
+ else get_addrinfo(*argv, AF_INET, &si->sin_addr);
+ xioctl(TT.sockfd, off, &ifre);
+ }
}
off = 0;
}
@@ -471,79 +496,7 @@ void ifconfig_main(void)
break;
}
- if (i != sizeof(try)/sizeof(*try)) continue;
-
- if (!strcmp(*argv, "hw")) {
- char *hw_addr, *ptr, *p;
- struct sockaddr *sock = &ifre.ifr_hwaddr;
- int count = 6;
-
- if (!*++argv) show_help();
-
- memset(sock, 0, sizeof(struct sockaddr));
- if (!strcmp("ether", *argv)) sock->sa_family = ARPHRD_ETHER;
- else if (!strcmp("infiniband", *argv)) {
- sock->sa_family = ARPHRD_INFINIBAND;
- count = 20;
- } else {
- toys.exithelp++;
- error_exit("bad hw '%s'", *argv);
- }
- hw_addr = *++argv;
-
- ptr = p = (char *) sock->sa_data;
-
- while (*hw_addr && (p-ptr) < count) {
- int val, len = 0;
-
- if (*hw_addr == ':') hw_addr++;
- sscanf(hw_addr, "%2x%n", &val, &len);
- if (len != 2) break;
- hw_addr += len;
- *p++ = val;
- }
-
- if ((p-ptr) != count || *hw_addr)
- error_exit("bad hw-addr '%s'", hw_addr ? hw_addr : "");
- xioctl(TT.sockfd, SIOCSIFHWADDR, &ifre);
-
- // Add/remove ipv6 address to interface
-
- } else if (!strcmp(*argv, "add") || !strcmp(*argv, "del")) {
- sockaddr_with_len *swl = NULL;
- struct ifreq_inet6 ifre6;
- char *prefix;
- int plen = 0, sockfd6 = xsocket(AF_INET6, SOCK_DGRAM, 0);
-
- if (!argv[1]) show_help();
-
- prefix = strchr(argv[1], '/');
- if (prefix) {
- plen = get_int_value(prefix + 1, 0, 128);
- *prefix = 0;
- }
- swl = get_sockaddr(argv[1], 0, AF_INET6);
- ifre6.ifrinte6_addr = swl->sock_u.sock_in6.sin6_addr;
- xioctl(sockfd6, SIOCGIFINDEX, &ifre);
- ifre6.ifrinet6_ifindex = ifre.ifr_ifindex;
- ifre6.ifrinet6_prefixlen = plen;
- xioctl(sockfd6, **argv=='a' ? SIOCSIFADDR : SIOCDIFADDR, &ifre6);
-
- free(swl);
- close(sockfd6);
-
- argv++;
- } else if (isdigit(**argv) || !strcmp(*argv, "default")) {
- set_address(*argv, &ifre, SIOCSIFADDR);
- //if the interface name is not an alias; set the flag and continue.
- if(!strchr(ifre.ifr_name, ':')) {
- xioctl(TT.sockfd, SIOCGIFFLAGS, &ifre);
- ifre.ifr_flags |= IFF_UP|IFF_RUNNING;
- xioctl(TT.sockfd, SIOCSIFFLAGS, &ifre);
- }
-
- } else {
- errno = EINVAL;
+ if (i == sizeof(try)/sizeof(*try)) {
toys.exithelp++;
error_exit("bad argument '%s'", *argv);
}