sulogin: use bb_error_msg instead of bb_info_msg; better message
[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++ & 0377));
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))
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))
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] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
708                          (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
709                 );
710         return auto_string(buff);
711 }
712
713 static const struct hwtype ether_hwtype = {
714         .name  = "ether",
715         .title = "Ethernet",
716         .type  = ARPHRD_ETHER,
717         .alen  = ETH_ALEN,
718         .print = ether_print,
719         .input = in_ether
720 };
721
722 static const struct hwtype ppp_hwtype = {
723         .name =         "ppp",
724         .title =        "Point-to-Point Protocol",
725         .type =         ARPHRD_PPP
726 };
727
728 #if ENABLE_FEATURE_IPV6
729 static const struct hwtype sit_hwtype = {
730         .name =                 "sit",
731         .title =                "IPv6-in-IPv4",
732         .type =                 ARPHRD_SIT,
733         .print =                UNSPEC_print,
734         .suppress_null_addr =   1
735 };
736 #endif
737 #if ENABLE_FEATURE_HWIB
738 static const struct hwtype ib_hwtype = {
739         .name  = "infiniband",
740         .title = "InfiniBand",
741         .type  = ARPHRD_INFINIBAND,
742         .alen  = INFINIBAND_ALEN,
743         .print = UNSPEC_print,
744         .input = in_ib,
745 };
746 #endif
747
748
749 static const struct hwtype *const hwtypes[] = {
750         &loop_hwtype,
751         &ether_hwtype,
752         &ppp_hwtype,
753         &unspec_hwtype,
754 #if ENABLE_FEATURE_IPV6
755         &sit_hwtype,
756 #endif
757 #if ENABLE_FEATURE_HWIB
758         &ib_hwtype,
759 #endif
760         NULL
761 };
762
763 #ifdef IFF_PORTSEL
764 static const char *const if_port_text[] = {
765         /* Keep in step with <linux/netdevice.h> */
766         "unknown",
767         "10base2",
768         "10baseT",
769         "AUI",
770         "100baseT",
771         "100baseTX",
772         "100baseFX",
773         NULL
774 };
775 #endif
776
777 /* Check our hardware type table for this type. */
778 const struct hwtype* FAST_FUNC get_hwtype(const char *name)
779 {
780         const struct hwtype *const *hwp;
781
782         hwp = hwtypes;
783         while (*hwp != NULL) {
784                 if (!strcmp((*hwp)->name, name))
785                         return (*hwp);
786                 hwp++;
787         }
788         return NULL;
789 }
790
791 /* Check our hardware type table for this type. */
792 const struct hwtype* FAST_FUNC get_hwntype(int type)
793 {
794         const struct hwtype *const *hwp;
795
796         hwp = hwtypes;
797         while (*hwp != NULL) {
798                 if ((*hwp)->type == type)
799                         return *hwp;
800                 hwp++;
801         }
802         return NULL;
803 }
804
805 /* return 1 if address is all zeros */
806 static int hw_null_address(const struct hwtype *hw, void *ap)
807 {
808         int i;
809         unsigned char *address = (unsigned char *) ap;
810
811         for (i = 0; i < hw->alen; i++)
812                 if (address[i])
813                         return 0;
814         return 1;
815 }
816
817 static const char TRext[] ALIGN1 = "\0\0\0Ki\0Mi\0Gi\0Ti";
818
819 static void print_bytes_scaled(unsigned long long ull, const char *end)
820 {
821         unsigned long long int_part;
822         const char *ext;
823         unsigned int frac_part;
824         int i;
825
826         frac_part = 0;
827         ext = TRext;
828         int_part = ull;
829         i = 4;
830         do {
831                 if (int_part >= 1024) {
832                         frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
833                         int_part /= 1024;
834                         ext += 3;       /* KiB, MiB, GiB, TiB */
835                 }
836                 --i;
837         } while (i);
838
839         printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
840 }
841
842
843 #ifdef HAVE_AFINET6
844 #define IPV6_ADDR_ANY           0x0000U
845
846 #define IPV6_ADDR_UNICAST       0x0001U
847 #define IPV6_ADDR_MULTICAST     0x0002U
848 #define IPV6_ADDR_ANYCAST       0x0004U
849
850 #define IPV6_ADDR_LOOPBACK      0x0010U
851 #define IPV6_ADDR_LINKLOCAL     0x0020U
852 #define IPV6_ADDR_SITELOCAL     0x0040U
853
854 #define IPV6_ADDR_COMPATv4      0x0080U
855
856 #define IPV6_ADDR_SCOPE_MASK    0x00f0U
857
858 #define IPV6_ADDR_MAPPED        0x1000U
859 #define IPV6_ADDR_RESERVED      0x2000U /* reserved address space */
860
861
862 static void ife_print6(struct interface *ptr)
863 {
864         FILE *f;
865         char addr6[40], devname[21];
866         struct sockaddr_in6 sap;
867         int plen, scope, dad_status, if_idx;
868         char addr6p[8][5];
869
870         f = fopen_for_read(_PATH_PROCNET_IFINET6);
871         if (f == NULL)
872                 return;
873
874         while (fscanf
875                    (f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
876                         addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
877                         addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
878                         &dad_status, devname) != EOF
879         ) {
880                 if (!strcmp(devname, ptr->name)) {
881                         sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
882                                         addr6p[0], addr6p[1], addr6p[2], addr6p[3],
883                                         addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
884                         memset(&sap, 0, sizeof(sap));
885                         inet_pton(AF_INET6, addr6,
886                                           (struct sockaddr *) &sap.sin6_addr);
887                         sap.sin6_family = AF_INET6;
888                         printf("          inet6 addr: %s/%d",
889                                 INET6_sprint((struct sockaddr *) &sap, 1),
890                                 plen);
891                         printf(" Scope:");
892                         switch (scope & IPV6_ADDR_SCOPE_MASK) {
893                         case 0:
894                                 puts("Global");
895                                 break;
896                         case IPV6_ADDR_LINKLOCAL:
897                                 puts("Link");
898                                 break;
899                         case IPV6_ADDR_SITELOCAL:
900                                 puts("Site");
901                                 break;
902                         case IPV6_ADDR_COMPATv4:
903                                 puts("Compat");
904                                 break;
905                         case IPV6_ADDR_LOOPBACK:
906                                 puts("Host");
907                                 break;
908                         default:
909                                 puts("Unknown");
910                         }
911                 }
912         }
913         fclose(f);
914 }
915 #else
916 #define ife_print6(a) ((void)0)
917 #endif
918
919 static void ife_print(struct interface *ptr)
920 {
921         const struct aftype *ap;
922         const struct hwtype *hw;
923         int hf;
924         int can_compress = 0;
925
926         ap = get_afntype(ptr->addr.sa_family);
927         if (ap == NULL)
928                 ap = get_afntype(0);
929
930         hf = ptr->type;
931
932         if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
933                 can_compress = 1;
934
935         hw = get_hwntype(hf);
936         if (hw == NULL)
937                 hw = get_hwntype(-1);
938
939         printf("%-9s Link encap:%s  ", ptr->name, hw->title);
940         /* For some hardware types (eg Ash, ATM) we don't print the
941            hardware address if it's null.  */
942         if (hw->print != NULL
943          && !(hw_null_address(hw, ptr->hwaddr) && hw->suppress_null_addr)
944         ) {
945                 printf("HWaddr %s  ", hw->print((unsigned char *)ptr->hwaddr));
946         }
947 #ifdef IFF_PORTSEL
948         if (ptr->flags & IFF_PORTSEL) {
949                 printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
950                 if (ptr->flags & IFF_AUTOMEDIA)
951                         printf("(auto)");
952         }
953 #endif
954         bb_putchar('\n');
955
956         if (ptr->has_ip) {
957                 printf("          %s addr:%s ", ap->name,
958                         ap->sprint(&ptr->addr, 1));
959                 if (ptr->flags & IFF_POINTOPOINT) {
960                         printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
961                 }
962                 if (ptr->flags & IFF_BROADCAST) {
963                         printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
964                 }
965                 printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
966         }
967
968         ife_print6(ptr);
969
970         printf("          ");
971         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
972
973         if (ptr->flags == 0) {
974                 printf("[NO FLAGS] ");
975         } else {
976                 static const char ife_print_flags_strs[] ALIGN1 =
977                         "UP\0"
978                         "BROADCAST\0"
979                         "DEBUG\0"
980                         "LOOPBACK\0"
981                         "POINTOPOINT\0"
982                         "NOTRAILERS\0"
983                         "RUNNING\0"
984                         "NOARP\0"
985                         "PROMISC\0"
986                         "ALLMULTI\0"
987                         "SLAVE\0"
988                         "MASTER\0"
989                         "MULTICAST\0"
990 #ifdef HAVE_DYNAMIC
991                         "DYNAMIC\0"
992 #endif
993                         ;
994                 static const unsigned short ife_print_flags_mask[] ALIGN2 = {
995                         IFF_UP,
996                         IFF_BROADCAST,
997                         IFF_DEBUG,
998                         IFF_LOOPBACK,
999                         IFF_POINTOPOINT,
1000                         IFF_NOTRAILERS,
1001                         IFF_RUNNING,
1002                         IFF_NOARP,
1003                         IFF_PROMISC,
1004                         IFF_ALLMULTI,
1005                         IFF_SLAVE,
1006                         IFF_MASTER,
1007                         IFF_MULTICAST
1008 #ifdef HAVE_DYNAMIC
1009                         ,IFF_DYNAMIC
1010 #endif
1011                 };
1012                 const unsigned short *mask = ife_print_flags_mask;
1013                 const char *str = ife_print_flags_strs;
1014                 do {
1015                         if (ptr->flags & *mask) {
1016                                 printf("%s ", str);
1017                         }
1018                         mask++;
1019                         str += strlen(str) + 1;
1020                 } while (*str);
1021         }
1022
1023         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
1024         printf(" MTU:%d  Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
1025 #ifdef SIOCSKEEPALIVE
1026         if (ptr->outfill || ptr->keepalive)
1027                 printf("  Outfill:%d  Keepalive:%d", ptr->outfill, ptr->keepalive);
1028 #endif
1029         bb_putchar('\n');
1030
1031         /* If needed, display the interface statistics. */
1032
1033         if (ptr->statistics_valid) {
1034                 /* XXX: statistics are currently only printed for the primary address,
1035                  *      not for the aliases, although strictly speaking they're shared
1036                  *      by all addresses.
1037                  */
1038                 printf("          ");
1039
1040                 printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
1041                         ptr->stats.rx_packets, ptr->stats.rx_errors,
1042                         ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
1043                         ptr->stats.rx_frame_errors);
1044                 if (can_compress)
1045                         printf("             compressed:%lu\n",
1046                                 ptr->stats.rx_compressed);
1047                 printf("          ");
1048                 printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
1049                         ptr->stats.tx_packets, ptr->stats.tx_errors,
1050                         ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
1051                         ptr->stats.tx_carrier_errors);
1052                 printf("          collisions:%lu ", ptr->stats.collisions);
1053                 if (can_compress)
1054                         printf("compressed:%lu ", ptr->stats.tx_compressed);
1055                 if (ptr->tx_queue_len != -1)
1056                         printf("txqueuelen:%d ", ptr->tx_queue_len);
1057                 printf("\n          R");
1058                 print_bytes_scaled(ptr->stats.rx_bytes, "  T");
1059                 print_bytes_scaled(ptr->stats.tx_bytes, "\n");
1060         }
1061
1062         if (ptr->map.irq || ptr->map.mem_start
1063          || ptr->map.dma || ptr->map.base_addr
1064         ) {
1065                 printf("          ");
1066                 if (ptr->map.irq)
1067                         printf("Interrupt:%d ", ptr->map.irq);
1068                 if (ptr->map.base_addr >= 0x100) /* Only print devices using it for I/O maps */
1069                         printf("Base address:0x%lx ",
1070                                 (unsigned long) ptr->map.base_addr);
1071                 if (ptr->map.mem_start) {
1072                         printf("Memory:%lx-%lx ", ptr->map.mem_start,
1073                                 ptr->map.mem_end);
1074                 }
1075                 if (ptr->map.dma)
1076                         printf("DMA chan:%x ", ptr->map.dma);
1077                 bb_putchar('\n');
1078         }
1079         bb_putchar('\n');
1080 }
1081
1082 static int do_if_print(struct interface *ife) /*, int *opt_a)*/
1083 {
1084         int res;
1085
1086         res = do_if_fetch(ife);
1087         if (res >= 0) {
1088                 if ((ife->flags & IFF_UP) || interface_opt_a)
1089                         ife_print(ife);
1090         }
1091         return res;
1092 }
1093
1094 static struct interface *lookup_interface(char *name)
1095 {
1096         struct interface *ife = NULL;
1097
1098         if (if_readlist_proc(name) < 0)
1099                 return NULL;
1100         ife = add_interface(name);
1101         return ife;
1102 }
1103
1104 #ifdef UNUSED
1105 static int for_all_interfaces(int (*doit) (struct interface *, void *),
1106                                                         void *cookie)
1107 {
1108         struct interface *ife;
1109
1110         if (!int_list && (if_readlist() < 0))
1111                 return -1;
1112         for (ife = int_list; ife; ife = ife->next) {
1113                 int err = doit(ife, cookie);
1114                 if (err)
1115                         return err;
1116         }
1117         return 0;
1118 }
1119 #endif
1120
1121 /* for ipv4 add/del modes */
1122 static int if_print(char *ifname)
1123 {
1124         struct interface *ife;
1125         int res;
1126
1127         if (!ifname) {
1128                 /*res = for_all_interfaces(do_if_print, &interface_opt_a);*/
1129                 if (!int_list && (if_readlist() < 0))
1130                         return -1;
1131                 for (ife = int_list; ife; ife = ife->next) {
1132                         int err = do_if_print(ife); /*, &interface_opt_a);*/
1133                         if (err)
1134                                 return err;
1135                 }
1136                 return 0;
1137         }
1138         ife = lookup_interface(ifname);
1139         res = do_if_fetch(ife);
1140         if (res >= 0)
1141                 ife_print(ife);
1142         return res;
1143 }
1144
1145 #if ENABLE_FEATURE_HWIB
1146 /* Input an Infiniband address and convert to binary. */
1147 int FAST_FUNC in_ib(const char *bufp, struct sockaddr *sap)
1148 {
1149         sap->sa_family = ib_hwtype.type;
1150 //TODO: error check?
1151         hex2bin((char*)sap->sa_data, bufp, INFINIBAND_ALEN);
1152 # ifdef HWIB_DEBUG
1153         fprintf(stderr, "in_ib(%s): %s\n", bufp, UNSPEC_print(sap->sa_data));
1154 # endif
1155         return 0;
1156 }
1157 #endif
1158
1159 int FAST_FUNC display_interfaces(char *ifname)
1160 {
1161         int status;
1162
1163         status = if_print(ifname);
1164
1165         return (status < 0); /* status < 0 == 1 -- error */
1166 }