/*
 * $OpenBSD: if.c,v 1.42 2005/03/13 16:05:50 mpf Exp $
 */
/*
 * $NetBSD: if.c,v 1.16.4.2 1996/06/07 21:46:46 thorpej Exp $
 */

/*
 * Copyright (c) 1983, 1988, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>

#include <stdint.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 1
#endif
#ifndef _XOPEN_SOURCE_EXTENDED
#define _XOPEN_SOURCE_EXTENDED 1
#endif
#include <signal.h>

#include "main.h"
#include "netstat.h"

#define	YES	1
#define	NO	0

static void     sidewaysintpr(u_int);

struct _if_info {
    char            name[128];
    char            descr[128];
    char            ip[128], route[128];
    int             mtu;
    int             drops;
    unsigned int    ifindex;
    /*
     * Save "expandable" fields as string values
     *  rather than integer statistics
     */
    char            s_ipkts[20], s_ierrs[20];
    char            s_opkts[20], s_oerrs[20];
    char            s_ibytes[20], s_obytes[20];
    char            s_outq[20];
    unsigned long   ipkts, opkts;       /* Need to combine 2 MIB values */
    int             operstatus;
    /*
     * u_long          netmask;
     * struct in_addr  ifip, ifroute;
     */
    struct _if_info *next;
};


static struct timeval deadline;

static void
timerSet(int interval_seconds)
{
    const struct timeval interval = { interval_seconds, 0 };

    netsnmp_get_monotonic_clock(&deadline);
    NETSNMP_TIMERADD(&deadline, &interval, &deadline);
}

static void
timerPause(void)
{
    struct timeval  now, delta;

    netsnmp_get_monotonic_clock(&now);
    NETSNMP_TIMERSUB(&deadline, &now, &delta);
    if (delta.tv_sec < 0)
        return;
#ifdef WIN32
    Sleep(delta.tv_sec * 1000 + delta.tv_usec / 1000);
#else
    if (select(0, NULL, NULL, NULL, &delta) < 0)
        snmp_perror("select");
#endif
}

/*
 * Retrieve the interface addressing information
 * XXX - This could also be extended to handle non-IP interfaces
 */
void
_set_address(struct _if_info *cur_if)
{
    oid             ipaddr_oid[] = { 1, 3, 6, 1, 2, 1, 4, 20, 1, 0 };
    size_t          ipaddr_len = OID_LENGTH(ipaddr_oid);
    static netsnmp_variable_list *addr_if_var = NULL;
    static netsnmp_variable_list *addr_mask_var = NULL;
    netsnmp_variable_list *vp, *vp2;
    union {
        in_addr_t       addr;
        char            data[4];
    } tmpAddr;
    char           *cp;
    in_addr_t       ifAddr, mask;

    /*
     *  Note that this information only needs to be retrieved
     *    once, and can be re-used for subsequent calls.
     */
    if (addr_if_var == NULL) {
        ipaddr_oid[9] = 2;      /* ipAdEntIfIndex */
        snmp_varlist_add_variable(&addr_if_var, ipaddr_oid, ipaddr_len,
                                  ASN_NULL, NULL, 0);
        netsnmp_query_walk(addr_if_var, ss);

        ipaddr_oid[9] = 3;      /* ipAdEntNetMask */
        snmp_varlist_add_variable(&addr_mask_var, ipaddr_oid, ipaddr_len,
                                  ASN_NULL, NULL, 0);
        netsnmp_query_walk(addr_mask_var, ss);
    }

    /*
     * Find the address row relevant to this interface
     */
    for (vp = addr_if_var, vp2 = addr_mask_var; vp;
         vp = vp->next_variable, vp2 = vp2->next_variable) {
        if (vp->val.integer && *vp->val.integer == (int) cur_if->ifindex)
            break;
    }
    if (vp2) {
        /*
         * Always want a numeric interface IP address
         */
        snprintf(cur_if->ip, 128, "%" NETSNMP_PRIo "u.%" NETSNMP_PRIo "u."
                 "%" NETSNMP_PRIo "u.%" NETSNMP_PRIo "u",
                 vp2->name[10],
                 vp2->name[11], vp2->name[12], vp2->name[13]);

        /*
         * But re-use the routing table utilities/code for
         *   displaying the local network information
         */
        cp = tmpAddr.data;
        cp[0] = (uint8_t) vp2->name[10];
        cp[1] = (uint8_t) vp2->name[11];
        cp[2] = (uint8_t) vp2->name[12];
        cp[3] = (uint8_t) vp2->name[13];
        ifAddr = tmpAddr.addr;
        cp = tmpAddr.data;
        cp[0] = (uint8_t) vp2->val.string[0];
        cp[1] = (uint8_t) vp2->val.string[1];
        cp[2] = (uint8_t) vp2->val.string[2];
        cp[3] = (uint8_t) vp2->val.string[3];
        mask = tmpAddr.addr;
        snprintf(cur_if->route, 128, "%s", netname(ifAddr, mask));
    }
}


