- Proposed fix to make aliases work (with uClibc-current).
[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 #ifdef 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 #ifdef 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 #ifdef 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 #if 0
347 /* like strcmp(), but knows about numbers */
348 except that the freshly added calls to xatoul() brf on ethernet aliases with
349 uClibc with e.g.: ife->name='lo'  name='eth0:1'
350 static int nstrcmp(const char *a, const char *b)
351 {
352         const char *a_ptr = a;
353         const char *b_ptr = b;
354
355         while (*a == *b) {
356                 if (*a == '\0') {
357                         return 0;
358                 }
359                 if (!isdigit(*a) && isdigit(*(a+1))) {
360                         a_ptr = a+1;
361                         b_ptr = b+1;
362                 }
363                 a++;
364                 b++;
365         }
366
367         if (isdigit(*a) && isdigit(*b)) {
368                 return xatoul(a_ptr) > xatoul(b_ptr) ? 1 : -1;
369         }
370         return *a - *b;
371 }
372 #endif
373
374 static struct interface *add_interface(char *name)
375 {
376         struct interface *ife, **nextp, *new;
377
378         for (ife = int_last; ife; ife = ife->prev) {
379                 int n = /*n*/strcmp(ife->name, name);
380
381                 if (n == 0)
382                         return ife;
383                 if (n < 0)
384                         break;
385         }
386
387         new = xzalloc(sizeof(*new));
388         safe_strncpy(new->name, name, IFNAMSIZ);
389         nextp = ife ? &ife->next : &int_list;
390         new->prev = ife;
391         new->next = *nextp;
392         if (new->next)
393                 new->next->prev = new;
394         else
395                 int_last = new;
396         *nextp = new;
397         return new;
398 }
399
400
401 static int if_readconf(void)
402 {
403         int numreqs = 30;
404         struct ifconf ifc;
405         struct ifreq *ifr;
406         int n, err = -1;
407         int skfd2;
408
409         /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
410            (as of 2.1.128) */
411         skfd2 = get_socket_for_af(AF_INET);
412         if (skfd2 < 0) {
413                 bb_perror_msg(("warning: no inet socket available"));
414                 /* Try to soldier on with whatever socket we can get hold of.  */
415                 skfd2 = sockets_open(0);
416                 if (skfd2 < 0)
417                         return -1;
418         }
419
420         ifc.ifc_buf = NULL;
421         for (;;) {
422                 ifc.ifc_len = sizeof(struct ifreq) * numreqs;
423                 ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
424
425                 if (ioctl(skfd2, SIOCGIFCONF, &ifc) < 0) {
426                         perror("SIOCGIFCONF");
427                         goto out;
428                 }
429                 if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) {
430                         /* assume it overflowed and try again */
431                         numreqs += 10;
432                         continue;
433                 }
434                 break;
435         }
436
437         ifr = ifc.ifc_req;
438         for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
439                 add_interface(ifr->ifr_name);
440                 ifr++;
441         }
442         err = 0;
443
444   out:
445         free(ifc.ifc_buf);
446         return err;
447 }
448
449 static char *get_name(char *name, char *p)
450 {
451         /* Extract <name> from nul-terminated p where p matches
452            <name>: after leading whitespace.
453            If match is not made, set name empty and return unchanged p */
454         int namestart=0, nameend=0;
455         while (isspace(p[namestart]))
456                 namestart++;
457         nameend=namestart;
458         while (p[nameend] && p[nameend]!=':' && !isspace(p[nameend]))
459                 nameend++;
460         if (p[nameend]==':') {
461                 if ((nameend-namestart)<IFNAMSIZ) {
462                         memcpy(name,&p[namestart],nameend-namestart);
463                         name[nameend-namestart]='\0';
464                         p=&p[nameend];
465                 } else {
466                         /* Interface name too large */
467                         name[0]='\0';
468                 }
469         } else {
470                 /* trailing ':' not found - return empty */
471                 name[0]='\0';
472         }
473         return p + 1;
474 }
475
476 /* If scanf supports size qualifiers for %n conversions, then we can
477  * use a modified fmt that simply stores the position in the fields
478  * having no associated fields in the proc string.  Of course, we need
479  * to zero them again when we're done.  But that is smaller than the
480  * old approach of multiple scanf occurrences with large numbers of
481  * args. */
482
483 /* static const char * const ss_fmt[] = { */
484 /*      "%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */
485 /*      "%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */
486 /*      "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */
487 /* }; */
488
489         /* Lie about the size of the int pointed to for %n. */
490 #if INT_MAX == LONG_MAX
491 static const char * const ss_fmt[] = {
492         "%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
493         "%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
494         "%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
495 };
496 #else
497 static const char * const ss_fmt[] = {
498         "%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu",
499         "%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu",
500         "%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu"
501 };
502
503 #endif
504
505 static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
506 {
507         memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
508
509         sscanf(bp, ss_fmt[procnetdev_vsn],
510                    &ife->stats.rx_bytes, /* missing for 0 */
511                    &ife->stats.rx_packets,
512                    &ife->stats.rx_errors,
513                    &ife->stats.rx_dropped,
514                    &ife->stats.rx_fifo_errors,
515                    &ife->stats.rx_frame_errors,
516                    &ife->stats.rx_compressed, /* missing for <= 1 */
517                    &ife->stats.rx_multicast, /* missing for <= 1 */
518                    &ife->stats.tx_bytes, /* missing for 0 */
519                    &ife->stats.tx_packets,
520                    &ife->stats.tx_errors,
521                    &ife->stats.tx_dropped,
522                    &ife->stats.tx_fifo_errors,
523                    &ife->stats.collisions,
524                    &ife->stats.tx_carrier_errors,
525                    &ife->stats.tx_compressed /* missing for <= 1 */
526                    );
527
528         if (procnetdev_vsn <= 1) {
529                 if (procnetdev_vsn == 0) {
530                         ife->stats.rx_bytes = 0;
531                         ife->stats.tx_bytes = 0;
532                 }
533                 ife->stats.rx_multicast = 0;
534                 ife->stats.rx_compressed = 0;
535                 ife->stats.tx_compressed = 0;
536         }
537 }
538
539 static inline int procnetdev_version(char *buf)
540 {
541         if (strstr(buf, "compressed"))
542                 return 2;
543         if (strstr(buf, "bytes"))
544                 return 1;
545         return 0;
546 }
547
548 static int if_readlist_proc(char *target)
549 {
550         static int proc_read;
551         FILE *fh;
552         char buf[512];
553         struct interface *ife;
554         int err, procnetdev_vsn;
555
556         if (proc_read)
557                 return 0;
558         if (!target)
559                 proc_read = 1;
560
561         fh = fopen(_PATH_PROCNET_DEV, "r");
562         if (!fh) {
563                 bb_perror_msg("warning: cannot open %s, limiting output", _PATH_PROCNET_DEV);
564                 return if_readconf();
565         }
566         fgets(buf, sizeof buf, fh);     /* eat line */
567         fgets(buf, sizeof buf, fh);
568
569         procnetdev_vsn = procnetdev_version(buf);
570
571         err = 0;
572         while (fgets(buf, sizeof buf, fh)) {
573                 char *s, name[128];
574
575                 s = get_name(name, buf);
576                 ife = add_interface(name);
577                 get_dev_fields(s, ife, procnetdev_vsn);
578                 ife->statistics_valid = 1;
579                 if (target && !strcmp(target, name))
580                         break;
581         }
582         if (ferror(fh)) {
583                 perror(_PATH_PROCNET_DEV);
584                 err = -1;
585                 proc_read = 0;
586         }
587         fclose(fh);
588         return err;
589 }
590
591 static int if_readlist(void)
592 {
593         int err = if_readlist_proc(NULL);
594
595         if (!err)
596                 err = if_readconf();
597         return err;
598 }
599
600 static int for_all_interfaces(int (*doit) (struct interface *, void *),
601                                                           void *cookie)
602 {
603         struct interface *ife;
604
605         if (!int_list && (if_readlist() < 0))
606                 return -1;
607         for (ife = int_list; ife; ife = ife->next) {
608                 int err = doit(ife, cookie);
609
610                 if (err)
611                         return err;
612         }
613         return 0;
614 }
615
616 /* Fetch the interface configuration from the kernel. */
617 static int if_fetch(struct interface *ife)
618 {
619         struct ifreq ifr;
620         int fd;
621         char *ifname = ife->name;
622
623         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
624         if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
625                 return -1;
626         ife->flags = ifr.ifr_flags;
627
628         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
629         if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
630                 memset(ife->hwaddr, 0, 32);
631         else
632                 memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
633
634         ife->type = ifr.ifr_hwaddr.sa_family;
635
636         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
637         if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0)
638                 ife->metric = 0;
639         else
640                 ife->metric = ifr.ifr_metric;
641
642         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
643         if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0)
644                 ife->mtu = 0;
645         else
646                 ife->mtu = ifr.ifr_mtu;
647
648 #ifdef SIOCGIFMAP
649         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
650         if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
651                 ife->map = ifr.ifr_map;
652         else
653 #endif
654                 memset(&ife->map, 0, sizeof(struct ifmap));
655
656 #ifdef HAVE_TXQUEUELEN
657         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
658         if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) < 0)
659                 ife->tx_queue_len = -1; /* unknown value */
660         else
661                 ife->tx_queue_len = ifr.ifr_qlen;
662 #else
663         ife->tx_queue_len = -1; /* unknown value */
664 #endif
665
666         /* IPv4 address? */
667         fd = get_socket_for_af(AF_INET);
668         if (fd >= 0) {
669                 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
670                 ifr.ifr_addr.sa_family = AF_INET;
671                 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
672                         ife->has_ip = 1;
673                         ife->addr = ifr.ifr_addr;
674                         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
675                         if (ioctl(fd, SIOCGIFDSTADDR, &ifr) < 0)
676                                 memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
677                         else
678                                 ife->dstaddr = ifr.ifr_dstaddr;
679
680                         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
681                         if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0)
682                                 memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
683                         else
684                                 ife->broadaddr = ifr.ifr_broadaddr;
685
686                         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
687                         if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0)
688                                 memset(&ife->netmask, 0, sizeof(struct sockaddr));
689                         else
690                                 ife->netmask = ifr.ifr_netmask;
691                 } else
692                         memset(&ife->addr, 0, sizeof(struct sockaddr));
693         }
694
695         return 0;
696 }
697
698
699 static int do_if_fetch(struct interface *ife)
700 {
701         if (if_fetch(ife) < 0) {
702                 char *errmsg;
703
704                 if (errno == ENODEV) {
705                         /* Give better error message for this case. */
706                         errmsg = "Device not found";
707                 } else {
708                         errmsg = strerror(errno);
709                 }
710                 bb_error_msg("%s: error fetching interface information: %s",
711                                 ife->name, errmsg);
712                 return -1;
713         }
714         return 0;
715 }
716
717 /* This structure defines hardware protocols and their handlers. */
718 struct hwtype {
719         const char * const name;
720         const char *title;
721         int type;
722         int alen;
723         char *(*print) (unsigned char *);
724         int (*input) (char *, struct sockaddr *);
725         int (*activate) (int fd);
726         int suppress_null_addr;
727 };
728
729 static const struct hwtype unspec_hwtype = {
730         .name =         "unspec",
731         .title =        "UNSPEC",
732         .type =         -1,
733         .print =        UNSPEC_print
734 };
735
736 static const struct hwtype loop_hwtype = {
737         .name =         "loop",
738         .title =        "Local Loopback",
739         .type =         ARPHRD_LOOPBACK
740 };
741
742 #include <net/if_arp.h>
743
744 #if (__GLIBC__ >=2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION)
745 #include <net/ethernet.h>
746 #else
747 #include <linux/if_ether.h>
748 #endif
749
750 /* Display an Ethernet address in readable format. */
751 static char *pr_ether(unsigned char *ptr)
752 {
753         static char buff[64];
754
755         snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X",
756                          (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
757                          (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
758                 );
759         return buff;
760 }
761
762 static const struct hwtype ether_hwtype = {
763         .name =         "ether",
764         .title =        "Ethernet",
765         .type =         ARPHRD_ETHER,
766         .alen =         ETH_ALEN,
767         .print =        pr_ether
768 };
769
770 #include <net/if_arp.h>
771
772 static const struct hwtype ppp_hwtype = {
773         .name =         "ppp",
774         .title =        "Point-to-Point Protocol",
775         .type =         ARPHRD_PPP
776 };
777
778 #ifdef CONFIG_FEATURE_IPV6
779 static const struct hwtype sit_hwtype = {
780         .name =                 "sit",
781         .title =                "IPv6-in-IPv4",
782         .type =                 ARPHRD_SIT,
783         .print =                UNSPEC_print,
784         .suppress_null_addr =   1
785 } ;
786 #endif
787
788 static const struct hwtype * const hwtypes[] = {
789         &loop_hwtype,
790         &ether_hwtype,
791         &ppp_hwtype,
792         &unspec_hwtype,
793 #ifdef CONFIG_FEATURE_IPV6
794         &sit_hwtype,
795 #endif
796         NULL
797 };
798
799 #ifdef IFF_PORTSEL
800 static const char * const if_port_text[] = {
801         /* Keep in step with <linux/netdevice.h> */
802         "unknown",
803         "10base2",
804         "10baseT",
805         "AUI",
806         "100baseT",
807         "100baseTX",
808         "100baseFX",
809         NULL
810 };
811 #endif
812
813 /* Check our hardware type table for this type. */
814 static const struct hwtype *get_hwntype(int type)
815 {
816         const struct hwtype * const *hwp;
817
818         hwp = hwtypes;
819         while (*hwp != NULL) {
820                 if ((*hwp)->type == type)
821                         return *hwp;
822                 hwp++;
823         }
824         return NULL;
825 }
826
827 /* return 1 if address is all zeros */
828 static int hw_null_address(const struct hwtype *hw, void *ap)
829 {
830         unsigned int i;
831         unsigned char *address = (unsigned char *) ap;
832
833         for (i = 0; i < hw->alen; i++)
834                 if (address[i])
835                         return 0;
836         return 1;
837 }
838
839 static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
840
841 static void print_bytes_scaled(unsigned long long ull, const char *end)
842 {
843         unsigned long long int_part;
844         const char *ext;
845         unsigned int frac_part;
846         int i;
847
848         frac_part = 0;
849         ext = TRext;
850         int_part = ull;
851         i = 4;
852         do {
853                 if (int_part >= 1024) {
854                         frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
855                         int_part /= 1024;
856                         ext += 3;       /* KiB, MiB, GiB, TiB */
857                 }
858                 --i;
859         } while (i);
860
861         printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
862 }
863
864 static const char * const ife_print_flags_strs[] = {
865         "UP ",
866         "BROADCAST ",
867         "DEBUG ",
868         "LOOPBACK ",
869         "POINTOPOINT ",
870         "NOTRAILERS ",
871         "RUNNING ",
872         "NOARP ",
873         "PROMISC ",
874         "ALLMULTI ",
875         "SLAVE ",
876         "MASTER ",
877         "MULTICAST ",
878 #ifdef HAVE_DYNAMIC
879         "DYNAMIC "
880 #endif
881 };
882
883 static const unsigned short ife_print_flags_mask[] = {
884         IFF_UP,
885         IFF_BROADCAST,
886         IFF_DEBUG,
887         IFF_LOOPBACK,
888         IFF_POINTOPOINT,
889         IFF_NOTRAILERS,
890         IFF_RUNNING,
891         IFF_NOARP,
892         IFF_PROMISC,
893         IFF_ALLMULTI,
894         IFF_SLAVE,
895         IFF_MASTER,
896         IFF_MULTICAST,
897 #ifdef HAVE_DYNAMIC
898         IFF_DYNAMIC
899 #endif
900         0
901 };
902
903 static void ife_print(struct interface *ptr)
904 {
905         struct aftype *ap;
906         const struct hwtype *hw;
907         int hf;
908         int can_compress = 0;
909
910 #ifdef HAVE_AFINET6
911         FILE *f;
912         char addr6[40], devname[20];
913         struct sockaddr_in6 sap;
914         int plen, scope, dad_status, if_idx;
915         char addr6p[8][5];
916 #endif
917
918         ap = get_afntype(ptr->addr.sa_family);
919         if (ap == NULL)
920                 ap = get_afntype(0);
921
922         hf = ptr->type;
923
924         if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
925                 can_compress = 1;
926
927         hw = get_hwntype(hf);
928         if (hw == NULL)
929                 hw = get_hwntype(-1);
930
931         printf("%-9.9s Link encap:%s  ", ptr->name, hw->title);
932         /* For some hardware types (eg Ash, ATM) we don't print the
933            hardware address if it's null.  */
934         if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
935                                                                 hw->suppress_null_addr)))
936                 printf("HWaddr %s  ", hw->print((unsigned char *)ptr->hwaddr));
937 #ifdef IFF_PORTSEL
938         if (ptr->flags & IFF_PORTSEL) {
939                 printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
940                 if (ptr->flags & IFF_AUTOMEDIA)
941                         printf("(auto)");
942         }
943 #endif
944         puts("");
945
946         if (ptr->has_ip) {
947                 printf("          %s addr:%s ", ap->name,
948                            ap->sprint(&ptr->addr, 1));
949                 if (ptr->flags & IFF_POINTOPOINT) {
950                         printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
951                 }
952                 if (ptr->flags & IFF_BROADCAST) {
953                         printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
954                 }
955                 printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
956         }
957
958 #ifdef HAVE_AFINET6
959
960 #define IPV6_ADDR_ANY           0x0000U
961
962 #define IPV6_ADDR_UNICAST       0x0001U
963 #define IPV6_ADDR_MULTICAST     0x0002U
964 #define IPV6_ADDR_ANYCAST       0x0004U
965
966 #define IPV6_ADDR_LOOPBACK      0x0010U
967 #define IPV6_ADDR_LINKLOCAL     0x0020U
968 #define IPV6_ADDR_SITELOCAL     0x0040U
969
970 #define IPV6_ADDR_COMPATv4      0x0080U
971
972 #define IPV6_ADDR_SCOPE_MASK    0x00f0U
973
974 #define IPV6_ADDR_MAPPED        0x1000U
975 #define IPV6_ADDR_RESERVED      0x2000U /* reserved address space */
976
977         if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) {
978                 while (fscanf
979                            (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
980                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
981                                 addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
982                                 &dad_status, devname) != EOF) {
983                         if (!strcmp(devname, ptr->name)) {
984                                 sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
985                                                 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
986                                                 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
987                                 inet_pton(AF_INET6, addr6,
988                                                   (struct sockaddr *) &sap.sin6_addr);
989                                 sap.sin6_family = AF_INET6;
990                                 printf("          inet6 addr: %s/%d",
991                                            inet6_aftype.sprint((struct sockaddr *) &sap, 1),
992                                            plen);
993                                 printf(" Scope:");
994                                 switch (scope & IPV6_ADDR_SCOPE_MASK) {
995                                 case 0:
996                                         printf("Global");
997                                         break;
998                                 case IPV6_ADDR_LINKLOCAL:
999                                         printf("Link");
1000                                         break;
1001                                 case IPV6_ADDR_SITELOCAL:
1002                                         printf("Site");
1003                                         break;
1004                                 case IPV6_ADDR_COMPATv4:
1005                                         printf("Compat");
1006                                         break;
1007                                 case IPV6_ADDR_LOOPBACK:
1008                                         printf("Host");
1009                                         break;
1010                                 default:
1011                                         printf("Unknown");
1012                                 }
1013                                 puts("");
1014                         }
1015                 }
1016                 fclose(f);
1017         }
1018 #endif
1019
1020         printf("          ");
1021         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
1022
1023         if (ptr->flags == 0) {
1024                 printf("[NO FLAGS] ");
1025         } else {
1026                 int i = 0;
1027                 do {
1028                         if (ptr->flags & ife_print_flags_mask[i]) {
1029                                 printf(ife_print_flags_strs[i]);
1030                         }
1031                 } while (ife_print_flags_mask[++i]);
1032         }
1033
1034         /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
1035         printf(" MTU:%d  Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
1036 #ifdef SIOCSKEEPALIVE
1037         if (ptr->outfill || ptr->keepalive)
1038                 printf("  Outfill:%d  Keepalive:%d", ptr->outfill, ptr->keepalive);
1039 #endif
1040         puts("");
1041
1042         /* If needed, display the interface statistics. */
1043
1044         if (ptr->statistics_valid) {
1045                 /* XXX: statistics are currently only printed for the primary address,
1046                  *      not for the aliases, although strictly speaking they're shared
1047                  *      by all addresses.
1048                  */
1049                 printf("          ");
1050
1051                 printf("RX packets:%llu errors:%lu dropped:%lu overruns:%lu frame:%lu\n",
1052                            ptr->stats.rx_packets, ptr->stats.rx_errors,
1053                            ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
1054                            ptr->stats.rx_frame_errors);
1055                 if (can_compress)
1056                         printf("             compressed:%lu\n",
1057                                    ptr->stats.rx_compressed);
1058                 printf("          ");
1059                 printf("TX packets:%llu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n",
1060                            ptr->stats.tx_packets, ptr->stats.tx_errors,
1061                            ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
1062                            ptr->stats.tx_carrier_errors);
1063                 printf("          collisions:%lu ", ptr->stats.collisions);
1064                 if (can_compress)
1065                         printf("compressed:%lu ", ptr->stats.tx_compressed);
1066                 if (ptr->tx_queue_len != -1)
1067                         printf("txqueuelen:%d ", ptr->tx_queue_len);
1068                 printf("\n          R");
1069                 print_bytes_scaled(ptr->stats.rx_bytes, "  T");
1070                 print_bytes_scaled(ptr->stats.tx_bytes, "\n");
1071
1072         }
1073
1074         if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma ||
1075                  ptr->map.base_addr)) {
1076                 printf("          ");
1077                 if (ptr->map.irq)
1078                         printf("Interrupt:%d ", ptr->map.irq);
1079                 if (ptr->map.base_addr >= 0x100)        /* Only print devices using it for
1080                                                                                            I/O maps */
1081                         printf("Base address:0x%lx ",
1082                                    (unsigned long) ptr->map.base_addr);
1083                 if (ptr->map.mem_start) {
1084                         printf("Memory:%lx-%lx ", ptr->map.mem_start,
1085                                    ptr->map.mem_end);
1086                 }
1087                 if (ptr->map.dma)
1088                         printf("DMA chan:%x ", ptr->map.dma);
1089                 puts("");
1090         }
1091         puts("");
1092 }
1093
1094
1095 static int do_if_print(struct interface *ife, void *cookie)
1096 {
1097         int *opt_a = (int *) cookie;
1098         int res;
1099
1100         res = do_if_fetch(ife);
1101         if (res >= 0) {
1102                 if ((ife->flags & IFF_UP) || *opt_a)
1103                         ife_print(ife);
1104         }
1105         return res;
1106 }
1107
1108 static struct interface *lookup_interface(char *name)
1109 {
1110         struct interface *ife = NULL;
1111
1112         if (if_readlist_proc(name) < 0)
1113                 return NULL;
1114         ife = add_interface(name);
1115         return ife;
1116 }
1117
1118 /* for ipv4 add/del modes */
1119 static int if_print(char *ifname)
1120 {
1121         int res;
1122
1123         if (!ifname) {
1124                 res = for_all_interfaces(do_if_print, &interface_opt_a);
1125         } else {
1126                 struct interface *ife;
1127
1128                 ife = lookup_interface(ifname);
1129                 res = do_if_fetch(ife);
1130                 if (res >= 0)
1131                         ife_print(ife);
1132         }
1133         return res;
1134 }
1135
1136 int display_interfaces(char *ifname);
1137 int display_interfaces(char *ifname)
1138 {
1139         int status;
1140
1141         /* Create a channel to the NET kernel. */
1142         if ((skfd = sockets_open(0)) < 0) {
1143                 bb_perror_msg_and_die("socket");
1144         }
1145
1146         /* Do we have to show the current setup? */
1147         status = if_print(ifname);
1148 #ifdef CONFIG_FEATURE_CLEAN_UP
1149         sockets_close();
1150 #endif
1151         exit(status < 0);
1152 }