/* 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, * * 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.4 2001/03/06 00:48:59 andersen Exp $ * * Majorly hacked up by Larry Doolittle * */ #include "busybox.h" #include #include #include #include #include // strcmp and friends #include // isdigit and friends #include #include #include #include #include #include #include static int sockfd; /* socket fd we use to manipulate stuff with */ #define TESTME 0 #if TESTME #define ioctl test_ioctl char *saddr_to_a(struct sockaddr *s) { if (s->sa_family == ARPHRD_ETHER) { static char hw[18]; sprintf(hw, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", s->sa_data[0], s->sa_data[1], s->sa_data[2], s->sa_data[3], s->sa_data[4], s->sa_data[5]); return hw; } else if (s->sa_family == AF_INET) { struct sockaddr_in *ss = (struct sockaddr_in *) s; return inet_ntoa(ss->sin_addr); } else { return NULL; } } int test_ioctl(int __fd, unsigned long int __request, void *param) { struct ifreq *i=(struct ifreq *)param; printf("ioctl fd=%d, request=%ld\n", __fd, __request); switch(__request) { case SIOCGIFFLAGS: printf(" SIOCGIFFLAGS\n"); i->ifr_flags = 0; break; case SIOCSIFFLAGS: printf(" SIOCSIFFLAGS, %x\n", i->ifr_flags); break; case SIOCSIFMETRIC: printf(" SIOCSIFMETRIC, %d\n", i->ifr_metric); break; case SIOCSIFMTU: printf(" SIOCSIFMTU, %d\n", i->ifr_mtu); break; case SIOCSIFBRDADDR: printf(" SIOCSIFBRDADDR, %s\n", saddr_to_a(&(i->ifr_broadaddr))); break; case SIOCSIFDSTADDR: printf(" SIOCSIFDSTADDR, %s\n", saddr_to_a(&(i->ifr_dstaddr ))); break; case SIOCSIFNETMASK: printf(" SIOCSIFNETMASK, %s\n", saddr_to_a(&(i->ifr_netmask ))); break; case SIOCSIFADDR: printf(" SIOCSIFADDR, %s\n", saddr_to_a(&(i->ifr_addr ))); break; case SIOCSIFHWADDR: printf(" SIOCSIFHWADDR, %s\n", saddr_to_a(&(i->ifr_hwaddr ))); break; /* broken */ default: } return 0; } #endif /* 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; } /* which element in struct ifreq to frob */ enum frob { L_METRIC, L_MTU, L_DATA, L_BROAD, L_DEST, L_MASK, L_HWAD, }; struct flag_map { char *name; enum frob frob; int flag; int sflag; int action; }; /* action: * 2 set * 4 clear * 6 set/clear * 8 clear/set * 10 numeric * 12 address * 14 address/clear */ const static struct flag_map flag_table[] = { {"arp", 0, IFF_NOARP, 0, 6}, {"trailers", 0, IFF_NOTRAILERS, 0, 6}, {"promisc", 0, IFF_PROMISC, 0, 8}, {"multicast", 0, IFF_MULTICAST, 0, 8}, {"allmulti", 0, IFF_ALLMULTI, 0, 8}, {"up", 0, (IFF_UP | IFF_RUNNING), 0, 2}, {"down", 0, IFF_UP, 0, 4}, {"metric", L_METRIC, 0, SIOCSIFMETRIC, 10}, {"mtu", L_MTU, 0, SIOCSIFMTU, 10}, #ifdef SIOCSKEEPALIVE {"keepalive", L_DATA, 0, SIOCSKEEPALIVE, 10}, #endif #ifdef SIOCSOUTFILL {"outfill", L_DATA, 0, SIOCSOUTFILL, 10}, #endif {"broadcast", L_BROAD, IFF_BROADCAST, SIOCSIFBRDADDR, 14}, {"dstaddr", L_DEST, 0, SIOCSIFDSTADDR, 12}, {"netmask", L_MASK, 0, SIOCSIFNETMASK, 12}, {"pointopoint", L_DEST, IFF_POINTOPOINT, SIOCSIFDSTADDR, 14}, {"hw", L_HWAD, 0, SIOCSIFHWADDR, 14}, }; /* 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")==0) { 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.. */ errno = EINVAL; 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 error_msg( _("in_ether(%s): invalid ether address!"), 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 error_msg( _("in_ether(%s): invalid ether address!"), orig); #endif errno = EINVAL; return -1; } if (c != 0) bufp++; *ptr++ = (unsigned char) (val & 0377); i++; /* optional colon already handled, don't swallow a second */ } if(i != ETH_ALEN) { errno = EINVAL; return -1; } return 0; } #ifdef BB_FEATURE_IFCONFIG_STATUS extern int display_interfaces(void); #else int display_interfaces(void) { show_usage(); } #endif int ifconfig_main(int argc, char **argv) { struct ifreq ifr; struct sockaddr_in sa; char **spp, *cmd; int goterr = 0; int r; /* int didnetmask = 0; special case input error detection no longer implemented */ char host[128]; const struct flag_map *ft; int i, sense; int a, ecode; struct sockaddr *d; if(argc < 2) { return(display_interfaces()); } /* Create a channel to the NET kernel. */ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror_msg_and_die("socket"); } /* 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) { cmd = *spp; sense=0; if (*cmd=='-') { sense=1; cmd++; } ft = NULL; for (i=0; i<(sizeof(flag_table)/sizeof(struct flag_map)); i++) { if (strcmp(cmd, flag_table[i].name)==0) { ft=flag_table+i; spp++; break; } } if (ft) { switch (ft->action+sense) { case 4: case 7: case 8: case 15: goterr |= clr_flag(ifr.ifr_name, ft->flag); break; case 2: case 6: case 9: goterr |= set_flag(ifr.ifr_name, ft->flag); break; case 10: if (*spp == NULL) show_usage(); a = atoi(*spp++); switch (ft->frob) { case L_METRIC: ifr.ifr_metric = a; break; case L_MTU: ifr.ifr_mtu = a; break; case L_DATA: ifr.ifr_data = (caddr_t) a; break; default: error_msg_and_die("bugaboo"); } if (ioctl(sockfd, ft->sflag, &ifr) < 0) { perror(ft->name); /* imperfect */ goterr++; } break; case 12: case 14: if (ft->action+sense==10 && *spp == NULL) { show_usage(); break; } if (*spp != NULL) { safe_strncpy(host, *spp, (sizeof host)); spp++; if (ft->frob == L_HWAD) { ecode = in_ether(host, &ifr.ifr_hwaddr); } else { switch (ft->frob) { case L_BROAD: d = &ifr.ifr_broadaddr; break; case L_DEST: d = &ifr.ifr_dstaddr; break; case L_MASK: d = &ifr.ifr_netmask; break; default: error_msg_and_die("bugaboo"); } ecode = INET_resolve(host, (struct sockaddr_in *) d); } if (ecode < 0 || ioctl(sockfd, ft->sflag, &ifr) < 0) { perror(ft->name); /* imperfect */ goterr++; } } if (ft->flag != 0) { goterr |= set_flag(ifr.ifr_name, ft->flag); } break; default: show_usage(); } /* end of switch */ continue; } /* If the next argument is a valid hostname, assume OK. */ safe_strncpy(host, *spp, (sizeof host)); if (INET_resolve(host, &sa) < 0) { show_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 */ return goterr; }