Added some words on use of getopt in applets.
[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.3 2001/02/20 06:14:07 andersen Exp $
19  *
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <string.h>   // strcmp and friends
26 #include <ctype.h>    // isdigit and friends
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/ioctl.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <net/if.h>
33 #include <net/if_arp.h>
34 #include <linux/if_ether.h>
35 #include "busybox.h"
36
37 static int sockfd;  /* socket fd we use to manipulate stuff with */
38
39 /* print usage and exit */
40
41 #define _(x) x
42
43 /* Set a certain interface flag. */
44 static int
45 set_flag(char *ifname, short flag)
46 {
47         struct ifreq ifr;
48         
49         strcpy(ifr.ifr_name, ifname);
50         if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
51                 perror("SIOCGIFFLAGS"); 
52                 return (-1);
53         }
54         strcpy(ifr.ifr_name, ifname);
55         ifr.ifr_flags |= flag;
56         if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
57                 perror("SIOCSIFFLAGS");
58                 return -1;
59         }
60         return (0);
61 }
62
63
64 /* Clear a certain interface flag. */
65 static int
66 clr_flag(char *ifname, short flag)
67 {
68         struct ifreq ifr;
69         
70         strcpy(ifr.ifr_name, ifname);
71         if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
72                 perror("SIOCGIFFLAGS");
73                 return -1;
74         }
75         strcpy(ifr.ifr_name, ifname);
76         ifr.ifr_flags &= ~flag;
77         if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
78                 perror("SIOCSIFFLAGS");
79                 return -1;
80         }
81         return (0);
82 }
83
84 /* resolve XXX.YYY.ZZZ.QQQ -> binary */
85
86 static int
87 INET_resolve(char *name, struct sockaddr_in *sin)
88 {
89         sin->sin_family = AF_INET;
90         sin->sin_port = 0;
91
92         /* Default is special, meaning 0.0.0.0. */
93         if (!strcmp(name, "default")) {
94                 sin->sin_addr.s_addr = INADDR_ANY;
95                 return (1);
96         }
97         /* Look to see if it's a dotted quad. */
98         if (inet_aton(name, &sin->sin_addr)) {
99                 return 0;
100         }
101         /* guess not.. */
102         return -1;
103 }
104
105 /* Input an Ethernet address and convert to binary. */
106 static int
107 in_ether(char *bufp, struct sockaddr *sap)
108 {
109         unsigned char *ptr;
110         char c, *orig;
111         int i;
112         unsigned val;
113         
114         sap->sa_family = ARPHRD_ETHER;
115         ptr = sap->sa_data;
116         
117         i = 0;
118         orig = bufp;
119         while ((*bufp != '\0') && (i < ETH_ALEN)) {
120                 val = 0;
121                 c = *bufp++;
122                 if (isdigit(c))
123                         val = c - '0';
124                 else if (c >= 'a' && c <= 'f')
125                         val = c - 'a' + 10;
126                 else if (c >= 'A' && c <= 'F')
127                         val = c - 'A' + 10;
128                 else {
129 #ifdef DEBUG
130                         fprintf(stderr,
131                                 _("in_ether(%s): invalid ether address!\n"),
132                                 orig);
133 #endif
134                         errno = EINVAL;
135                         return (-1);
136                 }
137                 val <<= 4;
138                 c = *bufp;
139                 if (isdigit(c))
140                         val |= c - '0';
141                 else if (c >= 'a' && c <= 'f')
142                         val |= c - 'a' + 10;
143                 else if (c >= 'A' && c <= 'F')
144                         val |= c - 'A' + 10;
145                 else if (c == ':' || c == 0)
146                         val >>= 4;
147                 else {
148 #ifdef DEBUG
149                         fprintf(stderr,
150                                 _("in_ether(%s): invalid ether address!\n"),
151                                 orig);
152 #endif
153                         errno = EINVAL;
154                         return (-1);
155                 }
156                 if (c != 0)
157                         bufp++;
158                 *ptr++ = (unsigned char) (val & 0377);
159                 i++;
160                 
161                 /* We might get a semicolon here - not required. */
162                 if (*bufp == ':')
163                         bufp++;
164                 
165         }
166
167         if(i != ETH_ALEN) {
168                 errno = EINVAL;
169                 return -1;
170         }
171
172         return 0;
173 }
174
175 int ifconfig_main(int argc, char **argv)
176 {
177         struct ifreq ifr;
178         struct sockaddr_in sa;
179         struct sockaddr sa2;
180         char **spp;
181         int goterr = 0;
182         int r, didnetmask = 0;
183         char host[128];
184
185         if(argc < 2) {
186                 show_usage();
187         }
188
189         /* Create a channel to the NET kernel. */
190         if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
191                 perror("socket");
192                 exit(1);
193         }
194
195         /* skip argv[0] */
196
197         argc--;
198         argv++;
199
200         spp = argv;
201
202         /* get interface name */
203
204         safe_strncpy(ifr.ifr_name, *spp++, IFNAMSIZ);
205
206         /* Process the remaining arguments. */
207         while (*spp != (char *) NULL) {
208                 if (!strcmp(*spp, "arp")) {
209                         goterr |= clr_flag(ifr.ifr_name, IFF_NOARP);
210                         spp++;
211                         continue;
212                 }
213                 if (!strcmp(*spp, "-arp")) {
214                         goterr |= set_flag(ifr.ifr_name, IFF_NOARP);
215                         spp++;
216                         continue;
217                 }
218                 
219                 if (!strcmp(*spp, "trailers")) {
220                         goterr |= clr_flag(ifr.ifr_name, IFF_NOTRAILERS);
221                         spp++;
222                         continue;
223                 }
224                 if (!strcmp(*spp, "-trailers")) {
225                         goterr |= set_flag(ifr.ifr_name, IFF_NOTRAILERS);
226                         spp++;
227                         continue;
228                 }
229                 if (!strcmp(*spp, "promisc")) {
230                         goterr |= set_flag(ifr.ifr_name, IFF_PROMISC);
231                         spp++;
232                         continue;
233                 }
234                 if (!strcmp(*spp, "-promisc")) {
235                         goterr |= clr_flag(ifr.ifr_name, IFF_PROMISC);
236                         spp++;
237                         continue;
238                 }
239                 if (!strcmp(*spp, "multicast")) {
240                         goterr |= set_flag(ifr.ifr_name, IFF_MULTICAST);
241                         spp++;
242                         continue;
243                 }
244                 if (!strcmp(*spp, "-multicast")) {
245                         goterr |= clr_flag(ifr.ifr_name, IFF_MULTICAST);
246                         spp++;
247                         continue;
248                 }
249                 if (!strcmp(*spp, "allmulti")) {
250                         goterr |= set_flag(ifr.ifr_name, IFF_ALLMULTI);
251                         spp++;
252                         continue;
253                 }
254                 if (!strcmp(*spp, "-allmulti")) {
255                         goterr |= clr_flag(ifr.ifr_name, IFF_ALLMULTI);
256                         spp++;
257                         continue;
258                 }
259                 if (!strcmp(*spp, "up")) {
260                         goterr |= set_flag(ifr.ifr_name, (IFF_UP | IFF_RUNNING));
261                         spp++;
262                         continue;
263                 }
264                 if (!strcmp(*spp, "down")) {
265                         goterr |= clr_flag(ifr.ifr_name, IFF_UP);
266                         spp++;
267                         continue;
268                 }
269
270                 if (!strcmp(*spp, "metric")) {
271                         if (*++spp == NULL)
272                                 show_usage();
273                         ifr.ifr_metric = atoi(*spp);
274                         if (ioctl(sockfd, SIOCSIFMETRIC, &ifr) < 0) {
275                                 fprintf(stderr, "SIOCSIFMETRIC: %s\n", strerror(errno));
276                                 goterr++;
277                         }
278                         spp++;
279                         continue;
280                 }
281                 if (!strcmp(*spp, "mtu")) {
282                         if (*++spp == NULL)
283                                 show_usage();
284                         ifr.ifr_mtu = atoi(*spp);
285                         if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) {
286                                 fprintf(stderr, "SIOCSIFMTU: %s\n", strerror(errno));
287                                 goterr++;
288                         }
289                         spp++;
290                         continue;
291                 }
292 #ifdef SIOCSKEEPALIVE
293                 if (!strcmp(*spp, "keepalive")) {
294                         if (*++spp == NULL)
295                                 show_usage();
296                         ifr.ifr_data = (caddr_t) atoi(*spp);
297                         if (ioctl(sockfd, SIOCSKEEPALIVE, &ifr) < 0) {
298                                 fprintf(stderr, "SIOCSKEEPALIVE: %s\n", strerror(errno));
299                                 goterr++;
300                         }
301                         spp++;
302                         continue;
303                 }
304 #endif
305
306 #ifdef SIOCSOUTFILL
307                 if (!strcmp(*spp, "outfill")) {
308                         if (*++spp == NULL)
309                                 show_usage();
310                         ifr.ifr_data = (caddr_t) atoi(*spp);
311                         if (ioctl(sockfd, SIOCSOUTFILL, &ifr) < 0) {
312                                 fprintf(stderr, "SIOCSOUTFILL: %s\n", strerror(errno));
313                                 goterr++;
314                         }
315                         spp++;
316                         continue;
317                 }
318 #endif
319
320                 if (!strcmp(*spp, "-broadcast")) {
321                         goterr |= clr_flag(ifr.ifr_name, IFF_BROADCAST);
322                         spp++;
323                         continue;
324                 }
325                 if (!strcmp(*spp, "broadcast")) {
326                         if (*++spp != NULL) {
327                                 safe_strncpy(host, *spp, (sizeof host));
328                                 if (INET_resolve(host, &sa) < 0) {
329                                         goterr++;
330                                         spp++;
331                                         continue;
332                                 }
333                                 memcpy((char *) &ifr.ifr_broadaddr,
334                                        (char *) &sa,
335                                        sizeof(struct sockaddr));
336                                 if (ioctl(sockfd, SIOCSIFBRDADDR, &ifr) < 0) {
337                                         perror("SIOCSIFBRDADDR");
338                                         goterr++;
339                                 }
340                                 spp++;
341                         }
342                         goterr |= set_flag(ifr.ifr_name, IFF_BROADCAST);
343                         continue;
344                 }
345                 if (!strcmp(*spp, "dstaddr")) {
346                         if (*++spp == NULL)
347                                 show_usage();
348                         safe_strncpy(host, *spp, (sizeof host));
349                         if (INET_resolve(host, &sa) < 0) {
350                                 goterr++;
351                                 spp++;
352                                 continue;
353                         }
354                         memcpy((char *) &ifr.ifr_dstaddr, (char *) &sa,
355                                sizeof(struct sockaddr));
356                         if (ioctl(sockfd, SIOCSIFDSTADDR, &ifr) < 0) {
357                                 fprintf(stderr, "SIOCSIFDSTADDR: %s\n",
358                                         strerror(errno));
359                                 goterr++;
360                         }
361                         spp++;
362                         continue;
363                 }
364                 if (!strcmp(*spp, "netmask")) {
365                         if (*++spp == NULL || didnetmask)
366                                 show_usage();
367                         safe_strncpy(host, *spp, (sizeof host));
368                         if (INET_resolve(host, &sa) < 0) {
369                                 goterr++;
370                                 spp++;
371                                 continue;
372                         }
373                         didnetmask++;
374                         memcpy((char *) &ifr.ifr_netmask, (char *) &sa,
375                                sizeof(struct sockaddr));
376                         if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
377                                 perror("SIOCSIFNETMASK");
378                                 goterr++;
379                         }
380                         spp++;
381                         continue;
382                 }
383
384                 if (!strcmp(*spp, "-pointopoint")) {
385                         goterr |= clr_flag(ifr.ifr_name, IFF_POINTOPOINT);
386                         spp++;
387                         continue;
388                 }
389                 if (!strcmp(*spp, "pointopoint")) {
390                         if (*(spp + 1) != NULL) {
391                                 spp++;
392                                 safe_strncpy(host, *spp, (sizeof host));
393                                 if (INET_resolve(host, &sa)) {
394                                         goterr++;
395                                         spp++;
396                                         continue;
397                                 }
398                                 memcpy((char *) &ifr.ifr_dstaddr, (char *) &sa,
399                                        sizeof(struct sockaddr));
400                                 if (ioctl(sockfd, SIOCSIFDSTADDR, &ifr) < 0) {
401                                         perror("SIOCSIFDSTADDR");
402                                         goterr++;
403                                 }
404                         }
405                         goterr |= set_flag(ifr.ifr_name, IFF_POINTOPOINT);
406                         spp++;
407                         continue;
408                 };
409
410                 if (!strcmp(*spp, "hw")) {
411                         if (*++spp == NULL || strcmp("ether", *spp)) {
412                                 show_usage();
413                         }
414                                 
415                         if (*++spp == NULL) {
416                                 /* silently ignore it if no address */
417                                 continue;
418                         }
419
420                         safe_strncpy(host, *spp, (sizeof host));
421                         if (in_ether(host, &sa2) < 0) {
422                                 fprintf(stderr, "invalid hw-addr %s\n", host);
423                                 goterr++;
424                                 spp++;
425                                 continue;
426                         }
427                         memcpy((char *) &ifr.ifr_hwaddr, (char *) &sa2,
428                                sizeof(struct sockaddr));
429                         if (ioctl(sockfd, SIOCSIFHWADDR, &ifr) < 0) {
430                                 perror("SIOCSIFHWADDR");
431                                 goterr++;
432                         }
433                         spp++;
434                         continue;
435                 }
436
437                 /* If the next argument is a valid hostname, assume OK. */
438                 safe_strncpy(host, *spp, (sizeof host));
439
440                 if (INET_resolve(host, &sa) < 0) {
441                         show_usage();
442                 }
443                 memcpy((char *) &ifr.ifr_addr,
444                        (char *) &sa, sizeof(struct sockaddr));
445
446                 r = ioctl(sockfd, SIOCSIFADDR, &ifr);
447
448                 if (r < 0) {
449                         perror("SIOCSIFADDR");
450                         goterr++;
451                 }
452
453                 /*
454                  * Don't do the set_flag() if the address is an alias with a - at the
455                  * end, since it's deleted already! - Roman
456                  *
457                  * Should really use regex.h here, not sure though how well it'll go
458                  * with the cross-platform support etc. 
459                  */
460                 {
461                         char *ptr;
462                         short int found_colon = 0;
463                         for (ptr = ifr.ifr_name; *ptr; ptr++ )
464                                 if (*ptr == ':') found_colon++;
465                         
466                         if (!(found_colon && *(ptr - 1) == '-'))
467                                 goterr |= set_flag(ifr.ifr_name, (IFF_UP | IFF_RUNNING));
468                 }
469                 
470                 spp++;
471
472         } /* end of while-loop */
473
474         exit(0);
475 }
476