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