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