1 /* vi: set sw=4 ts=4: */
3 * nameif.c - Naming Interfaces based on MAC address for busybox.
5 * Written 2000 by Andi Kleen.
6 * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua>
8 * Extended matching support 2008 by Nico Erfurth <masta@perlgolf.de>
10 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
13 //config:config NAMEIF
14 //config: bool "nameif"
16 //config: select PLATFORM_LINUX
17 //config: select FEATURE_SYSLOG
19 //config: nameif is used to rename network interface by its MAC address.
20 //config: Renamed interfaces MUST be in the down state.
21 //config: It is possible to use a file (default: /etc/mactab)
22 //config: with list of new interface names and MACs.
23 //config: Maximum interface name length: IFNAMSIZ = 16
24 //config: File fields are separated by space or tab.
25 //config: File format:
27 //config: new_interface_name XX:XX:XX:XX:XX:XX
29 //config:config FEATURE_NAMEIF_EXTENDED
30 //config: bool "Extended nameif"
32 //config: depends on NAMEIF
34 //config: This extends the nameif syntax to support the bus_info, driver,
35 //config: phyaddr selectors. The syntax is compatible to the normal nameif.
36 //config: File format:
37 //config: new_interface_name driver=asix bus=usb-0000:00:08.2-3
38 //config: new_interface_name bus=usb-0000:00:08.2-3 00:80:C8:38:91:B5
39 //config: new_interface_name phy_address=2 00:80:C8:38:91:B5
40 //config: new_interface_name mac=00:80:C8:38:91:B5
41 //config: new_interface_name 00:80:C8:38:91:B5
43 //usage:#define nameif_trivial_usage
44 //usage: IF_NOT_FEATURE_NAMEIF_EXTENDED(
45 //usage: "[-s] [-c FILE] [IFNAME HWADDR]..."
47 //usage: IF_FEATURE_NAMEIF_EXTENDED(
48 //usage: "[-s] [-c FILE] [IFNAME SELECTOR]..."
50 //usage:#define nameif_full_usage "\n\n"
51 //usage: "Rename network interface while it in the down state."
52 //usage: IF_NOT_FEATURE_NAMEIF_EXTENDED(
53 //usage: "\nThe device with address HWADDR is renamed to IFACE."
55 //usage: IF_FEATURE_NAMEIF_EXTENDED(
56 //usage: "\nThe device matched by SELECTOR is renamed to IFACE."
57 //usage: "\nSELECTOR can be a combination of:"
58 //usage: "\n driver=STRING"
59 //usage: "\n bus=STRING"
60 //usage: "\n phy_address=NUM"
61 //usage: "\n [mac=]XX:XX:XX:XX:XX:XX"
64 //usage: "\n -c FILE Configuration file (default: /etc/mactab)"
65 //usage: "\n -s Log to syslog"
67 //usage:#define nameif_example_usage
68 //usage: "$ nameif -s dmz0 00:A0:C9:8C:F6:3F\n"
70 //usage: "$ nameif -c /etc/my_mactab_file\n"
75 #include <netinet/ether.h>
76 #include <linux/sockios.h>
82 /* Taken from linux/sockios.h */
83 #define SIOCSIFNAME 0x8923 /* set interface name */
85 /* Octets in one Ethernet addr, from <linux/if_ether.h> */
89 #define ifr_newname ifr_ifru.ifru_slave
92 typedef struct ethtable_s
{
93 struct ethtable_s
*next
;
94 struct ethtable_s
*prev
;
96 struct ether_addr
*mac
;
97 #if ENABLE_FEATURE_NAMEIF_EXTENDED
104 #if ENABLE_FEATURE_NAMEIF_EXTENDED
105 /* Cut'n'paste from ethtool.h */
106 #define ETHTOOL_BUSINFO_LEN 32
107 /* these strings are set to whatever the driver author decides... */
108 struct ethtool_drvinfo
{
110 char driver
[32]; /* driver short name, "tulip", "eepro100" */
111 char version
[32]; /* driver version string */
112 char fw_version
[32]; /* firmware version string, if applicable */
113 char bus_info
[ETHTOOL_BUSINFO_LEN
]; /* Bus info for this IF. */
114 /* For PCI devices, use pci_dev->slot_name. */
117 uint32_t n_stats
; /* number of u64's from ETHTOOL_GSTATS */
118 uint32_t testinfo_len
;
119 uint32_t eedump_len
; /* Size of data from ETHTOOL_GEEPROM (bytes) */
120 uint32_t regdump_len
; /* Size of data from ETHTOOL_GREGS (bytes) */
125 uint32_t supported
; /* Features this interface supports */
126 uint32_t advertising
; /* Features this interface advertises */
127 uint16_t speed
; /* The forced speed, 10Mb, 100Mb, gigabit */
128 uint8_t duplex
; /* Duplex, half or full */
129 uint8_t port
; /* Which connector port */
131 uint8_t transceiver
; /* Which transceiver to use */
132 uint8_t autoneg
; /* Enable or disable autonegotiation */
133 uint32_t maxtxpkt
; /* Tx pkts before generating tx int */
134 uint32_t maxrxpkt
; /* Rx pkts before generating rx int */
137 uint32_t reserved
[3];
140 #define ETHTOOL_GSET 0x00000001 /* Get settings. */
141 #define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */
145 static void nameif_parse_selector(ethtable_t
*ch
, char *selector
)
147 struct ether_addr
*lmac
;
148 #if ENABLE_FEATURE_NAMEIF_EXTENDED
149 int found_selector
= 0;
154 selector
= skip_whitespace(selector
);
155 #if ENABLE_FEATURE_NAMEIF_EXTENDED
156 ch
->phy_address
= -1;
157 if (*selector
== '\0')
159 /* Search for the end .... */
160 next
= skip_non_whitespace(selector
);
163 /* Check for selectors, mac= is assumed */
164 if (strncmp(selector
, "bus=", 4) == 0) {
165 ch
->bus_info
= xstrdup(selector
+ 4);
167 } else if (strncmp(selector
, "driver=", 7) == 0) {
168 ch
->driver
= xstrdup(selector
+ 7);
170 } else if (strncmp(selector
, "phyaddr=", 8) == 0) {
171 ch
->phy_address
= xatoi_positive(selector
+ 8);
175 lmac
= xmalloc(ETH_ALEN
);
176 ch
->mac
= ether_aton_r(selector
+ (strncmp(selector
, "mac=", 4) != 0 ? 0 : 4), lmac
);
178 bb_error_msg_and_die("can't parse %s", selector
);
179 #if ENABLE_FEATURE_NAMEIF_EXTENDED
184 if (found_selector
== 0)
185 bb_error_msg_and_die("no selectors found for %s", ch
->ifname
);
189 static void prepend_new_eth_table(ethtable_t
**clist
, char *ifname
, char *selector
)
192 if (strlen(ifname
) >= IFNAMSIZ
)
193 bb_error_msg_and_die("interface name '%s' too long", ifname
);
194 ch
= xzalloc(sizeof(*ch
));
195 ch
->ifname
= xstrdup(ifname
);
196 nameif_parse_selector(ch
, selector
);
203 #if ENABLE_FEATURE_CLEAN_UP
204 static void delete_eth_table(ethtable_t
*ch
)
207 #if ENABLE_FEATURE_NAMEIF_EXTENDED
215 void delete_eth_table(ethtable_t
*ch
);
218 int nameif_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
219 int nameif_main(int argc UNUSED_PARAM
, char **argv
)
221 ethtable_t
*clist
= NULL
;
222 const char *fname
= "/etc/mactab";
228 if (1 & getopt32(argv
, "sc:", &fname
)) {
229 openlog(applet_name
, 0, LOG_LOCAL0
);
230 /* Why not just "="? I assume logging to stderr
231 * can't hurt. 2>/dev/null if you don't like it: */
232 logmode
|= LOGMODE_SYSLOG
;
240 prepend_new_eth_table(&clist
, argv
[0], argv
[1]);
244 parser
= config_open(fname
);
245 while (config_read(parser
, token
, 2, 2, "# \t", PARSE_NORMAL
))
246 prepend_new_eth_table(&clist
, token
[0], token
[1]);
247 config_close(parser
);
250 ctl_sk
= xsocket(PF_INET
, SOCK_DGRAM
, 0);
251 parser
= config_open2("/proc/net/dev", xfopen_for_read
);
253 while (clist
&& config_read(parser
, token
, 2, 2, "\0: \t", PARSE_NORMAL
)) {
255 #if ENABLE_FEATURE_NAMEIF_EXTENDED
256 struct ethtool_drvinfo drvinfo
;
257 struct ethtool_cmd eth_settings
;
259 if (parser
->lineno
<= 2)
260 continue; /* Skip the first two lines */
262 /* Find the current interface name and copy it to ifr.ifr_name */
263 memset(&ifr
, 0, sizeof(struct ifreq
));
264 strncpy_IFNAMSIZ(ifr
.ifr_name
, token
[0]);
266 #if ENABLE_FEATURE_NAMEIF_EXTENDED
267 /* Check for phy address */
268 memset(ð_settings
, 0, sizeof(eth_settings
));
269 eth_settings
.cmd
= ETHTOOL_GSET
;
270 ifr
.ifr_data
= (caddr_t
) ð_settings
;
271 ioctl(ctl_sk
, SIOCETHTOOL
, &ifr
);
273 /* Check for driver etc. */
274 memset(&drvinfo
, 0, sizeof(drvinfo
));
275 drvinfo
.cmd
= ETHTOOL_GDRVINFO
;
276 ifr
.ifr_data
= (caddr_t
) &drvinfo
;
277 /* Get driver and businfo first, so we have it in drvinfo */
278 ioctl(ctl_sk
, SIOCETHTOOL
, &ifr
);
280 ioctl(ctl_sk
, SIOCGIFHWADDR
, &ifr
);
282 /* Search the list for a matching device */
283 for (ch
= clist
; ch
; ch
= ch
->next
) {
284 #if ENABLE_FEATURE_NAMEIF_EXTENDED
285 if (ch
->bus_info
&& strcmp(ch
->bus_info
, drvinfo
.bus_info
) != 0)
287 if (ch
->driver
&& strcmp(ch
->driver
, drvinfo
.driver
) != 0)
289 if (ch
->phy_address
!= -1 && ch
->phy_address
!= eth_settings
.phy_address
)
292 if (ch
->mac
&& memcmp(ch
->mac
, ifr
.ifr_hwaddr
.sa_data
, ETH_ALEN
) != 0)
294 /* if we came here, all selectors have matched */
297 /* Nothing found for current interface */
301 if (strcmp(ifr
.ifr_name
, ch
->ifname
) != 0) {
302 strcpy(ifr
.ifr_newname
, ch
->ifname
);
303 ioctl_or_perror_and_die(ctl_sk
, SIOCSIFNAME
, &ifr
,
304 "can't change ifname %s to %s",
305 ifr
.ifr_name
, ch
->ifname
);
307 /* Remove list entry of renamed interface */
308 if (ch
->prev
!= NULL
)
309 ch
->prev
->next
= ch
->next
;
312 if (ch
->next
!= NULL
)
313 ch
->next
->prev
= ch
->prev
;
314 if (ENABLE_FEATURE_CLEAN_UP
)
315 delete_eth_table(ch
);
317 if (ENABLE_FEATURE_CLEAN_UP
) {
318 for (ch
= clist
; ch
; ch
= ch
->next
)
319 delete_eth_table(ch
);
320 config_close(parser
);