/*
 *  IP MIB group implementation - ip.c
 *
 */

#include <net-snmp/net-snmp-config.h>
#include "mibII_common.h"

#if HAVE_SYS_HASHING_H
#include <sys/hashing.h>
#endif
#if HAVE_NETINET_IN_VAR_H
#include <netinet/in_var.h>
#endif
#if HAVE_SYSLOG_H
#include <syslog.h>
#endif

#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/agent/auto_nlist.h>
#include <net-snmp/agent/sysORTable.h>

#include "util_funcs/MIB_STATS_CACHE_TIMEOUT.h"
#include "ip.h"
#include "ipAddr.h"
#include "interfaces.h"

#ifndef MIB_STATS_CACHE_TIMEOUT
#define MIB_STATS_CACHE_TIMEOUT	5
#endif
#ifndef IP_STATS_CACHE_TIMEOUT
#define IP_STATS_CACHE_TIMEOUT	MIB_STATS_CACHE_TIMEOUT
#endif

#if defined(HAVE_LIBPERFSTAT_H) && (defined(aix4) || defined(aix5) || defined(aix6) || defined(aix7)) && !defined(FIRST_PROTOCOL)
#ifdef HAVE_SYS_PROTOSW_H
#include <sys/protosw.h>
#endif
#include <libperfstat.h>
#ifdef FIRST_PROTOCOL
perfstat_protocol_t ps_proto;
perfstat_id_t ps_name;
#define _USE_PERFSTAT_PROTOCOL 1
#endif
#endif

        /*********************
	 *
	 *  Kernel & interface information,
	 *   and internal forward declarations
	 *
	 *********************/


        /*********************
	 *
	 *  Initialisation & common implementation functions
	 *
	 *********************/

/*
 * define the structure we're going to ask the agent to register our
 * information at 
 */
struct variable1 ipaddr_variables[] = {
    {IPADADDR,      ASN_IPADDRESS, NETSNMP_OLDAPI_RONLY,
     var_ipAddrEntry, 1, {1}},
    {IPADIFINDEX,   ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
     var_ipAddrEntry, 1, {2}},
#ifndef sunV3
    {IPADNETMASK,   ASN_IPADDRESS, NETSNMP_OLDAPI_RONLY,
     var_ipAddrEntry, 1, {3}},
#endif
    {IPADBCASTADDR, ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
     var_ipAddrEntry, 1, {4}},
    {IPADREASMMAX,  ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
     var_ipAddrEntry, 1, {5}}
};

struct variable1 iproute_variables[] = {
    {IPROUTEDEST,    ASN_IPADDRESS, NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {1}},
    {IPROUTEIFINDEX, ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {2}},
    {IPROUTEMETRIC1, ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {3}},
    {IPROUTEMETRIC2, ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {4}},
    {IPROUTEMETRIC3, ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {5}},
    {IPROUTEMETRIC4, ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {6}},
    {IPROUTENEXTHOP, ASN_IPADDRESS, NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {7}},
    {IPROUTETYPE,    ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {8}},
    {IPROUTEPROTO,   ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
     var_ipRouteEntry, 1, {9}},
    {IPROUTEAGE,     ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {10}},
    {IPROUTEMASK,    ASN_IPADDRESS, NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {11}},
    {IPROUTEMETRIC5, ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_ipRouteEntry, 1, {12}},
    {IPROUTEINFO,    ASN_OBJECT_ID, NETSNMP_OLDAPI_RONLY,
     var_ipRouteEntry, 1, {13}}
};

