204febf7d5a54fc3e8c8f4b3dec704acf29bc422
[oweals/busybox.git] / networking / interface.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * stolen from net-tools-1.59 and stripped down for busybox by
4  *                      Erik Andersen <andersen@codepoet.org>
5  *
6  * Heavily modified by Manuel Novoa III       Mar 12, 2001
7  *
8  * Added print_bytes_scaled function to reduce code size.
9  * Added some (potentially) missing defines.
10  * Improved display support for -a and for a named interface.
11  *
12  * -----------------------------------------------------------
13  *
14  * ifconfig   This file contains an implementation of the command
15  *              that either displays or sets the characteristics of
16  *              one or more of the system's networking interfaces.
17  *
18  *
19  * Author:      Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
20  *              and others.  Copyright 1993 MicroWalt Corporation
21  *
22  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
23  *
24  * Patched to support 'add' and 'del' keywords for INET(4) addresses
25  * by Mrs. Brisby <mrs.brisby@nimh.org>
26  *
27  * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
28  *                     - gettext instead of catgets for i18n
29  *          10/1998  - Andi Kleen. Use interface list primitives.
30  *          20001008 - Bernd Eckenfels, Patch from RH for setting mtu
31  *                      (default AF was wrong)
32  */
33
34 #include <net/if.h>
35 #include <net/if_arp.h>
36 #include "inet_common.h"
37 #include "libbb.h"
38
39 #if ENABLE_FEATURE_IPV6
40 # define HAVE_AFINET6 1
41 #else
42 # undef HAVE_AFINET6
43 #endif
44
45 #define _PATH_PROCNET_DEV               "/proc/net/dev"
46 #define _PATH_PROCNET_IFINET6           "/proc/net/if_inet6"
47
48 #ifdef HAVE_AFINET6
49
50 #ifndef _LINUX_IN6_H
51 /*
52  *    This is in linux/include/net/ipv6.h.
53  */
54
55 struct in6_ifreq {
56         struct in6_addr ifr6_addr;
57         uint32_t ifr6_prefixlen;
58         unsigned int ifr6_ifindex;
59 };
60
61 #endif
62
63 #endif /* HAVE_AFINET6 */
64
65 /* Defines for glibc2.0 users. */
66 #ifndef SIOCSIFTXQLEN
67 #define SIOCSIFTXQLEN      0x8943
68 #define SIOCGIFTXQLEN      0x8942
69 #endif
70
71 /* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
72 #ifndef ifr_qlen
73 #define ifr_qlen        ifr_ifru.ifru_mtu
74 #endif
75
76 #ifndef HAVE_TXQUEUELEN
77 #define HAVE_TXQUEUELEN 1
78 #endif
79
80 #ifndef IFF_DYNAMIC
81 #define IFF_DYNAMIC     0x8000  /* dialup device with changing addresses */
82 #endif
83
84 /* Display an Internet socket address. */
85 static const char *INET_sprint(struct sockaddr *sap, int numeric)
86 {
87         static char *buff;
88
89         free(buff);
90         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
91                 return "[NONE SET]";
92         buff = INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00);
93         return buff;
94 }
95
96 #ifdef UNUSED_AND_BUGGY
97 static int INET_getsock(char *bufp, struct sockaddr *sap)
98 {
99         char *sp = bufp, *bp;
100         unsigned int i;
101         unsigned val;
102         struct sockaddr_in *sock_in;
103
104         sock_in = (struct sockaddr_in *) sap;
105         sock_in->sin_family = AF_INET;
106         sock_in->sin_port = 0;
107
108         val = 0;
109         bp = (char *) &val;
110         for (i = 0; i < sizeof(sock_in->sin_addr.s_addr); i++) {
111                 *sp = toupper(*sp);
112
113                 if ((unsigned)(*sp - 'A') <= 5)
114                         bp[i] |= (int) (*sp - ('A' - 10));
115                 else if (isdigit(*sp))
116                         bp[i] |= (int) (*sp - '0');
117                 else
118                         return -1;
119
120                 bp[i] <<= 4;
121                 sp++;
122                 *sp = toupper(*sp);
123
124                 if ((unsigned)(*sp - 'A') <= 5)
125                         bp[i] |= (int) (*sp - ('A' - 10));
126                 else if (isdigit(*sp))
127                         bp[i] |= (int) (*sp - '0');
128                 else
129                         return -1;
130
131                 sp++;
132         }
133         sock_in->sin_addr.s_addr = htonl(val);
134
135         return (sp - bufp);
136 }
137 #endif
138
139 static int INET_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
140 {
141         return INET_resolve(bufp, (struct sockaddr_in *) sap, 0);
142 /*
143         switch (type) {
144         case 1:
145                 return (INET_getsock(bufp, sap));
146         case 256:
147                 return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
148         default:
149                 return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
150         }
151 */
152 }
153
154 static const struct aftype inet_aftype = {
155         .name =         "inet",
156         .title =        "DARPA Internet",
157         .af =           AF_INET,
158         .alen =         4,
159         .sprint =       INET_sprint,
160         .input =        INET_input,
161 };
162
163 #ifdef HAVE_AFINET6
164
165 /* Display an Internet socket address. */
166 /* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
167 static const char *INET6_sprint(struct sockaddr *sap, int numeric)
168 {
169         static char *buff;
170
171         free(buff);
172         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
173                 return "[NONE SET]";
174         buff = INET6_rresolve((struct sockaddr_in6 *) sap, numeric);
175         return buff;
176 }
177
178 #ifdef UNUSED
179 static int INET6_getsock(char *bufp, struct sockaddr *sap)
180 {
181         struct sockaddr_in6 *sin6;
182
183         sin6 = (struct sockaddr_in6 *) sap;
184         sin6->sin6_family = AF_INET6;
185         sin6->sin6_port = 0;
186
187         if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
188                 return -1;
189
190         return 16;                      /* ?;) */
191 }
192 #endif
193
194 static int INET6_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
195 {
196         return INET6_resolve(bufp, (struct sockaddr_in6 *) sap);
197 /*
198         switch (type) {
199         case 1:
200                 return (INET6_getsock(bufp, sap));
201         default:
202                 return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
203         }
204 */
205 }
206
207 static const struct aftype inet6_aftype = {
208         .name =         "inet6",
209         .title =        "IPv6",
210         .af =           AF_INET6,
211         .alen =         sizeof(struct in6_addr),
212         .sprint =       INET6_sprint,
213         .input =        INET6_input,
214 };
215
216 #endif /* HAVE_AFINET6 */
217
218 /* Display an UNSPEC address. */
219 static char *UNSPEC_print(unsigned char *ptr)
220 {
221         static char *buff;
222
223         char *pos;
224         unsigned int i;
225
226         if (!buff);
227                 buff = xmalloc(sizeof(struct sockaddr) * 3 + 1);
228         pos = buff;
229         for (i = 0; i < sizeof(struct sockaddr); i++) {
230                 /* careful -- not every libc's sprintf returns # bytes written */
231                 sprintf(pos, "%02X-", (*ptr++ & 0377));
232                 pos += 3;
233         }
234         /* Erase trailing "-".  Works as long as sizeof(struct sockaddr) != 0 */
235         *--pos = '\0';
236         return buff;
237 }
238
239 /* Display an UNSPEC socket address. */
240 static const char *UNSPEC_sprint(struct sockaddr *sap, int numeric)
241 {
242         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
243                 return "[NONE SET]";
244         return UNSPEC_print((unsigned char *)sap->sa_data);
245 }
246
247 static const struct aftype unspec_aftype = {
248         .name   = "unspec",
249         .title  = "UNSPEC",
250         .af     = AF_UNSPEC,
251         .alen    = 0,
252         .print  = UNSPEC_print,
253         .sprint = UNSPEC_sprint,
254 };
255
256 static const struct aftype *const aftypes[] = {
257         &inet_aftype,
258 #ifdef HAVE_AFINET6
259         &inet6_aftype,
260 #endif
261         &unspec_aftype,
262         NULL
263 };
264
265 /* Check our protocol family table for this family. */
266 const struct aftype *get_aftype(const char *name)
267 {
268         const struct aftype *const *afp;
269
270         afp = aftypes;
271         while (*afp != NULL) {
272                 if (!strcmp((*afp)->name, name))
273                         return (*afp);
274                 afp++;
275         }
276         return NULL;
277 }
278
279 /* Check our protocol family table for this family. */
280 static const struct aftype *get_afntype(int af)
281 {
282         const struct aftype *const *afp;
283
284         afp = aftypes;
285         while (*afp != NULL) {
286                 if ((*afp)->af == af)
287                         return *afp;
288                 afp++;
289         }
290         return NULL;
291 }
292
293 struct user_net_device_stats {
294         unsigned long long rx_packets;  /* total packets received       */
295         unsigned long long tx_packets;  /* total packets transmitted    */
296         unsigned long long rx_bytes;    /* total bytes received         */
297         unsigned long long tx_bytes;    /* total bytes transmitted      */
298         unsigned long rx_errors;        /* bad packets received         */
299         unsigned long tx_errors;        /* packet transmit problems     */
300         unsigned long rx_dropped;       /* no space in linux buffers    */
301         unsigned long tx_dropped;       /* no space available in linux  */
302         unsigned long rx_multicast;     /* multicast packets received   */
303         unsigned long rx_compressed;
304         unsigned long tx_compressed;
305         unsigned long collisions;
306
307         /* detailed rx_errors: */
308         unsigned long rx_length_errors;
309         unsigned long rx_over_errors;   /* receiver ring buff overflow  */
310         unsigned long rx_crc_errors;    /* recved pkt with crc error    */
311         unsigned long rx_frame_errors;  /* recv'd frame alignment error */
312         unsigned long rx_fifo_errors;   /* recv'r fifo overrun          */
313         unsigned long rx_missed_errors; /* receiver missed packet     */
314         /* detailed tx_errors */
315         unsigned long tx_aborted_errors;
316         unsigned long tx_carrier_errors;
317         unsigned long tx_fifo_errors;
318         unsigned long tx_heartbeat_errors;
319         unsigned long tx_window_errors;
320 };
321
322 struct interface {
323         struct interface *next, *prev;
324         char name[IFNAMSIZ];    /* interface name        */
325         short type;                     /* if type               */
326         short flags;            /* various flags         */
327         int metric;                     /* routing metric        */
328         int mtu;                        /* MTU value             */
329         int tx_queue_len;       /* transmit queue length */
330         struct ifmap map;       /* hardware setup        */
331         struct sockaddr addr;   /* IP address            */
332         struct sockaddr dstaddr;        /* P-P IP address        */
333         struct sockaddr broadaddr;      /* IP broadcast address  */
334         struct sockaddr netmask;        /* IP network mask       */
335         int has_ip;
336         char hwaddr[32];        /* HW address            */
337         int statistics_valid;
338         struct user_net_device_stats stats;     /* statistics            */
339         int keepalive;          /* keepalive value for SLIP */
340         int outfill;            /* outfill value for SLIP */
341 };
342
343
344 smallint interface_opt_a;       /* show all interfaces */
345
346 static struct interface *int_list, *int_last;
347
348
349 #if 0
350 /* like strcmp(), but knows about numbers */
351 except that the freshly added calls to xatoul() brf on ethernet aliases with
352 uClibc with e.g.: ife->name='lo'  name='eth0:1'
353 static int nstrcmp(const char *a, const char *b)
354 {
355         const char *a_ptr = a;
356         const char *b_ptr = b;
357
358         while (*a == *b) {
359                 if (*a == '\0') {
360                         return 0;
361                 }
362                 if (!isdigit(*a) && isdigit(*(a+1))) {
363                         a_ptr = a+1;
364                         b_ptr = b+1;
365                 }
366                 a++;
367                 b++;
368         }
369
370         if (isdigit(*a) && isdigit(*b)) {
371                 return xatoul(a_ptr) > xatoul(b_ptr) ? 1 : -1;
372         }
373         return *a - *b;
374 }
375 #endif
376
377 static struct interface *add_interface(char *name)
378 {
379         struct interface *ife, **nextp, *new;
380
381         for (ife = int_last; ife; ife = ife->prev) {
382                 int n = /*n*/strcmp(ife->name, name);
383
384                 if (n == 0)
385                         return ife;
386                 if (n < 0)
387                         break;
388         }
389
390         new = xzalloc(sizeof(*new));
391         safe_strncpy(new->name, name, IFNAMSIZ);
392         nextp = ife ? &ife->next : &int_list;
393         new->prev = ife;
394         new->next = *nextp;
395         if (new->next)
396                 new->next->prev = new;
397         else
398                 int_last = new;
399         *nextp = new;
400         return new;
401 }
402
403 static char *get_name(char *name, char *p)
404 {
405         /* Extract <name> from nul-terminated p where p matches
406            <name>: after leading whitespace.
407            If match is not made, set name empty and return unchanged p */
408         int namestart=0, nameend=0;
409         while (isspace(p[namestart]))
410                 namestart++;
411         nameend=namestart;
412         while (p[nameend] && p[nameend]!=':' && !isspace(p[nameend]))
413                 nameend++;
414         if (p[nameend]==':') {
415                 if ((nameend-namestart)<IFNAMSIZ) {
416                         memcpy(name,&p[namestart],nameend-namestart);
417                         name[nameend-namestart]='\0';
418                         p=&p[nameend];
419                 } else {
420                         /* Interface name too large */
421                         name[0]='\0';
422                 }
423         } else {
424                 /* trailing ':' not found - return empty */
425                 name[0]='\0';
426         }
427         return p + 1;
428 }
429
430 /* If scanf supports size qualifiers for %n conversions, then we can
431  * use a modified fmt that simply stores the position in the fields
432  * having no associated fields in the proc string.  Of course, we need
433  * to zero them again when we're done.  But that is smaller than the
434  * old approach of multiple scanf occurrences with large numbers of
435  * args. */
436
437 /* static const char *const ss_fmt[] = { */
438 /*      "%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */
439 /*      "%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */
440 /*      "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */
441 /* }; */
442
443         /* Lie about the size of the int pointed to for %n. */
444 #if INT_MAX == LONG_MAX
445 static const char *const ss_fmt[] = {
446         "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
447         "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
448         "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
449 };
450 #else
451 static const char *const ss_fmt[] = {
452         "%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu",
453         "%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu",
454         "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu"
455 };
456
457 #endif
458
459 static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
460 {
461         memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
462
463         sscanf(bp, ss_fmt[procnetdev_vsn],
464                    &ife->stats.rx_bytes, /* missing for 0 */
465                    &ife->stats.rx_packets,
466                    &ife->stats.rx_errors,
467                    &ife->stats.rx_dropped,
468                    &ife->stats.rx_fifo_errors,
469                    &ife->stats.rx_frame_errors,
470                    &ife->stats.rx_compressed, /* missing for <= 1 */
471                    &ife->stats.rx_multicast, /* missing for <= 1 */
472                    &ife->stats.tx_bytes, /* missing for 0 */
473                    &ife->stats.tx_packets,
474                    &ife->stats.tx_errors,
475                    &ife->stats.tx_dropped,
476                    &ife->stats.tx_fifo_errors,
477                    &ife->stats.collisions,
478                    &ife->stats.tx_carrier_errors,
479                    &ife->stats.tx_compressed /* missing for <= 1 */
480                    );
481
482         if (procnetdev_vsn <= 1) {
483                 if (procnetdev_vsn == 0) {
484                         ife->stats.rx_bytes = 0;
485                         ife->stats.tx_bytes = 0;
486                 }
487                 ife->stats.rx_multicast = 0;
488                 ife->stats.rx_compressed = 0;
489                 ife->stats.tx_compressed = 0;
490         }
491 }
492
493 static inline int procnetdev_version(char *buf)
494 {
495         if (strstr(buf, "compressed"))
496                 return 2;
497         if (strstr(buf, "bytes"))
498                 return 1;
499         return 0;
500 }
501
502 static int if_readconf(void)
503 {
504         int numreqs = 30;
505         struct ifconf ifc;
506         struct ifreq *ifr;
507         int n, err = -1;
508         int skfd;
509
510         ifc.ifc_buf = NULL;
511
512         /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
513            (as of 2.1.128) */
514         skfd = socket(AF_INET, SOCK_DGRAM, 0);
515         if (skfd < 0) {
516                 bb_perror_msg("error: no inet socket available");
517                 return -1;
518         }
519
520         for (;;) {
521                 ifc.ifc_len = sizeof(struct ifreq) * numreqs;
522                 ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
523
524                 if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) {
525                         bb_perror_msg("SIOCGIFCONF");
526                         goto out;
527                 }
528                 if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) {
529                         /* assume it overflowed and try again */
530                         numreqs += 10;
531                         continue;
532                 }
533                 break;
534         }
535
536         ifr = ifc.ifc_req;
537         for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
538                 add_interface(ifr->ifr_name);
539                 ifr++;
540         }
541         err = 0;
542
543  out:
544         close(skfd);
545         free(ifc.ifc_buf);
546         return err;
547 }
548
549 static int if_readlist_proc(char *target)
550 {
551         static smallint proc_read;
552
553         FILE *fh;
554         char buf[512];
555         struct interface *ife;
556         int err, procnetdev_vsn;
557
558         if (proc_read)
559                 return 0;
560         if (!target)
561                 proc_read = 1;
562
563         fh = fopen(_PATH_PROCNET_DEV, "r");
564         if (!fh) {
565                 bb_perror_msg("warning: cannot open %s, limiting output", _PATH_PROCNET_DEV);
566                 return if_readconf();
567         }
568         fgets(buf, sizeof buf, fh);     /* eat line */
569         fgets(buf, sizeof buf, fh);
570
571         procnetdev_vsn = procnetdev_version(buf);
572
573         err = 0;
574         while (fgets(buf, sizeof buf, fh)) {
575                 char *s, name[128];
576
577                 s = get_name(name, buf);
578                 ife = add_interface(name);
579                 get_dev_fields(s, ife, procnetdev_vsn);
580                 ife->statistics_valid = 1;
581                 if (target && !strcmp(target, name))
582                         break;
583         }
584         if (ferror(fh)) {
585                 bb_perror_msg(_PATH_PROCNET_DEV);
586                 err = -1;
587                 proc_read = 0;
588         }
589         fclose(fh);
590         return err;
591 }
592
593 static int if_readlist(void)
594 {
595         int err = if_readlist_proc(NULL);
596         /* Needed in order to get ethN:M aliases */
597         if (!err)
598                 err = if_readconf();
599         return err;
600 }
601
602 /* Fetch the interface configuration from the kernel. */
603 static int if_fetch(struct interface *ife)
604 {
605         struct ifreq ifr;
606         char *ifname = ife->name;
607         int skfd;
608
609         skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
610
611         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
612         if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
613                 close(skfd);
614                 return -1;
615         }
616         ife->flags = ifr.ifr_flags;
617
618         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
619         memset(ife->hwaddr, 0, 32);
620         if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
621                 memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
622
623         ife->type = ifr.ifr_hwaddr.sa_family;
624
625         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
626         ife->metric = 0;
627         if (ioctl(skfd, SIOCGIFMETRIC, &ifr) >= 0)
628                 ife->metric = ifr.ifr_metric;
629
630         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
631         ife->mtu = 0;
632         if (ioctl(skfd, SIOCGIFMTU, &ifr) >= 0)
633                 ife->mtu = ifr.ifr_mtu;
634
635         memset(&ife->map, 0, sizeof(struct ifmap));
636 #ifdef SIOCGIFMAP
637         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
638         if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
639                 ife->map = ifr.ifr_map;
640 #endif
641
642 #ifdef HAVE_TXQUEUELEN
643         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
644         ife->tx_queue_len = -1; /* unknown value */
645         if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0)
646                 ife->tx_queue_len = ifr.ifr_qlen;
647 #else
648         ife->tx_queue_len = -1; /* unknown value */
649 #endif
650
651         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
652         ifr.ifr_addr.sa_family = AF_INET;
653         memset(&ife->addr, 0, sizeof(struct sockaddr));
654         if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
655                 ife->has_ip = 1;
656                 ife->addr = ifr.ifr_addr;
657                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
658                 memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
659                 if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) >= 0)
660                         ife->dstaddr = ifr.ifr_dstaddr;
661
662                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
663                 memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
664                 if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0)
665                         ife->broadaddr = ifr.ifr_broadaddr;
666
667                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
668                 memset(&ife->netmask, 0, sizeof(struct sockaddr));
669                 if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0)
670                         ife->netmask = ifr.ifr_netmask;
671         }
672
673         close(skfd);
674         return 0;
675 }
676
677
678 static int do_if_fetch(struct interface *ife)
679 {
680         if (if_fetch(ife) < 0) {
681                 const char *errmsg;
682
683                 if (errno == ENODEV) {
684                         /* Give better error message for this case. */
685                         errmsg = "Device not found";
686                 } else {
687                         errmsg = strerror(errno);
688                 }
689                 bb_error_msg("%s: error fetching interface information: %s",
690                                 ife->name, errmsg);
691                 return -1;
692         }
693         return 0;
694 }
695
696 static const struct hwtype unspec_hwtype = {
697         .name =         "unspec",
698         .title =        "UNSPEC",
699         .type =         -1,
700         .print =        UNSPEC_print
701 };
702
703 static const struct hwtype loop_hwtype = {
704         .name =         "loop",
705         .title =        "Local Loopback",
706         .type =         ARPHRD_LOOPBACK
707 };
708
709 #include <net/if_arp.h>
710
711 #if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
712 #include <net/ethernet.h>
713 #else
714 #include <linux/if_ether.h>
715 #endif
716
717 /* Display an Ethernet address in readable format. */
718 static char *pr_ether(unsigned char *ptr)
719 {
720         static char *buff;
721
722         free(buff);
723         buff = xasprintf("%02X:%02X:%02X:%02X:%02X:%02X",
724                          (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
725                          (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
726                 );
727         return buff;
728 }
729
730 static int in_ether(const char *bufp, struct sockaddr *sap);
731
732 static const struct hwtype ether_hwtype = {
733         .name =         "ether",
734         .title =        "Ethernet",
735         .type =         ARPHRD_ETHER,
736         .alen =         ETH_ALEN,
737         .print =        pr_ether,
738         .input =        in_ether
739 };
740
741 static unsigned hexchar2int(char c)
742 {
743         if (isdigit(c))
744                 return c - '0';
745         c &= ~0x20; /* a -> A */
746         if ((unsigned)(c - 'A') <= 5)
747                 return c - ('A' - 10);
748         return ~0U;
749 }
750
751 /* Input an Ethernet address and convert to binary. */
752 static int in_ether(const char *bufp, struct sockaddr *sap)
753 {
754         unsigned char *ptr;
755         char c;
756         int i;
757         unsigned val;
758
759         sap->sa_family = ether_hwtype.type;
760         ptr = (unsigned char*) sap->sa_data;
761
762         i = 0;
763         while ((*bufp != '\0') && (i < ETH_ALEN)) {
764                 val = hexchar2int(*bufp++) * 0x10;
765                 if (val > 0xff) {
766                         errno = EINVAL;
767                         return -1;
768                 }
769                 c = *bufp;
770                 if (c == ':' || c == 0)
771                         val >>= 4;
772                 else {
773                         val |= hexchar2int(c);
774                         if (val > 0xff) {
775                                 errno = EINVAL;
776                                 return -1;
777                         }
778                 }
779                 if (c != 0)
780                         bufp++;
781                 *ptr++ = (unsigned char) val;
782                 i++;
783
784                 /* We might get a semicolon here - not required. */
785                 if (*bufp == ':') {
786                         bufp++;
787                 }
788         }
789         return 0;
790 }
791
792 #include <net/if_arp.h>
793
794 static const struct hwtype ppp_hwtype = {
795         .name =         "ppp",
796         .title =        "Point-to-Point Protocol",
797         .type =         ARPHRD_PPP
798 };
799
800 #if ENABLE_FEATURE_IPV6
801 static const struct hwtype sit_hwtype = {
802         .name =                 "sit",
803         .title =                "IPv6-in-IPv4",
804         .type =                 ARPHRD_SIT,
805         .print =                UNSPEC_print,
806         .suppress_null_addr =   1
807 } ;
808 #endif
809
810 static const struct hwtype *const hwtypes[] = {
811         &loop_hwtype,
812         &ether_hwtype,
813         &ppp_hwtype,
814         &unspec_hwtype,
815 #if ENABLE_FEATURE_IPV6
816         &sit_hwtype,
817 #endif
818         NULL
819 };
820
821 #ifdef IFF_PORTSEL
822 static const char *const if_port_text[] = {
823         /* Keep in step with <linux/netdevice.h> */
824         "unknown",
825         "10base2",
826         "10baseT",
827         "AUI",
828         "100baseT",
829         "100baseTX",
830         "100baseFX",
831         NULL
832 };
833 #endif
834
835 /* Check our hardware type table for this type. */
836 const struct hwtype *get_hwtype(const char *name)
837 {
838         const struct hwtype *const *hwp;
839
840         hwp = hwtypes;
841         while (*hwp != NULL) {
842                 if (!strcmp((*hwp)->name, name))
843                         return (*hwp);
844                 hwp++;
845         }
846         return NULL;
847 }
848
849 /* Check our hardware type table for this type. */
850 const struct hwtype *get_hwntype(int type)
851 {
852         const struct hwtype *const *hwp;
853
854         hwp = hwtypes;
855         while (*hwp != NULL) {
856                 if ((*hwp)->type == type)
857                         return *hwp;
858                 hwp++;
859         }
860         return NULL;
861 }
862
863 /* return 1 if address is all zeros */
864 static int hw_null_address(const struct hwtype *hw, void *ap)
865 {
866         unsigned int i;
867         unsigned char *address = (unsigned char *) ap;
868
869         for (i = 0; i < hw->alen; i++)
870                 if (address[i])
871                         return 0;
872         return 1;
873 }
874
875 static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
876
877 static void print_bytes_scaled(unsigned long long ull, const char *end)
878 {
879         unsigned long long int_part;
880         const char *ext;
881         unsigned int frac_part;
882         int i;
883
884         frac_part = 0;
885         ext = TRext;
886         int_part = ull;
887         i = 4;
888         do {
889                 if (int_part >= 1024) {
890                         frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
891                         int_part /= 1024;
892                         ext += 3;       /* KiB, MiB, GiB, TiB */
893                 }
894                 --i;
895         } while (i);
896
897         printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
898 }
899
900 static const char *const ife_print_flags_strs[] = {
901         "UP ",
902         "BROADCAST ",
903         "DEBUG ",
904         "LOOPBACK ",
905         "POINTOPOINT ",
906         "NOTRAILERS ",
907         "RUNNING ",
908         "NOARP ",
909         "PROMISC ",
910         "ALLMULTI ",
911         "SLAVE ",
912         "MASTER ",
913         "MULTICAST ",
914 #ifdef HAVE_DYNAMIC
915         "DYNAMIC "
916 #endif
917 };
918
919 static const unsigned short ife_print_flags_mask[] = {
920         IFF_UP,
921         IFF_BROADCAST,
922         IFF_DEBUG,
923         IFF_LOOPBACK,
924         IFF_POINTOPOINT,
925         IFF_NOTRAILERS,
926         IFF_RUNNING,
927         IFF_NOARP,
928         IFF_PROMISC,
929         IFF_ALLMULTI,
930         IFF_SLAVE,
931         IFF_MASTER,
932         IFF_MULTICAST,
933 #ifdef HAVE_DYNAMIC
934         IFF_DYNAMIC
935 #endif
936         0
937 };
938
939 static void ife_print(struct interface *ptr)
940 {
941         const struct aftype *ap;
942         const struct hwtype *hw;
943         int hf;
944         int can_compress = 0;
945
946 #ifdef HAVE_AFINET6
947         FILE *f;
948         char addr6[40], devname[20];
949         struct sockaddr_in6 sap;
950         int plen, scope, dad_status, if_idx;
951         char addr6p[8][5];
952 #endif
953
954         ap = get_afntype(ptr->addr.sa_family);
955         if (ap == NULL)
956                 ap = get_afntype(0);
957
958         hf = ptr->type;
959
960         if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
961                 can_compress = 1;
962
963         hw = get_hwntype(hf);
964         if (hw == NULL)
965                 hw = get_hwntype(-1);
966
967         printf("%-9.9s Link encap:%s  ", ptr->name, hw->title);
968         /* For some hardware types (eg Ash, ATM) we don't print the
969            hardware address if it's null.  */
970         if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
971                                                                 hw->suppress_null_addr)))
972                 printf("HWaddr %s  ", hw->print((unsigned char *)ptr->hwaddr));
973 #ifdef IFF_PORTSEL
974         if (ptr->flags & IFF_PORTSEL) {
975                 printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
976                 if (ptr->flags & IFF_AUTOMEDIA)
977                         printf("(auto)");
978         }
979 #endif
980         puts("");
981
982         if (ptr->has_ip) {
983                 printf("          %s addr:%s ", ap->name,
984                            ap->sprint(&ptr->addr, 1));
985                 if (ptr->flags & IFF_POINTOPOINT) {
986                         printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
987                 }
988                 if (ptr->flags & IFF_BROADCAST) {
989                         printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
990                 }
991                 printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
992         }
993
994 #ifdef HAVE_AFINET6
995
996 #define IPV6_ADDR_ANY           0x0000U
997
998 #define IPV6_ADDR_UNICAST       0x0001U
999 #define IPV6_ADDR_MULTICAST     0x0002U
1000 #define IPV6_ADDR_ANYCAST       0x0004U
1001
1002 #define IPV6_ADDR_LOOPBACK      0x0010U
1003 #define IPV6_ADDR_LINKLOCAL     0x0020U
1004 #define IPV6_ADDR_SITELOCAL     0x0040U
1005
1006 #define IPV6_ADDR_COMPATv4      0x0080U
1007
1008 #define IPV6_ADDR_SCOPE_MASK    0x00f0U
1009
1010 #define IPV6_ADDR_MAPPED        0x1000U
1011 #define IPV6_ADDR_RESERVED      0x2000U /* reserved address space */
1012
1013         f = fopen(_PATH_PROCNET_IFINET6, "r");
1014         if (f != NULL) {
1015                 while (fscanf
1016                            (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
1017                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
1018                                 addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
1019                                 &dad_status, devname) != EOF
1020                 ) {
1021                         if (!strcmp(devname, ptr->name)) {
1022                                 sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
1023                                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
1024                                                 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
1025                                 inet_pton(AF_INET6, addr6,
1026                                                   (struct sockaddr *) &sap.sin6_addr);
1027                                 sap.sin6_family = AF_INET6;
1028                                 printf("          inet6 addr: %s/%d",
1029                                            INET6_sprint((struct sockaddr *) &sap, 1),
1030                                            plen);
1031                                 printf(" Scope:");
1032                                 switch (scope & IPV6_ADDR_SCOPE_MASK) {
1033                                 case 0:
1034                                         puts("Global");
1035                                         break;
1036                                 case IPV6_ADDR_LINKLOCAL:
1037                                         puts("Link");
1038                                         break;
1039                                 case IPV6_ADDR_SITELOCAL:
1040                                         puts("Site");
1041                                         break;
1042                                 case IPV6_ADDR_COMPATv4:
1043                                         puts("Compat");
1044                                         break;
1045                                 case IPV6_ADDR_LOOPBACK:
1046                                         puts("Host");
1047                                         break;
1048                                 default:
1049                                         puts("Unknown");
1050                                 }
1051                         }
1052                 }
1053                 fclose(f);
1054         }
1055 #endif
1056
1057         printf("          ");
1058         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
1059
1060         if (ptr->flags == 0) {
1061                 printf("[NO FLAGS] ");
1062         } else {
1063                 int i = 0;
1064                 do {
1065                         if (ptr->flags & ife_print_flags_mask[i]) {
1066                                 printf(ife_print_flags_strs[i]);
1067                         }
1068                 } while (ife_print_flags_mask[++i]);
1069         }
1070
1071         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
1072         printf(" MTU:%d  Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
1073 #ifdef SIOCSKEEPALIVE
1074         if (ptr->outfill || ptr->keepalive)
1075                 printf("  Outfill:%d  Keepalive:%d", ptr->outfill, ptr->keepalive);
1076 #endif
1077         puts("");
1078
1079         /* If needed, display the interface statistics. */
1080
1081         if (ptr->statistics_valid) {
1082                 /* XXX: statistics are currently only printed for the primary address,
1083                  *      not for the aliases, although strictly speaking they're shared
1084                  *      by all addresses.
1085                  */
1086                 printf("          ");
1087
1088                 printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
1089                            ptr->stats.rx_packets, ptr->stats.rx_errors,
1090                            ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
1091                            ptr->stats.rx_frame_errors);
1092                 if (can_compress)
1093                         printf("             compressed:%lu\n",
1094                                    ptr->stats.rx_compressed);
1095                 printf("          ");
1096                 printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
1097                            ptr->stats.tx_packets, ptr->stats.tx_errors,
1098                            ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
1099                            ptr->stats.tx_carrier_errors);
1100                 printf("          collisions:%lu ", ptr->stats.collisions);
1101                 if (can_compress)
1102                         printf("compressed:%lu ", ptr->stats.tx_compressed);
1103                 if (ptr->tx_queue_len != -1)
1104                         printf("txqueuelen:%d ", ptr->tx_queue_len);
1105                 printf("\n          R");
1106                 print_bytes_scaled(ptr->stats.rx_bytes, "  T");
1107                 print_bytes_scaled(ptr->stats.tx_bytes, "\n");
1108
1109         }
1110
1111         if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma ||
1112                  ptr->map.base_addr)) {
1113                 printf("          ");
1114                 if (ptr->map.irq)
1115                         printf("Interrupt:%d ", ptr->map.irq);
1116                 if (ptr->map.base_addr >= 0x100)        /* Only print devices using it for
1117                                                                                            I/O maps */
1118                         printf("Base address:0x%lx ",
1119                                    (unsigned long) ptr->map.base_addr);
1120                 if (ptr->map.mem_start) {
1121                         printf("Memory:%lx-%lx ", ptr->map.mem_start,
1122                                    ptr->map.mem_end);
1123                 }
1124                 if (ptr->map.dma)
1125                         printf("DMA chan:%x ", ptr->map.dma);
1126                 puts("");
1127         }
1128         puts("");
1129 }
1130
1131
1132 static int do_if_print(struct interface *ife) /*, int *opt_a)*/
1133 {
1134         int res;
1135
1136         res = do_if_fetch(ife);
1137         if (res >= 0) {
1138                 if ((ife->flags & IFF_UP) || interface_opt_a)
1139                         ife_print(ife);
1140         }
1141         return res;
1142 }
1143
1144 static struct interface *lookup_interface(char *name)
1145 {
1146         struct interface *ife = NULL;
1147
1148         if (if_readlist_proc(name) < 0)
1149                 return NULL;
1150         ife = add_interface(name);
1151         return ife;
1152 }
1153
1154 #ifdef UNUSED
1155 static int for_all_interfaces(int (*doit) (struct interface *, void *),
1156                                                           void *cookie)
1157 {
1158         struct interface *ife;
1159
1160         if (!int_list && (if_readlist() < 0))
1161                 return -1;
1162         for (ife = int_list; ife; ife = ife->next) {
1163                 int err = doit(ife, cookie);
1164
1165                 if (err)
1166                         return err;
1167         }
1168         return 0;
1169 }
1170 #endif
1171
1172 /* for ipv4 add/del modes */
1173 static int if_print(char *ifname)
1174 {
1175         struct interface *ife;
1176         int res;
1177
1178         if (!ifname) {
1179                 /*res = for_all_interfaces(do_if_print, &interface_opt_a);*/
1180                 if (!int_list && (if_readlist() < 0))
1181                         return -1;
1182                 for (ife = int_list; ife; ife = ife->next) {
1183                         int err = do_if_print(ife); /*, &interface_opt_a);*/
1184                         if (err)
1185                                 return err;
1186                 }
1187                 return 0;
1188         }
1189         ife = lookup_interface(ifname);
1190         res = do_if_fetch(ife);
1191         if (res >= 0)
1192                 ife_print(ife);
1193         return res;
1194 }
1195
1196 int display_interfaces(char *ifname)
1197 {
1198         int status;
1199
1200         status = if_print(ifname);
1201
1202         return (status < 0); /* status < 0 == 1 -- error */
1203 }