/*
 *  Linux kernel interface
 *
 */

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

#include "util_funcs.h"

#if HAVE_STRING_H
#include <string.h>
#endif
#include <sys/types.h>
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#include "kernel_linux.h"

struct ip_mib   cached_ip_mib;
struct icmp_mib cached_icmp_mib;
struct tcp_mib  cached_tcp_mib;
struct udp_mib  cached_udp_mib;

#define IP_STATS_LINE	"Ip: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu"
#define ICMP_STATS_LINE	"Icmp: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu"
#define TCP_STATS_LINE	"Tcp: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu"
#define UDP_STATS_LINE	"Udp: %lu %lu %lu %lu"
#define UDP6_STATS_LINE "Udp6"

#define IP_STATS_PREFIX_LEN	4
#define ICMP_STATS_PREFIX_LEN	6
#define TCP_STATS_PREFIX_LEN	5
#define UDP_STATS_PREFIX_LEN	5
#define UDP6_STATS_PREFIX_LEN   4


int
linux_read_mibII_stats(void)
{
    FILE           *in = fopen("/proc/net/snmp", "r");
#ifdef NETSNMP_ENABLE_IPV6
    FILE           *in6;
    unsigned long   value;
#endif
    char            line[1024];

    if (!in) {
        DEBUGMSGTL(("mibII/kernel_linux","Unable to open /proc/net/snmp"));
        return -1;
    }


    while (line == fgets(line, sizeof(line), in)) {
        if (!strncmp(line, IP_STATS_LINE, IP_STATS_PREFIX_LEN)) {
            sscanf(line, IP_STATS_LINE,
                   &cached_ip_mib.ipForwarding,
                   &cached_ip_mib.ipDefaultTTL,
                   &cached_ip_mib.ipInReceives,
                   &cached_ip_mib.ipInHdrErrors,
                   &cached_ip_mib.ipInAddrErrors,
                   &cached_ip_mib.ipForwDatagrams,
                   &cached_ip_mib.ipInUnknownProtos,
                   &cached_ip_mib.ipInDiscards,
                   &cached_ip_mib.ipInDelivers,
                   &cached_ip_mib.ipOutRequests,
                   &cached_ip_mib.ipOutDiscards,
                   &cached_ip_mib.ipOutNoRoutes,
                   &cached_ip_mib.ipReasmTimeout,
                   &cached_ip_mib.ipReasmReqds,
                   &cached_ip_mib.ipReasmOKs,
                   &cached_ip_mib.ipReasmFails,
                   &cached_ip_mib.ipFragOKs,
                   &cached_ip_mib.ipFragFails,
                   &cached_ip_mib.ipFragCreates);
            cached_ip_mib.ipRoutingDiscards = 0;        /* XXX */
        } else if (!strncmp(line, ICMP_STATS_LINE, ICMP_STATS_PREFIX_LEN)) {
            sscanf(line, ICMP_STATS_LINE,
                   &cached_icmp_mib.icmpInMsgs,
                   &cached_icmp_mib.icmpInErrors,
                   &cached_icmp_mib.icmpInDestUnreachs,
                   &cached_icmp_mib.icmpInTimeExcds,
                   &cached_icmp_mib.icmpInParmProbs,
                   &cached_icmp_mib.icmpInSrcQuenchs,
                   &cached_icmp_mib.icmpInRedirects,
                   &cached_icmp_mib.icmpInEchos,
                   &cached_icmp_mib.icmpInEchoReps,
                   &cached_icmp_mib.icmpInTimestamps,
                   &cached_icmp_mib.icmpInTimestampReps,
                   &cached_icmp_mib.icmpInAddrMasks,
                   &cached_icmp_mib.icmpInAddrMaskReps,
                   &cached_icmp_mib.icmpOutMsgs,
                   &cached_icmp_mib.icmpOutErrors,
                   &cached_icmp_mib.icmpOutDestUnreachs,
                   &cached_icmp_mib.icmpOutTimeExcds,
                   &cached_icmp_mib.icmpOutParmProbs,
                   &cached_icmp_mib.icmpOutSrcQuenchs,
                   &cached_icmp_mib.icmpOutRedirects,
                   &cached_icmp_mib.icmpOutEchos,
                   &cached_icmp_mib.icmpOutEchoReps,
                   &cached_icmp_mib.icmpOutTimestamps,
                   &cached_icmp_mib.icmpOutTimestampReps,
                   &cached_icmp_mib.icmpOutAddrMasks,
                   &cached_icmp_mib.icmpOutAddrMaskReps);
        } else if (!strncmp(line, TCP_STATS_LINE, TCP_STATS_PREFIX_LEN)) {
            int             ret = sscanf(line, TCP_STATS_LINE,
                                         &cached_tcp_mib.tcpRtoAlgorithm,
                                         &cached_tcp_mib.tcpRtoMin,
                                         &cached_tcp_mib.tcpRtoMax,
                                         &cached_tcp_mib.tcpMaxConn,
                                         &cached_tcp_mib.tcpActiveOpens,
                                         &cached_tcp_mib.tcpPassiveOpens,
                                         &cached_tcp_mib.tcpAttemptFails,
                                         &cached_tcp_mib.tcpEstabResets,
                                         &cached_tcp_mib.tcpCurrEstab,
                                         &cached_tcp_mib.tcpInSegs,
                                         &cached_tcp_mib.tcpOutSegs,
                                         &cached_tcp_mib.tcpRetransSegs,
                                         &cached_tcp_mib.tcpInErrs,
                                         &cached_tcp_mib.tcpOutRsts);
            cached_tcp_mib.tcpInErrsValid = (ret > 12) ? 1 : 0;
            cached_tcp_mib.tcpOutRstsValid = (ret > 13) ? 1 : 0;
        } else if (!strncmp(line, UDP_STATS_LINE, UDP_STATS_PREFIX_LEN)) {
            sscanf(line, UDP_STATS_LINE,
                   &cached_udp_mib.udpInDatagrams,
                   &cached_udp_mib.udpNoPorts,
                   &cached_udp_mib.udpInErrors,
                   &cached_udp_mib.udpOutDatagrams);
        }
    }
    fclose(in);

#ifdef NETSNMP_ENABLE_IPV6
    in6 = fopen("/proc/net/snmp6", "r");
    if (in6) {
      
        while (line == fgets(line, sizeof(line), in6)) {

            if (('U' != line[0]) ||
                (0 != strncmp(line, UDP6_STATS_LINE, UDP6_STATS_PREFIX_LEN)))
                continue;

            sscanf(line, "%*s %lu" , &value);
                 
            if ('O' == line[4]) /* Udp6OutDatagrams */
                cached_udp_mib.udpOutDatagrams += value;
            else if ('N' == line[4]) /* Udp6NoPorts */
                cached_udp_mib.udpNoPorts += value;
            else if ('D' == line[6]) /* Udp6InDatagrams */
                cached_udp_mib.udpInDatagrams += value;
            else if ('E' == line[6]) /* Udp6InErrors */
                cached_udp_mib.udpInErrors += value;

        }
        fclose(in6);
    } else {
        DEBUGMSGTL(("mibII/kernel_linux","Unable to open /proc/net/snmp6"));
    }

#endif

    /*
     * Tweak illegal values:
     *
     * valid values for ipForwarding are 1 == yup, 2 == nope
     * a 0 is forbidden, so patch:
     */
    if (!cached_ip_mib.ipForwarding)
        cached_ip_mib.ipForwarding = 2;

    /*
     * 0 is illegal for tcpRtoAlgorithm
     * so assume `other' algorithm:
     */
    if (!cached_tcp_mib.tcpRtoAlgorithm)
        cached_tcp_mib.tcpRtoAlgorithm = 1;
    return 0;
}