/*
 * Print a description of the network interfaces.
 */
void
intpr(int interval)
{
    oid             ifcol_oid[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 0 };
    size_t          ifcol_len = OID_LENGTH(ifcol_oid);
    oid             ifxcol_oid[] = { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 0 };
    size_t          ifxcol_len = OID_LENGTH(ifxcol_oid);

    struct _if_info *if_head, *if_tail, *cur_if;
    netsnmp_variable_list *var, *vp;
    /*
     * Track maximum field widths, expanding as necessary
     *   This is one reason why results can't be
     *   displayed immediately they are retrieved.
     */
    int             max_name = 4, max_ip = 7, max_route = 7, max_outq = 5;
    int             max_ipkts = 5, max_ierrs = 5, max_opkts =
        5, max_oerrs = 5;
    int             max_ibytes = 6, max_obytes = 6;
    int             i;


    if (interval) {
        sidewaysintpr((unsigned) interval);
        return;
    }

    /*
     * The traditional "netstat -i" output combines information
     *   from two SNMP tables:
     *      ipAddrTable   (for the IP address/network)
     *      ifTable       (for the interface statistics)
     *
     * The previous approach was to retrieve (and save) the
     *   address information first. Then walk the main ifTable,
     *   add the relevant stored addresses, and saving the
     *   full information for each interface, before displaying
     *   the results as a separate pass.
     *
     * This code reverses this general structure, by first retrieving
     *   (and storing) the interface statistics for the whole table,
     *   then inserting the address information obtained from the
     *   ipAddrTable, and finally displaying the results.
     * Such an arrangement should make it easier to extend this
     *   to handle non-IP interfaces (hence not in ipAddrTable)
     */
    if_head = NULL;
    if_tail = NULL;
    var = NULL;

#define ADD_IFVAR( x ) ifcol_oid[ ifcol_len-1 ] = x; \
    snmp_varlist_add_variable( &var, ifcol_oid, ifcol_len, ASN_NULL, NULL,  0)
    ADD_IFVAR(2);               /* ifName  */
    ADD_IFVAR(4);               /* ifMtu   */
    ADD_IFVAR(8);               /* ifOperStatus */
    /*
     * The Net/Open-BSD behaviour is to display *either* byte
     *   counts *or* packet/error counts (but not both). FreeBSD
     *   integrates the byte counts into the traditional display.
     *
     * The previous 'snmpnetstat' implementation followed the
     *   separatist model.  This re-write offers an opportunity
     *   to adopt the (more useful, IMO) Free-BSD approach.
     *
     * Or we could perhaps support both styles? :-)
     */
    if (bflag || oflag) {
        ADD_IFVAR(10);          /* ifInOctets   */
        ADD_IFVAR(16);          /* ifOutOctets  */
    }
    if (!oflag) {
        ADD_IFVAR(11);          /* ifInUcastPkts  */
        ADD_IFVAR(12);          /* ifInNUcastPkts */
        ADD_IFVAR(14);          /* ifInErrors     */
        ADD_IFVAR(17);          /* ifOutUcastPkts */
        ADD_IFVAR(18);          /* ifOutNUcastPkts */
        ADD_IFVAR(20);          /* ifOutErrors    */
        ADD_IFVAR(21);          /* ifOutQLen      */
    }
#if 0
    if (tflag) {
        ADD_IFVAR(XX);          /* ??? */
    }
#endif
    if (dflag) {
        ADD_IFVAR(19);          /* ifOutDiscards  */
    }
#undef ADD_IFVAR
#define ADD_IFXVAR( x ) ifxcol_oid[ ifxcol_len-1 ] = x; \
    snmp_varlist_add_variable( &var, ifxcol_oid, ifxcol_len, ASN_NULL, NULL,  0)
    ADD_IFXVAR(1);              /* ifName */
#undef ADD_IFXVAR

    /*
     * Now walk the ifTable, creating a list of interfaces
     */
    while (1) {
        if (netsnmp_query_getnext(var, ss) != SNMP_ERR_NOERROR)
            break;
        if ((var->type & 0xF0) == 0x80) /* Exception */
            break;
        ifcol_oid[ifcol_len - 1] = 2;   /* ifDescr */
        if (snmp_oid_compare(ifcol_oid, ifcol_len,
                             var->name, ifcol_len) != 0)
            break;              /* End of Table */
        cur_if = SNMP_MALLOC_TYPEDEF(struct _if_info);
        if (!cur_if)
            break;
        cur_if->ifindex = var->name[var->name_length - 1];
        for (vp = var; vp; vp = vp->next_variable) {
            if ((vp->type & 0xF0) == 0x80)      /* Exception */
                continue;
            if (!vp->val.integer)
                continue;
            if (var->name[var->name_length - 1] != cur_if->ifindex) {
                /*
                 * Inconsistent index information
                 * XXX - Try to recover ?
                 */
                SNMP_FREE(cur_if);
                break;          /* not for now, no */
            }
            switch (vp->name[var->name_length - 2]) {
            case 2:            /* ifDescr */
                if (vp->val_len >= sizeof(cur_if->name))
                    vp->val_len = sizeof(cur_if->name) - 1;
                memmove(cur_if->name, vp->val.string, vp->val_len);
                cur_if->name[vp->val_len] = 0;
                memmove(cur_if->descr, vp->val.string, vp->val_len);
                cur_if->descr[vp->val_len] = 0;
                break;
            case 4:            /* ifMtu   */
                cur_if->mtu = *vp->val.integer;
                break;
            case 8:            /* ifOperStatus   */
                cur_if->operstatus = *vp->val.integer;
                /*
                 * XXX - any special processing ??
                 */
                break;
            case 10:           /* ifInOctets     */
                snprintf(cur_if->s_ibytes, sizeof cur_if->s_ibytes, "%lu", *vp->val.integer);
                i = strlen(cur_if->s_ibytes);
                if (i > max_ibytes)
                    max_ibytes = i;
                break;
            case 11:           /* ifInUcastPkts  */
                cur_if->ipkts += *vp->val.integer;
                snprintf(cur_if->s_ipkts, sizeof cur_if->s_ipkts, "%lu", cur_if->ipkts);
                i = strlen(cur_if->s_ipkts);
                if (i > max_ipkts)
                    max_ipkts = i;
                break;
            case 12:           /* ifInNUcastPkts  */
                cur_if->ipkts += *vp->val.integer;
                snprintf(cur_if->s_ipkts, sizeof cur_if->s_ipkts, "%lu", cur_if->ipkts);
                i = strlen(cur_if->s_ipkts);
                if (i > max_ipkts)
                    max_ipkts = i;
                break;
            case 14:           /* ifInErrors      */
                snprintf(cur_if->s_ierrs, sizeof cur_if->s_ierrs, "%lu", *vp->val.integer);
                i = strlen(cur_if->s_ierrs);
                if (i > max_ierrs)
                    max_ierrs = i;
                break;
            case 16:           /* ifOutOctets      */
                snprintf(cur_if->s_obytes, sizeof cur_if->s_obytes, "%lu", *vp->val.integer);
                i = strlen(cur_if->s_obytes);
                if (i > max_obytes)
                    max_obytes = i;
                break;
            case 17:           /* ifOutUcastPkts */
                cur_if->opkts += *vp->val.integer;
                snprintf(cur_if->s_opkts, sizeof cur_if->s_opkts, "%lu", cur_if->opkts);
                i = strlen(cur_if->s_opkts);
                if (i > max_opkts)
                    max_opkts = i;
                break;
            case 18:           /* ifOutNUcastPkts */
                cur_if->opkts += *vp->val.integer;
                snprintf(cur_if->s_opkts, sizeof cur_if->s_opkts, "%lu", cur_if->opkts);
                i = strlen(cur_if->s_opkts);
                if (i > max_opkts)
                    max_opkts = i;
                break;
            case 19:           /* ifOutDiscards   */
                cur_if->drops = *vp->val.integer;
                break;
            case 20:           /* ifOutErrors     */
                snprintf(cur_if->s_oerrs, sizeof cur_if->s_oerrs, "%lu", *vp->val.integer);
                i = strlen(cur_if->s_oerrs);
                if (i > max_oerrs)
                    max_oerrs = i;
                break;
            case 21:           /* ifOutQLen       */
                snprintf(cur_if->s_outq, sizeof cur_if->s_outq, "%lu", *vp->val.integer);
                i = strlen(cur_if->s_outq);
                if (i > max_outq)
                    max_outq = i;
                break;
            case 1:            /* ifName */
                if (vp->val_len >= sizeof(cur_if->name))
                    vp->val_len = sizeof(cur_if->name) - 1;
                memmove(cur_if->name, vp->val.string, vp->val_len);
                cur_if->name[vp->val_len] = 0;
                if ((i = strlen(cur_if->name) + 1) > max_name)
                    max_name = i;
                break;
            }
        }

        /*
         *  XXX - Perhaps query ifXTable for additional info ??
         *    (ifName/ifAlias, or HC counters)
         */

        /*
         * If we're to monitor a particular interface, then
         *   ignore all others.  It would be more efficient
         *   to check this earlier (as part of processing
         *   the varbind list).  But performing this test here
         *   means we can recognise ifXTable names as well)
         */
        if (intrface && strcmp(cur_if->name, intrface) != 0
            && strcmp(cur_if->descr, intrface) != 0) {
            SNMP_FREE(cur_if);
        }

        /*
         * Insert the IP address and network settings, and
         *   add the new _if_stat structure to the list.
         */
        if (cur_if) {
            if ((i = strlen(cur_if->name) + 1) > max_name)
                max_name = i;
            _set_address(cur_if);
            i = strlen(cur_if->ip);
            if (i > max_ip)
                max_ip = i;
            i = strlen(cur_if->route);
            if (i > max_route)
                max_route = i;

            if (if_tail) {
                if_tail->next = cur_if;
                if_tail = cur_if;
            } else {
                if_head = cur_if;
                if_tail = cur_if;
            }
        }
    }                           /* while (1) */
    snmp_free_varbind(var);

    /*
     * Now display the specified results (in Free-BSD format)
     *   setting the field widths appropriately....
     */
    printf("%*.*s %5.5s %*.*s %*.*s",
           -max_name, max_name, "Name", "Mtu",
           -max_route, max_route, "Network", -max_ip, max_ip, "Address");
    if (oflag) {
        printf(" %*s %*s", max_ibytes, "Ibytes", max_obytes, "Obytes");
    } else {
        printf(" %*s %*s", max_ipkts, "Ipkts", max_ierrs, "Ierrs");
        if (bflag)
            printf(" %*s", max_ibytes, "Ibytes");

        printf(" %*s %*s", max_opkts, "Opkts", max_oerrs, "Oerrs");
        if (bflag)
            printf(" %*s", max_obytes, "Obytes");

        printf(" %*s", max_outq, "Queue");
    }
    /*
     * if (tflag)
     * printf(" %s", "Time");
     */
    if (dflag)
        printf(" %s", "Drop");
    putchar('\n');

    for (cur_if = if_head; cur_if; cur_if = cur_if->next) {
        if (cur_if->name[0] == 0)
            continue;
        printf("%*.*s %5d", -max_name, max_name, cur_if->name,
               cur_if->mtu);
        printf(" %*.*s", -max_route, max_route, cur_if->route);
        printf(" %*.*s", -max_ip, max_ip, cur_if->ip);

        if (oflag) {
            printf(" %*s %*s", max_ibytes, cur_if->s_ibytes,
                   max_obytes, cur_if->s_obytes);
        } else {
            printf(" %*s %*s", max_ipkts, cur_if->s_ipkts,
                   max_ierrs, cur_if->s_ierrs);
            if (bflag)
                printf(" %*s", max_ibytes, cur_if->s_ibytes);

            printf(" %*s %*s", max_opkts, cur_if->s_opkts,
                   max_oerrs, cur_if->s_oerrs);
            if (bflag)
                printf(" %*s", max_obytes, cur_if->s_obytes);
            printf(" %*s", max_outq, cur_if->s_outq);
        }
        /*
         * if (tflag)
         * printf(" %4d", cur_if->???);
         */
        if (dflag)
            printf(" %4d", cur_if->drops);
        putchar('\n');
    }

    /*
     * ... and tidy up.
     */
    for (cur_if = if_head; cur_if; cur_if = if_head) {
        if_head = cur_if->next;
        cur_if->next = NULL;
        SNMP_FREE(cur_if);
    }
}


