/*
 *  Host Resources MIB - network device group implementation - hr_network.c
 *
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-features.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/data_access/interface.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif

#ifdef hpux11
#include <sys/mib.h>
#include <netinet/mib_kern.h>
#endif

#include "host_res.h"
#include "mibII/interfaces.h"
#include "hr_network.h"

#if !defined( solaris2 )
netsnmp_feature_require(interface_legacy);
#endif /* !solaris2 */

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

void            Init_HR_Network(void);
int             Get_Next_HR_Network(void);
void            Save_HR_Network_Info(void);

const char     *describe_networkIF(int);
int             network_status(int);
int             network_errors(int);
int             header_hrnet(struct variable *, oid *, size_t *, int,
                             size_t *, WriteMethod **);

#define HRN_MONOTONICALLY_INCREASING

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

#define	HRNET_IFINDEX		1

struct variable4 hrnet_variables[] = {
    {HRNET_IFINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
     var_hrnet, 2, {1, 1}}
};
oid             hrnet_variables_oid[] = { 1, 3, 6, 1, 2, 1, 25, 3, 4 };


void
init_hr_network(void)
{
    init_device[HRDEV_NETWORK] = Init_HR_Network;
    next_device[HRDEV_NETWORK] = Get_Next_HR_Network;
    save_device[HRDEV_NETWORK] = Save_HR_Network_Info;
#ifdef HRN_MONOTONICALLY_INCREASING
    dev_idx_inc[HRDEV_NETWORK] = 1;
#endif

    device_descr[HRDEV_NETWORK] = describe_networkIF;
    device_status[HRDEV_NETWORK] = network_status;
    device_errors[HRDEV_NETWORK] = network_errors;

    REGISTER_MIB("host/hr_network", hrnet_variables, variable4,
                 hrnet_variables_oid);
}

/*
 * header_hrnet(...
 * 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
 * 
 */

int
header_hrnet(struct variable *vp,
             oid * name,
             size_t * length,
             int exact, size_t * var_len, WriteMethod ** write_method)
{
#define HRNET_ENTRY_NAME_LENGTH	11
    oid             newname[MAX_OID_LEN];
    int             net_idx;
    int             result;
    int             LowIndex = -1;

    DEBUGMSGTL(("host/hr_network", "var_hrnet: "));
    DEBUGMSGOID(("host/hr_network", name, *length));
    DEBUGMSG(("host/hr_network", " %d\n", exact));

    memcpy((char *) newname, (char *) vp->name, vp->namelen * sizeof(oid));
    /*
     * Find "next" net entry 
     */

    Init_HR_Network();
    for (;;) {
        net_idx = Get_Next_HR_Network();
        if (net_idx == -1)
            break;
        newname[HRNET_ENTRY_NAME_LENGTH] = net_idx;
        result = snmp_oid_compare(name, *length, newname, vp->namelen + 1);
        if (exact && (result == 0)) {
            LowIndex = net_idx;
            break;
        }
        if (!exact && (result < 0) &&
            (LowIndex == -1 || net_idx < LowIndex)) {
            LowIndex = net_idx;
#ifdef HRN_MONOTONICALLY_INCREASING
            break;
#endif
        }
    }

    if (LowIndex == -1) {
        DEBUGMSGTL(("host/hr_network", "... index out of range\n"));
        return (MATCH_FAILED);
    }

    newname[HRNET_ENTRY_NAME_LENGTH] = LowIndex;
    memcpy((char *) name, (char *) newname,
           (vp->namelen + 1) * sizeof(oid));
    *length = vp->namelen + 1;
    *write_method = (WriteMethod*)0;
    *var_len = sizeof(long);    /* default to 'long' results */

    DEBUGMSGTL(("host/hr_network", "... get net stats "));
    DEBUGMSGOID(("host/hr_network", name, *length));
    DEBUGMSG(("host/hr_network", "\n"));
    return LowIndex;
}


        /*********************
	 *
	 *  System specific implementation functions
	 *
	 *********************/


u_char         *
var_hrnet(struct variable * vp,
          oid * name,
          size_t * length,
          int exact, size_t * var_len, WriteMethod ** write_method)
{
    int             net_idx;

    net_idx = header_hrnet(vp, name, length, exact, var_len, write_method);
    if (net_idx == MATCH_FAILED)
        return NULL;


    switch (vp->magic) {
    case HRNET_IFINDEX:
        long_return = net_idx & ((1 << HRDEV_TYPE_SHIFT) - 1);
        return (u_char *) & long_return;
    default:
        DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_hrnet\n",
                    vp->magic));
    }
    return NULL;
}


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


