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