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