Remember to delete un-expandable variables, and do a better job of expanding
[oweals/busybox.git] / ifconfig.c
1 /* ifconfig
2  *
3  * Similar to the standard Unix ifconfig, but with only the necessary
4  * parts for AF_INET, and without any printing of if info (for now).
5  *
6  * Bjorn Wesen, Axis Communications AB
7  *
8  *
9  * Authors of the original ifconfig was:      
10  *              Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
11  *
12  * This program is free software; you can redistribute it
13  * and/or  modify it under  the terms of  the GNU General
14  * Public  License as  published  by  the  Free  Software
15  * Foundation;  either  version 2 of the License, or  (at
16  * your option) any later version.
17  *
18  * $Id: ifconfig.c,v 1.4 2001/03/06 00:48:59 andersen Exp $
19  *
20  * Majorly hacked up by Larry Doolittle <ldoolitt@recycle.lbl.gov> 
21  *
22  */
23
24 #include "busybox.h"
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <string.h>   // strcmp and friends
30 #include <ctype.h>    // isdigit and friends
31 #include <sys/socket.h>
32 #include <sys/ioctl.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <net/if.h>
36 #include <net/if_arp.h>
37 #include <linux/if_ether.h>
38
39 static int sockfd;  /* socket fd we use to manipulate stuff with */
40
41 #define TESTME 0
42 #if TESTME
43 #define ioctl test_ioctl
44 char *saddr_to_a(struct sockaddr *s)
45 {
46         if (s->sa_family == ARPHRD_ETHER) {
47                 static char hw[18];
48                 sprintf(hw, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
49                         s->sa_data[0], s->sa_data[1], s->sa_data[2],
50                         s->sa_data[3], s->sa_data[4], s->sa_data[5]);
51                 return hw;
52         } else if (s->sa_family == AF_INET) {
53                 struct sockaddr_in *ss = (struct sockaddr_in *) s;
54                 return inet_ntoa(ss->sin_addr);
55         } else {
56                 return NULL;
57         }
58 }
59
60 int test_ioctl(int __fd, unsigned long int __request, void *param)
61 {
62         struct ifreq *i=(struct ifreq *)param;
63         printf("ioctl fd=%d, request=%ld\n", __fd, __request);
64         
65         switch(__request) {
66                 case SIOCGIFFLAGS:   printf("  SIOCGIFFLAGS\n");       i->ifr_flags = 0;   break;
67                 case SIOCSIFFLAGS:   printf("  SIOCSIFFLAGS, %x\n",    i->ifr_flags);     break;
68                 case SIOCSIFMETRIC:  printf("  SIOCSIFMETRIC, %d\n",   i->ifr_metric);    break;
69                 case SIOCSIFMTU:     printf("  SIOCSIFMTU, %d\n",      i->ifr_mtu);       break;
70                 case SIOCSIFBRDADDR: printf("  SIOCSIFBRDADDR, %s\n",  saddr_to_a(&(i->ifr_broadaddr))); break;
71                 case SIOCSIFDSTADDR: printf("  SIOCSIFDSTADDR, %s\n",  saddr_to_a(&(i->ifr_dstaddr  ))); break;
72                 case SIOCSIFNETMASK: printf("  SIOCSIFNETMASK, %s\n",  saddr_to_a(&(i->ifr_netmask  ))); break;
73                 case SIOCSIFADDR:    printf("  SIOCSIFADDR, %s\n",     saddr_to_a(&(i->ifr_addr     ))); break;
74                 case SIOCSIFHWADDR:  printf("  SIOCSIFHWADDR, %s\n",   saddr_to_a(&(i->ifr_hwaddr   ))); break;  /* broken */
75                 default:
76         }
77         return 0;
78 }
79 #endif
80
81
82 /* print usage and exit */
83
84 #define _(x) x
85
86 /* Set a certain interface flag. */
87 static int
88 set_flag(char *ifname, short flag)
89 {
90         struct ifreq ifr;
91         
92         strcpy(ifr.ifr_name, ifname);
93         if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
94                 perror("SIOCGIFFLAGS"); 
95                 return -1;
96         }
97         strcpy(ifr.ifr_name, ifname);
98         ifr.ifr_flags |= flag;
99         if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
100                 perror("SIOCSIFFLAGS");
101                 return -1;
102         }
103         return 0;
104 }
105
106
107 /* Clear a certain interface flag. */
108 static int
109 clr_flag(char *ifname, short flag)
110 {
111         struct ifreq ifr;
112         
113         strcpy(ifr.ifr_name, ifname);
114         if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
115                 perror("SIOCGIFFLAGS");
116                 return -1;
117         }
118         strcpy(ifr.ifr_name, ifname);
119         ifr.ifr_flags &= ~flag;
120         if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
121                 perror("SIOCSIFFLAGS");
122                 return -1;
123         }
124         return 0;
125 }
126
127 /* which element in struct ifreq to frob */
128 enum frob {
129         L_METRIC,
130         L_MTU,
131         L_DATA,
132         L_BROAD,
133         L_DEST,
134         L_MASK,
135         L_HWAD,
136 };
137
138
139 struct flag_map {
140         char *name;
141         enum frob frob;
142         int flag;
143         int sflag;
144         int action;
145 };
146
147 /* action:
148  *  2   set
149  *  4   clear
150  *  6   set/clear
151  *  8   clear/set
152  *  10  numeric
153  *  12  address
154  *  14  address/clear
155  */
156 const static struct flag_map flag_table[] = {
157         {"arp",         0,  IFF_NOARP,             0, 6},
158         {"trailers",    0,  IFF_NOTRAILERS,        0, 6},
159         {"promisc",     0,  IFF_PROMISC,           0, 8},
160         {"multicast",   0,  IFF_MULTICAST,         0, 8},
161         {"allmulti",    0,  IFF_ALLMULTI,          0, 8},
162         {"up",          0, (IFF_UP | IFF_RUNNING), 0, 2},
163         {"down",        0,  IFF_UP,                0, 4},
164         {"metric",      L_METRIC,              0, SIOCSIFMETRIC,  10},
165         {"mtu",         L_MTU,                 0, SIOCSIFMTU,     10},
166 #ifdef SIOCSKEEPALIVE
167         {"keepalive",   L_DATA,                0, SIOCSKEEPALIVE, 10},
168 #endif
169 #ifdef SIOCSOUTFILL
170         {"outfill",     L_DATA,                0, SIOCSOUTFILL,   10},
171 #endif
172         {"broadcast",   L_BROAD, IFF_BROADCAST,   SIOCSIFBRDADDR, 14},
173         {"dstaddr",     L_DEST,                0, SIOCSIFDSTADDR, 12},
174         {"netmask",     L_MASK,                0, SIOCSIFNETMASK, 12},
175         {"pointopoint", L_DEST,  IFF_POINTOPOINT, SIOCSIFDSTADDR, 14},
176         {"hw",          L_HWAD,                0, SIOCSIFHWADDR,  14},
177 };
178
179
180 /* resolve XXX.YYY.ZZZ.QQQ -> binary */
181
182 static int
183 INET_resolve(char *name, struct sockaddr_in *sin)
184 {
185         sin->sin_family = AF_INET;
186         sin->sin_port = 0;
187
188         /* Default is special, meaning 0.0.0.0. */
189         if (strcmp(name, "default")==0) {
190                 sin->sin_addr.s_addr = INADDR_ANY;
191                 return 1;
192         }
193         /* Look to see if it's a dotted quad. */
194         if (inet_aton(name, &sin->sin_addr)) {
195                 return 0;
196         }
197         /* guess not.. */
198         errno = EINVAL;
199         return -1;
200 }
201
202 /* Input an Ethernet address and convert to binary. */
203 static int
204 in_ether(char *bufp, struct sockaddr *sap)
205 {
206         unsigned char *ptr;
207         char c, *orig;
208         int i;
209         unsigned val;
210         
211         sap->sa_family = ARPHRD_ETHER;
212         ptr = sap->sa_data;
213         
214         i = 0;
215         orig = bufp;
216         while ((*bufp != '\0') && (i < ETH_ALEN)) {
217                 val = 0;
218                 c = *bufp++;
219                 if (isdigit(c))
220                         val = c - '0';
221                 else if (c >= 'a' && c <= 'f')
222                         val = c - 'a' + 10;
223                 else if (c >= 'A' && c <= 'F')
224                         val = c - 'A' + 10;
225                 else {
226 #ifdef DEBUG
227                         error_msg(
228                                 _("in_ether(%s): invalid ether address!"),
229                                 orig);
230 #endif
231                         errno = EINVAL;
232                         return -1;
233                 }
234                 val <<= 4;
235                 c = *bufp;
236                 if (isdigit(c))
237                         val |= c - '0';
238                 else if (c >= 'a' && c <= 'f')
239                         val |= c - 'a' + 10;
240                 else if (c >= 'A' && c <= 'F')
241                         val |= c - 'A' + 10;
242                 else if (c == ':' || c == 0)
243                         val >>= 4;
244                 else {
245 #ifdef DEBUG
246                         error_msg(
247                                 _("in_ether(%s): invalid ether address!"),
248                                 orig);
249 #endif
250                         errno = EINVAL;
251                         return -1;
252                 }
253                 if (c != 0)
254                         bufp++;
255                 *ptr++ = (unsigned char) (val & 0377);
256                 i++;
257                 
258                 /* optional colon already handled, don't swallow a second */
259         }
260
261         if(i != ETH_ALEN) {
262                 errno = EINVAL;
263                 return -1;
264         }
265
266         return 0;
267 }
268                 
269 #ifdef BB_FEATURE_IFCONFIG_STATUS
270 extern int display_interfaces(void);
271 #else
272 int display_interfaces(void)
273 {
274     show_usage();
275 }
276 #endif
277
278 int ifconfig_main(int argc, char **argv)
279 {
280         struct ifreq ifr;
281         struct sockaddr_in sa;
282         char **spp, *cmd;
283         int goterr = 0;
284         int r;
285         /* int didnetmask = 0;   special case input error detection no longer implemented */
286         char host[128];
287         const struct flag_map *ft;
288         int i, sense;
289         int a, ecode;
290         struct sockaddr *d;
291
292         if(argc < 2) {
293                 return(display_interfaces());
294         }
295
296         /* Create a channel to the NET kernel. */
297         if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
298                 perror_msg_and_die("socket");
299         }
300
301         /* skip argv[0] */
302
303         argc--;
304         argv++;
305
306         spp = argv;
307
308         /* get interface name */
309
310         safe_strncpy(ifr.ifr_name, *spp++, IFNAMSIZ);
311
312         /* Process the remaining arguments. */
313         while (*spp != (char *) NULL) {
314                 cmd = *spp;
315                 sense=0;
316                 if (*cmd=='-') {
317                         sense=1;
318                         cmd++;
319                 }
320                 ft = NULL;
321                 for (i=0; i<(sizeof(flag_table)/sizeof(struct flag_map)); i++) {
322                         if (strcmp(cmd, flag_table[i].name)==0) {
323                                 ft=flag_table+i;
324                                 spp++;
325                                 break;
326                         }
327                 }
328                 if (ft) {
329                         switch (ft->action+sense) {
330                         case 4:
331                         case 7:
332                         case 8:
333                         case 15:
334                                 goterr |= clr_flag(ifr.ifr_name, ft->flag);
335                                 break;
336                         case 2:
337                         case 6:
338                         case 9:
339                                 goterr |= set_flag(ifr.ifr_name, ft->flag);
340                                 break;
341                         case 10:
342                                 if (*spp == NULL)
343                                         show_usage();
344                                 a = atoi(*spp++);
345                                 switch (ft->frob) {
346                                         case L_METRIC: ifr.ifr_metric = a; break;
347                                         case L_MTU:    ifr.ifr_mtu    = a; break;
348                                         case L_DATA:   ifr.ifr_data   = (caddr_t) a; break;
349                                         default: error_msg_and_die("bugaboo");
350                                 }
351
352                                 if (ioctl(sockfd, ft->sflag, &ifr) < 0) {
353                                         perror(ft->name);  /* imperfect */
354                                         goterr++;
355                                 }
356                                 break;
357                         case 12:
358                         case 14:
359                                 if (ft->action+sense==10 && *spp == NULL) {
360                                         show_usage();
361                                         break;
362                                 }
363                                 if (*spp != NULL) {
364                                         safe_strncpy(host, *spp, (sizeof host));
365                                         spp++;
366                                         if (ft->frob == L_HWAD) {
367                                                 ecode = in_ether(host, &ifr.ifr_hwaddr);
368                                         } else {
369                                                 switch (ft->frob) {
370                                                         case L_BROAD: d = &ifr.ifr_broadaddr; break;
371                                                         case L_DEST:  d = &ifr.ifr_dstaddr;   break;
372                                                         case L_MASK:  d = &ifr.ifr_netmask;   break;
373                                                         default: error_msg_and_die("bugaboo");
374                                                 }
375                                                 ecode = INET_resolve(host, (struct sockaddr_in *) d);
376                                         }
377                                         if (ecode < 0 || ioctl(sockfd, ft->sflag, &ifr) < 0) {
378                                                 perror(ft->name);  /* imperfect */
379                                                 goterr++;
380                                         }
381                                 }
382                                 if (ft->flag != 0) {
383                                         goterr |= set_flag(ifr.ifr_name, ft->flag);
384                                 }
385                                 break;
386                         default:
387                                 show_usage();
388                         } /* end of switch */
389                         continue;
390                 }
391                 
392                 /* If the next argument is a valid hostname, assume OK. */
393                 safe_strncpy(host, *spp, (sizeof host));
394
395                 if (INET_resolve(host, &sa) < 0) {
396                         show_usage();
397                 }
398                 memcpy((char *) &ifr.ifr_addr,
399                        (char *) &sa, sizeof(struct sockaddr));
400
401                 r = ioctl(sockfd, SIOCSIFADDR, &ifr);
402
403                 if (r < 0) {
404                         perror("SIOCSIFADDR");
405                         goterr++;
406                 }
407
408                 /*
409                  * Don't do the set_flag() if the address is an alias with a - at the
410                  * end, since it's deleted already! - Roman
411                  *
412                  * Should really use regex.h here, not sure though how well it'll go
413                  * with the cross-platform support etc. 
414                  */
415                 {
416                         char *ptr;
417                         short int found_colon = 0;
418                         for (ptr = ifr.ifr_name; *ptr; ptr++ )
419                                 if (*ptr == ':') found_colon++;
420                         
421                         if (!(found_colon && *(ptr - 1) == '-'))
422                                 goterr |= set_flag(ifr.ifr_name, (IFF_UP | IFF_RUNNING));
423                 }
424                 
425                 spp++;
426
427         } /* end of while-loop */
428
429         return goterr;
430 }
431