2c5f3284ea4b529154e3959eac53dbdb0eaa37cd
[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 "busybox.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[128];
88
89         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
90                 return "[NONE SET]";
91
92         if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
93                                           numeric, 0xffffff00) != 0)
94                 return NULL;
95
96         return buff;
97 }
98
99 #ifdef UNUSED_AND_BUGGY
100 static int INET_getsock(char *bufp, struct sockaddr *sap)
101 {
102         char *sp = bufp, *bp;
103         unsigned int i;
104         unsigned val;
105         struct sockaddr_in *sock_in;
106
107         sock_in = (struct sockaddr_in *) sap;
108         sock_in->sin_family = AF_INET;
109         sock_in->sin_port = 0;
110
111         val = 0;
112         bp = (char *) &val;
113         for (i = 0; i < sizeof(sock_in->sin_addr.s_addr); i++) {
114                 *sp = toupper(*sp);
115
116                 if ((unsigned)(*sp - 'A') <= 5)
117                         bp[i] |= (int) (*sp - ('A' - 10));
118                 else if (isdigit(*sp))
119                         bp[i] |= (int) (*sp - '0');
120                 else
121                         return -1;
122
123                 bp[i] <<= 4;
124                 sp++;
125                 *sp = toupper(*sp);
126
127                 if ((unsigned)(*sp - 'A') <= 5)
128                         bp[i] |= (int) (*sp - ('A' - 10));
129                 else if (isdigit(*sp))
130                         bp[i] |= (int) (*sp - '0');
131                 else
132                         return -1;
133
134                 sp++;
135         }
136         sock_in->sin_addr.s_addr = htonl(val);
137
138         return (sp - bufp);
139 }
140 #endif
141
142 static int INET_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
143 {
144         return INET_resolve(bufp, (struct sockaddr_in *) sap, 0);
145 /*
146         switch (type) {
147         case 1:
148                 return (INET_getsock(bufp, sap));
149         case 256:
150                 return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
151         default:
152                 return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
153         }
154 */
155 }
156
157 static const struct aftype inet_aftype = {
158         .name =         "inet",
159         .title =        "DARPA Internet",
160         .af =           AF_INET,
161         .alen =         4,
162         .sprint =       INET_sprint,
163         .input =        INET_input,
164 };
165
166 #ifdef HAVE_AFINET6
167
168 /* Display an Internet socket address. */
169 /* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
170 static const char *INET6_sprint(struct sockaddr *sap, int numeric)
171 {
172         static char buff[128];
173
174         if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
175                 return "[NONE SET]";
176         if (INET6_rresolve(buff, sizeof(buff), (struct sockaddr_in6 *) sap, numeric))
177                 return "[UNKNOWN]";
178         return buff;
179 }
180
181 #ifdef UNUSED
182 static int INET6_getsock(char *bufp, struct sockaddr *sap)
183 {
184         struct sockaddr_in6 *sin6;
185
186         sin6 = (struct sockaddr_in6 *) sap;
187         sin6->sin6_family = AF_INET6;
188         sin6->sin6_port = 0;
189
190         if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
191                 return -1;
192
193         return 16;                      /* ?;) */
194 }
195 #endif
196
197 static int INET6_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
198 {
199         return INET6_resolve(bufp, (struct sockaddr_in6 *) sap);
200 /*
201         switch (type) {
202         case 1:
203                 return (INET6_getsock(bufp, sap));
204         default:
205                 return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
206         }
207 */
208 }
209
210 static const struct aftype inet6_aftype = {
211         .name =         "inet6",
212         .title =        "IPv6",
213         .af =           AF_INET6,
214         .alen =         sizeof(struct in6_addr),
215         .sprint =       INET6_sprint,
216         .input =        INET6_input,
217 };
218
219 #endif /* HAVE_AFINET6 */
220
221 /* Display an UNSPEC address. */
222 static char *UNSPEC_print(unsigned char *ptr)
223 {
224         static char buff[sizeof(struct sockaddr) * 3 + 1];
225         char *pos;
226         unsigned int i;
227
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 int 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 /* Used only if "/proc/net/dev" isn't available */
503 static int if_readconf(void)
504 {
505         int numreqs = 30;
506         struct ifconf ifc;
507         struct ifreq *ifr;
508         int n, err = -1;
509         int skfd;
510
511         ifc.ifc_buf = NULL;
512
513         /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
514            (as of 2.1.128) */
515         skfd = socket(AF_INET, SOCK_DGRAM, 0);
516         if (skfd < 0) {
517                 bb_perror_msg("error: no inet socket available");
518                 return -1;
519         }
520
521         for (;;) {
522                 ifc.ifc_len = sizeof(struct ifreq) * numreqs;
523                 ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
524
525                 if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) {
526                         perror("SIOCGIFCONF");
527                         goto out;
528                 }
529                 if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) {
530                         /* assume it overflowed and try again */
531                         numreqs += 10;
532                         continue;
533                 }
534                 break;
535         }
536
537         ifr = ifc.ifc_req;
538         for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
539                 add_interface(ifr->ifr_name);
540                 ifr++;
541         }
542         err = 0;
543
544  out:
545         close(skfd);
546         free(ifc.ifc_buf);
547         return err;
548 }
549
550 static int if_readlist_proc(char *target)
551 {
552         static int proc_read;
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                 perror(_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         return if_readlist_proc(NULL);
596 }
597
598 static int for_all_interfaces(int (*doit) (struct interface *, void *),
599                                                           void *cookie)
600 {
601         struct interface *ife;
602
603         if (!int_list && (if_readlist() < 0))
604                 return -1;
605         for (ife = int_list; ife; ife = ife->next) {
606                 int err = doit(ife, cookie);
607
608                 if (err)
609                         return err;
610         }
611         return 0;
612 }
613
614 /* Fetch the interface configuration from the kernel. */
615 static int if_fetch(struct interface *ife)
616 {
617         struct ifreq ifr;
618         char *ifname = ife->name;
619         int skfd;
620
621         skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
622
623         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
624         if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
625                 close(skfd);
626                 return -1;
627         }
628         ife->flags = ifr.ifr_flags;
629
630         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
631         memset(ife->hwaddr, 0, 32);
632         if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
633                 memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
634
635         ife->type = ifr.ifr_hwaddr.sa_family;
636
637         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
638         ife->metric = 0;
639         if (ioctl(skfd, SIOCGIFMETRIC, &ifr) >= 0)
640                 ife->metric = ifr.ifr_metric;
641
642         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
643         ife->mtu = 0;
644         if (ioctl(skfd, SIOCGIFMTU, &ifr) >= 0)
645                 ife->mtu = ifr.ifr_mtu;
646
647         memset(&ife->map, 0, sizeof(struct ifmap));
648 #ifdef SIOCGIFMAP
649         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
650         if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
651                 ife->map = ifr.ifr_map;
652 #endif
653
654 #ifdef HAVE_TXQUEUELEN
655         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
656         ife->tx_queue_len = -1; /* unknown value */
657         if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0)
658                 ife->tx_queue_len = ifr.ifr_qlen;
659 #else
660         ife->tx_queue_len = -1; /* unknown value */
661 #endif
662
663         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
664         ifr.ifr_addr.sa_family = AF_INET;
665         memset(&ife->addr, 0, sizeof(struct sockaddr));
666         if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
667                 ife->has_ip = 1;
668                 ife->addr = ifr.ifr_addr;
669                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
670                 memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
671                 if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) >= 0)
672                         ife->dstaddr = ifr.ifr_dstaddr;
673
674                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
675                 memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
676                 if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0)
677                         ife->broadaddr = ifr.ifr_broadaddr;
678
679                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
680                 memset(&ife->netmask, 0, sizeof(struct sockaddr));
681                 if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0)
682                         ife->netmask = ifr.ifr_netmask;
683         }
684
685         close(skfd);
686         return 0;
687 }
688
689
690 static int do_if_fetch(struct interface *ife)
691 {
692         if (if_fetch(ife) < 0) {
693                 const char *errmsg;
694
695                 if (errno == ENODEV) {
696                         /* Give better error message for this case. */
697                         errmsg = "Device not found";
698                 } else {
699                         errmsg = strerror(errno);
700                 }
701                 bb_error_msg("%s: error fetching interface information: %s",
702                                 ife->name, errmsg);
703                 return -1;
704         }
705         return 0;
706 }
707
708 static const struct hwtype unspec_hwtype = {
709         .name =         "unspec",
710         .title =        "UNSPEC",
711         .type =         -1,
712         .print =        UNSPEC_print
713 };
714
715 static const struct hwtype loop_hwtype = {
716         .name =         "loop",
717         .title =        "Local Loopback",
718         .type =         ARPHRD_LOOPBACK
719 };
720
721 #include <net/if_arp.h>
722
723 #if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
724 #include <net/ethernet.h>
725 #else
726 #include <linux/if_ether.h>
727 #endif
728
729 /* Display an Ethernet address in readable format. */
730 static char *pr_ether(unsigned char *ptr)
731 {
732         static char buff[64];
733
734         snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X",
735                          (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
736                          (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
737                 );
738         return buff;
739 }
740
741 static int in_ether(const char *bufp, struct sockaddr *sap);
742
743 static struct hwtype ether_hwtype = {
744         .name =         "ether",
745         .title =        "Ethernet",
746         .type =         ARPHRD_ETHER,
747         .alen =         ETH_ALEN,
748         .print =        pr_ether,
749         .input =        in_ether
750 };
751
752 static unsigned hexchar2int(char c)
753 {
754         if (isdigit(c))
755                 return c - '0';
756         c &= ~0x20; /* a -> A */
757         if ((unsigned)(c - 'A') <= 5)
758                 return c - ('A' - 10);
759         return ~0U;
760 }
761
762 /* Input an Ethernet address and convert to binary. */
763 static int in_ether(const char *bufp, struct sockaddr *sap)
764 {
765         unsigned char *ptr;
766         char c;
767         int i;
768         unsigned val;
769
770         sap->sa_family = ether_hwtype.type;
771         ptr = (unsigned char*) sap->sa_data;
772
773         i = 0;
774         while ((*bufp != '\0') && (i < ETH_ALEN)) {
775                 val = hexchar2int(*bufp++) * 0x10;
776                 if (val > 0xff) {
777                         errno = EINVAL;
778                         return -1;
779                 }
780                 c = *bufp;
781                 if (c == ':' || c == 0)
782                         val >>= 4;
783                 else {
784                         val |= hexchar2int(c);
785                         if (val > 0xff) {
786                                 errno = EINVAL;
787                                 return -1;
788                         }
789                 }
790                 if (c != 0)
791                         bufp++;
792                 *ptr++ = (unsigned char) val;
793                 i++;
794
795                 /* We might get a semicolon here - not required. */
796                 if (*bufp == ':') {
797                         bufp++;
798                 }
799         }
800         return 0;
801 }
802
803 #include <net/if_arp.h>
804
805 static const struct hwtype ppp_hwtype = {
806         .name =         "ppp",
807         .title =        "Point-to-Point Protocol",
808         .type =         ARPHRD_PPP
809 };
810
811 #if ENABLE_FEATURE_IPV6
812 static const struct hwtype sit_hwtype = {
813         .name =                 "sit",
814         .title =                "IPv6-in-IPv4",
815         .type =                 ARPHRD_SIT,
816         .print =                UNSPEC_print,
817         .suppress_null_addr =   1
818 } ;
819 #endif
820
821 static const struct hwtype *const hwtypes[] = {
822         &loop_hwtype,
823         &ether_hwtype,
824         &ppp_hwtype,
825         &unspec_hwtype,
826 #if ENABLE_FEATURE_IPV6
827         &sit_hwtype,
828 #endif
829         NULL
830 };
831
832 #ifdef IFF_PORTSEL
833 static const char *const if_port_text[] = {
834         /* Keep in step with <linux/netdevice.h> */
835         "unknown",
836         "10base2",
837         "10baseT",
838         "AUI",
839         "100baseT",
840         "100baseTX",
841         "100baseFX",
842         NULL
843 };
844 #endif
845
846 /* Check our hardware type table for this type. */
847 const struct hwtype *get_hwtype(const char *name)
848 {
849         const struct hwtype *const *hwp;
850
851         hwp = hwtypes;
852         while (*hwp != NULL) {
853                 if (!strcmp((*hwp)->name, name))
854                         return (*hwp);
855                 hwp++;
856         }
857         return NULL;
858 }
859
860 /* Check our hardware type table for this type. */
861 const struct hwtype *get_hwntype(int type)
862 {
863         const struct hwtype *const *hwp;
864
865         hwp = hwtypes;
866         while (*hwp != NULL) {
867                 if ((*hwp)->type == type)
868                         return *hwp;
869                 hwp++;
870         }
871         return NULL;
872 }
873
874 /* return 1 if address is all zeros */
875 static int hw_null_address(const struct hwtype *hw, void *ap)
876 {
877         unsigned int i;
878         unsigned char *address = (unsigned char *) ap;
879
880         for (i = 0; i < hw->alen; i++)
881                 if (address[i])
882                         return 0;
883         return 1;
884 }
885
886 static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
887
888 static void print_bytes_scaled(unsigned long long ull, const char *end)
889 {
890         unsigned long long int_part;
891         const char *ext;
892         unsigned int frac_part;
893         int i;
894
895         frac_part = 0;
896         ext = TRext;
897         int_part = ull;
898         i = 4;
899         do {
900                 if (int_part >= 1024) {
901                         frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
902                         int_part /= 1024;
903                         ext += 3;       /* KiB, MiB, GiB, TiB */
904                 }
905                 --i;
906         } while (i);
907
908         printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
909 }
910
911 static const char *const ife_print_flags_strs[] = {
912         "UP ",
913         "BROADCAST ",
914         "DEBUG ",
915         "LOOPBACK ",
916         "POINTOPOINT ",
917         "NOTRAILERS ",
918         "RUNNING ",
919         "NOARP ",
920         "PROMISC ",
921         "ALLMULTI ",
922         "SLAVE ",
923         "MASTER ",
924         "MULTICAST ",
925 #ifdef HAVE_DYNAMIC
926         "DYNAMIC "
927 #endif
928 };
929
930 static const unsigned short ife_print_flags_mask[] = {
931         IFF_UP,
932         IFF_BROADCAST,
933         IFF_DEBUG,
934         IFF_LOOPBACK,
935         IFF_POINTOPOINT,
936         IFF_NOTRAILERS,
937         IFF_RUNNING,
938         IFF_NOARP,
939         IFF_PROMISC,
940         IFF_ALLMULTI,
941         IFF_SLAVE,
942         IFF_MASTER,
943         IFF_MULTICAST,
944 #ifdef HAVE_DYNAMIC
945         IFF_DYNAMIC
946 #endif
947         0
948 };
949
950 static void ife_print(struct interface *ptr)
951 {
952         const struct aftype *ap;
953         const struct hwtype *hw;
954         int hf;
955         int can_compress = 0;
956
957 #ifdef HAVE_AFINET6
958         FILE *f;
959         char addr6[40], devname[20];
960         struct sockaddr_in6 sap;
961         int plen, scope, dad_status, if_idx;
962         char addr6p[8][5];
963 #endif
964
965         ap = get_afntype(ptr->addr.sa_family);
966         if (ap == NULL)
967                 ap = get_afntype(0);
968
969         hf = ptr->type;
970
971         if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
972                 can_compress = 1;
973
974         hw = get_hwntype(hf);
975         if (hw == NULL)
976                 hw = get_hwntype(-1);
977
978         printf("%-9.9s Link encap:%s  ", ptr->name, hw->title);
979         /* For some hardware types (eg Ash, ATM) we don't print the
980            hardware address if it's null.  */
981         if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
982                                                                 hw->suppress_null_addr)))
983                 printf("HWaddr %s  ", hw->print((unsigned char *)ptr->hwaddr));
984 #ifdef IFF_PORTSEL
985         if (ptr->flags & IFF_PORTSEL) {
986                 printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
987                 if (ptr->flags & IFF_AUTOMEDIA)
988                         printf("(auto)");
989         }
990 #endif
991         puts("");
992
993         if (ptr->has_ip) {
994                 printf("          %s addr:%s ", ap->name,
995                            ap->sprint(&ptr->addr, 1));
996                 if (ptr->flags & IFF_POINTOPOINT) {
997                         printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
998                 }
999                 if (ptr->flags & IFF_BROADCAST) {
1000                         printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
1001                 }
1002                 printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
1003         }
1004
1005 #ifdef HAVE_AFINET6
1006
1007 #define IPV6_ADDR_ANY           0x0000U
1008
1009 #define IPV6_ADDR_UNICAST       0x0001U
1010 #define IPV6_ADDR_MULTICAST     0x0002U
1011 #define IPV6_ADDR_ANYCAST       0x0004U
1012
1013 #define IPV6_ADDR_LOOPBACK      0x0010U
1014 #define IPV6_ADDR_LINKLOCAL     0x0020U
1015 #define IPV6_ADDR_SITELOCAL     0x0040U
1016
1017 #define IPV6_ADDR_COMPATv4      0x0080U
1018
1019 #define IPV6_ADDR_SCOPE_MASK    0x00f0U
1020
1021 #define IPV6_ADDR_MAPPED        0x1000U
1022 #define IPV6_ADDR_RESERVED      0x2000U /* reserved address space */
1023
1024         f = fopen(_PATH_PROCNET_IFINET6, "r");
1025         if (f != NULL) {
1026                 while (fscanf
1027                            (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
1028                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
1029                                 addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
1030                                 &dad_status, devname) != EOF
1031                 ) {
1032                         if (!strcmp(devname, ptr->name)) {
1033                                 sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
1034                                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
1035                                                 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
1036                                 inet_pton(AF_INET6, addr6,
1037                                                   (struct sockaddr *) &sap.sin6_addr);
1038                                 sap.sin6_family = AF_INET6;
1039                                 printf("          inet6 addr: %s/%d",
1040                                            inet6_aftype.sprint((struct sockaddr *) &sap, 1),
1041                                            plen);
1042                                 printf(" Scope:");
1043                                 switch (scope & IPV6_ADDR_SCOPE_MASK) {
1044                                 case 0:
1045                                         printf("Global");
1046                                         break;
1047                                 case IPV6_ADDR_LINKLOCAL:
1048                                         printf("Link");
1049                                         break;
1050                                 case IPV6_ADDR_SITELOCAL:
1051                                         printf("Site");
1052                                         break;
1053                                 case IPV6_ADDR_COMPATv4:
1054                                         printf("Compat");
1055                                         break;
1056                                 case IPV6_ADDR_LOOPBACK:
1057                                         printf("Host");
1058                                         break;
1059                                 default:
1060                                         printf("Unknown");
1061                                 }
1062                                 puts("");
1063                         }
1064                 }
1065                 fclose(f);
1066         }
1067 #endif
1068
1069         printf("          ");
1070         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
1071
1072         if (ptr->flags == 0) {
1073                 printf("[NO FLAGS] ");
1074         } else {
1075                 int i = 0;
1076                 do {
1077                         if (ptr->flags & ife_print_flags_mask[i]) {
1078                                 printf(ife_print_flags_strs[i]);
1079                         }
1080                 } while (ife_print_flags_mask[++i]);
1081         }
1082
1083         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
1084         printf(" MTU:%d  Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
1085 #ifdef SIOCSKEEPALIVE
1086         if (ptr->outfill || ptr->keepalive)
1087                 printf("  Outfill:%d  Keepalive:%d", ptr->outfill, ptr->keepalive);
1088 #endif
1089         puts("");
1090
1091         /* If needed, display the interface statistics. */
1092
1093         if (ptr->statistics_valid) {
1094                 /* XXX: statistics are currently only printed for the primary address,
1095                  *      not for the aliases, although strictly speaking they're shared
1096                  *      by all addresses.
1097                  */
1098                 printf("          ");
1099
1100                 printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
1101                            ptr->stats.rx_packets, ptr->stats.rx_errors,
1102                            ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
1103                            ptr->stats.rx_frame_errors);
1104                 if (can_compress)
1105                         printf("             compressed:%lu\n",
1106                                    ptr->stats.rx_compressed);
1107                 printf("          ");
1108                 printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
1109                            ptr->stats.tx_packets, ptr->stats.tx_errors,
1110                            ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
1111                            ptr->stats.tx_carrier_errors);
1112                 printf("          collisions:%lu ", ptr->stats.collisions);
1113                 if (can_compress)
1114                         printf("compressed:%lu ", ptr->stats.tx_compressed);
1115                 if (ptr->tx_queue_len != -1)
1116                         printf("txqueuelen:%d ", ptr->tx_queue_len);
1117                 printf("\n          R");
1118                 print_bytes_scaled(ptr->stats.rx_bytes, "  T");
1119                 print_bytes_scaled(ptr->stats.tx_bytes, "\n");
1120
1121         }
1122
1123         if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma ||
1124                  ptr->map.base_addr)) {
1125                 printf("          ");
1126                 if (ptr->map.irq)
1127                         printf("Interrupt:%d ", ptr->map.irq);
1128                 if (ptr->map.base_addr >= 0x100)        /* Only print devices using it for
1129                                                                                            I/O maps */
1130                         printf("Base address:0x%lx ",
1131                                    (unsigned long) ptr->map.base_addr);
1132                 if (ptr->map.mem_start) {
1133                         printf("Memory:%lx-%lx ", ptr->map.mem_start,
1134                                    ptr->map.mem_end);
1135                 }
1136                 if (ptr->map.dma)
1137                         printf("DMA chan:%x ", ptr->map.dma);
1138                 puts("");
1139         }
1140         puts("");
1141 }
1142
1143
1144 static int do_if_print(struct interface *ife, void *cookie)
1145 {
1146         int *opt_a = (int *) cookie;
1147         int res;
1148
1149         res = do_if_fetch(ife);
1150         if (res >= 0) {
1151                 if ((ife->flags & IFF_UP) || *opt_a)
1152                         ife_print(ife);
1153         }
1154         return res;
1155 }
1156
1157 static struct interface *lookup_interface(char *name)
1158 {
1159         struct interface *ife = NULL;
1160
1161         if (if_readlist_proc(name) < 0)
1162                 return NULL;
1163         ife = add_interface(name);
1164         return ife;
1165 }
1166
1167 /* for ipv4 add/del modes */
1168 static int if_print(char *ifname)
1169 {
1170         int res;
1171
1172         if (!ifname) {
1173                 res = for_all_interfaces(do_if_print, &interface_opt_a);
1174         } else {
1175                 struct interface *ife;
1176
1177                 ife = lookup_interface(ifname);
1178                 res = do_if_fetch(ife);
1179                 if (res >= 0)
1180                         ife_print(ife);
1181         }
1182         return res;
1183 }
1184
1185 int display_interfaces(char *ifname)
1186 {
1187         int status;
1188
1189         status = if_print(ifname);
1190
1191         return (status < 0); /* status < 0 == 1 -- error */
1192 }