OSDN Git Service

style fix (stray space before ';')
[android-x86/external-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_or_warn(skfd, SIOCGIFCONF, &ifc) < 0) {
525                         goto out;
526                 }
527                 if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) {
528                         /* assume it overflowed and try again */
529                         numreqs += 10;
530                         continue;
531                 }
532                 break;
533         }
534
535         ifr = ifc.ifc_req;
536         for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
537                 add_interface(ifr->ifr_name);
538                 ifr++;
539         }
540         err = 0;
541
542  out:
543         close(skfd);
544         free(ifc.ifc_buf);
545         return err;
546 }
547
548 static int if_readlist_proc(char *target)
549 {
550         static smallint proc_read;
551
552         FILE *fh;
553         char buf[512];
554         struct interface *ife;
555         int err, procnetdev_vsn;
556
557         if (proc_read)
558                 return 0;
559         if (!target)
560                 proc_read = 1;
561
562         fh = fopen(_PATH_PROCNET_DEV, "r");
563         if (!fh) {
564                 bb_perror_msg("warning: cannot open %s, limiting output", _PATH_PROCNET_DEV);
565                 return if_readconf();
566         }
567         fgets(buf, sizeof buf, fh);     /* eat line */
568         fgets(buf, sizeof buf, fh);
569
570         procnetdev_vsn = procnetdev_version(buf);
571
572         err = 0;
573         while (fgets(buf, sizeof buf, fh)) {
574                 char *s, name[128];
575
576                 s = get_name(name, buf);
577                 ife = add_interface(name);
578                 get_dev_fields(s, ife, procnetdev_vsn);
579                 ife->statistics_valid = 1;
580                 if (target && !strcmp(target, name))
581                         break;
582         }
583         if (ferror(fh)) {
584                 bb_perror_msg(_PATH_PROCNET_DEV);
585                 err = -1;
586                 proc_read = 0;
587         }
588         fclose(fh);
589         return err;
590 }
591
592 static int if_readlist(void)
593 {
594         int err = if_readlist_proc(NULL);
595         /* Needed in order to get ethN:M aliases */
596         if (!err)
597                 err = if_readconf();
598         return err;
599 }
600
601 /* Fetch the interface configuration from the kernel. */
602 static int if_fetch(struct interface *ife)
603 {
604         struct ifreq ifr;
605         char *ifname = ife->name;
606         int skfd;
607
608         skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
609
610         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
611         if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
612                 close(skfd);
613                 return -1;
614         }
615         ife->flags = ifr.ifr_flags;
616
617         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
618         memset(ife->hwaddr, 0, 32);
619         if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
620                 memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
621
622         ife->type = ifr.ifr_hwaddr.sa_family;
623
624         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
625         ife->metric = 0;
626         if (ioctl(skfd, SIOCGIFMETRIC, &ifr) >= 0)
627                 ife->metric = ifr.ifr_metric;
628
629         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
630         ife->mtu = 0;
631         if (ioctl(skfd, SIOCGIFMTU, &ifr) >= 0)
632                 ife->mtu = ifr.ifr_mtu;
633
634         memset(&ife->map, 0, sizeof(struct ifmap));
635 #ifdef SIOCGIFMAP
636         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
637         if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
638                 ife->map = ifr.ifr_map;
639 #endif
640
641 #ifdef HAVE_TXQUEUELEN
642         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
643         ife->tx_queue_len = -1; /* unknown value */
644         if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0)
645                 ife->tx_queue_len = ifr.ifr_qlen;
646 #else
647         ife->tx_queue_len = -1; /* unknown value */
648 #endif
649
650         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
651         ifr.ifr_addr.sa_family = AF_INET;
652         memset(&ife->addr, 0, sizeof(struct sockaddr));
653         if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
654                 ife->has_ip = 1;
655                 ife->addr = ifr.ifr_addr;
656                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
657                 memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
658                 if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) >= 0)
659                         ife->dstaddr = ifr.ifr_dstaddr;
660
661                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
662                 memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
663                 if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0)
664                         ife->broadaddr = ifr.ifr_broadaddr;
665
666                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
667                 memset(&ife->netmask, 0, sizeof(struct sockaddr));
668                 if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0)
669                         ife->netmask = ifr.ifr_netmask;
670         }
671
672         close(skfd);
673         return 0;
674 }
675
676
677 static int do_if_fetch(struct interface *ife)
678 {
679         if (if_fetch(ife) < 0) {
680                 const char *errmsg;
681
682                 if (errno == ENODEV) {
683                         /* Give better error message for this case. */
684                         errmsg = "Device not found";
685                 } else {
686                         errmsg = strerror(errno);
687                 }
688                 bb_error_msg("%s: error fetching interface information: %s",
689                                 ife->name, errmsg);
690                 return -1;
691         }
692         return 0;
693 }
694
695 static const struct hwtype unspec_hwtype = {
696         .name =         "unspec",
697         .title =        "UNSPEC",
698         .type =         -1,
699         .print =        UNSPEC_print
700 };
701
702 static const struct hwtype loop_hwtype = {
703         .name =         "loop",
704         .title =        "Local Loopback",
705         .type =         ARPHRD_LOOPBACK
706 };
707
708 #include <net/if_arp.h>
709
710 #if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
711 #include <net/ethernet.h>
712 #else
713 #include <linux/if_ether.h>
714 #endif
715
716 /* Display an Ethernet address in readable format. */
717 static char *pr_ether(unsigned char *ptr)
718 {
719         static char *buff;
720
721         free(buff);
722         buff = xasprintf("%02X:%02X:%02X:%02X:%02X:%02X",
723                          (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
724                          (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
725                 );
726         return buff;
727 }
728
729 static int in_ether(const char *bufp, struct sockaddr *sap);
730
731 static const struct hwtype ether_hwtype = {
732         .name =         "ether",
733         .title =        "Ethernet",
734         .type =         ARPHRD_ETHER,
735         .alen =         ETH_ALEN,
736         .print =        pr_ether,
737         .input =        in_ether
738 };
739
740 static unsigned hexchar2int(char c)
741 {
742         if (isdigit(c))
743                 return c - '0';
744         c &= ~0x20; /* a -> A */
745         if ((unsigned)(c - 'A') <= 5)
746                 return c - ('A' - 10);
747         return ~0U;
748 }
749
750 /* Input an Ethernet address and convert to binary. */
751 static int in_ether(const char *bufp, struct sockaddr *sap)
752 {
753         unsigned char *ptr;
754         char c;
755         int i;
756         unsigned val;
757
758         sap->sa_family = ether_hwtype.type;
759         ptr = (unsigned char*) sap->sa_data;
760
761         i = 0;
762         while ((*bufp != '\0') && (i < ETH_ALEN)) {
763                 val = hexchar2int(*bufp++) * 0x10;
764                 if (val > 0xff) {
765                         errno = EINVAL;
766                         return -1;
767                 }
768                 c = *bufp;
769                 if (c == ':' || c == 0)
770                         val >>= 4;
771                 else {
772                         val |= hexchar2int(c);
773                         if (val > 0xff) {
774                                 errno = EINVAL;
775                                 return -1;
776                         }
777                 }
778                 if (c != 0)
779                         bufp++;
780                 *ptr++ = (unsigned char) val;
781                 i++;
782
783                 /* We might get a semicolon here - not required. */
784                 if (*bufp == ':') {
785                         bufp++;
786                 }
787         }
788         return 0;
789 }
790
791 #include <net/if_arp.h>
792
793 static const struct hwtype ppp_hwtype = {
794         .name =         "ppp",
795         .title =        "Point-to-Point Protocol",
796         .type =         ARPHRD_PPP
797 };
798
799 #if ENABLE_FEATURE_IPV6
800 static const struct hwtype sit_hwtype = {
801         .name =                 "sit",
802         .title =                "IPv6-in-IPv4",
803         .type =                 ARPHRD_SIT,
804         .print =                UNSPEC_print,
805         .suppress_null_addr =   1
806 };
807 #endif
808
809 static const struct hwtype *const hwtypes[] = {
810         &loop_hwtype,
811         &ether_hwtype,
812         &ppp_hwtype,
813         &unspec_hwtype,
814 #if ENABLE_FEATURE_IPV6
815         &sit_hwtype,
816 #endif
817         NULL
818 };
819
820 #ifdef IFF_PORTSEL
821 static const char *const if_port_text[] = {
822         /* Keep in step with <linux/netdevice.h> */
823         "unknown",
824         "10base2",
825         "10baseT",
826         "AUI",
827         "100baseT",
828         "100baseTX",
829         "100baseFX",
830         NULL
831 };
832 #endif
833
834 /* Check our hardware type table for this type. */
835 const struct hwtype *get_hwtype(const char *name)
836 {
837         const struct hwtype *const *hwp;
838
839         hwp = hwtypes;
840         while (*hwp != NULL) {
841                 if (!strcmp((*hwp)->name, name))
842                         return (*hwp);
843                 hwp++;
844         }
845         return NULL;
846 }
847
848 /* Check our hardware type table for this type. */
849 const struct hwtype *get_hwntype(int type)
850 {
851         const struct hwtype *const *hwp;
852
853         hwp = hwtypes;
854         while (*hwp != NULL) {
855                 if ((*hwp)->type == type)
856                         return *hwp;
857                 hwp++;
858         }
859         return NULL;
860 }
861
862 /* return 1 if address is all zeros */
863 static int hw_null_address(const struct hwtype *hw, void *ap)
864 {
865         unsigned int i;
866         unsigned char *address = (unsigned char *) ap;
867
868         for (i = 0; i < hw->alen; i++)
869                 if (address[i])
870                         return 0;
871         return 1;
872 }
873
874 static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
875
876 static void print_bytes_scaled(unsigned long long ull, const char *end)
877 {
878         unsigned long long int_part;
879         const char *ext;
880         unsigned int frac_part;
881         int i;
882
883         frac_part = 0;
884         ext = TRext;
885         int_part = ull;
886         i = 4;
887         do {
888                 if (int_part >= 1024) {
889                         frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
890                         int_part /= 1024;
891                         ext += 3;       /* KiB, MiB, GiB, TiB */
892                 }
893                 --i;
894         } while (i);
895
896         printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
897 }
898
899 static const char *const ife_print_flags_strs[] = {
900         "UP ",
901         "BROADCAST ",
902         "DEBUG ",
903         "LOOPBACK ",
904         "POINTOPOINT ",
905         "NOTRAILERS ",
906         "RUNNING ",
907         "NOARP ",
908         "PROMISC ",
909         "ALLMULTI ",
910         "SLAVE ",
911         "MASTER ",
912         "MULTICAST ",
913 #ifdef HAVE_DYNAMIC
914         "DYNAMIC "
915 #endif
916 };
917
918 static const unsigned short ife_print_flags_mask[] = {
919         IFF_UP,
920         IFF_BROADCAST,
921         IFF_DEBUG,
922         IFF_LOOPBACK,
923         IFF_POINTOPOINT,
924         IFF_NOTRAILERS,
925         IFF_RUNNING,
926         IFF_NOARP,
927         IFF_PROMISC,
928         IFF_ALLMULTI,
929         IFF_SLAVE,
930         IFF_MASTER,
931         IFF_MULTICAST,
932 #ifdef HAVE_DYNAMIC
933         IFF_DYNAMIC
934 #endif
935         0
936 };
937
938 static void ife_print(struct interface *ptr)
939 {
940         const struct aftype *ap;
941         const struct hwtype *hw;
942         int hf;
943         int can_compress = 0;
944
945 #ifdef HAVE_AFINET6
946         FILE *f;
947         char addr6[40], devname[20];
948         struct sockaddr_in6 sap;
949         int plen, scope, dad_status, if_idx;
950         char addr6p[8][5];
951 #endif
952
953         ap = get_afntype(ptr->addr.sa_family);
954         if (ap == NULL)
955                 ap = get_afntype(0);
956
957         hf = ptr->type;
958
959         if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
960                 can_compress = 1;
961
962         hw = get_hwntype(hf);
963         if (hw == NULL)
964                 hw = get_hwntype(-1);
965
966         printf("%-9.9s Link encap:%s  ", ptr->name, hw->title);
967         /* For some hardware types (eg Ash, ATM) we don't print the
968            hardware address if it's null.  */
969         if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
970                                                                 hw->suppress_null_addr)))
971                 printf("HWaddr %s  ", hw->print((unsigned char *)ptr->hwaddr));
972 #ifdef IFF_PORTSEL
973         if (ptr->flags & IFF_PORTSEL) {
974                 printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
975                 if (ptr->flags & IFF_AUTOMEDIA)
976                         printf("(auto)");
977         }
978 #endif
979         puts("");
980
981         if (ptr->has_ip) {
982                 printf("          %s addr:%s ", ap->name,
983                            ap->sprint(&ptr->addr, 1));
984                 if (ptr->flags & IFF_POINTOPOINT) {
985                         printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
986                 }
987                 if (ptr->flags & IFF_BROADCAST) {
988                         printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
989                 }
990                 printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
991         }
992
993 #ifdef HAVE_AFINET6
994
995 #define IPV6_ADDR_ANY           0x0000U
996
997 #define IPV6_ADDR_UNICAST       0x0001U
998 #define IPV6_ADDR_MULTICAST     0x0002U
999 #define IPV6_ADDR_ANYCAST       0x0004U
1000
1001 #define IPV6_ADDR_LOOPBACK      0x0010U
1002 #define IPV6_ADDR_LINKLOCAL     0x0020U
1003 #define IPV6_ADDR_SITELOCAL     0x0040U
1004
1005 #define IPV6_ADDR_COMPATv4      0x0080U
1006
1007 #define IPV6_ADDR_SCOPE_MASK    0x00f0U
1008
1009 #define IPV6_ADDR_MAPPED        0x1000U
1010 #define IPV6_ADDR_RESERVED      0x2000U /* reserved address space */
1011
1012         f = fopen(_PATH_PROCNET_IFINET6, "r");
1013         if (f != NULL) {
1014                 while (fscanf
1015                            (f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
1016                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
1017                                 addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
1018                                 &dad_status, devname) != EOF
1019                 ) {
1020                         if (!strcmp(devname, ptr->name)) {
1021                                 sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
1022                                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
1023                                                 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
1024                                 inet_pton(AF_INET6, addr6,
1025                                                   (struct sockaddr *) &sap.sin6_addr);
1026                                 sap.sin6_family = AF_INET6;
1027                                 printf("          inet6 addr: %s/%d",
1028                                            INET6_sprint((struct sockaddr *) &sap, 1),
1029                                            plen);
1030                                 printf(" Scope:");
1031                                 switch (scope & IPV6_ADDR_SCOPE_MASK) {
1032                                 case 0:
1033                                         puts("Global");
1034                                         break;
1035                                 case IPV6_ADDR_LINKLOCAL:
1036                                         puts("Link");
1037                                         break;
1038                                 case IPV6_ADDR_SITELOCAL:
1039                                         puts("Site");
1040                                         break;
1041                                 case IPV6_ADDR_COMPATv4:
1042                                         puts("Compat");
1043                                         break;
1044                                 case IPV6_ADDR_LOOPBACK:
1045                                         puts("Host");
1046                                         break;
1047                                 default:
1048                                         puts("Unknown");
1049                                 }
1050                         }
1051                 }
1052                 fclose(f);
1053         }
1054 #endif
1055
1056         printf("          ");
1057         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
1058
1059         if (ptr->flags == 0) {
1060                 printf("[NO FLAGS] ");
1061         } else {
1062                 int i = 0;
1063                 do {
1064                         if (ptr->flags & ife_print_flags_mask[i]) {
1065                                 printf(ife_print_flags_strs[i]);
1066                         }
1067                 } while (ife_print_flags_mask[++i]);
1068         }
1069
1070         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
1071         printf(" MTU:%d  Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
1072 #ifdef SIOCSKEEPALIVE
1073         if (ptr->outfill || ptr->keepalive)
1074                 printf("  Outfill:%d  Keepalive:%d", ptr->outfill, ptr->keepalive);
1075 #endif
1076         puts("");
1077
1078         /* If needed, display the interface statistics. */
1079
1080         if (ptr->statistics_valid) {
1081                 /* XXX: statistics are currently only printed for the primary address,
1082                  *      not for the aliases, although strictly speaking they're shared
1083                  *      by all addresses.
1084                  */
1085                 printf("          ");
1086
1087                 printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
1088                            ptr->stats.rx_packets, ptr->stats.rx_errors,
1089                            ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
1090                            ptr->stats.rx_frame_errors);
1091                 if (can_compress)
1092                         printf("             compressed:%lu\n",
1093                                    ptr->stats.rx_compressed);
1094                 printf("          ");
1095                 printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
1096                            ptr->stats.tx_packets, ptr->stats.tx_errors,
1097                            ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
1098                            ptr->stats.tx_carrier_errors);
1099                 printf("          collisions:%lu ", ptr->stats.collisions);
1100                 if (can_compress)
1101                         printf("compressed:%lu ", ptr->stats.tx_compressed);
1102                 if (ptr->tx_queue_len != -1)
1103                         printf("txqueuelen:%d ", ptr->tx_queue_len);
1104                 printf("\n          R");
1105                 print_bytes_scaled(ptr->stats.rx_bytes, "  T");
1106                 print_bytes_scaled(ptr->stats.tx_bytes, "\n");
1107
1108         }
1109
1110         if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma ||
1111                  ptr->map.base_addr)) {
1112                 printf("          ");
1113                 if (ptr->map.irq)
1114                         printf("Interrupt:%d ", ptr->map.irq);
1115                 if (ptr->map.base_addr >= 0x100)        /* Only print devices using it for
1116                                                                                            I/O maps */
1117                         printf("Base address:0x%lx ",
1118                                    (unsigned long) ptr->map.base_addr);
1119                 if (ptr->map.mem_start) {
1120                         printf("Memory:%lx-%lx ", ptr->map.mem_start,
1121                                    ptr->map.mem_end);
1122                 }
1123                 if (ptr->map.dma)
1124                         printf("DMA chan:%x ", ptr->map.dma);
1125                 puts("");
1126         }
1127         puts("");
1128 }
1129
1130
1131 static int do_if_print(struct interface *ife) /*, int *opt_a)*/
1132 {
1133         int res;
1134
1135         res = do_if_fetch(ife);
1136         if (res >= 0) {
1137                 if ((ife->flags & IFF_UP) || interface_opt_a)
1138                         ife_print(ife);
1139         }
1140         return res;
1141 }
1142
1143 static struct interface *lookup_interface(char *name)
1144 {
1145         struct interface *ife = NULL;
1146
1147         if (if_readlist_proc(name) < 0)
1148                 return NULL;
1149         ife = add_interface(name);
1150         return ife;
1151 }
1152
1153 #ifdef UNUSED
1154 static int for_all_interfaces(int (*doit) (struct interface *, void *),
1155                                                           void *cookie)
1156 {
1157         struct interface *ife;
1158
1159         if (!int_list && (if_readlist() < 0))
1160                 return -1;
1161         for (ife = int_list; ife; ife = ife->next) {
1162                 int err = doit(ife, cookie);
1163
1164                 if (err)
1165                         return err;
1166         }
1167         return 0;
1168 }
1169 #endif
1170
1171 /* for ipv4 add/del modes */
1172 static int if_print(char *ifname)
1173 {
1174         struct interface *ife;
1175         int res;
1176
1177         if (!ifname) {
1178                 /*res = for_all_interfaces(do_if_print, &interface_opt_a);*/
1179                 if (!int_list && (if_readlist() < 0))
1180                         return -1;
1181                 for (ife = int_list; ife; ife = ife->next) {
1182                         int err = do_if_print(ife); /*, &interface_opt_a);*/
1183                         if (err)
1184                                 return err;
1185                 }
1186                 return 0;
1187         }
1188         ife = lookup_interface(ifname);
1189         res = do_if_fetch(ife);
1190         if (res >= 0)
1191                 ife_print(ife);
1192         return res;
1193 }
1194
1195 int display_interfaces(char *ifname)
1196 {
1197         int status;
1198
1199         status = if_print(ifname);
1200
1201         return (status < 0); /* status < 0 == 1 -- error */
1202 }