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