/*
 * NetBSD implementation for mapping the IP stat arrays into structs
 * Required for NetBSD versions produced after April 7th 2008 (4+)
 *
 * Based on: http://mail-index.netbsd.org/pkgsrc-users/2008/04/27/msg007095.html
 */

#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/protosw.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_var.h>
#include <netinet/icmp_var.h>
#include <netinet/icmp6.h>
#include <netinet/tcp.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>

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

#include "kernel_netbsd.h"

#if defined(NETBSD_STATS_VIA_SYSCTL)

int
netbsd_read_icmp_stat(struct icmp_mib *mib)
{
    uint64_t icmpstat[ICMP_NSTATS];
    size_t   size = sizeof(icmpstat);
    int      i;

    (void)memset(mib, 0, sizeof(*mib));

    if (-1 == sysctlbyname("net.inet.icmp.stats", icmpstat, &size, NULL, 0)) {
	snmp_perror("netbsd_read_icmp_stat: net.inet.icmp.stats");
        return -1;
    }

    mib->icmpInMsgs = icmpstat[ICMP_STAT_BADCODE]
        + icmpstat[ICMP_STAT_TOOSHORT]
        + icmpstat[ICMP_STAT_CHECKSUM]
        + icmpstat[ICMP_STAT_BADLEN];
    for (i = 0; i <= ICMP_MAXTYPE; i++)
        mib->icmpInMsgs  += icmpstat[ICMP_STAT_INHIST + i];
    mib->icmpInErrors = icmpstat[ICMP_STAT_BADCODE]
        + icmpstat[ICMP_STAT_TOOSHORT]
        + icmpstat[ICMP_STAT_CHECKSUM]
        + icmpstat[ICMP_STAT_BADLEN];
    mib->icmpInDestUnreachs = icmpstat[ICMP_STAT_INHIST + ICMP_UNREACH];
    mib->icmpInTimeExcds = icmpstat[ICMP_STAT_INHIST + ICMP_TIMXCEED];
    mib->icmpInParmProbs = icmpstat[ICMP_STAT_INHIST + ICMP_PARAMPROB];
    mib->icmpInSrcQuenchs = icmpstat[ICMP_STAT_INHIST + ICMP_SOURCEQUENCH];
    mib->icmpInRedirects = icmpstat[ICMP_STAT_INHIST + ICMP_REDIRECT];
    mib->icmpInEchos = icmpstat[ICMP_STAT_INHIST + ICMP_ECHO];
    mib->icmpInEchoReps = icmpstat[ICMP_STAT_INHIST + ICMP_ECHOREPLY];
    mib->icmpInTimestamps = icmpstat[ICMP_STAT_INHIST + ICMP_TSTAMP];
    mib->icmpInTimestampReps
        = icmpstat[ICMP_STAT_INHIST + ICMP_TSTAMPREPLY];
    mib->icmpInAddrMasks = icmpstat[ICMP_STAT_INHIST + ICMP_MASKREQ];
    mib->icmpInAddrMaskReps = icmpstat[ICMP_STAT_INHIST + ICMP_MASKREPLY];
    mib->icmpOutMsgs = icmpstat[ICMP_STAT_OLDSHORT]
        + icmpstat[ICMP_STAT_OLDICMP];
    for (i = 0; i <= ICMP_MAXTYPE; i++)
        mib->icmpOutMsgs += icmpstat[ICMP_STAT_OUTHIST + i];
    mib->icmpOutErrors = icmpstat[ICMP_STAT_OLDSHORT]
        + icmpstat[ICMP_STAT_OLDICMP];
    mib->icmpOutDestUnreachs = icmpstat[ICMP_STAT_OUTHIST + ICMP_UNREACH];
    mib->icmpOutTimeExcds = icmpstat[ICMP_STAT_OUTHIST + ICMP_TIMXCEED];
    mib->icmpOutParmProbs = icmpstat[ICMP_STAT_OUTHIST + ICMP_PARAMPROB];
    mib->icmpOutSrcQuenchs
        = icmpstat[ICMP_STAT_OUTHIST + ICMP_SOURCEQUENCH];
    mib->icmpOutRedirects = icmpstat[ICMP_STAT_OUTHIST + ICMP_REDIRECT];
    mib->icmpOutEchos = icmpstat[ICMP_STAT_OUTHIST + ICMP_ECHO];
    mib->icmpOutEchoReps = icmpstat[ICMP_STAT_OUTHIST + ICMP_ECHOREPLY];
    mib->icmpOutTimestamps = icmpstat[ICMP_STAT_OUTHIST + ICMP_TSTAMP];
    mib->icmpOutTimestampReps
        = icmpstat[ICMP_STAT_OUTHIST + ICMP_TSTAMPREPLY];
    mib->icmpOutAddrMasks = icmpstat[ICMP_STAT_OUTHIST + ICMP_MASKREQ];
    mib->icmpOutAddrMaskReps = icmpstat[ICMP_STAT_OUTHIST + ICMP_MASKREPLY];

    return 0;
}


