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