Get interface names using ioctl rather than /proc, from Nick Fedchik
[oweals/busybox.git] / networking / nameif.c
1 /* 
2  * nameif.c - Naming Interfaces based on MAC address for busybox.
3  *
4  * Writen 2000 by Andi Kleen.
5  * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua>
6  *                      Glenn McGrath <bug1@optushome.com.au>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21  * 02111-1307 USA
22  *
23  */
24
25
26 #include <sys/syslog.h>
27 #include <sys/socket.h>
28 #include <sys/ioctl.h>
29 #include <errno.h>
30 #include <getopt.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <net/if.h>
34 #include <netinet/ether.h>
35 #include <linux/sockios.h>
36
37 #include "busybox.h"
38
39 /* Octets in one ethernet addr, from <linux/if_ether.h>  */
40 #define ETH_ALEN        6
41
42 #ifndef ifr_newname
43 #define ifr_newname ifr_ifru.ifru_slave
44 #endif
45
46 typedef struct mactable_s {
47         struct mactable_s *next;
48         struct mactable_s *prev;
49         char *ifname;
50         struct ether_addr *mac;
51 } mactable_t;
52
53 static void serror(const char use_syslog, const char *s, ...)
54 {
55         va_list ap;
56
57         va_start(ap, s);
58
59         if (use_syslog) {
60                 openlog("nameif", 0, LOG_LOCAL0);
61                 syslog(LOG_ERR, s, ap);
62                 closelog();
63         } else {
64                 vfprintf(stderr, s, ap);
65                 putc('\n', stderr);
66         }
67
68         va_end(ap);
69
70         exit(EXIT_FAILURE);
71 }
72
73 /* Check ascii str_macaddr, convert and copy to *mac */
74 struct ether_addr *cc_macaddr(char *str_macaddr, unsigned char use_syslog)
75 {
76         struct ether_addr *lmac, *mac;
77
78         lmac = ether_aton(str_macaddr);
79         if (lmac == NULL)
80                 serror(use_syslog, "cannot parse MAC %s", str_macaddr);
81         mac = xcalloc(1, ETH_ALEN);
82         memcpy(mac, lmac, ETH_ALEN);
83
84         return mac;
85 }
86
87 int nameif_main(int argc, char **argv)
88 {
89         mactable_t *clist = NULL;
90         FILE *ifh;
91         char *fname = "/etc/mactab";
92         char *line;
93         unsigned char use_syslog = 0;
94         int ctl_sk = -1;
95         int opt;
96         int if_index = 1;
97         mactable_t *ch = NULL;
98
99         static struct option opts[] = {
100                 {"syslog", 0, NULL, 's'},
101                 {"configfile", 1, NULL, 'c'},
102                 {NULL},
103         };
104
105         while ((opt = getopt_long(argc, argv, "c:s", opts, NULL)) != -1) {
106                 switch (opt) {
107                 case 'c':
108                         fname = optarg;
109                         break;
110                 case 's':
111                         use_syslog = 1;
112                         break;
113                 default:
114                         show_usage();
115                 }
116         }
117
118         if ((argc - optind) & 1)
119                 show_usage();
120
121         if (optind < argc) {
122                 while (optind < argc) {
123
124                         if (strlen(argv[optind]) > IF_NAMESIZE)
125                                 serror(use_syslog, "interface name `%s' too long",
126                                            argv[optind]);
127                         optind++;
128
129                         ch = xcalloc(1, sizeof(mactable_t));
130                         ch->next = NULL;
131                         ch->prev = NULL;
132                         ch->ifname = strdup(argv[optind - 1]);
133                         ch->mac = cc_macaddr(argv[optind], use_syslog);
134                         optind++;
135                         if (clist)
136                                 clist->prev = ch->next;
137                         ch->next = clist;
138                         ch->prev = clist;
139                         clist = ch;
140                 }
141         } else {
142                 ifh = xfopen(fname, "r");
143
144                 while ((line = get_line_from_file(ifh)) != NULL) {
145                         char *line_ptr;
146                         unsigned short name_length;
147
148                         line_ptr = line + strspn(line, " \t");
149                         if ((line_ptr[0] == '#') || (line_ptr[0] == '\n'))
150                                 continue;
151                         name_length = strcspn(line_ptr, " \t");
152                         if (name_length > IF_NAMESIZE)
153                                 serror(use_syslog, "interface name `%s' too long",
154                                            argv[optind]);
155                         ch = xcalloc(1, sizeof(mactable_t));
156                         ch->next = NULL;
157                         ch->prev = NULL;
158                         ch->ifname = strndup(line_ptr, name_length);
159                         line_ptr += name_length;
160                         line_ptr += strspn(line_ptr, " \t");
161                         name_length = strspn(line_ptr, "0123456789ABCDEFabcdef:");
162                         line_ptr[name_length] = '\0';
163                         ch->mac = cc_macaddr(line_ptr, use_syslog);
164                         if (clist)
165                                 clist->prev = ch;
166                         ch->next = clist;
167                         clist = ch;
168                         free(line);
169                 }
170                 fclose(ifh);
171         }
172
173         if ((ctl_sk = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
174                 serror(use_syslog, "socket: %s", strerror(errno));
175
176         while (clist) {
177                 struct ifreq ifr;
178
179                 bzero(&ifr, sizeof(struct ifreq));
180                 if_index++;
181                 ifr.ifr_ifindex = if_index;
182
183                 /* Get ifname by index or die */
184                 if (ioctl(ctl_sk, SIOCGIFNAME, &ifr))
185                         break;
186
187                 /* Has this device hwaddr? */
188                 if (ioctl(ctl_sk, SIOCGIFHWADDR, &ifr))
189                         continue;
190
191                 /* Search for mac like in ifr.ifr_hwaddr.sa_data */
192                 for (ch = clist; ch; ch = ch->next)
193                         if (!memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN))
194                                 break;
195
196                 /* Nothing found for current ifr.ifr_hwaddr.sa_data */
197                 if (ch == NULL)
198                         continue;
199
200                 strcpy(ifr.ifr_newname, ch->ifname);
201                 if (ioctl(ctl_sk, SIOCSIFNAME, &ifr) < 0)
202                         serror(use_syslog, "cannot change ifname %s to %s: %s",
203                                    ifr.ifr_name, ch->ifname, strerror(errno));
204
205                 /* Remove list entry of renamed interface */
206                 if (ch->prev != NULL) {
207                         (ch->prev)->next = ch->next;
208                 } else {
209                         clist = ch->next;
210                 }
211                 if (ch->next != NULL)
212                         (ch->next)->prev = ch->prev;
213                 free(ch);
214         }
215
216         while (clist) {
217                 ch = clist;
218                 clist = clist->next;
219                 free(ch);
220         }
221
222         return 0;
223 }