#if defined( USING_IF_MIB_IFTABLE_IFTABLE_DATA_ACCESS_MODULE )
static char     HRN_name[16];
static netsnmp_interface_entry *HRN_ifnet;
#define M_Interface_Scan_Next(a, b, c, d)	Interface_Scan_NextInt(a, b, c, d)
#elif defined(hpux11)
static char     HRN_name[MAX_PHYSADDR_LEN];
static nmapi_phystat HRN_ifnet;
#define M_Interface_Scan_Next(a, b, c, d)	Interface_Scan_NextInt(a, b, c)
#elif defined darwin
static char     HRN_name[IFNAMSIZ];
static struct if_msghdr HRN_ifnet;
#define M_Interface_Scan_Next(a, b, c, d)	Interface_Scan_NextInt(a, b, c, d)
#else                           /* hpux11 */
static char     HRN_name[16];
#ifndef WIN32
static struct ifnet HRN_ifnet;
#endif /* WIN32 */
#define M_Interface_Scan_Next(a, b, c, d)	Interface_Scan_NextInt(a, b, c, d)
#endif

#ifdef hpux11
static char     HRN_savedName[MAX_PHYSADDR_LEN];
#else
static char     HRN_savedName[16];
#endif
static u_short  HRN_savedFlags;
static int      HRN_savedErrors;


void
Init_HR_Network(void)
{
#if !defined( solaris2 )
    Interface_Scan_Init();
#endif
}

int
Get_Next_HR_Network(void)
{
int      HRN_index;
#if !(defined(solaris2) || defined(darwin) || defined(WIN32))
    if (M_Interface_Scan_Next(&HRN_index, HRN_name, &HRN_ifnet, NULL) == 0)
        HRN_index = -1;
#else
    HRN_index = -1;
#endif
    if (-1 == HRN_index)
        return HRN_index;

    /*
     * If the index is greater than the shift registry space,
     * this will overrun into the next device type block,
     * potentially resulting in duplicate index values
     * which may cause the agent to crash.
     *   To avoid this, we silently drop interfaces greater
     * than the shift registry size can handle.
     */
    if (HRN_index > (1 << HRDEV_TYPE_SHIFT ))
        return -1;

    return (HRDEV_NETWORK << HRDEV_TYPE_SHIFT) + HRN_index;
}

void
Save_HR_Network_Info(void)
{
    strlcpy(HRN_savedName, HRN_name, sizeof(HRN_savedName));
#if defined( USING_IF_MIB_IFTABLE_IFTABLE_DATA_ACCESS_MODULE )
    HRN_savedFlags  = HRN_ifnet->os_flags;
    HRN_savedErrors = HRN_ifnet->stats.ierrors + HRN_ifnet->stats.oerrors;
#elif defined( hpux11 )
    HRN_savedFlags  = HRN_ifnet.if_entry.ifOper;
    HRN_savedErrors = HRN_ifnet.if_entry.ifInErrors +
                      HRN_ifnet.if_entry.ifOutErrors;
#elif defined(__APPLE__)       /* or darwin? */
    HRN_savedFlags  = HRN_ifnet.ifm_flags;
    HRN_savedErrors = HRN_ifnet.ifm_data.ifi_ierrors +
                      HRN_ifnet.ifm_data.ifi_oerrors;
#elif !defined(WIN32)
    HRN_savedFlags = HRN_ifnet.if_flags;
    HRN_savedErrors = HRN_ifnet.if_ierrors + HRN_ifnet.if_oerrors;
#endif
}


const char     *
describe_networkIF(int idx)
{
    static char     string[1024];

    snprintf(string, sizeof(string)-1, "network interface %s", HRN_savedName);
    string[ sizeof(string)-1 ] = 0;
    return string;
}


int
network_status(int idx)
{
#ifndef WIN32
#ifdef hpux11
    if (HRN_savedFlags == LINK_UP)
#else
    if (HRN_savedFlags & IFF_UP)
#endif
        return 2;               /* running */
    else
        return 5;               /* down */
#else
    /* To do: implement network_status() for Windows. */
    return 2;                   /* running */
#endif /* WIN32 */
}

int
network_errors(int idx)
{
    return HRN_savedErrors;
}
