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