nameif.c 6.1 KB

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