struct variable1 ipmedia_variables[] = {
#ifdef USING_MIBII_AT_MODULE
#if defined (WIN32) || defined (cygwin)
    {IPMEDIAIFINDEX,     ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_atEntry, 1, {1}},
    {IPMEDIAPHYSADDRESS, ASN_OCTET_STR, NETSNMP_OLDAPI_RWRITE,
     var_atEntry, 1, {2}},
    {IPMEDIANETADDRESS,  ASN_IPADDRESS, NETSNMP_OLDAPI_RWRITE,
     var_atEntry, 1, {3}},
    {IPMEDIATYPE,        ASN_INTEGER,   NETSNMP_OLDAPI_RWRITE,
     var_atEntry, 1, {4}}
#else
    {IPMEDIAIFINDEX,     ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
     var_atEntry, 1, {1}},
    {IPMEDIAPHYSADDRESS, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
     var_atEntry, 1, {2}},
    {IPMEDIANETADDRESS,  ASN_IPADDRESS, NETSNMP_OLDAPI_RONLY,
     var_atEntry, 1, {3}},
    {IPMEDIATYPE,        ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
     var_atEntry, 1, {4}}
#endif
#endif
};

/*
 * Define the OID pointer to the top of the mib tree that we're
 * registering underneath, and the OID of the MIB module 
 */
oid             ip_oid[]                = { SNMP_OID_MIB2, 4 };

oid             ipaddr_variables_oid[]  = { SNMP_OID_MIB2, 4, 20, 1 };
oid             iproute_variables_oid[] = { SNMP_OID_MIB2, 4, 21, 1 };
oid             ipmedia_variables_oid[] = { SNMP_OID_MIB2, 4, 22, 1 };
oid             ip_module_oid[] = { SNMP_OID_MIB2, 4 };
int             ip_module_oid_len = sizeof(ip_module_oid) / sizeof(oid);
int             ip_module_count = 0;    /* Need to liaise with icmp.c */

void
init_ip(void)
{
    netsnmp_handler_registration *reginfo;
    int rc;

    /*
     * register ourselves with the agent as a group of scalars...
     */
    DEBUGMSGTL(("mibII/ip", "Initialising IP group\n"));
    reginfo = netsnmp_create_handler_registration("ip", ip_handler,
                            ip_oid, OID_LENGTH(ip_oid), HANDLER_CAN_RONLY);
    rc = netsnmp_register_scalar_group(reginfo, IPFORWARDING, IPROUTEDISCARDS);
    if (rc != SNMPERR_SUCCESS)
        return;

    /*
     * .... with a local cache
     *    (except for HP-UX 11, which extracts objects individually)
     */
#ifndef hpux11
    netsnmp_inject_handler( reginfo,
		    netsnmp_get_cache_handler(IP_STATS_CACHE_TIMEOUT,
			   		ip_load, ip_free,
					ip_oid, OID_LENGTH(ip_oid)));
#endif

    /*
     * register (using the old-style API) to handle the IP tables
     */
    REGISTER_MIB("mibII/ipaddr",  ipaddr_variables,
                       variable1, ipaddr_variables_oid);
    REGISTER_MIB("mibII/iproute", iproute_variables,
                       variable1, iproute_variables_oid);
    REGISTER_MIB("mibII/ipmedia", ipmedia_variables,
                       variable1, ipmedia_variables_oid);
    if (++ip_module_count == 2)
        REGISTER_SYSOR_ENTRY(ip_module_oid,
                             "The MIB module for managing IP and ICMP implementations");


    /*
     * for speed optimization, we call this now to do the lookup 
     */
#ifndef _USE_PERFSTAT_PROTOCOL
#ifdef IPSTAT_SYMBOL
    auto_nlist(IPSTAT_SYMBOL, 0, 0);
#endif
#ifdef IP_FORWARDING_SYMBOL
    auto_nlist(IP_FORWARDING_SYMBOL, 0, 0);
#endif
#ifdef TCP_TTL_SYMBOL
    auto_nlist(TCP_TTL_SYMBOL, 0, 0);
#endif
#ifdef MIB_IPCOUNTER_SYMBOL
    auto_nlist(MIB_IPCOUNTER_SYMBOL, 0, 0);
#endif
#ifdef solaris2
    init_kernel_sunos5();
#endif
#endif
}


        /*********************
	 *
	 *  System specific data formats
	 *
	 *********************/

#ifdef hpux11
#define IP_STAT_STRUCTURE	int
#endif

#ifdef linux
#define IP_STAT_STRUCTURE	struct ip_mib
#define	USES_SNMP_DESIGNED_IPSTAT
#endif

