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