/*
 *  Template MIB group implementation - at.c
 *
 */

/* Portions of this file are subject to the following copyright(s).  See
 * the Net-SNMP's COPYING file for more details and other copyrights
 * that may apply:
 */
/*
 * Portions of this file are copyrighted by:
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms specified in the COPYING file
 * distributed with the Net-SNMP package.
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/data_access/interface.h>
#include "../at.h"

/*
 * var_atEntry(...
 * Arguments:
 * vp     IN      - pointer to variable entry that points here
 * name    IN/OUT  - IN/name requested, OUT/name found
 * length  IN/OUT  - length of IN/OUT oid's
 * exact   IN      - TRUE if an exact match was requested
 * var_len OUT     - length of variable or 0 if function returned
 * write_method
 *
 */

u_char         *
var_atEntry(struct variable *vp,
            oid * name,
            size_t * length,
            int exact, size_t * var_len, WriteMethod ** write_method)
{
    /*
     * Address Translation table object identifier is of form:
     * 1.3.6.1.2.1.3.1.1.1.interface.1.A.B.C.D,  where A.B.C.D is IP address.
     * Interface is at offset 10,
     * IPADDR starts at offset 12.
     *
     * IP Net to Media table object identifier is of form:
     * 1.3.6.1.2.1.4.22.1.1.1.interface.A.B.C.D,  where A.B.C.D is IP address.
     * Interface is at offset 10,
     * IPADDR starts at offset 11.
     */
    u_char         *cp;
    oid            *op;
    oid             lowest[16];
    oid             current[16];
    static char     PhysAddr[MAX_MAC_ADDR_LEN], LowPhysAddr[MAX_MAC_ADDR_LEN];
    static int      PhysAddrLen, LowPhysAddrLen;
    in_addr_t       Addr, LowAddr;
    int             foundone;
    static in_addr_t      addr_ret;
    u_short         ifIndex, lowIfIndex = 0;
    u_long          ifType, lowIfType = 0;

    int             oid_length;

    /*
     * fill in object part of name for current (less sizeof instance part)
     */
    memcpy(current, vp->name, vp->namelen * sizeof(oid));

    if (current[6] == 3) {      /* AT group oid */
        oid_length = 16;
    } else {                    /* IP NetToMedia group oid */
        oid_length = 15;
    }

    LowAddr = 0;                /* Don't have one yet */
    foundone = 0;
    ARP_Scan_Init();
    for (;;) {
        if (ARP_Scan_Next(&Addr, PhysAddr, &PhysAddrLen, &ifType, &ifIndex) == 0)
            break;
        current[10] = ifIndex;

        if (current[6] == 3) {  /* AT group oid */
            current[11] = 1;
            op = current + 12;
        } else {                /* IP NetToMedia group oid */
            op = current + 11;
        }
        cp = (u_char *) & Addr;
        *op++ = *cp++;
        *op++ = *cp++;
        *op++ = *cp++;
        *op++ = *cp++;

        if (exact) {
            if (snmp_oid_compare(current, oid_length, name, *length) == 0) {
                memcpy((char *) lowest, (char *) current,
                       oid_length * sizeof(oid));
                LowAddr = Addr;
                foundone = 1;
                lowIfIndex = ifIndex;
                memcpy(LowPhysAddr, PhysAddr, sizeof(PhysAddr));
                LowPhysAddrLen = PhysAddrLen;
                lowIfType = ifType;
                break;          /* no need to search further */
            }
        } else {
            if ((snmp_oid_compare(current, oid_length, name, *length) > 0)
                && ((foundone == 0)
                    ||
                    (snmp_oid_compare
                     (current, oid_length, lowest, oid_length) < 0))) {
                /*
                 * if new one is greater than input and closer to input than
                 * previous lowest, save this one as the "next" one.
                 */
                memcpy(lowest, current, oid_length * sizeof(oid));
                LowAddr = Addr;
                foundone = 1;
                lowIfIndex = ifIndex;
                memcpy(LowPhysAddr, PhysAddr, sizeof(PhysAddr));
                LowPhysAddrLen = PhysAddrLen;
                lowIfType = ifType;
            }
        }
    }
    if (foundone == 0)
        return NULL;

    memcpy(name, lowest, oid_length * sizeof(oid));
    *length = oid_length;
    *write_method = NULL;
    switch (vp->magic) {
    case IPMEDIAIFINDEX:       /* also ATIFINDEX */
        *var_len = sizeof long_return;
        long_return = lowIfIndex ? lowIfIndex : 1;
#ifdef NETSNMP_NO_DUMMY_VALUES
        if (lowIfIndex == 0)
            return NULL;
#endif
        return (u_char *) & long_return;
    case IPMEDIAPHYSADDRESS:   /* also ATPHYSADDRESS */
        *var_len = LowPhysAddrLen;
        return (u_char *) LowPhysAddr;
    case IPMEDIANETADDRESS:    /* also ATNETADDRESS */
        *var_len = sizeof(addr_ret);
        addr_ret = LowAddr;
        return (u_char *) & addr_ret;
    case IPMEDIATYPE:
        *var_len = sizeof long_return;
        long_return = lowIfType;
        return (u_char *) & long_return;
    default:
        DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_atEntry\n",
                    vp->magic));
    }
    return NULL;
}
