diff options
Diffstat (limited to 'ifconfig.c')
-rw-r--r-- | ifconfig.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/ifconfig.c b/ifconfig.c new file mode 100644 index 0000000..2134f8a --- /dev/null +++ b/ifconfig.c @@ -0,0 +1,476 @@ +/* ifconfig + * + * Similar to the standard Unix ifconfig, but with only the necessary + * parts for AF_INET, and without any printing of if info (for now). + * + * Bjorn Wesen, Axis Communications AB + * + * + * Authors of the original ifconfig was: + * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * $Id: ifconfig.c,v 1.1 2001/02/14 08:11:27 andersen Exp $ + * + */ + +#include "busybox.h" +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> // strcmp and friends +#include <ctype.h> // isdigit and friends +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <linux/if_ether.h> + +static int sockfd; /* socket fd we use to manipulate stuff with */ + +/* print usage and exit */ + +#define _(x) x + +/* Set a certain interface flag. */ +static int +set_flag(char *ifname, short flag) +{ + struct ifreq ifr; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { + perror("SIOCGIFFLAGS"); + return (-1); + } + strcpy(ifr.ifr_name, ifname); + ifr.ifr_flags |= flag; + if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return -1; + } + return (0); +} + + +/* Clear a certain interface flag. */ +static int +clr_flag(char *ifname, short flag) +{ + struct ifreq ifr; + + strcpy(ifr.ifr_name, ifname); + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { + perror("SIOCGIFFLAGS"); + return -1; + } + strcpy(ifr.ifr_name, ifname); + ifr.ifr_flags &= ~flag; + if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return -1; + } + return (0); +} + +/* resolve XXX.YYY.ZZZ.QQQ -> binary */ + +static int +INET_resolve(char *name, struct sockaddr_in *sin) +{ + sin->sin_family = AF_INET; + sin->sin_port = 0; + + /* Default is special, meaning 0.0.0.0. */ + if (!strcmp(name, "default")) { + sin->sin_addr.s_addr = INADDR_ANY; + return (1); + } + /* Look to see if it's a dotted quad. */ + if (inet_aton(name, &sin->sin_addr)) { + return 0; + } + /* guess not.. */ + return -1; +} + +/* Input an Ethernet address and convert to binary. */ +static int +in_ether(char *bufp, struct sockaddr *sap) +{ + unsigned char *ptr; + char c, *orig; + int i; + unsigned val; + + sap->sa_family = ARPHRD_ETHER; + ptr = sap->sa_data; + + i = 0; + orig = bufp; + while ((*bufp != '\0') && (i < ETH_ALEN)) { + val = 0; + c = *bufp++; + if (isdigit(c)) + val = c - '0'; + else if (c >= 'a' && c <= 'f') + val = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = c - 'A' + 10; + else { +#ifdef DEBUG + fprintf(stderr, + _("in_ether(%s): invalid ether address!\n"), + orig); +#endif + errno = EINVAL; + return (-1); + } + val <<= 4; + c = *bufp; + if (isdigit(c)) + val |= c - '0'; + else if (c >= 'a' && c <= 'f') + val |= c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val |= c - 'A' + 10; + else if (c == ':' || c == 0) + val >>= 4; + else { +#ifdef DEBUG + fprintf(stderr, + _("in_ether(%s): invalid ether address!\n"), + orig); +#endif + errno = EINVAL; + return (-1); + } + if (c != 0) + bufp++; + *ptr++ = (unsigned char) (val & 0377); + i++; + + /* We might get a semicolon here - not required. */ + if (*bufp == ':') + bufp++; + + } + + if(i != ETH_ALEN) { + errno = EINVAL; + return -1; + } + + return 0; +} + +int ifconfig_main(int argc, char **argv) +{ + struct ifreq ifr; + struct sockaddr_in sa; + struct sockaddr sa2; + char **spp; + int goterr = 0; + int r, didnetmask = 0; + char host[128]; + + if(argc < 2) { + usage(ifconfig_usage); + } + + /* Create a channel to the NET kernel. */ + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + exit(1); + } + + /* skip argv[0] */ + + argc--; + argv++; + + spp = argv; + + /* get interface name */ + + safe_strncpy(ifr.ifr_name, *spp++, IFNAMSIZ); + + /* Process the remaining arguments. */ + while (*spp != (char *) NULL) { + if (!strcmp(*spp, "arp")) { + goterr |= clr_flag(ifr.ifr_name, IFF_NOARP); + spp++; + continue; + } + if (!strcmp(*spp, "-arp")) { + goterr |= set_flag(ifr.ifr_name, IFF_NOARP); + spp++; + continue; + } + + if (!strcmp(*spp, "trailers")) { + goterr |= clr_flag(ifr.ifr_name, IFF_NOTRAILERS); + spp++; + continue; + } + if (!strcmp(*spp, "-trailers")) { + goterr |= set_flag(ifr.ifr_name, IFF_NOTRAILERS); + spp++; + continue; + } + if (!strcmp(*spp, "promisc")) { + goterr |= set_flag(ifr.ifr_name, IFF_PROMISC); + spp++; + continue; + } + if (!strcmp(*spp, "-promisc")) { + goterr |= clr_flag(ifr.ifr_name, IFF_PROMISC); + spp++; + continue; + } + if (!strcmp(*spp, "multicast")) { + goterr |= set_flag(ifr.ifr_name, IFF_MULTICAST); + spp++; + continue; + } + if (!strcmp(*spp, "-multicast")) { + goterr |= clr_flag(ifr.ifr_name, IFF_MULTICAST); + spp++; + continue; + } + if (!strcmp(*spp, "allmulti")) { + goterr |= set_flag(ifr.ifr_name, IFF_ALLMULTI); + spp++; + continue; + } + if (!strcmp(*spp, "-allmulti")) { + goterr |= clr_flag(ifr.ifr_name, IFF_ALLMULTI); + spp++; + continue; + } + if (!strcmp(*spp, "up")) { + goterr |= set_flag(ifr.ifr_name, (IFF_UP | IFF_RUNNING)); + spp++; + continue; + } + if (!strcmp(*spp, "down")) { + goterr |= clr_flag(ifr.ifr_name, IFF_UP); + spp++; + continue; + } + + if (!strcmp(*spp, "metric")) { + if (*++spp == NULL) + usage(ifconfig_usage); + ifr.ifr_metric = atoi(*spp); + if (ioctl(sockfd, SIOCSIFMETRIC, &ifr) < 0) { + fprintf(stderr, "SIOCSIFMETRIC: %s\n", strerror(errno)); + goterr++; + } + spp++; + continue; + } + if (!strcmp(*spp, "mtu")) { + if (*++spp == NULL) + usage(ifconfig_usage); + ifr.ifr_mtu = atoi(*spp); + if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) { + fprintf(stderr, "SIOCSIFMTU: %s\n", strerror(errno)); + goterr++; + } + spp++; + continue; + } +#ifdef SIOCSKEEPALIVE + if (!strcmp(*spp, "keepalive")) { + if (*++spp == NULL) + usage(ifconfig_usage); + ifr.ifr_data = (caddr_t) atoi(*spp); + if (ioctl(sockfd, SIOCSKEEPALIVE, &ifr) < 0) { + fprintf(stderr, "SIOCSKEEPALIVE: %s\n", strerror(errno)); + goterr++; + } + spp++; + continue; + } +#endif + +#ifdef SIOCSOUTFILL + if (!strcmp(*spp, "outfill")) { + if (*++spp == NULL) + usage(ifconfig_usage); + ifr.ifr_data = (caddr_t) atoi(*spp); + if (ioctl(sockfd, SIOCSOUTFILL, &ifr) < 0) { + fprintf(stderr, "SIOCSOUTFILL: %s\n", strerror(errno)); + goterr++; + } + spp++; + continue; + } +#endif + + if (!strcmp(*spp, "-broadcast")) { + goterr |= clr_flag(ifr.ifr_name, IFF_BROADCAST); + spp++; + continue; + } + if (!strcmp(*spp, "broadcast")) { + if (*++spp != NULL) { + safe_strncpy(host, *spp, (sizeof host)); + if (INET_resolve(host, &sa) < 0) { + goterr++; + spp++; + continue; + } + memcpy((char *) &ifr.ifr_broadaddr, + (char *) &sa, + sizeof(struct sockaddr)); + if (ioctl(sockfd, SIOCSIFBRDADDR, &ifr) < 0) { + perror("SIOCSIFBRDADDR"); + goterr++; + } + spp++; + } + goterr |= set_flag(ifr.ifr_name, IFF_BROADCAST); + continue; + } + if (!strcmp(*spp, "dstaddr")) { + if (*++spp == NULL) + usage(ifconfig_usage); + safe_strncpy(host, *spp, (sizeof host)); + if (INET_resolve(host, &sa) < 0) { + goterr++; + spp++; + continue; + } + memcpy((char *) &ifr.ifr_dstaddr, (char *) &sa, + sizeof(struct sockaddr)); + if (ioctl(sockfd, SIOCSIFDSTADDR, &ifr) < 0) { + fprintf(stderr, "SIOCSIFDSTADDR: %s\n", + strerror(errno)); + goterr++; + } + spp++; + continue; + } + if (!strcmp(*spp, "netmask")) { + if (*++spp == NULL || didnetmask) + usage(ifconfig_usage); + safe_strncpy(host, *spp, (sizeof host)); + if (INET_resolve(host, &sa) < 0) { + goterr++; + spp++; + continue; + } + didnetmask++; + memcpy((char *) &ifr.ifr_netmask, (char *) &sa, + sizeof(struct sockaddr)); + if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) { + perror("SIOCSIFNETMASK"); + goterr++; + } + spp++; + continue; + } + + if (!strcmp(*spp, "-pointopoint")) { + goterr |= clr_flag(ifr.ifr_name, IFF_POINTOPOINT); + spp++; + continue; + } + if (!strcmp(*spp, "pointopoint")) { + if (*(spp + 1) != NULL) { + spp++; + safe_strncpy(host, *spp, (sizeof host)); + if (INET_resolve(host, &sa)) { + goterr++; + spp++; + continue; + } + memcpy((char *) &ifr.ifr_dstaddr, (char *) &sa, + sizeof(struct sockaddr)); + if (ioctl(sockfd, SIOCSIFDSTADDR, &ifr) < 0) { + perror("SIOCSIFDSTADDR"); + goterr++; + } + } + goterr |= set_flag(ifr.ifr_name, IFF_POINTOPOINT); + spp++; + continue; + }; + + if (!strcmp(*spp, "hw")) { + if (*++spp == NULL || strcmp("ether", *spp)) { + usage(ifconfig_usage); + } + + if (*++spp == NULL) { + /* silently ignore it if no address */ + continue; + } + + safe_strncpy(host, *spp, (sizeof host)); + if (in_ether(host, &sa2) < 0) { + fprintf(stderr, "invalid hw-addr %s\n", host); + goterr++; + spp++; + continue; + } + memcpy((char *) &ifr.ifr_hwaddr, (char *) &sa2, + sizeof(struct sockaddr)); + if (ioctl(sockfd, SIOCSIFHWADDR, &ifr) < 0) { + perror("SIOCSIFHWADDR"); + goterr++; + } + spp++; + continue; + } + + /* If the next argument is a valid hostname, assume OK. */ + safe_strncpy(host, *spp, (sizeof host)); + + if (INET_resolve(host, &sa) < 0) { + usage(ifconfig_usage); + } + memcpy((char *) &ifr.ifr_addr, + (char *) &sa, sizeof(struct sockaddr)); + + r = ioctl(sockfd, SIOCSIFADDR, &ifr); + + if (r < 0) { + perror("SIOCSIFADDR"); + goterr++; + } + + /* + * Don't do the set_flag() if the address is an alias with a - at the + * end, since it's deleted already! - Roman + * + * Should really use regex.h here, not sure though how well it'll go + * with the cross-platform support etc. + */ + { + char *ptr; + short int found_colon = 0; + for (ptr = ifr.ifr_name; *ptr; ptr++ ) + if (*ptr == ':') found_colon++; + + if (!(found_colon && *(ptr - 1) == '-')) + goterr |= set_flag(ifr.ifr_name, (IFF_UP | IFF_RUNNING)); + } + + spp++; + + } /* end of while-loop */ + + exit(0); +} + |