#ifdef solaris2
#define IP_STAT_STRUCTURE	mib2_ip_t
#define	USES_SNMP_DESIGNED_IPSTAT
#endif

#ifdef NETBSD_STATS_VIA_SYSCTL
#define IP_STAT_STRUCTURE	struct ip_mib
#define USES_SNMP_DESIGNED_IPSTAT
#undef IP_NSTATS
#endif

#ifdef HAVE_IPHLPAPI_H
#include <iphlpapi.h>
#define IP_STAT_STRUCTURE MIB_IPSTATS
long            ipForwarding;
long            oldipForwarding;
long            ipTTL, oldipTTL;
#endif                          /* WIN32 cygwin */

#ifdef HAVE_SYS_TCPIPSTATS_H
#define IP_STAT_STRUCTURE	struct kna
#define	USES_TRADITIONAL_IPSTAT
#endif

#ifdef dragonfly
#define IP_STAT_STRUCTURE	struct ip_stats
#define	USES_TRADITIONAL_IPSTAT
#endif

#if !defined(IP_STAT_STRUCTURE)
#define IP_STAT_STRUCTURE	struct ipstat
#define	USES_TRADITIONAL_IPSTAT
#endif

IP_STAT_STRUCTURE ipstat;



        /*********************
	 *
	 *  System independent handler
	 *      (mostly)
	 *
	 *********************/


