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