int
netbsd_read_icmp6_stat(struct icmp6_mib *mib)
{
    uint64_t icmpstat[ICMP6_NSTATS];
    size_t   size = sizeof(icmpstat);
    int      i;

    (void)memset(mib, 0, sizeof(*mib));

    if (-1 == sysctlbyname("net.inet6.icmp6.stats", icmpstat, &size, NULL, 0)) {
	snmp_perror("netbsd_read_icmp6_stat: net.inet6.icmp6.stats");
        return -1;
    }

    mib->icmp6InMsgs = icmpstat[ICMP6_STAT_BADCODE]
            + icmpstat[ICMP_STAT_TOOSHORT]
	    + icmpstat[ICMP_STAT_CHECKSUM]
            + icmpstat[ICMP_STAT_BADLEN];
    for (i = 0; i <= ICMP6_MAXTYPE; i++)
        mib->icmp6InMsgs  += icmpstat[ICMP6_STAT_INHIST + i];
    mib->icmp6InErrors = icmpstat[ICMP6_STAT_BADCODE]
        + icmpstat[ICMP6_STAT_TOOSHORT]
        + icmpstat[ICMP6_STAT_CHECKSUM]
        + icmpstat[ICMP6_STAT_BADLEN];
    mib->icmp6InDestUnreachs = icmpstat[ICMP6_STAT_INHIST + ICMP6_DST_UNREACH];
    mib->icmp6InPktTooBigs = icmpstat[ICMP6_STAT_INHIST + ICMP6_PACKET_TOO_BIG];
    mib->icmp6InTimeExcds = icmpstat[ICMP6_STAT_INHIST + ICMP6_TIME_EXCEEDED];
    mib->icmp6InParmProblems = icmpstat[ICMP6_STAT_INHIST + ICMP6_PARAM_PROB];
    mib->icmp6InEchos = icmpstat[ICMP6_STAT_INHIST + ICMP6_ECHO_REQUEST];
    mib->icmp6InEchoReplies = icmpstat[ICMP6_STAT_INHIST + ICMP6_ECHO_REPLY];
    mib->icmp6InGroupMembQueries = icmpstat[ICMP6_STAT_INHIST + MLD_LISTENER_QUERY];
    mib->icmp6InGroupMembResponses = icmpstat[ICMP6_STAT_INHIST + MLD_LISTENER_REPORT];
    mib->icmp6InRouterSolicits = icmpstat[ICMP6_STAT_INHIST + ND_ROUTER_SOLICIT];
    mib->icmp6InRouterAdvertisements = icmpstat[ICMP6_STAT_INHIST + ND_ROUTER_ADVERT];
    mib->icmp6InNeighborSolicits = icmpstat[ICMP6_STAT_INHIST + ND_NEIGHBOR_SOLICIT];
    mib->icmp6InNeighborAdvertisements = icmpstat[ICMP6_STAT_INHIST + ND_NEIGHBOR_ADVERT];
    mib->icmp6InRedirects = icmpstat[ICMP6_STAT_INHIST + ND_REDIRECT];

    mib->icmp6OutMsgs = icmpstat[ICMP6_STAT_BADCODE]
        + icmpstat[ICMP6_STAT_TOOSHORT]
        + icmpstat[ICMP6_STAT_CHECKSUM]
        + icmpstat[ICMP6_STAT_BADLEN];
    for (i = 0; i <= ICMP6_MAXTYPE; i++)
        mib->icmp6OutMsgs += icmpstat[ICMP6_STAT_OUTHIST + i];
    mib->icmp6OutDestUnreachs = icmpstat[ICMP6_STAT_OUTHIST + ICMP6_DST_UNREACH];
    mib->icmp6OutPktTooBigs =  icmpstat[ICMP6_STAT_OUTHIST + ICMP6_PACKET_TOO_BIG];
    mib->icmp6OutTimeExcds = icmpstat[ICMP6_STAT_OUTHIST + ICMP6_TIME_EXCEEDED];
    mib->icmp6OutParmProblems = icmpstat[ICMP6_STAT_OUTHIST + ICMP6_PARAM_PROB];
    mib->icmp6OutEchos = icmpstat[ICMP6_STAT_OUTHIST + ICMP6_ECHO_REQUEST];
    mib->icmp6OutEchoReplies = icmpstat[ICMP6_STAT_OUTHIST + ICMP6_ECHO_REPLY];
    mib->icmp6OutRouterSolicits =  icmpstat[ICMP6_STAT_OUTHIST + ND_ROUTER_SOLICIT];
    mib->icmp6OutNeighborSolicits =  icmpstat[ICMP6_STAT_OUTHIST + ND_NEIGHBOR_SOLICIT];
    mib->icmp6OutNeighborAdvertisements =  icmpstat[ICMP6_STAT_OUTHIST + ND_NEIGHBOR_ADVERT];
    mib->icmp6OutRedirects = icmpstat[ICMP6_STAT_OUTHIST + ND_REDIRECT];
    mib->icmp6OutGroupMembResponses =  icmpstat[ICMP6_STAT_OUTHIST + MLD_LISTENER_REPORT];
    mib->icmp6OutGroupMembReductions =  icmpstat[ICMP6_STAT_OUTHIST + MLD_LISTENER_DONE];

    return 0;
}