int
ip_handler(netsnmp_mib_handler          *handler,
           netsnmp_handler_registration *reginfo,
           netsnmp_agent_request_info   *reqinfo,
           netsnmp_request_info         *requests)
{
    netsnmp_request_info  *request;
    netsnmp_variable_list *requestvb;
    long     ret_value;
    oid      subid;
    int      type = ASN_COUNTER;

    /*
     * The cached data should already have been loaded by the
     *    cache handler, higher up the handler chain.
     */
#ifdef _USE_PERFSTAT_PROTOCOL
    ip_load(NULL, NULL);
#endif


    /*
     * 
     *
     */
    DEBUGMSGTL(("mibII/ip", "Handler - mode %s\n",
                    se_find_label_in_slist("agent_mode", reqinfo->mode)));
    switch (reqinfo->mode) {
    case MODE_GET:
        for (request=requests; request; request=request->next) {
            requestvb = request->requestvb;
            subid = requestvb->name[OID_LENGTH(ip_oid)];  /* XXX */
            DEBUGMSGTL(( "mibII/ip", "oid: "));
            DEBUGMSGOID(("mibII/ip", requestvb->name,
                                     requestvb->name_length));
            DEBUGMSG((   "mibII/ip", "\n"));

            switch (subid) {
#ifdef USES_SNMP_DESIGNED_IPSTAT
    case IPFORWARDING:
        ret_value = ipstat.ipForwarding;
        type = ASN_INTEGER;
        break;
    case IPDEFAULTTTL:
        ret_value = ipstat.ipDefaultTTL;
        type = ASN_INTEGER;
        break;
    case IPINRECEIVES:
        ret_value = ipstat.ipInReceives & 0xffffffff;
        break;
    case IPINHDRERRORS:
        ret_value = ipstat.ipInHdrErrors;
        break;
    case IPINADDRERRORS:
        ret_value = ipstat.ipInAddrErrors;
        break;
    case IPFORWDATAGRAMS:
        ret_value = ipstat.ipForwDatagrams;
        break;
    case IPINUNKNOWNPROTOS:
        ret_value = ipstat.ipInUnknownProtos;
        break;
    case IPINDISCARDS:
        ret_value = ipstat.ipInDiscards;
        break;
    case IPINDELIVERS:
        ret_value = ipstat.ipInDelivers & 0xffffffff;
        break;
    case IPOUTREQUESTS:
        ret_value = ipstat.ipOutRequests & 0xffffffff;
        break;
    case IPOUTDISCARDS:
        ret_value = ipstat.ipOutDiscards;
        break;
    case IPOUTNOROUTES:
        ret_value = ipstat.ipOutNoRoutes;
        break;
    case IPREASMTIMEOUT:
        ret_value = ipstat.ipReasmTimeout;
        type = ASN_INTEGER;
        break;
    case IPREASMREQDS:
        ret_value = ipstat.ipReasmReqds;
        break;
    case IPREASMOKS:
        ret_value = ipstat.ipReasmOKs;
        break;
    case IPREASMFAILS:
        ret_value = ipstat.ipReasmFails;
        break;
    case IPFRAGOKS:
        ret_value = ipstat.ipFragOKs;
        break;
    case IPFRAGFAILS:
        ret_value = ipstat.ipFragFails;
        break;
    case IPFRAGCREATES:
        ret_value = ipstat.ipFragCreates;
        break;
    case IPROUTEDISCARDS:
        ret_value = ipstat.ipRoutingDiscards;
        break;

#elif defined(USES_TRADITIONAL_IPSTAT) && !defined(_USE_PERFSTAT_PROTOCOL)
#ifdef HAVE_SYS_TCPIPSTATS_H
    /*
     * This actually reads statistics for *all* the groups together,
     * so we need to isolate the IP-specific bits.  
     */
#define	ipstat		ipstat.ipstat
#endif
    case IPFORWARDING:
    case IPDEFAULTTTL:
        /* 
         * Query these two individually
         */
        ret_value = ip_load(NULL, (void *)subid);
        if (ret_value == -1 ) {
            netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
            continue;
	}
        type = ASN_INTEGER;
        break;
    case IPINRECEIVES:
        ret_value = ipstat.ips_total & 0xffffffff;
        break;
    case IPINHDRERRORS:
        ret_value = ipstat.ips_badsum
            + ipstat.ips_tooshort
            + ipstat.ips_toosmall + ipstat.ips_badhlen + ipstat.ips_badlen;
        break;
    case IPINADDRERRORS:
        ret_value = ipstat.ips_cantforward;
        break;
    case IPFORWDATAGRAMS:
        ret_value = ipstat.ips_forward;
        break;
    case IPINUNKNOWNPROTOS:
#if HAVE_STRUCT_IPSTAT_IPS_NOPROTO
        ret_value = ipstat.ips_noproto;
        break;
#else
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
#endif
    case IPINDISCARDS:
#if HAVE_STRUCT_IPSTAT_IPS_FRAGDROPPED
        ret_value = ipstat.ips_fragdropped;   /* ?? */
        break;
#else
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
#endif
    case IPINDELIVERS:
#if HAVE_STRUCT_IPSTAT_IPS_DELIVERED
        ret_value = ipstat.ips_delivered & 0xffffffff;
        break;
#else
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
#endif
    case IPOUTREQUESTS:
#if HAVE_STRUCT_IPSTAT_IPS_LOCALOUT
        ret_value = ipstat.ips_localout & 0xffffffff;
        break;
#else
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
#endif
    case IPOUTDISCARDS:
#if HAVE_STRUCT_IPSTAT_IPS_ODROPPED
        ret_value = ipstat.ips_odropped;
        break;
#else
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
#endif
    case IPOUTNOROUTES:
        /*
         * XXX: how to calculate this (counts dropped routes, not packets)?
         * ipstat.ips_cantforward isn't right, as it counts packets.
         * ipstat.ips_noroute is also incorrect.
         */
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
    case IPREASMTIMEOUT:
        ret_value = IPFRAGTTL;
        type = ASN_INTEGER;
        break;
    case IPREASMREQDS:
        ret_value = ipstat.ips_fragments;
        break;
    case IPREASMOKS:
#if HAVE_STRUCT_IPSTAT_IPS_REASSEMBLED
        ret_value = ipstat.ips_reassembled;
        break;
#else
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
#endif
    case IPREASMFAILS:
        ret_value = ipstat.ips_fragdropped + ipstat.ips_fragtimeout;
        break;
    case IPFRAGOKS:
#if HAVE_STRUCT_IPSTAT_IPS_FRAGMENTED
        ret_value = ipstat.ips_fragments;
        break;
#else            /* XXX */
        ret_value = ipstat.ips_fragments
            - (ipstat.ips_fragdropped + ipstat.ips_fragtimeout);
        break;
#endif
    case IPFRAGFAILS:
#if HAVE_STRUCT_IPSTAT_IPS_CANTFRAG
        ret_value = ipstat.ips_cantfrag;
        break;
#else
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
#endif
    case IPFRAGCREATES:
#if HAVE_STRUCT_IPSTAT_IPS_OFRAGMENTS
        ret_value = ipstat.ips_ofragments;
        break;
#else
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
#endif
    case IPROUTEDISCARDS:
#if HAVE_STRUCT_IPSTAT_IPS_NOROUTE
        ret_value = ipstat.ips_noroute;
        break;
#else
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;
#endif
#ifdef HAVE_SYS_TCPIPSTATS_H
#undef ipstat
#endif
#elif defined(hpux11)
    case IPFORWARDING:
    case IPDEFAULTTTL:
    case IPREASMTIMEOUT:
        type = ASN_INTEGER;
    case IPINRECEIVES:
    case IPINHDRERRORS:
    case IPINADDRERRORS:
    case IPFORWDATAGRAMS:
    case IPINUNKNOWNPROTOS:
    case IPINDISCARDS:
    case IPINDELIVERS:
    case IPOUTREQUESTS:
    case IPOUTDISCARDS:
    case IPOUTNOROUTES:
    case IPREASMREQDS:
    case IPREASMOKS:
    case IPREASMFAILS:
    case IPFRAGOKS:
    case IPFRAGFAILS:
    case IPFRAGCREATES:
    case IPROUTEDISCARDS:
	/*
	 * This is a bit of a hack, to shoehorn the HP-UX 11
	 * single-object retrieval approach into the caching
	 * architecture.
	 */
	if (ip_load(NULL, (void*)subid) == -1 ) {
            netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
            continue;
	}
        ret_value = ipstat;
        break;
#elif defined (WIN32) || defined (cygwin)
    case IPFORWARDING:
        ipForwarding = ipstat.dwForwarding;
        ret_value    = ipstat.dwForwarding;
        type = ASN_INTEGER;
        break;
    case IPDEFAULTTTL:
        ipTTL     = ipstat.dwDefaultTTL;
        ret_value = ipstat.dwDefaultTTL;
        type = ASN_INTEGER;
        break;
    case IPINRECEIVES:
        ret_value = ipstat.dwInReceives;
        break;
    case IPINHDRERRORS:
        ret_value = ipstat.dwInHdrErrors;
        break;
    case IPINADDRERRORS:
        ret_value = ipstat.dwInAddrErrors;
        break;
    case IPFORWDATAGRAMS:
        ret_value = ipstat.dwForwDatagrams;
        break;
    case IPINUNKNOWNPROTOS:
        ret_value = ipstat.dwInUnknownProtos;
        break;
    case IPINDISCARDS:
        ret_value = ipstat.dwInDiscards;
        break;
    case IPINDELIVERS:
        ret_value = ipstat.dwInDelivers;
        break;
    case IPOUTREQUESTS:
        ret_value = ipstat.dwOutRequests;
        break;
    case IPOUTDISCARDS:
        ret_value = ipstat.dwOutDiscards;
        break;
    case IPOUTNOROUTES:
        ret_value = ipstat.dwOutNoRoutes;
        break;
    case IPREASMTIMEOUT:
        ret_value = ipstat.dwReasmTimeout;
        type = ASN_INTEGER;
        break;
    case IPREASMREQDS:
        ret_value = ipstat.dwReasmReqds;
        break;
    case IPREASMOKS:
        ret_value = ipstat.dwReasmOks;
        break;
    case IPREASMFAILS:
        ret_value = ipstat.dwReasmFails;
        break;
    case IPFRAGOKS:
        ret_value = ipstat.dwFragOks;
        break;
    case IPFRAGFAILS:
        ret_value = ipstat.dwFragFails;
        break;
    case IPFRAGCREATES:
        ret_value = ipstat.dwFragCreates;
        break;
    case IPROUTEDISCARDS:
        ret_value = ipstat.dwRoutingDiscards;
        break;
#elif defined(_USE_PERFSTAT_PROTOCOL)
    case IPFORWARDING:
        ret_value    = 0;
        type = ASN_INTEGER;
        break;
    case IPDEFAULTTTL:
        ret_value = 0;
        type = ASN_INTEGER;
        break;
    case IPINRECEIVES:
        ret_value = ps_proto.u.ip.ipackets;
        break;
    case IPINHDRERRORS:
    case IPINADDRERRORS:
    case IPFORWDATAGRAMS:
        ret_value = 0;
        break;
    case IPINUNKNOWNPROTOS:
        ret_value = ps_proto.u.ip.ierrors;
        break;
    case IPINDISCARDS:
        ret_value = 0;
        break;
    case IPINDELIVERS:
    case IPOUTREQUESTS:
        ret_value = ps_proto.u.ip.opackets;
        break;
    case IPOUTDISCARDS:
    case IPOUTNOROUTES:
        ret_value = 0;
        break;
    case IPREASMTIMEOUT:
        ret_value = 0;
        type = ASN_INTEGER;
        break;
    case IPREASMREQDS:
    case IPREASMOKS:
    case IPREASMFAILS:
    case IPFRAGOKS:
    case IPFRAGFAILS:
    case IPFRAGCREATES:
        ret_value = 0;
        break;
    case IPROUTEDISCARDS:
        ret_value = ps_proto.u.ip.oerrors;
        break;
#endif			/* USES_SNMP_DESIGNED_IPSTAT */

    case IPADDRTABLE:
    case IPROUTETABLE:
    case IPMEDIATABLE:
        /*
	 * These are not actually valid scalar objects.
	 * The relevant table registrations should take precedence,
	 *   so skip these three subtrees, regardless of architecture.
	 */
        netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
        continue;

	    }
	    snmp_set_var_typed_value(request->requestvb, (u_char)type,
			             (u_char *)&ret_value, sizeof(ret_value));
	}
        break;

    case MODE_GETNEXT:
    case MODE_GETBULK:
#ifndef NETSNMP_NO_WRITE_SUPPORT
    case MODE_SET_RESERVE1:
		/* XXX - Windows currently supports setting this */
    case MODE_SET_RESERVE2:
    case MODE_SET_ACTION:
    case MODE_SET_COMMIT:
    case MODE_SET_FREE:
    case MODE_SET_UNDO:
        snmp_log(LOG_WARNING, "mibII/ip: Unsupported mode (%d)\n",
                               reqinfo->mode);
        break;
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
    default:
        snmp_log(LOG_WARNING, "mibII/ip: Unrecognised mode (%d)\n",
                               reqinfo->mode);
        break;
    }

    return SNMP_ERR_NOERROR;
}



        /*********************
	 *
	 *  Internal implementation functions
	 *
	 *********************/


