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