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