int
linux_read_ip_stat(struct ip_mib *ipstat)
{
    memset((char *) ipstat, (0), sizeof(*ipstat));
    if (linux_read_mibII_stats() == -1)
        return -1;
    memcpy((char *) ipstat, (char *) &cached_ip_mib, sizeof(*ipstat));
    return 0;
}

int
linux_read_icmp_stat(struct icmp_mib *icmpstat)
{
    memset((char *) icmpstat, (0), sizeof(*icmpstat));
    if (linux_read_mibII_stats() == -1)
        return -1;
    memcpy((char *) icmpstat, (char *) &cached_icmp_mib,
           sizeof(*icmpstat));
    return 0;
}

int
linux_read_tcp_stat(struct tcp_mib *tcpstat)
{
    memset((char *) tcpstat, (0), sizeof(*tcpstat));
    if (linux_read_mibII_stats() == -1)
        return -1;
    memcpy((char *) tcpstat, (char *) &cached_tcp_mib, sizeof(*tcpstat));
    return 0;
}

int
linux_read_udp_stat(struct udp_mib *udpstat)
{
    memset((char *) udpstat, (0), sizeof(*udpstat));
    if (linux_read_mibII_stats() == -1)
        return -1;
    memcpy((char *) udpstat, (char *) &cached_udp_mib, sizeof(*udpstat));
    return 0;
}
