*: add -Wunused-parameter; fix resulting breakage
[platform/upstream/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 ATTRIBUTE_UNUSED)
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
410         while (isspace(p[namestart]))
411                 namestart++;
412         nameend = namestart;
413         while (p[nameend] && p[nameend] != ':' && !isspace(p[nameend]))
414                 nameend++;
415         if (p[nameend] == ':') {
416                 if ((nameend - namestart) < IFNAMSIZ) {
417                         memcpy(name, &p[namestart], nameend - namestart);
418                         name[nameend - namestart] = '\0';
419                         p = &p[nameend];
420                 } else {
421                         /* Interface name too large */
422                         name[0] = '\0';
423                 }
424         } else {
425                 /* trailing ':' not found - return empty */
426                 name[0] = '\0';
427         }
428         return p + 1;
429 }
430
431 /* If scanf supports size qualifiers for %n conversions, then we can
432  * use a modified fmt that simply stores the position in the fields
433  * having no associated fields in the proc string.  Of course, we need
434  * to zero them again when we're done.  But that is smaller than the
435  * old approach of multiple scanf occurrences with large numbers of
436  * args. */
437
438 /* static const char *const ss_fmt[] = { */
439 /*      "%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */
440 /*      "%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */
441 /*      "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */
442 /* }; */
443
444         /* Lie about the size of the int pointed to for %n. */
445 #if INT_MAX == LONG_MAX
446 static const char *const ss_fmt[] = {
447         "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
448         "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
449         "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
450 };
451 #else
452 static const char *const ss_fmt[] = {
453         "%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu",
454         "%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu",
455         "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu"
456 };
457
458 #endif
459
460 static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
461 {
462         memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
463
464         sscanf(bp, ss_fmt[procnetdev_vsn],
465                    &ife->stats.rx_bytes, /* missing for 0 */
466                    &ife->stats.rx_packets,
467                    &ife->stats.rx_errors,
468                    &ife->stats.rx_dropped,
469                    &ife->stats.rx_fifo_errors,
470                    &ife->stats.rx_frame_errors,
471                    &ife->stats.rx_compressed, /* missing for <= 1 */
472                    &ife->stats.rx_multicast, /* missing for <= 1 */
473                    &ife->stats.tx_bytes, /* missing for 0 */
474                    &ife->stats.tx_packets,
475                    &ife->stats.tx_errors,
476                    &ife->stats.tx_dropped,
477                    &ife->stats.tx_fifo_errors,
478                    &ife->stats.collisions,
479                    &ife->stats.tx_carrier_errors,
480                    &ife->stats.tx_compressed /* missing for <= 1 */
481                    );
482
483         if (procnetdev_vsn <= 1) {
484                 if (procnetdev_vsn == 0) {
485                         ife->stats.rx_bytes = 0;
486                         ife->stats.tx_bytes = 0;
487                 }
488                 ife->stats.rx_multicast = 0;
489                 ife->stats.rx_compressed = 0;
490                 ife->stats.tx_compressed = 0;
491         }
492 }
493
494 static inline int procnetdev_version(char *buf)
495 {
496         if (strstr(buf, "compressed"))
497                 return 2;
498         if (strstr(buf, "bytes"))
499                 return 1;
500         return 0;
501 }
502
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_or_warn(skfd, SIOCGIFCONF, &ifc) < 0) {
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[] ALIGN1 = "\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 void ife_print(struct interface *ptr)
901 {
902         const struct aftype *ap;
903         const struct hwtype *hw;
904         int hf;
905         int can_compress = 0;
906
907 #ifdef HAVE_AFINET6
908         FILE *f;
909         char addr6[40], devname[20];
910         struct sockaddr_in6 sap;
911         int plen, scope, dad_status, if_idx;
912         char addr6p[8][5];
913 #endif
914
915         ap = get_afntype(ptr->addr.sa_family);
916         if (ap == NULL)
917                 ap = get_afntype(0);
918
919         hf = ptr->type;
920
921         if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
922                 can_compress = 1;
923
924         hw = get_hwntype(hf);
925         if (hw == NULL)
926                 hw = get_hwntype(-1);
927
928         printf("%-9.9s Link encap:%s  ", ptr->name, hw->title);
929         /* For some hardware types (eg Ash, ATM) we don't print the
930            hardware address if it's null.  */
931         if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
932                                                                 hw->suppress_null_addr)))
933                 printf("HWaddr %s  ", hw->print((unsigned char *)ptr->hwaddr));
934 #ifdef IFF_PORTSEL
935         if (ptr->flags & IFF_PORTSEL) {
936                 printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
937                 if (ptr->flags & IFF_AUTOMEDIA)
938                         printf("(auto)");
939         }
940 #endif
941         bb_putchar('\n');
942
943         if (ptr->has_ip) {
944                 printf("          %s addr:%s ", ap->name,
945                            ap->sprint(&ptr->addr, 1));
946                 if (ptr->flags & IFF_POINTOPOINT) {
947                         printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
948                 }
949                 if (ptr->flags & IFF_BROADCAST) {
950                         printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
951                 }
952                 printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
953         }
954
955 #ifdef HAVE_AFINET6
956
957 #define IPV6_ADDR_ANY           0x0000U
958
959 #define IPV6_ADDR_UNICAST       0x0001U
960 #define IPV6_ADDR_MULTICAST     0x0002U
961 #define IPV6_ADDR_ANYCAST       0x0004U
962
963 #define IPV6_ADDR_LOOPBACK      0x0010U
964 #define IPV6_ADDR_LINKLOCAL     0x0020U
965 #define IPV6_ADDR_SITELOCAL     0x0040U
966
967 #define IPV6_ADDR_COMPATv4      0x0080U
968
969 #define IPV6_ADDR_SCOPE_MASK    0x00f0U
970
971 #define IPV6_ADDR_MAPPED        0x1000U
972 #define IPV6_ADDR_RESERVED      0x2000U /* reserved address space */
973
974         f = fopen(_PATH_PROCNET_IFINET6, "r");
975         if (f != NULL) {
976                 while (fscanf
977                            (f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
978                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
979                                 addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
980                                 &dad_status, devname) != EOF
981                 ) {
982                         if (!strcmp(devname, ptr->name)) {
983                                 sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
984                                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
985                                                 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
986                                 inet_pton(AF_INET6, addr6,
987                                                   (struct sockaddr *) &sap.sin6_addr);
988                                 sap.sin6_family = AF_INET6;
989                                 printf("          inet6 addr: %s/%d",
990                                            INET6_sprint((struct sockaddr *) &sap, 1),
991                                            plen);
992                                 printf(" Scope:");
993                                 switch (scope & IPV6_ADDR_SCOPE_MASK) {
994                                 case 0:
995                                         puts("Global");
996                                         break;
997                                 case IPV6_ADDR_LINKLOCAL:
998                                         puts("Link");
999                                         break;
1000                                 case IPV6_ADDR_SITELOCAL:
1001                                         puts("Site");
1002                                         break;
1003                                 case IPV6_ADDR_COMPATv4:
1004                                         puts("Compat");
1005                                         break;
1006                                 case IPV6_ADDR_LOOPBACK:
1007                                         puts("Host");
1008                                         break;
1009                                 default:
1010                                         puts("Unknown");
1011                                 }
1012                         }
1013                 }
1014                 fclose(f);
1015         }
1016 #endif
1017
1018         printf("          ");
1019         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
1020
1021         if (ptr->flags == 0) {
1022                 printf("[NO FLAGS] ");
1023         } else {
1024                 static const char ife_print_flags_strs[] ALIGN1 =
1025                         "UP\0"
1026                         "BROADCAST\0"
1027                         "DEBUG\0"
1028                         "LOOPBACK\0"
1029                         "POINTOPOINT\0"
1030                         "NOTRAILERS\0"
1031                         "RUNNING\0"
1032                         "NOARP\0"
1033                         "PROMISC\0"
1034                         "ALLMULTI\0"
1035                         "SLAVE\0"
1036                         "MASTER\0"
1037                         "MULTICAST\0"
1038 #ifdef HAVE_DYNAMIC
1039                         "DYNAMIC\0"
1040 #endif
1041                         ;
1042                 static const unsigned short ife_print_flags_mask[] ALIGN2 = {
1043                         IFF_UP,
1044                         IFF_BROADCAST,
1045                         IFF_DEBUG,
1046                         IFF_LOOPBACK,
1047                         IFF_POINTOPOINT,
1048                         IFF_NOTRAILERS,
1049                         IFF_RUNNING,
1050                         IFF_NOARP,
1051                         IFF_PROMISC,
1052                         IFF_ALLMULTI,
1053                         IFF_SLAVE,
1054                         IFF_MASTER,
1055                         IFF_MULTICAST
1056 #ifdef HAVE_DYNAMIC
1057                         ,IFF_DYNAMIC
1058 #endif
1059                 };
1060                 const unsigned short *mask = ife_print_flags_mask;
1061                 const char *str = ife_print_flags_strs;
1062                 do {
1063                         if (ptr->flags & *mask) {
1064                                 printf("%s ", str);
1065                         }
1066                         mask++;
1067                         str += strlen(str) + 1;
1068                 } while (*str);
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         bb_putchar('\n');
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                 bb_putchar('\n');
1127         }
1128         bb_putchar('\n');
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 }