/*
 *  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 "kernel_sunos5.h"
#include "../at.h"

typedef struct if_ip {
    int             ifIdx;
    IpAddress       ipAddr;
} if_ip_t;

static int
AT_Cmp(void *addr, void *ep)
{
    mib2_ipNetToMediaEntry_t *mp = (mib2_ipNetToMediaEntry_t *) ep;
    int             ret = -1;
    oid             index;

    mp->ipNetToMediaIfIndex.o_bytes[mp->ipNetToMediaIfIndex.o_length] = '\0';
    index = netsnmp_access_interface_index_find(
                    mp->ipNetToMediaIfIndex.o_bytes);
    DEBUGMSGTL(("mibII/at", "......... AT_Cmp %lx<>%lx %d<>%" NETSNMP_PRIo "d (%.5s)\n",
                (unsigned long)mp->ipNetToMediaNetAddress,
                (unsigned long)((if_ip_t *) addr)->ipAddr,
                ((if_ip_t *) addr)->ifIdx, index,
                mp->ipNetToMediaIfIndex.o_bytes));
    if (mp->ipNetToMediaNetAddress != ((if_ip_t *) addr)->ipAddr)
        ret = 1;
    else if (((if_ip_t *) addr)->ifIdx != index)
        ret = 1;
    else
        ret = 0;
    DEBUGMSGTL(("mibII/at", "......... AT_Cmp returns %d\n", ret));
    return ret;
}

u_char         *
var_atEntry(struct variable * vp,
            oid * name,
            size_t * length,
            int exact, size_t * var_len, WriteMethod ** write_method)
{
    /*
     * 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.
     */
#define AT_MAX_NAME_LENGTH	16
#define AT_IFINDEX_OFF	10
    u_char         *cp;
    oid            *op;
    oid             lowest[AT_MAX_NAME_LENGTH];
    oid             current[AT_MAX_NAME_LENGTH];
    if_ip_t         NextAddr;
    mib2_ipNetToMediaEntry_t entry;
    static mib2_ipNetToMediaEntry_t Lowentry;
    int             Found = 0;
    req_e           req_type;
    int             offset, olength = 0;
    static in_addr_t      addr_ret;

    /*
     * fill in object part of name for current (less sizeof instance part)
     */

    DEBUGMSGTL(("mibII/at", "var_atEntry: "));
    DEBUGMSGOID(("mibII/at", vp->name, vp->namelen));
    DEBUGMSG(("mibII/at", " %d\n", exact));

    memset(&Lowentry, 0, sizeof(Lowentry));
    memcpy((char *) current, (char *) vp->name, vp->namelen * sizeof(oid));
    lowest[0] = 1024;
    for (NextAddr.ipAddr = (u_long) - 1, NextAddr.ifIdx = 255, req_type =
         GET_FIRST;;
         NextAddr.ipAddr = entry.ipNetToMediaNetAddress, NextAddr.ifIdx =
         current[AT_IFINDEX_OFF], req_type = GET_NEXT) {
        if (getMibstat
            (MIB_IP_NET, &entry, sizeof(mib2_ipNetToMediaEntry_t),
             req_type, &AT_Cmp, &NextAddr) != 0)
            break;
        entry.ipNetToMediaIfIndex.o_bytes[entry.ipNetToMediaIfIndex.o_length] = '\0';
        current[AT_IFINDEX_OFF] = netsnmp_access_interface_index_find(
           entry.ipNetToMediaIfIndex.o_bytes);
        if (current[6] == 3) {  /* AT group oid */
            current[AT_IFINDEX_OFF + 1] = 1;
            offset = AT_IFINDEX_OFF + 2;
            olength = AT_IFINDEX_OFF + 6;
        } else {
            offset = AT_IFINDEX_OFF + 1;
            olength = AT_IFINDEX_OFF + 5;
        }
        COPY_IPADDR(cp, (u_char *) & entry.ipNetToMediaNetAddress, op,
                    current + offset);
        if (exact) {
            if (snmp_oid_compare(current, olength, name, *length) == 0) {
                memcpy((char *) lowest, (char *) current,
                       olength * sizeof(oid));
                Lowentry = entry;
                Found++;
                break;          /* no need to search further */
            }
        } else {
            if (snmp_oid_compare(current, olength, name, *length) > 0 &&
                (Found == 0 ||
                 snmp_oid_compare(current, olength, lowest, olength) < 0)) {
                /*
                 * if new one is greater than input and closer to input than
                 * previous lowest, and is not equal to it, save this one as the "next" one.
                 */
                memcpy((char *) lowest, (char *) current,
                       olength * sizeof(oid));
                Lowentry = entry;
                Found++;
            }
        }
    }
    DEBUGMSGTL(("mibII/at", "... Found = %d\n", Found));
    if (Found == 0)
        return (NULL);
    memcpy((char *) name, (char *) lowest, olength * sizeof(oid));
    *length = olength;
    *write_method = 0;
    switch (vp->magic) {
    case IPMEDIAIFINDEX:
        *var_len = sizeof long_return;
        Lowentry.ipNetToMediaIfIndex.o_bytes[
            Lowentry.ipNetToMediaIfIndex.o_length] = '\0';
        long_return = netsnmp_access_interface_index_find(
            Lowentry.ipNetToMediaIfIndex.o_bytes);
        return (u_char *) & long_return;
    case IPMEDIAPHYSADDRESS:
        *var_len = Lowentry.ipNetToMediaPhysAddress.o_length;
        return (u_char *) Lowentry.ipNetToMediaPhysAddress.o_bytes;
    case IPMEDIANETADDRESS:
        *var_len = sizeof(addr_ret);
        addr_ret = Lowentry.ipNetToMediaNetAddress;
        return (u_char *) &addr_ret;
    case IPMEDIATYPE:
        *var_len = sizeof long_return;
        long_return = Lowentry.ipNetToMediaType;
        return (u_char *) & long_return;
    default:
        DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_atEntry\n",
                    vp->magic));
    }
    return NULL;
}
