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