1/* vi: set sw=4 ts=4: */ 2/* 3 * nameif.c - Naming Interfaces based on MAC address for busybox. 4 * 5 * Written 2000 by Andi Kleen. 6 * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua> 7 * Glenn McGrath 8 * Extended matching support 2008 by Nico Erfurth <masta@perlgolf.de> 9 * 10 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. 11 */ 12 13#include "libbb.h" 14#include <syslog.h> 15#include <net/if.h> 16#include <netinet/ether.h> 17#include <linux/sockios.h> 18 19#ifndef IFNAMSIZ 20#define IFNAMSIZ 16 21#endif 22 23/* Taken from linux/sockios.h */ 24#define SIOCSIFNAME 0x8923 /* set interface name */ 25 26/* Octets in one Ethernet addr, from <linux/if_ether.h> */ 27#define ETH_ALEN 6 28 29#ifndef ifr_newname 30#define ifr_newname ifr_ifru.ifru_slave 31#endif 32 33typedef struct ethtable_s { 34 struct ethtable_s *next; 35 struct ethtable_s *prev; 36 char *ifname; 37 struct ether_addr *mac; 38#if ENABLE_FEATURE_NAMEIF_EXTENDED 39 char *bus_info; 40 char *driver; 41#endif 42} ethtable_t; 43 44#if ENABLE_FEATURE_NAMEIF_EXTENDED 45/* Cut'n'paste from ethtool.h */ 46#define ETHTOOL_BUSINFO_LEN 32 47/* these strings are set to whatever the driver author decides... */ 48struct ethtool_drvinfo { 49 uint32_t cmd; 50 char driver[32]; /* driver short name, "tulip", "eepro100" */ 51 char version[32]; /* driver version string */ 52 char fw_version[32]; /* firmware version string, if applicable */ 53 char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ 54 /* For PCI devices, use pci_dev->slot_name. */ 55 char reserved1[32]; 56 char reserved2[16]; 57 uint32_t n_stats; /* number of u64's from ETHTOOL_GSTATS */ 58 uint32_t testinfo_len; 59 uint32_t eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ 60 uint32_t regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ 61}; 62#define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */ 63#endif 64 65 66static void nameif_parse_selector(ethtable_t *ch, char *selector) 67{ 68 struct ether_addr *lmac; 69#if ENABLE_FEATURE_NAMEIF_EXTENDED 70 int found_selector = 0; 71 72 while (*selector) { 73 char *next; 74#endif 75 selector = skip_whitespace(selector); 76#if ENABLE_FEATURE_NAMEIF_EXTENDED 77 if (*selector == '\0') 78 break; 79 /* Search for the end .... */ 80 next = skip_non_whitespace(selector); 81 if (*next) 82 *next++ = '\0'; 83 /* Check for selectors, mac= is assumed */ 84 if (strncmp(selector, "bus=", 4) == 0) { 85 ch->bus_info = xstrdup(selector + 4); 86 found_selector++; 87 } else if (strncmp(selector, "driver=", 7) == 0) { 88 ch->driver = xstrdup(selector + 7); 89 found_selector++; 90 } else { 91#endif 92 lmac = xmalloc(ETH_ALEN); 93 ch->mac = ether_aton_r(selector + (strncmp(selector, "mac=", 4) != 0 ? 0 : 4), lmac); 94 if (ch->mac == NULL) 95 bb_error_msg_and_die("can't parse %s", selector); 96#if ENABLE_FEATURE_NAMEIF_EXTENDED 97 found_selector++; 98 }; 99 selector = next; 100 } 101 if (found_selector == 0) 102 bb_error_msg_and_die("no selectors found for %s", ch->ifname); 103#endif 104} 105 106static void prepend_new_eth_table(ethtable_t **clist, char *ifname, char *selector) 107{ 108 ethtable_t *ch; 109 if (strlen(ifname) >= IFNAMSIZ) 110 bb_error_msg_and_die("interface name '%s' too long", ifname); 111 ch = xzalloc(sizeof(*ch)); 112 ch->ifname = xstrdup(ifname); 113 nameif_parse_selector(ch, selector); 114 ch->next = *clist; 115 if (*clist) 116 (*clist)->prev = ch; 117 *clist = ch; 118} 119 120#if ENABLE_FEATURE_CLEAN_UP 121static void delete_eth_table(ethtable_t *ch) 122{ 123 free(ch->ifname); 124#if ENABLE_FEATURE_NAMEIF_EXTENDED 125 free(ch->bus_info); 126 free(ch->driver); 127#endif 128 free(ch->mac); 129 free(ch); 130}; 131#else 132void delete_eth_table(ethtable_t *ch); 133#endif 134 135int nameif_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 136int nameif_main(int argc, char **argv) 137{ 138 ethtable_t *clist = NULL; 139 const char *fname = "/etc/mactab"; 140 int ctl_sk; 141 ethtable_t *ch; 142 parser_t *parser; 143 char *token[2]; 144 145 if (1 & getopt32(argv, "sc:", &fname)) { 146 openlog(applet_name, 0, LOG_LOCAL0); 147 /* Why not just "="? I assume logging to stderr 148 * can't hurt. 2>/dev/null if you don't like it: */ 149 logmode |= LOGMODE_SYSLOG; 150 } 151 argc -= optind; 152 argv += optind; 153 154 if (argc & 1) 155 bb_show_usage(); 156 157 if (argc) { 158 while (*argv) { 159 char *ifname = xstrdup(*argv++); 160 prepend_new_eth_table(&clist, ifname, *argv++); 161 } 162 } else { 163 parser = config_open(fname); 164 while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) 165 prepend_new_eth_table(&clist, token[0], token[1]); 166 config_close(parser); 167 } 168 169 ctl_sk = xsocket(PF_INET, SOCK_DGRAM, 0); 170 parser = config_open2("/proc/net/dev", xfopen_for_read); 171 172 while (clist && config_read(parser, token, 2, 2, "\0: \t", PARSE_NORMAL)) { 173 struct ifreq ifr; 174#if ENABLE_FEATURE_NAMEIF_EXTENDED 175 struct ethtool_drvinfo drvinfo; 176#endif 177 if (parser->lineno < 2) 178 continue; /* Skip the first two lines */ 179 180 /* Find the current interface name and copy it to ifr.ifr_name */ 181 memset(&ifr, 0, sizeof(struct ifreq)); 182 strncpy_IFNAMSIZ(ifr.ifr_name, token[0]); 183 184#if ENABLE_FEATURE_NAMEIF_EXTENDED 185 /* Check for driver etc. */ 186 memset(&drvinfo, 0, sizeof(struct ethtool_drvinfo)); 187 drvinfo.cmd = ETHTOOL_GDRVINFO; 188 ifr.ifr_data = (caddr_t) &drvinfo; 189 /* Get driver and businfo first, so we have it in drvinfo */ 190 ioctl(ctl_sk, SIOCETHTOOL, &ifr); 191#endif 192 ioctl(ctl_sk, SIOCGIFHWADDR, &ifr); 193 194 /* Search the list for a matching device */ 195 for (ch = clist; ch; ch = ch->next) { 196#if ENABLE_FEATURE_NAMEIF_EXTENDED 197 if (ch->bus_info && strcmp(ch->bus_info, drvinfo.bus_info) != 0) 198 continue; 199 if (ch->driver && strcmp(ch->driver, drvinfo.driver) != 0) 200 continue; 201#endif 202 if (ch->mac && memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN) != 0) 203 continue; 204 /* if we came here, all selectors have matched */ 205 break; 206 } 207 /* Nothing found for current interface */ 208 if (!ch) 209 continue; 210 211 if (strcmp(ifr.ifr_name, ch->ifname) != 0) { 212 strcpy(ifr.ifr_newname, ch->ifname); 213 ioctl_or_perror_and_die(ctl_sk, SIOCSIFNAME, &ifr, 214 "can't change ifname %s to %s", 215 ifr.ifr_name, ch->ifname); 216 } 217 /* Remove list entry of renamed interface */ 218 if (ch->prev != NULL) 219 ch->prev->next = ch->next; 220 else 221 clist = ch->next; 222 if (ch->next != NULL) 223 ch->next->prev = ch->prev; 224 if (ENABLE_FEATURE_CLEAN_UP) 225 delete_eth_table(ch); 226 } 227 if (ENABLE_FEATURE_CLEAN_UP) { 228 for (ch = clist; ch; ch = ch->next) 229 delete_eth_table(ch); 230 config_close(parser); 231 }; 232 233 return 0; 234} 235