/*==============================================================================
I F . C
This module hides all the network interface junk from the other modules.
if.c,v 1.2 1998/10/12 02:13:16 rneswold Exp
==============================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <err.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#if (__FreeBSD_version >= 300003)
#include <net/if_var.h>
#include <net/if_types.h>
#endif
#include <fcntl.h>
#include <kvm.h>
#include <nlist.h>
#include <assert.h>
#include "wmnet.h"
#define IF_STEP 10
/*
Local data types...
*/
typedef struct {
unsigned long ifNetAddr;
unsigned long flags;
unsigned current;
unsigned long xmt[G_WIDTH], xmtLast;
unsigned long rcv[G_WIDTH], rcvLast;
} IfData;
/*
Local prototypes...
*/
static int addIfData(unsigned long);
static struct ifnet const* dereference(unsigned long);
static void ifTerm(void);
/*
Local data...
*/
static kvm_t* kd = 0;
static unsigned long root = 0;
static int total = 0;
static int size = 0;
static IfData* ifData = 0;
/*------------------------------------------------------------------------------
addIfData
This function adds a network interface to the list. If the list is
out of space, more space is allocated. If the interface already
exists in the list, we don't do anything.
------------------------------------------------------------------------------*/
static int addIfData(unsigned long theAddr)
{
struct ifnet const* const theData = dereference(theAddr);
assert(total <= size);
/* Now read in the data so we can get to some of its goodies. */
if (0 != theData) {
IfData* ptr = 0;
int ii;
/* First check to see if we have enough space in the list. If
we don't, we'll have to add more space. */
if (total == size) {
IfData* const newData =
(IfData*) realloc(ifData,
(size += IF_STEP) * sizeof(IfData));
if (!newData) {
size -= IF_STEP;
#if defined(__FreeBSD__) && __FreeBSD_version < 501113
fprintf(stderr, "wmnet: Warning -- low memory; "
"ignoring %s interface\n", theData->if_name);
#else
fprintf(stderr, "wmnet: Warning -- low memory; "
"ignoring %s interface\n", theData->if_xname);
#endif
return 0;
}
/* Good. we got more space. */
ifData = newData;
}
assert(0 != ifData);
assert(total < size);
/* If we reached here, then 'total' points to the next
bucket. Initialize the new bucket. XXX: This would be a
good spot to insert-sort the new record. As it stands, the
list builds up by the order that the interfaces were
bootstrapped. */
ptr = ifData + total;
ptr->ifNetAddr = theAddr;
for (ii = 0; ii < G_WIDTH; ++ii)
ptr->rcv[ii] = ptr->xmt[ii] = 0;
ptr->rcvLast = theData->if_ibytes;
ptr->xmtLast = theData->if_obytes;
ptr->current = 0;
ptr->flags = 0;
#ifndef NDEBUG
#if defined(__FreeBSD__) && __FreeBSD_version < 501113
printf("Added '%.*s%d' to list.\n", IFNAMSIZ, theData->if_name,
theData->if_unit);
#else
printf("Added '%.*s' to list.\n", IFNAMSIZ, theData->if_xname);
#endif
#endif
// Bump the total.
++total;
} else
fprintf(stderr, "Couldn't read interface information\n");
return 1;
}
/*------------------------------------------------------------------------------
dereference
Takes the kernel memory "pointer" and copies its pointed-to data
into a local buffer. The only type of "pointer" we're using is a
struct ifnet*, so this function only returns this type of data.
Passing in 0 will reset the address cache and force the function
to reload the data.
------------------------------------------------------------------------------*/
static struct ifnet const* dereference(unsigned long a)
{
static unsigned long c = 0;
static struct ifnet d;
#if defined(__FreeBSD__) && __FreeBSD_version < 501113
static char name[IFNAMSIZ];
#endif
/* If we are passed a NULL, the caller wants us to stop caching
the current interface. */
if (!a) {
c = 0;
return 0;
}
/* If the requested address is the same that has been cached, just
return the data. */
else if (a == c)
return &d;
else if (sizeof(d) == kvm_read(kd, a, &d, sizeof(d))) {
#if defined(__FreeBSD__) && __FreeBSD_version < 501113
/* We've read the structure's data, but the 'name' field still
points to kernel memory. We transfer the name to a local
buffer, and then modify the pointer to point to our
buffer. */
ssize_t const n = kvm_read(kd, (unsigned long) d.if_name, name,
sizeof(name));
if (sizeof(name) == n) {
name[IFNAMSIZ - 1] = '\0';
d.if_name = name;
#endif
#ifndef NDEBUG
/* These are other pointer fields that we shouldn't need
to look at. While debugging, set these to NULL to trap
any attempts. */
d.if_softc = 0;
#if (__FreeBSD_version >= 300003)
d.if_addrhead.tqh_first = 0;
#else
d.if_addrlist = 0;
#endif
d.if_bpf = 0;
d.if_linkmib = 0;
#endif
c = a;
return &d;
#if defined(__FreeBSD__) && __FreeBSD_version < 501113
} else
return 0;
#endif
} else
return 0;
}
/*------------------------------------------------------------------------------
ifGetData
------------------------------------------------------------------------------*/
int ifGetData(unsigned ifIdx, unsigned n, unsigned long* x, unsigned long* r)
{
assert(x && r);
if (ifIdx < total && n < G_WIDTH) {
IfData const* const d = ifData + ifIdx;
*x = d->xmt[(d->current + n) % G_WIDTH];
*r = d->rcv[(d->current + n) % G_WIDTH];
return 1;
} else
return 0;
}
/*------------------------------------------------------------------------------
ifInit
Initialize the module. This function should be called before any
other functions, in this module, are called.
------------------------------------------------------------------------------*/
int ifInit(void)
{
char errBuf[_POSIX2_LINE_MAX];
/* First try to open the kernel image. If we can't, the rest of
the program is essentially useless. */
if (0 != (kd = kvm_openfiles(0, 0, 0, O_RDONLY, errBuf))) {
static struct nlist nl[] = {
{ "_ifnet" },
{ "" }
};
/* Try to pull the address for the global kernel variable,
ifnet. This variable is the root of the singly-linked list
of network interfaces. */
if (0 == kvm_nlist(kd, nl)) {
ssize_t const n = kvm_read(kd, nl[0].n_value, &root, sizeof(root));
/* We'll go ahead and make one dereference. We never
really want this variable. We want what it points to. */
if (sizeof(root) == n) {
unsigned long current = root;
while (current) {
addIfData(current);
#if (__FreeBSD_version >= 300003)
current = (unsigned long) dereference(current)->if_link.tqe_next;
#else
current = (unsigned long) dereference(current)->if_next;
#endif
}
/* Try to register our termination function. If it
returns an error, we essentially ignore it because
the OS will clean up our resources. */
(void) atexit(ifTerm);
return 1;
}
}
/* If we can't get a symbol list, close the kernel image and
return an error. */
kvm_close(kd);
kd = 0;
} else
fprintf(stderr, "kvm_open: %s\n", errBuf);
return 0;
}
/*------------------------------------------------------------------------------
ifIsPromisc
------------------------------------------------------------------------------*/
int ifIsPromisc(unsigned ifIdx)
{
return ifIdx < total ? (ifData[ifIdx].flags & IFF_PROMISC) : 0;
}
/*------------------------------------------------------------------------------
ifIsRunning
------------------------------------------------------------------------------*/
int ifIsRunning(unsigned ifIdx)
{
return ifIdx < total ? (ifData[ifIdx].flags & IFF_RUNNING) : 0;
}
/*------------------------------------------------------------------------------
ifIsUp
------------------------------------------------------------------------------*/
int ifIsUp(unsigned ifIdx)
{
return ifIdx < total ? (ifData[ifIdx].flags & IFF_UP) : 0;
}
/*------------------------------------------------------------------------------
ifName
------------------------------------------------------------------------------*/
char const* ifName(unsigned idx)
{
if (idx < total) {
struct ifnet const* const ptr = dereference(ifData[idx].ifNetAddr);
#if defined(__FreeBSD__) && __FreeBSD_version < 501113
if (ptr) {
static char buffer[IFNAMSIZ + 1];
sprintf(buffer, "%.*s%d", IFNAMSIZ - 1, ptr->if_name, ptr->if_unit);
return buffer;
}
#else
return ptr->if_xname;
#endif
}
return 0;
}
/*------------------------------------------------------------------------------
ifSample
Samples each of the network interfaces and updates the respective
histories.
------------------------------------------------------------------------------*/
int ifSample(void)
{
int ii;
/* Flush the kernel memory cache so that we're guaranteed to get
new data during the next dereference(). */
dereference(0);
/* Loop through all the network interfaces. Even though we display
one interface's statistics, we keep track of all of them. */
for (ii = 0; ii < total; ++ii) {
IfData* const d = ifData + ii;
struct ifnet const* const ptr = dereference(d->ifNetAddr);
if (!ptr) {
fprintf(stderr, "couldn't sample interface.\n");
return 0;
}
d->flags = ptr->if_flags;
/* Save the new delta for the transmit channel. */
d->xmt[d->current] = ptr->if_obytes - d->xmtLast;
d->xmtLast = ptr->if_obytes;
/* Save the new delta for the receive channel. */
d->rcv[d->current] = ptr->if_ibytes - d->rcvLast;
d->rcvLast = ptr->if_ibytes;
/* Prep the index for the next location to be written. */
d->current = (d->current + 1) % G_WIDTH;
}
return 1;
}
/*------------------------------------------------------------------------------
ifTerm
This function will be called once when the application is ready to exit
------------------------------------------------------------------------------*/
static void ifTerm(void)
{
#ifndef NDEBUG
printf("Cleaning up resources.\n");
#endif
/* If the kernel image is open, close it. */
if (0 != kd) {
kvm_close(kd);
kd = 0;
}
/* Now free up any memory we're using. */
if (ifData) {
free(ifData);
ifData = 0;
}
}
/*------------------------------------------------------------------------------
ifTotal
------------------------------------------------------------------------------*/
int ifTotal(void)
{
return total;
}
syntax highlighted by Code2HTML, v. 0.9.1