int
netbsd_read_icmp_msg_stat(struct icmp_mib *mib,
                          struct icmp4_msg_mib *msgmib,
			  int *flag)
{
    netbsd_read_icmp_stat(mib);
    *flag = 0;
    return 0;
}


int
netbsd_read_icmp6_msg_stat(struct icmp6_mib *mib,
                           struct icmp6_msg_mib *msgmib,
			   int *support)
{
    uint64_t icmpstat[ICMP6_NSTATS];
    size_t   size = sizeof(icmpstat);
    int      i;

    netbsd_read_icmp6_stat(mib);

    if (-1 == sysctlbyname("net.inet6.icmp6.stats", icmpstat, &size, NULL, 0)) {
	snmp_perror("netbsd_read_icmp6_stat: net.inet6.icmp6.stats");
        return -1;
    }

    for (i = 0; i < 256; i++) {
	msgmib->vals[i].InType = icmpstat[ICMP6_STAT_INHIST+i];
	msgmib->vals[i].OutType = icmpstat[ICMP6_STAT_OUTHIST+i];
    }
    *support = 1;
    return 0;
}


int
netbsd_read_ip_stat(struct ip_mib *mib)
{
    uint64_t ipstat[IP_NSTATS];
    size_t   size = sizeof(ipstat);
    int      i;
    static   int sname[4] = { 4, 2, 0, 0 }; /* CTL_NET, PF_INET, IPPROTO_IP, 0 */
    size_t   len;

    (void)memset(mib, 0, sizeof(*mib));

    if (-1 == sysctlbyname("net.inet.ip.stats", ipstat, &size, NULL, 0)) {
	snmp_perror("netbsd_read_ip_stat: net.inet.ip.stats");
        return -1;
    }

    mib->ipForwarding = 0;
    len = sizeof i;
    sname[3] = IPCTL_FORWARDING;
    if (0 == sysctl(sname, 4, &i, &len, 0, 0)) {
        mib->ipForwarding = (long)i;
    }

    mib->ipDefaultTTL = 0;
    sname[3] = IPCTL_DEFTTL;     
    if (0 == sysctl(sname, 4, &i, &len, 0, 0)) {
        mib->ipDefaultTTL = (long)i;
    }

    mib->ipInReceives = ipstat[IP_STAT_TOTAL];
    mib->ipInHdrErrors = ipstat[IP_STAT_BADSUM]
        + ipstat[IP_STAT_TOOSHORT] + ipstat[IP_STAT_TOOSMALL]
        + ipstat[IP_STAT_BADHLEN] + ipstat[IP_STAT_BADLEN];
    mib->ipInAddrErrors = ipstat[IP_STAT_CANTFORWARD];
    mib->ipForwDatagrams = ipstat[IP_STAT_FORWARD];
    mib->ipInUnknownProtos = ipstat[IP_STAT_NOPROTO];
    mib->ipInDiscards = ipstat[IP_STAT_FRAGDROPPED]; /* FIXME */
    mib->ipInDelivers = ipstat[IP_STAT_DELIVERED];
    mib->ipOutRequests = ipstat[IP_STAT_LOCALOUT];
    mib->ipOutDiscards = ipstat[IP_STAT_ODROPPED];
    mib->ipOutNoRoutes = 0; /* FIXME */
    mib->ipReasmTimeout = 0; /* IPFRAGTTL; */
    mib->ipReasmReqds = ipstat[IP_STAT_FRAGMENTS];
    mib->ipReasmOKs = ipstat[IP_STAT_REASSEMBLED];
    mib->ipReasmFails = ipstat[IP_STAT_FRAGDROPPED]
        + ipstat[IP_STAT_FRAGTIMEOUT];
    mib->ipFragOKs = ipstat[IP_STAT_FRAGMENTS];
    mib->ipFragFails = ipstat[IP_STAT_CANTFRAG];
    mib->ipFragCreates = ipstat[IP_STAT_OFRAGMENTS];
    mib->ipRoutingDiscards = ipstat[IP_STAT_NOROUTE];
    
    return 0;
}


