hostres_network_tbl.c revision 154133
121308Sache/*-
221308Sache * Copyright (c) 2005-2006 The FreeBSD Project
321308Sache * All rights reserved.
421308Sache *
521308Sache * Author: Victor Cruceru <soc-victor@freebsd.org>
621308Sache *
721308Sache * Redistribution of this software and documentation and use in source and
821308Sache * binary forms, with or without modification, are permitted provided that
921308Sache * the following conditions are met:
1058310Sache *
1121308Sache * 1. Redistributions of source code or documentation must retain the above
1221308Sache *    copyright notice, this list of conditions and the following disclaimer.
1321308Sache * 2. Redistributions in binary form must reproduce the above copyright
1421308Sache *    notice, this list of conditions and the following disclaimer in the
1521308Sache *    documentation and/or other materials provided with the distribution.
1621308Sache *
1721308Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1821308Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1921308Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2021308Sache * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2158310Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2221308Sache * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2321308Sache * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2421308Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2521308Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2621308Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2721308Sache * SUCH DAMAGE.
2826497Sache *
2926497Sache * $FreeBSD: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c 154133 2006-01-09 12:33:45Z harti $
3058310Sache */
3158310Sache
3221308Sache/*
3321308Sache * Host Resources MIB implementation for SNMPd: instrumentation for
3421308Sache * hrNetworkTable
3521308Sache */
3621308Sache
3721308Sache#include <sys/types.h>
3821308Sache#include <sys/ioctl.h>
3921308Sache#include <sys/socket.h>
4021308Sache#include <sys/sysctl.h>
4121308Sache
4221308Sache#include <net/if.h>
4321308Sache#include <net/if_mib.h>
4421308Sache
4521308Sache#include <assert.h>
4621308Sache#include <ctype.h>
4721308Sache#include <err.h>
4821308Sache#include <errno.h>
4958310Sache#include <ifaddrs.h>
5058310Sache#include <stdarg.h>
5158310Sache#include <stdlib.h>
5221308Sache#include <string.h>
5326497Sache#include <syslog.h>
5421308Sache#include <unistd.h>
5521308Sache
5621308Sache#include "hostres_snmp.h"
5721308Sache#include "hostres_oid.h"
5821308Sache#include "hostres_tree.h"
5921308Sache
6021308Sache#include <bsnmp/snmp_mibII.h>
6121308Sache
6221308Sache/*
6321308Sache * This structure is used to hold a SNMP table entry
6421308Sache * for HOST-RESOURCES-MIB's hrNetworkTable
6521308Sache */
6621308Sachestruct network_entry {
6721308Sache	int32_t		index;
6821308Sache	int32_t		ifIndex;
6921308Sache	TAILQ_ENTRY(network_entry) link;
7035486Sache#define HR_NETWORK_FOUND		0x001
7121308Sache	uint32_t	flags;
7221308Sache
7321308Sache};
74119610SacheTAILQ_HEAD(network_tbl, network_entry);
75119610Sache
7626497Sache/* the head of the list with hrNetworkTable's entries */
7721308Sachestatic struct network_tbl network_tbl = TAILQ_HEAD_INITIALIZER(network_tbl);
78136644Sache
79136644Sache/* last (agent) tick when hrNetworkTable was updated */
80136644Sachestatic uint64_t network_tick;
81136644Sache
82136644Sache/* maximum number of ticks between updates of network table */
83136644Sacheuint32_t network_tbl_refresh = HR_NETWORK_TBL_REFRESH * 100;
84136644Sache
85136644Sache/* Constants */
86136644Sachestatic const struct asn_oid OIDX_hrDeviceNetwork_c = OIDX_hrDeviceNetwork;
87136644Sache
88136644Sache/**
89136644Sache * Create a new entry into the network table
90136644Sache */
91136644Sachestatic struct network_entry *
92136644Sachenetwork_entry_create(const struct device_entry *devEntry)
93136644Sache{
94136644Sache	struct network_entry *entry;
9521308Sache
9621308Sache	assert(devEntry != NULL);
9721308Sache	if (devEntry == NULL)
9821308Sache		return (NULL);
9921308Sache
10021308Sache	if ((entry = malloc(sizeof(*entry))) == NULL) {
10126497Sache		syslog(LOG_WARNING, "%s: %m", __func__);
10226497Sache		return (NULL);
10326497Sache	}
104136644Sache
10526497Sache	memset(entry, 0, sizeof(*entry));
10626497Sache	entry->index = devEntry->index;
107136644Sache	INSERT_OBJECT_INT(entry, &network_tbl);
108136644Sache
109136644Sache	return (entry);
110136644Sache}
111136644Sache
112136644Sache/**
113136644Sache * Delete an entry in the network table
114136644Sache */
115136644Sachestatic void
116136644Sachenetwork_entry_delete(struct network_entry* entry)
117136644Sache{
118136644Sache
11926497Sache	TAILQ_REMOVE(&network_tbl, entry, link);
12026497Sache	free(entry);
12126497Sache}
12226497Sache
12326497Sache/**
12426497Sache * Fetch the interfaces from the mibII module, get their real name from the
12526497Sache * kernel and try to find it in the device table.
12626497Sache */
12726497Sachestatic void
12826497Sachenetwork_get_interfaces(void)
12926497Sache{
13021308Sache	struct device_entry *dev;
13121308Sache	struct network_entry *net;
13221308Sache	struct mibif *ifp;
13326497Sache	int name[6];
13426497Sache	size_t len;
13526497Sache	char *dname;
136136644Sache
137136644Sache	name[0] = CTL_NET;
13821308Sache	name[1] = PF_LINK;
13921308Sache	name[2] = NETLINK_GENERIC;
14021308Sache	name[3] = IFMIB_IFDATA;
14121308Sache	name[5] = IFDATA_DRIVERNAME;
14221308Sache
14321308Sache	for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) {
14421308Sache		HRDBG("%s %s", ifp->name, ifp->descr);
14521308Sache
14621308Sache		name[4] = ifp->sysindex;
14721308Sache
14821308Sache		/* get the original name */
14921308Sache		len = 0;
15026497Sache		if (sysctl(name, 6, NULL, &len, 0, 0) < 0) {
15126497Sache			syslog(LOG_ERR, "sysctl(net.link.ifdata.%d."
15221308Sache			    "drivername): %m", ifp->sysindex);
15321308Sache			continue;
15426497Sache		}
15521308Sache		if ((dname = malloc(len)) == NULL) {
15621308Sache			syslog(LOG_ERR, "malloc: %m");
15721308Sache			continue;
15821308Sache		}
15921308Sache		if (sysctl(name, 6, dname, &len, 0, 0) < 0) {
16021308Sache			syslog(LOG_ERR, "sysctl(net.link.ifdata.%d."
16121308Sache			    "drivername): %m", ifp->sysindex);
16221308Sache			free(dname);
16321308Sache			continue;
16421308Sache		}
16521308Sache
16621308Sache		HRDBG("got device %s (%s)", ifp->name, dname);
16721308Sache
16821308Sache		if ((dev = device_find_by_name(dname)) == NULL) {
16921308Sache			HRDBG("%s not in hrDeviceTable", dname);
17021308Sache			free(dname);
171119610Sache			continue;
17221308Sache		}
17321308Sache		HRDBG("%s found in hrDeviceTable", dname);
174119610Sache
17521308Sache		dev->type = OIDX_hrDeviceNetwork_c;
17621308Sache		dev->flags |= HR_DEVICE_IMMUTABLE;
17721308Sache
17821308Sache		free(dname);
17921308Sache
18021308Sache		/* Then check hrNetworkTable for this device */
18121308Sache		TAILQ_FOREACH(net, &network_tbl, link)
18221308Sache			if (net->index == dev->index)
18321308Sache				break;
18421308Sache
18521308Sache		if (net == NULL && (net = network_entry_create(dev)) == NULL)
18621308Sache			continue;
18721308Sache
18821308Sache		net->flags |= HR_NETWORK_FOUND;
18921308Sache		net->ifIndex = ifp->index;
19021308Sache	}
19121308Sache
192119610Sache	network_tick = this_tick;
193119610Sache}
194119610Sache
19521308Sache/**
19621308Sache * Finalization routine for hrNetworkTable.
19721308Sache * It destroys the lists and frees any allocated heap memory.
19821308Sache */
19921308Sachevoid
20021308Sachefini_network_tbl(void)
20121308Sache{
20221308Sache	struct network_entry *n1;
20321308Sache
20421308Sache	while ((n1 = TAILQ_FIRST(&network_tbl)) != NULL) {
20521308Sache		TAILQ_REMOVE(&network_tbl, n1, link);
20621308Sache		free(n1);
20721308Sache	}
20821308Sache}
20921308Sache
21021308Sache/**
21121308Sache * Get the interface list from mibII only at this point to be sure that
21221308Sache * it is there already.
21321308Sache */
21421308Sachevoid
21521308Sachestart_network_tbl(void)
21621308Sache{
21721308Sache
21821308Sache	mib_refresh_iflist();
21921308Sache	network_get_interfaces();
22021308Sache}
22121308Sache
22221308Sache/**
22321308Sache * Refresh the table.
22421308Sache */
22521308Sachestatic void
22621308Sacherefresh_network_tbl(void)
22721308Sache{
22821308Sache	struct network_entry *entry, *entry_tmp;
22921308Sache
23021308Sache	if (this_tick - network_tick < network_tbl_refresh) {
23121308Sache		HRDBG("no refresh needed");
23221308Sache		return;
23321308Sache	}
23421308Sache
23521308Sache	/* mark each entry as missing */
23621308Sache	TAILQ_FOREACH(entry, &network_tbl, link)
23721308Sache		entry->flags &= ~HR_NETWORK_FOUND;
23821308Sache
23921308Sache	network_get_interfaces();
24021308Sache
24121308Sache	/*
24221308Sache	 * Purge items that disappeared
24321308Sache	 */
24421308Sache	TAILQ_FOREACH_SAFE(entry, &network_tbl, link, entry_tmp) {
24521308Sache		if (!(entry->flags & HR_NETWORK_FOUND))
24621308Sache			network_entry_delete(entry);
24721308Sache	}
24821308Sache
24921308Sache	HRDBG("refresh DONE");
25021308Sache}
25121308Sache
25226497Sache/*
253 * This is the implementation for a generated (by our SNMP tool)
254 * function prototype, see hostres_tree.h
255 * It handles the SNMP operations for hrNetworkTable
256 */
257int
258op_hrNetworkTable(struct snmp_context *ctx __unused, struct snmp_value *value,
259    u_int sub, u_int iidx __unused, enum snmp_op curr_op)
260{
261	struct network_entry *entry;
262
263	refresh_network_tbl();
264
265	switch (curr_op) {
266
267	case SNMP_OP_GETNEXT:
268		if ((entry = NEXT_OBJECT_INT(&network_tbl,
269		    &value->var, sub)) == NULL)
270			return (SNMP_ERR_NOSUCHNAME);
271		value->var.len = sub + 1;
272		value->var.subs[sub] = entry->index;
273		goto get;
274
275	case SNMP_OP_GET:
276		if ((entry = FIND_OBJECT_INT(&network_tbl,
277		    &value->var, sub)) == NULL)
278			return (SNMP_ERR_NOSUCHNAME);
279		goto get;
280
281	case SNMP_OP_SET:
282		if ((entry = FIND_OBJECT_INT(&network_tbl,
283		    &value->var, sub)) == NULL)
284			return (SNMP_ERR_NO_CREATION);
285		return (SNMP_ERR_NOT_WRITEABLE);
286
287	case SNMP_OP_ROLLBACK:
288	case SNMP_OP_COMMIT:
289		abort();
290	}
291	abort();
292
293  get:
294	switch (value->var.subs[sub - 1]) {
295
296	case LEAF_hrNetworkIfIndex:
297		value->v.integer = entry->ifIndex;
298		return (SNMP_ERR_NOERROR);
299
300	}
301	abort();
302}
303