nameif.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. #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 : 4), lmac);
  84. if (ch->mac == NULL)
  85. bb_error_msg_and_die("cannot 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. logmode = LOGMODE_SYSLOG;
  134. }
  135. argc -= optind;
  136. argv += optind;
  137. if (argc & 1)
  138. bb_show_usage();
  139. if (argc) {
  140. while (*argv) {
  141. char *ifname = xstrdup(*argv++);
  142. prepend_new_eth_table(&clist, ifname, *argv++);
  143. }
  144. } else {
  145. parser = config_open(fname);
  146. while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL))
  147. prepend_new_eth_table(&clist, token[0], token[1]);
  148. config_close(parser);
  149. }
  150. ctl_sk = xsocket(PF_INET, SOCK_DGRAM, 0);
  151. parser = config_open2("/proc/net/dev", xfopen_for_read);
  152. while (clist && config_read(parser, token, 2, 2, "\0: \t", PARSE_NORMAL)) {
  153. struct ifreq ifr;
  154. #if ENABLE_FEATURE_NAMEIF_EXTENDED
  155. struct ethtool_drvinfo drvinfo;
  156. #endif
  157. if (parser->lineno < 2)
  158. continue; /* Skip the first two lines */
  159. /* Find the current interface name and copy it to ifr.ifr_name */
  160. memset(&ifr, 0, sizeof(struct ifreq));
  161. strncpy(ifr.ifr_name, token[0], sizeof(ifr.ifr_name));
  162. #if ENABLE_FEATURE_NAMEIF_EXTENDED
  163. /* Check for driver etc. */
  164. memset(&drvinfo, 0, sizeof(struct ethtool_drvinfo));
  165. drvinfo.cmd = ETHTOOL_GDRVINFO;
  166. ifr.ifr_data = (caddr_t) &drvinfo;
  167. /* Get driver and businfo first, so we have it in drvinfo */
  168. ioctl(ctl_sk, SIOCETHTOOL, &ifr);
  169. #endif
  170. ioctl(ctl_sk, SIOCGIFHWADDR, &ifr);
  171. /* Search the list for a matching device */
  172. for (ch = clist; ch; ch = ch->next) {
  173. #if ENABLE_FEATURE_NAMEIF_EXTENDED
  174. if (ch->bus_info && strcmp(ch->bus_info, drvinfo.bus_info) != 0)
  175. continue;
  176. if (ch->driver && strcmp(ch->driver, drvinfo.driver) != 0)
  177. continue;
  178. #endif
  179. if (ch->mac && memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN) != 0)
  180. continue;
  181. /* if we came here, all selectors have matched */
  182. break;
  183. }
  184. /* Nothing found for current interface */
  185. if (!ch)
  186. continue;
  187. if (strcmp(ifr.ifr_name, ch->ifname) != 0) {
  188. strcpy(ifr.ifr_newname, ch->ifname);
  189. ioctl_or_perror_and_die(ctl_sk, SIOCSIFNAME, &ifr,
  190. "cannot change ifname %s to %s",
  191. ifr.ifr_name, ch->ifname);
  192. }
  193. /* Remove list entry of renamed interface */
  194. if (ch->prev != NULL)
  195. ch->prev->next = ch->next;
  196. else
  197. clist = ch->next;
  198. if (ch->next != NULL)
  199. ch->next->prev = ch->prev;
  200. if (ENABLE_FEATURE_CLEAN_UP)
  201. delete_eth_table(ch);
  202. }
  203. if (ENABLE_FEATURE_CLEAN_UP) {
  204. for (ch = clist; ch; ch = ch->next)
  205. delete_eth_table(ch);
  206. config_close(parser);
  207. };
  208. return 0;
  209. }