/*
 * Copyright (c) 2002 Peter Memishian (meem) <meem@gnu.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * wmnetload - A dockapp to monitor network interface usage.
 *	       Inspired by Seiichi SATO's nifty CPU usage monitor.
 *
 * NetBSD-specific interface statistics gathering routines.
 */

#pragma ident "@(#)ifstat_netbsd.c	1.2	02/01/29 meem"

#include <config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
#include <nlist.h>
#include <stdlib.h>
#include <string.h>

#include "ifstat.h"
#include "utils.h"

struct ifstatstate {
	void	*ifnet_head;
	kvm_t	*kd;
};

/*
 * Do one-time setup stuff for accessing the interface statistics and store
 * the gathered information in an interface statistics state structure.
 * Return the state structure.
 */
ifstatstate_t *
if_statinit(void)
{
	ifstatstate_t	*statep;
	struct nlist	ifnet[] = { { "_ifnet" }, { NULL }};
	char		errbuf[_POSIX2_LINE_MAX];

	statep = malloc(sizeof (ifstatstate_t));
	if (statep == NULL) {
		warn("cannot allocate interface statistics state");
		return (NULL);
	}

	/*
	 * Just for the duration of kmem_openfiles(), get privileges
	 * needed to access kmem.
	 */
	chpriv(PRIV_GAIN);
	statep->kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
	chpriv(PRIV_DROP);
	if (statep->kd == NULL) {
		warn("cannot access raw kernel memory: %s\n", errbuf);
		free(statep);
		return (NULL);
	}

	if (kvm_nlist(statep->kd, ifnet) == -1) {
		warn("cannot populate kernel namelist: %s\n",
		    kvm_geterr(statep->kd));
		goto fail;
	}

	if (kvm_read(statep->kd, ifnet->n_value, &statep->ifnet_head,
	    sizeof (ifnet->n_value)) != sizeof (ifnet->n_value)) {
		warn("cannot find ifnet list head: %s\n",
		    kvm_geterr(statep->kd));
		goto fail;
	}

	return (statep);
fail:
	(void) kvm_close(statep->kd);
	free(statep);
	return (NULL);
}

/*
 * Optionally using state stored in `statep', retrieve stats on interface
 * `ifname', and store the statistics in `ifstatsp'.
 */
int
if_stats(const char *ifname, ifstatstate_t *statep, ifstats_t *ifstatsp)
{
	void		*ifnet_addr = statep->ifnet_head;
	struct ifnet	ifnet;

	for (; ifnet_addr != NULL; ifnet_addr = TAILQ_NEXT(&ifnet, if_list)) {

		if (kvm_read(statep->kd, (unsigned long)ifnet_addr, &ifnet,
		    sizeof (struct ifnet)) != sizeof (struct ifnet))
			return (0);

		if (strcmp(ifnet.if_xname, ifname) == 0) {
			ifstatsp->rxbytes = ifnet.if_ibytes;
			ifstatsp->txbytes = ifnet.if_obytes;
			return (1);
		}
	}

	return (0);
}

/*
 * Clean up the interface state structure pointed to by `statep'.
 */
void
if_statfini(ifstatstate_t *statep)
{
	(void) kvm_close(statep->kd);
	free(statep);
}


syntax highlighted by Code2HTML, v. 0.9.1