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