int
netbsd_read_tcp_stat(struct tcp_mib *mib)
{
    uint64_t tcpstat[TCP_NSTATS];
    size_t   size = sizeof(tcpstat);

    (void)memset(mib, 0, sizeof(*mib));

    if (-1 == sysctlbyname("net.inet.tcp.stats", tcpstat, &size, NULL, 0)) {
	snmp_perror("netbsd_read_tcp_stat: net.inet.tcp.stats");
        return -1;
    }

    mib->tcpRtoAlgorithm = 4; /* Assume Van Jacobsen's algorithm */
    mib->tcpRtoMin = TCPTV_MIN;
    mib->tcpRtoMax = TCPTV_REXMTMAX;
    mib->tcpMaxConn = -1; /* Dynamic Maximum */
    mib->tcpActiveOpens = tcpstat[TCP_STAT_CONNATTEMPT];
    mib->tcpPassiveOpens = tcpstat[TCP_STAT_ACCEPTS];
    mib->tcpAttemptFails = tcpstat[TCP_STAT_CONNDROPS];
    mib->tcpEstabResets = tcpstat[TCP_STAT_DROPS];
    mib->tcpCurrEstab = 0; /* FIXME */
    mib->tcpInSegs = tcpstat[TCP_STAT_RCVTOTAL];
    mib->tcpOutSegs = tcpstat[TCP_STAT_SNDTOTAL]
        - tcpstat[TCP_STAT_SNDREXMITPACK];
    mib->tcpRetransSegs = tcpstat[TCP_STAT_SNDREXMITPACK];
    mib->tcpInErrs = tcpstat[TCP_STAT_RCVBADSUM]
        + tcpstat[TCP_STAT_RCVBADOFF]
        + tcpstat[TCP_STAT_RCVMEMDROP]
        + tcpstat[TCP_STAT_RCVSHORT];
    mib->tcpOutRsts = tcpstat[TCP_STAT_SNDCTRL]
        - tcpstat[TCP_STAT_CLOSED];
    mib->tcpInErrsValid = mib->tcpInErrs; /* FIXME */
    mib->tcpOutRstsValid = mib->tcpOutRsts; /* FIXME */

    return 0;
}


int
netbsd_read_udp_stat(struct udp_mib *mib)
{
    uint64_t udpstat[UDP_NSTATS];
    size_t   size = sizeof(udpstat);

    (void)memset(mib, 0, sizeof(*mib));

    if (-1 == sysctlbyname("net.inet.udp.stats", udpstat, &size, NULL, 0)) {
	snmp_perror("netbsd_read_udp_stat: net.inet.udp.stats");
        return -1;
    }

    mib->udpInDatagrams = udpstat[UDP_STAT_IPACKETS];
    mib->udpNoPorts = udpstat[UDP_STAT_NOPORT];
    mib->udpOutDatagrams = udpstat[UDP_STAT_OPACKETS];
    mib->udpInErrors = udpstat[UDP_STAT_HDROPS]
        + udpstat[UDP_STAT_BADSUM] /* + udpstat[UDP_STAT_DISCARD] */ /* FIXME */
        + udpstat[UDP_STAT_FULLSOCK] + udpstat[UDP_STAT_BADLEN];

    return 0;
}

#endif