#define	MAXIF	100
struct iftot {
    char            ift_name[128];      /* interface name */
    int             ifIndex;
    u_long          ift_ip;     /* input packets */
    u_long          ift_ib;     /* input bytes */
    u_long          ift_ie;     /* input errors */
    u_long          ift_op;     /* output packets */
    u_long          ift_ob;     /* output bytes */
    u_long          ift_oe;     /* output errors */
    u_long          ift_co;     /* collisions */
    u_long          ift_dr;     /* drops */
};

int             signalled;      /* set if alarm goes off "early" */

/*
 * Print a running summary of interface statistics.
 * Repeat display every interval seconds, showing statistics
 * collected over that interval.  Assumes that interval is non-zero.
 * First line printed at top of screen is always cumulative.
 */
static void
sidewaysintpr(unsigned int interval)
{
    /*
     * As with the "one-shot" interface display, there are
     *   two different possible output formats.  The Net/
     *   Open-BSD style displays both information about a
     *   single interface *and* the overall totals.
     * The equivalent Free-BSD approach is to report on one
     *   or the other (rather than both).  This is probably
     *   more useful (IMO), and significantly more efficient.
     *   So that's the style implemented here.
     *
     * Note that the 'ifcol' OID buffer can represent a full
     *   instance (including ifIndex), rather than just a
     *   column object OID, as with the one-shot code.
     */
    oid             ifcol_oid[] = { 1, 3, 6, 1, 2, 1, 2, 2, 1, 0, 0 };
    size_t          ifcol_len = OID_LENGTH(ifcol_oid);
    netsnmp_variable_list *var, *vp;
    struct iftot   *ip = NULL, *cur_if = NULL;  /* single I/F display */
    struct iftot   *sum = NULL, *total = NULL;  /* overall summary    */
    int             line;
    int             first;
    size_t          i;

    var = NULL;
    if (intrface) {
        /*
         * Locate the ifIndex of the interface to monitor,
         *   by walking the ifDescr column of the ifTable
         */
        ifcol_oid[ifcol_len - 2] = 2;   /* ifDescr  */
        snmp_varlist_add_variable(&var, ifcol_oid, ifcol_len - 1,
                                  ASN_NULL, NULL, 0);
        i = strlen(intrface);
        netsnmp_query_walk(var, ss);
        for (vp = var; vp; vp = vp->next_variable) {
            if (strncmp(intrface, (char *) vp->val.string, i) == 0 &&
                i == vp->val_len)
                break;          /* found requested interface */
        }
        /*
         * XXX - Might be worth searching ifName/ifAlias as well
         */
        if (!vp) {
            oid             ifname_oid[] =
                { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 1, 0 };
            size_t          ifname_len = OID_LENGTH(ifname_oid);
            snmp_free_var(var);
            var = NULL;
            snmp_varlist_add_variable(&var, ifname_oid, ifname_len - 1,
                                      ASN_NULL, NULL, 0);
            i = strlen(intrface);
            netsnmp_query_walk(var, ss);
            for (vp = var; vp; vp = vp->next_variable) {
                if (strncmp(intrface, (char *) vp->val.string, i) == 0 &&
                    i == vp->val_len)
                    break;      /* found requested interface */
            }
        }
        if (!vp) {
            oid             ifalias_oid[] =
                { 1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 18, 0 };
            size_t          ifalias_len = OID_LENGTH(ifalias_oid);
            snmp_free_var(var);
            var = NULL;
            snmp_varlist_add_variable(&var, ifalias_oid, ifalias_len - 1,
                                      ASN_NULL, NULL, 0);
            i = strlen(intrface);
            netsnmp_query_walk(var, ss);
            for (vp = var; vp; vp = vp->next_variable) {
                if (strncmp(intrface, (char *) vp->val.string, i) == 0 &&
                    i == vp->val_len)
                    break;      /* found requested interface */
            }
        }
        if (!vp) {
            fprintf(stderr, "%s: unknown interface\n", intrface);
            exit(1);
        }

        /*
         *  Prepare the current and previous 'iftot' structures,
         *    and set the ifIndex value in the OID buffer.
         */
        ip = SNMP_MALLOC_TYPEDEF(struct iftot);
        cur_if = SNMP_MALLOC_TYPEDEF(struct iftot);
        if (!ip || !cur_if) {
            fprintf(stderr, "internal error\n");
            exit(1);
        }
        ifcol_oid[ifcol_len - 1] = vp->name[ifcol_len - 1];
        snmp_free_var(var);
        var = NULL;
    } else {
        /*
         *  Prepare the current and previous 'iftot' structures.
         *    (using different pointers, for consistency with *BSD code)
         */
        sum = SNMP_MALLOC_TYPEDEF(struct iftot);
        total = SNMP_MALLOC_TYPEDEF(struct iftot);
        if (!sum || !total) {
            fprintf(stderr, "internal error\n");
            exit(1);
        }
    }

    timerSet(interval);
    first = 1;
  banner:
    printf("%17s %14s %16s", "input",
           intrface ? intrface : "(Total)", "output");
    putchar('\n');
    printf("%10s %5s %10s %10s %5s %10s %5s",
           "packets", "errs", "bytes", "packets", "errs", "bytes",
           "colls");
    if (dflag)
        printf(" %5.5s", "drops");
    putchar('\n');
    fflush(stdout);
    line = 0;
  loop:
    if (intrface) {
#define ADD_IFVAR( x ) ifcol_oid[ ifcol_len-2 ] = x; \
    snmp_varlist_add_variable( &var, ifcol_oid, ifcol_len, ASN_NULL, NULL,  0)
        /*
         * if (bflag) {
         */
        ADD_IFVAR(10);          /* ifInOctets     */
        ADD_IFVAR(16);          /* ifOutOctets    */
        /*
         * }
         */
        ADD_IFVAR(11);          /* ifInUcastPkts  */
        ADD_IFVAR(12);          /* ifInNUcastPkts */
        ADD_IFVAR(14);          /* ifInErrors     */
        ADD_IFVAR(17);          /* ifOutUcastPkts */
        ADD_IFVAR(18);          /* ifOutNUcastPkts */
        ADD_IFVAR(20);          /* ifOutErrors    */
        ADD_IFVAR(21);          /* ifOutQLen      */
        if (dflag) {
            ADD_IFVAR(19);      /* ifOutDiscards  */
        }
#undef ADD_IFVAR

        netsnmp_query_get(var, ss);     /* Or parallel walk ?? */
        cur_if->ift_ip = 0;
        cur_if->ift_ib = 0;
        cur_if->ift_ie = 0;
        cur_if->ift_op = 0;
        cur_if->ift_ob = 0;
        cur_if->ift_oe = 0;
        cur_if->ift_co = 0;
        cur_if->ift_dr = 0;
        cur_if->ifIndex = var->name[ifcol_len - 1];
        for (vp = var; vp; vp = vp->next_variable) {
            if ((var->type & 0xF0) == 0x80)     /* Exception */
                continue;
            if (!vp->val.integer)
                continue;
            switch (vp->name[ifcol_len - 2]) {
            case 10:           /* ifInOctets */
                cur_if->ift_ib = *vp->val.integer;
                break;
            case 11:           /* ifInUcastPkts */
                cur_if->ift_ip += *vp->val.integer;
                break;
            case 12:           /* ifInNUcastPkts */
                cur_if->ift_ip += *vp->val.integer;
                break;
            case 14:           /* ifInErrors */
                cur_if->ift_ie = *vp->val.integer;
                break;
            case 16:           /* ifOutOctets */
                cur_if->ift_ob = *vp->val.integer;
                break;
            case 17:           /* ifOutUcastPkts */
                cur_if->ift_op += *vp->val.integer;
                break;
            case 18:           /* ifOutNUcastPkts */
                cur_if->ift_op += *vp->val.integer;
                break;
            case 19:           /* ifOutDiscards */
                cur_if->ift_dr = *vp->val.integer;
                break;
            case 20:           /* ifOutErrors */
                cur_if->ift_oe = *vp->val.integer;
                break;
            case 21:           /* ifOutQLen */
                cur_if->ift_co = *vp->val.integer;
                break;
            }
        }
        snmp_free_varbind(var);
        var = NULL;

        if (!first) {
            printf("%10lu %5lu %10lu %10lu %5lu %10lu %5lu",
                   cur_if->ift_ip - ip->ift_ip,
                   cur_if->ift_ie - ip->ift_ie,
                   cur_if->ift_ib - ip->ift_ib,
                   cur_if->ift_op - ip->ift_op,
                   cur_if->ift_oe - ip->ift_oe,
                   cur_if->ift_ob - ip->ift_ob,
                   cur_if->ift_co - ip->ift_co);
            if (dflag)
                printf(" %5lu", cur_if->ift_dr - ip->ift_dr);
            putchar('\n');
            fflush(stdout);
        }
        ip->ift_ip = cur_if->ift_ip;
        ip->ift_ie = cur_if->ift_ie;
        ip->ift_ib = cur_if->ift_ib;
        ip->ift_op = cur_if->ift_op;
        ip->ift_oe = cur_if->ift_oe;
        ip->ift_ob = cur_if->ift_ob;
        ip->ift_co = cur_if->ift_co;
        ip->ift_dr = cur_if->ift_dr;
    }                           /* (single) interface */
    else {
        sum->ift_ip = 0;
        sum->ift_ib = 0;
        sum->ift_ie = 0;
        sum->ift_op = 0;
        sum->ift_ob = 0;
        sum->ift_oe = 0;
        sum->ift_co = 0;
        sum->ift_dr = 0;
#define ADD_IFVAR( x ) ifcol_oid[ ifcol_len-2 ] = x; \
    snmp_varlist_add_variable( &var, ifcol_oid, ifcol_len-1, ASN_NULL, NULL,  0)
        ADD_IFVAR(11);          /* ifInUcastPkts  */
        ADD_IFVAR(12);          /* ifInNUcastPkts */
        ADD_IFVAR(14);          /* ifInErrors     */
        ADD_IFVAR(17);          /* ifOutUcastPkts */
        ADD_IFVAR(18);          /* ifOutNUcastPkts */
        ADD_IFVAR(20);          /* ifOutErrors    */
        ADD_IFVAR(21);          /* ifOutQLen      */
        /*
         * if (bflag) {
         */
        ADD_IFVAR(10);          /* ifInOctets     */
        ADD_IFVAR(16);          /* ifOutOctets    */
        /*
         * }
         */
        if (dflag) {
            ADD_IFVAR(19);      /* ifOutDiscards  */
        }
#undef ADD_IFVAR

        ifcol_oid[ifcol_len - 2] = 11;  /* ifInUcastPkts */
        while (1) {
            if (netsnmp_query_getnext(var, ss) != SNMP_ERR_NOERROR)
                break;
            if ((var->type & 0xF0) == 0x80)     /* Exception */
                break;
            if (snmp_oid_compare(ifcol_oid, ifcol_len - 2,
                                 var->name, ifcol_len - 2) != 0)
                break;          /* End of Table */

            for (vp = var; vp; vp = vp->next_variable) {
                if ((vp->type & 0xF0) == 0x80)  /* Exception */
                    continue;
                if (!vp->val.integer)
                    continue;
                switch (vp->name[ifcol_len - 2]) {
                case 10:       /* ifInOctets */
                    sum->ift_ib += *vp->val.integer;
                    break;
                case 11:       /* ifInUcastPkts */
                    sum->ift_ip += *vp->val.integer;
                    break;
                case 12:       /* ifInNUcastPkts */
                    sum->ift_ip += *vp->val.integer;
                    break;
                case 14:       /* ifInErrors */
                    sum->ift_ie += *vp->val.integer;
                    break;
                case 16:       /* ifOutOctets */
                    sum->ift_ob += *vp->val.integer;
                    break;
                case 17:       /* ifOutUcastPkts */
                    sum->ift_op += *vp->val.integer;
                    break;
                case 18:       /* ifOutNUcastPkts */
                    sum->ift_op += *vp->val.integer;
                    break;
                case 19:       /* ifOutDiscards */
                    sum->ift_dr += *vp->val.integer;
                    break;
                case 20:       /* ifOutErrors */
                    sum->ift_oe += *vp->val.integer;
                    break;
                case 21:       /* ifOutQLen */
                    sum->ift_co += *vp->val.integer;
                    break;
                }
            }
            /*
             * Now loop to retrieve the next entry from the table.
             */
        }                       /* while (1) */

        snmp_free_varbind(var);
        var = NULL;

        if (!first) {
            printf("%10lu %5lu %10lu %10lu %5lu %10lu %5lu",
                   sum->ift_ip - total->ift_ip,
                   sum->ift_ie - total->ift_ie,
                   sum->ift_ib - total->ift_ib,
                   sum->ift_op - total->ift_op,
                   sum->ift_oe - total->ift_oe,
                   sum->ift_ob - total->ift_ob,
                   sum->ift_co - total->ift_co);
            if (dflag)
                printf(" %5lu", sum->ift_dr - total->ift_dr);
            putchar('\n');
            fflush(stdout);
        }
        total->ift_ip = sum->ift_ip;
        total->ift_ie = sum->ift_ie;
        total->ift_ib = sum->ift_ib;
        total->ift_op = sum->ift_op;
        total->ift_oe = sum->ift_oe;
        total->ift_ob = sum->ift_ob;
        total->ift_co = sum->ift_co;
        total->ift_dr = sum->ift_dr;
    }                           /* overall summary */

    timerPause();
    timerSet(interval);
    line++;
    first = 0;
    if (line == 21)
        goto banner;
    else
        goto loop;
    /*NOTREACHED*/
}
