#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_mib.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <ifaddrs.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "hostres_snmp.h"
#include "hostres_oid.h"
#include "hostres_tree.h"
#include <bsnmp/snmp_mibII.h>
struct network_entry {
int32_t index;
int32_t ifIndex;
TAILQ_ENTRY(network_entry) link;
#define HR_NETWORK_FOUND 0x001
uint32_t flags;
};
TAILQ_HEAD(network_tbl, network_entry);
static struct network_tbl network_tbl = TAILQ_HEAD_INITIALIZER(network_tbl);
static uint64_t network_tick;
uint32_t network_tbl_refresh = HR_NETWORK_TBL_REFRESH * 100;
static const struct asn_oid OIDX_hrDeviceNetwork_c = OIDX_hrDeviceNetwork;
static struct network_entry *
network_entry_create(const struct device_entry *devEntry)
{
struct network_entry *entry;
assert(devEntry != NULL);
if (devEntry == NULL)
return (NULL);
if ((entry = malloc(sizeof(*entry))) == NULL) {
syslog(LOG_WARNING, "%s: %m", __func__);
return (NULL);
}
memset(entry, 0, sizeof(*entry));
entry->index = devEntry->index;
INSERT_OBJECT_INT(entry, &network_tbl);
return (entry);
}
static void
network_entry_delete(struct network_entry* entry)
{
TAILQ_REMOVE(&network_tbl, entry, link);
free(entry);
}
static void
network_get_interfaces(void)
{
struct device_entry *dev;
struct network_entry *net;
struct mibif *ifp;
int name[6];
size_t len;
char *dname;
name[0] = CTL_NET;
name[1] = PF_LINK;
name[2] = NETLINK_GENERIC;
name[3] = IFMIB_IFDATA;
name[5] = IFDATA_DRIVERNAME;
for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) {
HRDBG("%s %s", ifp->name, ifp->descr);
name[4] = ifp->sysindex;
len = 0;
if (sysctl(name, 6, NULL, &len, 0, 0) < 0) {
syslog(LOG_ERR, "sysctl(net.link.ifdata.%d."
"drivername): %m", ifp->sysindex);
continue;
}
if ((dname = malloc(len)) == NULL) {
syslog(LOG_ERR, "malloc: %m");
continue;
}
if (sysctl(name, 6, dname, &len, 0, 0) < 0) {
syslog(LOG_ERR, "sysctl(net.link.ifdata.%d."
"drivername): %m", ifp->sysindex);
free(dname);
continue;
}
HRDBG("got device %s (%s)", ifp->name, dname);
if ((dev = device_find_by_name(dname)) == NULL) {
HRDBG("%s not in hrDeviceTable", dname);
free(dname);
continue;
}
HRDBG("%s found in hrDeviceTable", dname);
dev->type = &OIDX_hrDeviceNetwork_c;
dev->flags |= HR_DEVICE_IMMUTABLE;
free(dname);
TAILQ_FOREACH(net, &network_tbl, link)
if (net->index == dev->index)
break;
if (net == NULL && (net = network_entry_create(dev)) == NULL)
continue;
net->flags |= HR_NETWORK_FOUND;
net->ifIndex = ifp->index;
}
network_tick = this_tick;
}
void
fini_network_tbl(void)
{
struct network_entry *n1;
while ((n1 = TAILQ_FIRST(&network_tbl)) != NULL) {
TAILQ_REMOVE(&network_tbl, n1, link);
free(n1);
}
}
void
start_network_tbl(void)
{
mib_refresh_iflist();
network_get_interfaces();
}
static void
refresh_network_tbl(void)
{
struct network_entry *entry, *entry_tmp;
if (this_tick - network_tick < network_tbl_refresh) {
HRDBG("no refresh needed");
return;
}
TAILQ_FOREACH(entry, &network_tbl, link)
entry->flags &= ~HR_NETWORK_FOUND;
network_get_interfaces();
TAILQ_FOREACH_SAFE(entry, &network_tbl, link, entry_tmp) {
if (!(entry->flags & HR_NETWORK_FOUND))
network_entry_delete(entry);
}
HRDBG("refresh DONE");
}
int
op_hrNetworkTable(struct snmp_context *ctx __unused, struct snmp_value *value,
u_int sub, u_int iidx __unused, enum snmp_op curr_op)
{
struct network_entry *entry;
refresh_network_tbl();
switch (curr_op) {
case SNMP_OP_GETNEXT:
if ((entry = NEXT_OBJECT_INT(&network_tbl,
&value->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
value->var.len = sub + 1;
value->var.subs[sub] = entry->index;
goto get;
case SNMP_OP_GET:
if ((entry = FIND_OBJECT_INT(&network_tbl,
&value->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
goto get;
case SNMP_OP_SET:
if ((entry = FIND_OBJECT_INT(&network_tbl,
&value->var, sub)) == NULL)
return (SNMP_ERR_NO_CREATION);
return (SNMP_ERR_NOT_WRITEABLE);
case SNMP_OP_ROLLBACK:
case SNMP_OP_COMMIT:
abort();
}
abort();
get:
switch (value->var.subs[sub - 1]) {
case LEAF_hrNetworkIfIndex:
value->v.integer = entry->ifIndex;
return (SNMP_ERR_NOERROR);
}
abort();
}