#ifdef hpux11
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    int             fd;
    struct nmparms  p;
    unsigned int    ulen;
    int             ret;
    int             magic = (int) vmagic;
    
    if ((fd = open_mib("/dev/ip", O_RDONLY, 0, NM_ASYNC_OFF)) < 0) {
        DEBUGMSGTL(("mibII/ip", "Failed to load IP object %d (hpux11)\n", magic));
        return (-1);            /* error */
    }

    switch (magic) {
    case IPFORWARDING:
        p.objid = ID_ipForwarding;
        break;
    case IPDEFAULTTTL:
        p.objid = ID_ipDefaultTTL;
        break;
    case IPINRECEIVES:
        p.objid = ID_ipInReceives;
        break;
    case IPINHDRERRORS:
        p.objid = ID_ipInHdrErrors;
        break;
    case IPINADDRERRORS:
        p.objid = ID_ipInAddrErrors;
        break;
    case IPFORWDATAGRAMS:
        p.objid = ID_ipForwDatagrams;
        break;
    case IPINUNKNOWNPROTOS:
        p.objid = ID_ipInUnknownProtos;
        break;
    case IPINDISCARDS:
        p.objid = ID_ipInDiscards;
        break;
    case IPINDELIVERS:
        p.objid = ID_ipInDelivers;
        break;
    case IPOUTREQUESTS:
        p.objid = ID_ipOutRequests;
        break;
    case IPOUTDISCARDS:
        p.objid = ID_ipOutDiscards;
        break;
    case IPOUTNOROUTES:
        p.objid = ID_ipOutNoRoutes;
        break;
    case IPREASMTIMEOUT:
        p.objid = ID_ipReasmTimeout;
        break;
    case IPREASMREQDS:
        p.objid = ID_ipReasmReqds;
        break;
    case IPREASMOKS:
        p.objid = ID_ipReasmOKs;
        break;
    case IPREASMFAILS:
        p.objid = ID_ipReasmFails;
        break;
    case IPFRAGOKS:
        p.objid = ID_ipFragOKs;
        break;
    case IPFRAGFAILS:
        p.objid = ID_ipFragFails;
        break;
    case IPFRAGCREATES:
        p.objid = ID_ipFragCreates;
        break;
    case IPROUTEDISCARDS:
        p.objid = ID_ipRoutingDiscards;
        break;
    default:
        ipstat = 0;
        close_mib(fd);
        return (0);
    }

    p.buffer = (void *)&ipstat;
    ulen = sizeof(IP_STAT_STRUCTURE);
    p.len = &ulen;
    ret = get_mib_info(fd, &p);
    close_mib(fd);

    DEBUGMSGTL(("mibII/ip", "%s IP object %d (hpux11)\n",
               (ret < 0 ? "Failed to load" : "Loaded"),  magic));
    return (ret);         /* 0: ok, < 0: error */
}
#elif defined(linux)
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    long ret_value = -1;

    ret_value = linux_read_ip_stat(&ipstat);

    if ( ret_value < 0 ) {
        DEBUGMSGTL(("mibII/ip", "Failed to load IP Group (linux)\n"));
    } else {
        DEBUGMSGTL(("mibII/ip", "Loaded IP Group (linux)\n"));
    }
    return ret_value;
}
#elif defined(solaris2)
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    long ret_value = -1;

    ret_value =
        getMibstat(MIB_IP, &ipstat, sizeof(mib2_ip_t), GET_FIRST,
                   &Get_everything, NULL);

    if ( ret_value < 0 ) {
        DEBUGMSGTL(("mibII/ip", "Failed to load IP Group (solaris)\n"));
    } else {
        DEBUGMSGTL(("mibII/ip", "Loaded IP Group (solaris)\n"));
    }
    return ret_value;
}
#elif defined (NETBSD_STATS_VIA_SYSCTL)
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    long ret_value = -1;

    ret_value = netbsd_read_ip_stat(&ipstat);

    if ( ret_value < 0) {
	DEBUGMSGTL(("mibII/ip", "Failed to load IP Group (netbsd)\n"));
    } else {
	DEBUGMSGTL(("mibII/ip", "Loaded IP Group (netbsd)\n"));
    }
    return ret_value;
}
#elif defined (WIN32) || defined (cygwin)
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    long ret_value = -1;

    ret_value = GetIpStatistics(&ipstat);

    if ( ret_value < 0 ) {
        DEBUGMSGTL(("mibII/ip", "Failed to load IP Group (win32)\n"));
    } else {
        DEBUGMSGTL(("mibII/ip", "Loaded IP Group (win32)\n"));
    }
    return ret_value;
}
#elif defined(_USE_PERFSTAT_PROTOCOL)
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    long ret_value = -1;

    strcpy(ps_name.name, "ip");
    ret_value = perfstat_protocol(&ps_name, &ps_proto, sizeof(ps_proto), 1);

    if ( ret_value < 0 ) {
        DEBUGMSGTL(("mibII/ip", "Failed to load IP Group (AIX)\n"));
    } else {
        ret_value = 0;
        DEBUGMSGTL(("mibII/ip", "Loaded IP Group (AIX)\n"));
    }
    return ret_value;
}
#elif defined(NETSNMP_CAN_USE_SYSCTL) && defined(IPCTL_STATS)
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    long            ret_value = 0;
    int             i;
    static int      sname[4] = { CTL_NET, PF_INET, IPPROTO_IP, 0 };
    size_t          len;
    int             magic = (uintptr_t) vmagic;

    switch (magic) {
    case IPFORWARDING:
        len = sizeof i;
        sname[3] = IPCTL_FORWARDING;
        if (sysctl(sname, 4, &i, &len, 0, 0) < 0)
            return -1;
        else
            return (i ? 1 /* GATEWAY */
                      : 2 /* HOST    */ );

    case IPDEFAULTTTL:
        len = sizeof i;
        sname[3] = IPCTL_DEFTTL;
        if (sysctl(sname, 4, &i, &len, 0, 0) < 0)
            return -1;
        else
            return i;

    default:
        len = sizeof(ipstat);
        sname[3] = IPCTL_STATS;
        ret_value = sysctl(sname, 4, &ipstat, &len, 0, 0);

        if ( ret_value < 0 ) {
            DEBUGMSGTL(("mibII/ip", "Failed to load IP Group (sysctl)\n"));
        } else {
            DEBUGMSGTL(("mibII/ip", "Loaded IP Group (sysctl)\n"));
        }
        return ret_value;
    }
}
#elif defined(HAVE_SYS_TCPIPSTATS_H)
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    long ret_value = -1;
    int  magic     = (int) vmagic;

    switch (magic) {
    case IPFORWARDING:
        if (!auto_nlist
            (IP_FORWARDING_SYMBOL, (char *) &ret_value, sizeof(ret_value)))
            return -1;
        else
            return (ret_value ? 1 /* GATEWAY */
                              : 2 /* HOST    */ );

    case IPDEFAULTTTL:
        if (!auto_nlist
            (TCP_TTL_SYMBOL, (char *) &ret_value, sizeof(ret_value)))
            return -1;
        else
            return ret_value;

    default:
        ret_value = sysmp(MP_SAGET, MPSA_TCPIPSTATS, &ipstat, sizeof ipstat);

        if ( ret_value < 0 ) {
            DEBUGMSGTL(("mibII/ip", "Failed to load IP Group (tcpipstats)\n"));
        } else {
            DEBUGMSGTL(("mibII/ip", "Loaded IP Group (tcpipstats)\n"));
        }
        return ret_value;
    }
}
#elif defined(IPSTAT_SYMBOL)
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    long ret_value = -1;
    int  magic     = (int) vmagic;

    switch (magic) {
    case IPFORWARDING:
        if (!auto_nlist
            (IP_FORWARDING_SYMBOL, (char *) &ret_value, sizeof(ret_value)))
            return -1;
        else
            return (ret_value ? 1 /* GATEWAY */
                              : 2 /* HOST    */ );

    case IPDEFAULTTTL:
        if (!auto_nlist
            (TCP_TTL_SYMBOL, (char *) &ret_value, sizeof(ret_value)))
            return -1;
        else
            return ret_value;

    default:
        if (auto_nlist(IPSTAT_SYMBOL, (char *)&ipstat, sizeof(ipstat)))
            ret_value = 0;

        if ( ret_value < 0 ) {
            DEBUGMSGTL(("mibII/ip", "Failed to load IP Group (ipstat)\n"));
        } else {
            DEBUGMSGTL(("mibII/ip", "Loaded IP Group (ipstat)\n"));
        }
        return ret_value;
    }
}
#else				/* IPSTAT_SYMBOL */
int
ip_load(netsnmp_cache *cache, void *vmagic)
{
    long ret_value = -1;

    DEBUGMSGTL(("mibII/ip", "Failed to load IP Group (null)\n"));
    return ret_value;
}
#endif                          /* hpux11 */

void
ip_free(netsnmp_cache *cache, void *magic)
{
#if defined(_USE_PERFSTAT_PROTOCOL)
    memset(&ps_proto, 0, sizeof(ps_proto));
#else
    memset(&ipstat, 0, sizeof(ipstat));
#endif
}

